package com.cyksj.service.coupon.impl; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.cyksj.common.constant.Constant; import com.cyksj.common.exception.BusinessRuntimeException; import com.cyksj.common.task.GlobalThreadPoolTaskExecutor; import com.cyksj.common.util.Jsons; import com.cyksj.enums.GatewayApiCode; import com.cyksj.mapper.OrderDonMapper; import com.cyksj.mapper.UserMapper; import com.cyksj.mapper.channel.ChannelPopularizeMapper; import com.cyksj.mapper.manage.coupon.*; import com.cyksj.model.entity.*; import com.cyksj.model.request.ChannelPopularizeCouponReq; import com.cyksj.model.request.CouponCollectReq; import com.cyksj.model.views.*; import com.cyksj.service.coupon.CouponFontService; import com.ejlchina.searcher.BeanSearcher; import com.ejlchina.searcher.param.Operator; import com.ejlchina.searcher.util.MapUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.*; /* *项目名: netflix *文件名: CouponFontServiceImpl *创建者: JavaZou *创建时间:2022/11/18 13:34 */ @Service @RequiredArgsConstructor @Slf4j public class CouponFontServiceImpl implements CouponFontService { private final CouponMapper couponMapper; private final CouponActiveMapper couponActiveMapper; private final CouponUserMapper couponUserMapper; private final BeanSearcher beanSearcher; private final CouponSkuMapper couponSkuMapper; private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance(); private final CouponRecommendMapper recommendMapper; private final ChannelPopularizeMapper channelPopularizeMapper; private final UserMapper userMapper; private final OrderDonMapper orderDonMapper; @Override @Transactional(rollbackFor = Throwable.class) public void collectCoupon(CouponCollectReq couponCollectReq, Long userId) { collectCommonCheck(couponCollectReq.getCouponId(), userId); CouponActive couponActive = couponActiveMapper.selectById(couponCollectReq.getCouponActiveId()); if (couponActive == null) { throw BusinessRuntimeException.getInstance("该发放优惠券不存在"); } if (couponActive.getRemainNum() <= 0) { throw BusinessRuntimeException.getInstance("该优惠券已领取完"); } receiveCouponUser(couponCollectReq.getCouponId(), userId, CouponUser.Channel.center); if (couponActive != null) { //发放优惠券记录剩余数量 Integer remainNum = couponActive.getRemainNum(); Integer update = couponActiveMapper.subRemainNum(couponActive.getId(), remainNum); if (update != 1) { throw BusinessRuntimeException.getInstance("系统繁忙,请稍后再试.."); } } } @Override @Transactional(rollbackFor = Throwable.class) public void collectRecommendCoupon(CouponCollectReq couponCollectReq, Long userId) { collectCommonCheck(couponCollectReq.getCouponId(), userId); CouponRecommend couponRecommend = recommendMapper.selectOne(Wrappers.lambdaQuery(CouponRecommend.class).eq(CouponRecommend::getCouponId, couponCollectReq.getCouponId()).last("limit 1")); if (couponRecommend == null || !couponRecommend.getDeleted() || !couponRecommend.getStatus()) { return; } Date upTime = couponRecommend.getUpTime(); Date downTime = couponRecommend.getDownTime(); if (upTime != null && upTime.after(DateTime.now())) { return; } if (downTime != null && downTime.before(DateTime.now())) { return; } receiveCouponUser(couponCollectReq.getCouponId(), userId, CouponUser.Channel.recommend); } @Override @Transactional(rollbackFor = Throwable.class) public void collectWholeRecommendCoupon(Long userId) { CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build()); Boolean isNewUser = true; if (couponNewUserView != null) { isNewUser = false; } //查询所有可领取的推荐优惠券 List couponAvailableRecommendViews = recommendMapper.getRecommendCouponList(userId, isNewUser); couponAvailableRecommendViews.stream().forEach(data -> { //领取优惠券 CouponUser couponUser = new CouponUser(); couponUser.setUserId(userId); couponUser.setCouponId(data.getCouponId()); //推荐渠道 couponUser.setChannel(CouponUser.Channel.recommend); couponUser.setStatus(CouponUser.Status.unused); couponUser.setRecommendId(data.getRecommendId()); //优惠券有效时间 this.updateCouponUserValidTime(data.getCouponId(), couponUser); try { couponUserMapper.insert(couponUser); } catch (DuplicateKeyException e) { //插入失败继续插入 不影响 log.info("用户:{}一键领取推荐优惠券{}插入数据错误", userId, data.getCouponId()); } }); } @Override public Page getAvailableCouponList(Long skuId, Long couponId, Long userId, Boolean isRenew, Long start, Long limit) { DateTime now = DateTime.now(); Long goodsId = null; //实物还是虚物 GoodsDonSkuView goodsDonSkuView = beanSearcher.searchFirst(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getSkuId, skuId).op(Operator.Equal).build()); if (goodsDonSkuView == null) { return new Page<>(); } BigDecimal skuMoney = goodsDonSkuView.getMoney(); if (goodsDonSkuView.getType() == 2) { goodsId = goodsDonSkuView.getGoodsId(); } Page availableCouponList = couponUserMapper.getAvailableCouponList(skuId, skuMoney, couponId, goodsId, userId, isRenew, new Page<>(start, limit)); Optional.ofNullable(availableCouponList).ifPresent(page->{ page.getRecords().stream().forEach(data->{ String str = couponSkuMapper.selectGoodsSkuStr(data.getCouponId()); data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", "")); List couponSkus = couponSkuMapper.getGoodsSkuByCouponId(data.getCouponId()); try { data.setGoodsSkuIds(Jsons.toJson(couponSkus)); } catch (Exception e) { } if (!data.getIsAvailable()) { data.setReason(String.format("仅限%s", data.getGoodsSkuStr())); if (data.getUseScope() == Coupon.UseScope.renew) { data.setReason(String.format("%s续费时可使用", data.getReason())); } else if (data.getUseScope() == Coupon.UseScope.orders) { data.setReason(String.format("%s下单时可使用", data.getReason())); } } if (goodsDonSkuView.getType() == 2 && data.getType() == Coupon.Type.reduce) { if (data.getIsAvailable() && skuMoney.compareTo(data.getReduceCondition()) < 0) { data.setIsAvailable(false); data.setReason(String.format("满%s可用", data.getReduceCondition().divide(BigDecimal.valueOf(100)))); } } if (isRenew != null && isRenew && data.getUseScope() == Coupon.UseScope.orders) { data.setReason("仅下单可使用"); } //为了重新校验 data.setCouponUserStatus(CouponUser.Status.unused); this.setCouponUserStatus(data, now); }); }); if (goodsDonSkuView.getType() == 2) { availableCouponList.getRecords().sort(Comparator.comparing(CouponUserView::getIsAvailable).reversed()); } return availableCouponList; } /** * 设置用户优惠券状态 */ @Override public void setCouponUserStatus(CouponUserView data, DateTime now) { Boolean isFlag = false; if (data.getCouponUserStatus() == CouponUser.Status.unused) { if (data.getExpiryStatus() != null && !data.getExpiryStatus()) { data.setCouponUserStatus(CouponUser.Status.invalid); data.setIsAvailable(false); data.setReason("该优惠券已失效"); isFlag = true; } else if (now.isAfter(data.getValidEndTime())) { data.setCouponUserStatus(CouponUser.Status.expired); data.setIsAvailable(false); data.setReason("该优惠券已过期"); isFlag = true; } else if (now.isBefore(data.getValidStartTime())) { data.setCouponUserStatus(CouponUser.Status.notEffective); data.setIsAvailable(false); data.setReason("该优惠券未生效"); isFlag = true; } } if (isFlag) { TASK_EXECUTOR.execute(() -> { if (data.getCouponUserStatus() != CouponUser.Status.notEffective) { CouponUser couponUser = new CouponUser(); couponUser.setId(data.getId()); couponUser.setStatus(data.getCouponUserStatus()); couponUserMapper.updateById(couponUser); } }); } } @Override public Integer collectRecommendCouponNum(Long userId, Boolean isNewUser) { List recommendCouponList = Optional.ofNullable(recommendMapper.getRecommendCouponList(userId, isNewUser)).orElse(new ArrayList<>()); return recommendCouponList.size(); } public void collectCommonCheck(Long couponId, Long userId) { Coupon coupon = couponMapper.selectById(couponId); if (coupon == null || !coupon.getDeleted()) { throw BusinessRuntimeException.getInstance("该优惠券不存在"); } //是否可领取 if (coupon.getScope() == Coupon.Scope.newUser) { //注册后从没产生过虚拟订单的用户,产生过退款后也不是新用户 CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build()); if (couponNewUserView != null) { throw BusinessRuntimeException.getInstance("该优惠券只能新用户才能领取"); } } CouponUser couponUser = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponId)); if (couponUser != null) { if (couponUser.getChannel() == CouponUser.Channel.exCode) { throw BusinessRuntimeException.getInstance("您已兑换过该优惠券.."); } throw BusinessRuntimeException.getInstance("您已领取该优惠券.."); } } @Override public void updateCouponUserValidTime(Long couponId, CouponUser couponUser) { Coupon entity = couponMapper.getCouponById(couponId); Optional.ofNullable(entity).ifPresent(coupon -> { if (coupon.getIsValidTime()) { couponUser.setValidStartTime(coupon.getValidStartTime()); couponUser.setValidEndTime(coupon.getValidEndTime()); return; } couponUser.setValidStartTime(DateTime.now()); if (coupon.getValidHours() > 0) { couponUser.setValidEndTime(DateUtil.offsetHour(couponUser.getValidStartTime(), coupon.getValidHours())); return; } couponUser.setValidEndTime(DateUtil.offsetDay(couponUser.getValidStartTime(), coupon.getValidDays())); }); } @Override public void updateOrderDonCouponStatus(OrderDon orderDon) { log.info("订单:{}退款,优惠券返还start...", orderDon.getId()); CouponUser couponUser = couponUserMapper.selectById(orderDon.getCouponUserId()); if (CouponUser.Channel.exCode == couponUser.getChannel()) { //兑换码兑换的优惠券退款时,直接删除 couponUserMapper.deleteById(couponUser.getId()); } else { CouponUserView couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder() .field(CouponUserView::getCouponId, couponUser.getCouponId()).op(Operator.Equal) .field(CouponUserView::getUserId, orderDon.getUserId()).op(Operator.Equal) .build()); couponUserView.setCouponUserStatus(CouponUser.Status.unused); this.setCouponUserStatus(couponUserView, DateTime.now()); couponUser.setStatus(couponUserView.getCouponUserStatus()); couponUserMapper.updateById(couponUser); } log.info("订单:{}退款,优惠券返还end...", orderDon.getId()); } @Override public void receiveChannelPopularizeCoupon(ChannelPopularizeCouponReq req) { Long userId = req.getUserId(); Long popularizeId = req.getPopularizeId(); Long couponId = req.getCouponId(); ChannelPopularize channelPopularize = checkPopularizeChannelCoupon(userId, popularizeId); String couponIds = channelPopularize.getCouponIds(); List couponIdList = null; try { couponIdList = Jsons.parseList(couponIds, Long.class); if (!couponIdList.contains(couponId)) { throw BusinessRuntimeException.getInstance("链接未关联该优惠券"); } } catch (Exception e) { throw BusinessRuntimeException.getInstance("链接未关联该优惠券"); } //优惠券领取状态校验 collectCommonCheck(couponId, userId); //领取优惠券 receiveCouponUser(couponId, userId, CouponUser.Channel.channel_popularize); } @Override public void collectAllChannelCoupons(long userId, Long popularizeId) { ChannelPopularize channelPopularize = checkPopularizeChannelCoupon(userId, popularizeId); String couponIds = channelPopularize.getCouponIds(); List couponIdList = null; try { couponIdList = Jsons.parseList(couponIds, Long.class); } catch (Exception e) { throw BusinessRuntimeException.getInstance("链接未关联优惠券"); } //一键领取优惠券 couponIdList.forEach(couponId -> { try { receiveCouponUser(couponId, userId, CouponUser.Channel.channel_popularize); } catch (Exception e) { } }); } public ChannelPopularize checkPopularizeChannelCoupon(Long userId, Long popularizeId) { //是否是该渠道来源新用户 User user = userMapper.selectById(userId); if (user == null) { throw BusinessRuntimeException.getGatewayApiCode(GatewayApiCode.CMS_USER_NO_FIND); } if (!user.getPopularizeId().equals(popularizeId)) { throw BusinessRuntimeException.getInstance("渠道推广新用户才能领取该优惠券"); } //渠道链接是否存在 ChannelPopularize channelPopularize = channelPopularizeMapper.selectById(popularizeId); Assert.notNull(channelPopularize, "链接不存在"); //是否关联该优惠券 String couponIds = channelPopularize.getCouponIds(); if (StrUtil.isEmpty(couponIds)) { throw BusinessRuntimeException.getInstance("链接未关联优惠券"); } return channelPopularize; } /** * 领取优惠券 */ public void receiveCouponUser(Long couponId, Long userId, CouponUser.Channel channel) { CouponUser couponUser = new CouponUser(); couponUser.setUserId(userId); couponUser.setCouponId(couponId); couponUser.setChannel(channel); couponUser.setStatus(CouponUser.Status.unused); //优惠券有效时间 this.updateCouponUserValidTime(couponId, couponUser); try { couponUserMapper.insert(couponUser); } catch (DuplicateKeyException e) { log.info("用户:{}重复领取优惠券{}插入数据错误", userId, couponId); } } @Override public CouponUserView getNewUserWelfareCoupons(long userId,Long couponId) { return couponUserMapper.getNewUserWelfareCouponsByUserId(userId, couponId); } @Override public CouponUserView getCouponDetailById(long userId, Long couponId) { CouponUserView couponUserView = couponUserMapper.getCouponDetailById(userId, couponId); if (couponUserView != null) { String str = couponSkuMapper.selectGoodsSkuStr(couponUserView.getCouponId()); couponUserView.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", "")); } return couponUserView; } @Override public CouponUserView receiveCoupon(long userId, Long couponId, CouponUser.Channel channel) { try { receiveCouponUser(couponId, userId, channel); } catch (Exception e) { return null; } return getCouponDetailById(userId, couponId); } @Override public List getGoodsUserActivityCoupons(long userId) throws Exception { UserActivityDistributeDetailView goodsActivity = getGoodsActivity(); if (goodsActivity == null) { return null; } String couponIds = goodsActivity.getCouponIds(); List couponIdList = Jsons.parseList(couponIds, Long.class); //是否领取过 Integer selectCount = couponUserMapper.selectCount(Wrappers.lambdaQuery(CouponUser.class).in(CouponUser::getCouponId, couponIdList).eq(CouponUser::getUserId, userId)); if (selectCount == couponIdList.size()) { return null; } //未领取 //是否购买过 List skuIdList = couponMapper.getCouponSkuIds(couponIdList); Integer orderCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getUserId, userId).notIn(OrderDon::getStatus, Constant.noOrderAllStatus).in(OrderDon::getSkuId, skuIdList)); if (orderCount == 0) { return null; } List couponViews = beanSearcher.searchAll(CouponView.class, MapUtils.builder().field(CouponView::getCouponId, couponIdList).op(Operator.InList).orderBy(CouponView::getCouponId).build()); return couponViews; } @Override public void collectAllGoodsActivityCoupons(long userId) throws Exception { UserActivityDistributeDetailView goodsActivity = getGoodsActivity(); if (goodsActivity != null) { String couponIds = goodsActivity.getCouponIds(); List couponIdList = Jsons.parseList(couponIds, Long.class); //是否领取过 Integer selectCount = couponUserMapper.selectCount(Wrappers.lambdaQuery(CouponUser.class).in(CouponUser::getCouponId, couponIdList).eq(CouponUser::getUserId, userId)); if (selectCount != couponIdList.size()) { //未领取 //是否购买过 List skuIdList = couponMapper.getCouponSkuIds(couponIdList); Integer orderCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getUserId, userId).notIn(OrderDon::getStatus, Constant.noOrderAllStatus).in(OrderDon::getSkuId, skuIdList)); if (orderCount != 0) { //一键领取优惠券 couponIdList.forEach(couponId -> { try { receiveCouponUser(couponId, userId, CouponUser.Channel.ptfq); } catch (Exception e) { } }); } } } } public UserActivityDistributeDetailView getGoodsActivity() { String dateStr = DateTime.now().toString(); String time = String.format(" and (ua.start_time is null or (ua.start_time < '%s' and ua.end_time > '%s'))", dateStr, dateStr); UserActivityDistributeDetailView goodsActivity = beanSearcher.searchFirst(UserActivityDistributeDetailView.class, MapUtils.builder() .field(UserActivityDistributeDetailView::getType, 3) .put("time", time) .build()); return goodsActivity; } }