CouponFontServiceImpl.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. package com.cyksj.service.coupon.impl;
  2. import cn.hutool.core.date.DateTime;
  3. import cn.hutool.core.date.DateUtil;
  4. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  5. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6. import com.cyksj.common.exception.BusinessRuntimeException;
  7. import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
  8. import com.cyksj.common.util.Jsons;
  9. import com.cyksj.mapper.manage.coupon.*;
  10. import com.cyksj.model.entity.Coupon;
  11. import com.cyksj.model.entity.CouponActive;
  12. import com.cyksj.model.entity.CouponUser;
  13. import com.cyksj.model.entity.OrderDon;
  14. import com.cyksj.model.request.CouponCollectReq;
  15. import com.cyksj.model.views.*;
  16. import com.cyksj.service.coupon.CouponFontService;
  17. import com.ejlchina.searcher.BeanSearcher;
  18. import com.ejlchina.searcher.param.Operator;
  19. import com.ejlchina.searcher.util.MapUtils;
  20. import lombok.RequiredArgsConstructor;
  21. import lombok.extern.slf4j.Slf4j;
  22. import org.springframework.dao.DuplicateKeyException;
  23. import org.springframework.stereotype.Service;
  24. import org.springframework.transaction.annotation.Transactional;
  25. import java.math.BigDecimal;
  26. import java.util.ArrayList;
  27. import java.util.Comparator;
  28. import java.util.List;
  29. import java.util.Optional;
  30. /*
  31. *项目名: netflix
  32. *文件名: CouponFontServiceImpl
  33. *创建者: JavaZou
  34. *创建时间:2022/11/18 13:34
  35. */
  36. @Service
  37. @RequiredArgsConstructor
  38. @Slf4j
  39. public class CouponFontServiceImpl implements CouponFontService {
  40. private final CouponMapper couponMapper;
  41. private final CouponActiveMapper couponActiveMapper;
  42. private final CouponUserMapper couponUserMapper;
  43. private final BeanSearcher beanSearcher;
  44. private final CouponSkuMapper couponSkuMapper;
  45. private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
  46. private final CouponRecommendMapper recommendMapper;
  47. @Override
  48. @Transactional(rollbackFor = Throwable.class)
  49. public void collectCoupon(CouponCollectReq couponCollectReq, Long userId) {
  50. collectCommonCheck(couponCollectReq, userId);
  51. CouponActive couponActive = couponActiveMapper.selectById(couponCollectReq.getCouponActiveId());
  52. if (couponActive == null) {
  53. throw BusinessRuntimeException.getInstance("该发放优惠券不存在");
  54. }
  55. if (couponActive.getRemainNum() <= 0) {
  56. throw BusinessRuntimeException.getInstance("该优惠券已领取完");
  57. }
  58. CouponUser couponUser = new CouponUser();
  59. couponUser.setUserId(userId);
  60. couponUser.setCouponId(couponCollectReq.getCouponId());
  61. //领券中心
  62. couponUser.setChannel(CouponUser.Channel.center);
  63. couponUser.setStatus(CouponUser.Status.unused);
  64. couponUser.setCouponActiveId(couponCollectReq.getCouponActiveId());
  65. //优惠券有效时间
  66. this.updateCouponUserValidTime(couponActive.getCouponId(), couponUser);
  67. try {
  68. couponUserMapper.insert(couponUser);
  69. } catch (DuplicateKeyException e) {
  70. log.info("用户:{}重复领取优惠券{}插入数据错误", userId, couponCollectReq.getCouponId());
  71. throw BusinessRuntimeException.getInstance("已领取成功");
  72. }
  73. if (couponActive != null) {
  74. //发放优惠券记录剩余数量
  75. Integer remainNum = couponActive.getRemainNum();
  76. Integer update = couponActiveMapper.subRemainNum(couponActive.getId(), remainNum);
  77. if (update != 1) {
  78. throw BusinessRuntimeException.getInstance("系统繁忙,请稍后再试..");
  79. }
  80. }
  81. }
  82. @Override
  83. @Transactional(rollbackFor = Throwable.class)
  84. public void collectRecommendCoupon(CouponCollectReq couponCollectReq, Long userId) {
  85. collectCommonCheck(couponCollectReq, userId);
  86. CouponUser couponUser = new CouponUser();
  87. couponUser.setUserId(userId);
  88. couponUser.setCouponId(couponCollectReq.getCouponId());
  89. //推荐渠道
  90. couponUser.setChannel(CouponUser.Channel.recommend);
  91. couponUser.setStatus(CouponUser.Status.unused);
  92. couponUser.setRecommendId(couponCollectReq.getCouponRecommendId());
  93. //优惠券有效时间
  94. this.updateCouponUserValidTime(couponCollectReq.getCouponId(), couponUser);
  95. try {
  96. couponUserMapper.insert(couponUser);
  97. } catch (DuplicateKeyException e) {
  98. log.info("用户:{}重复领取推荐优惠券{}插入数据错误", userId, couponCollectReq.getCouponId());
  99. throw BusinessRuntimeException.getInstance("已领取成功");
  100. }
  101. }
  102. @Override
  103. @Transactional(rollbackFor = Throwable.class)
  104. public void collectWholeRecommendCoupon(Long userId) {
  105. CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
  106. Boolean isNewUser = true;
  107. if (couponNewUserView != null) {
  108. isNewUser = false;
  109. }
  110. //查询所有可领取的推荐优惠券
  111. List<CouponAvailableRecommendView> couponAvailableRecommendViews = recommendMapper.getRecommendCouponList(userId, isNewUser);
  112. couponAvailableRecommendViews.stream().forEach(data -> {
  113. //领取优惠券
  114. CouponUser couponUser = new CouponUser();
  115. couponUser.setUserId(userId);
  116. couponUser.setCouponId(data.getCouponId());
  117. //推荐渠道
  118. couponUser.setChannel(CouponUser.Channel.recommend);
  119. couponUser.setStatus(CouponUser.Status.unused);
  120. couponUser.setRecommendId(data.getRecommendId());
  121. //优惠券有效时间
  122. this.updateCouponUserValidTime(data.getCouponId(), couponUser);
  123. try {
  124. couponUserMapper.insert(couponUser);
  125. } catch (DuplicateKeyException e) {
  126. //插入失败继续插入 不影响
  127. log.info("用户:{}一键领取推荐优惠券{}插入数据错误", userId, data.getCouponId());
  128. }
  129. });
  130. }
  131. @Override
  132. public Page<CouponUserView> getAvailableCouponList(Long skuId, Long couponId, Long userId, Boolean isRenew, Long start, Long limit) {
  133. DateTime now = DateTime.now();
  134. Long goodsId = null;
  135. //实物还是虚物
  136. GoodsDonSkuView goodsDonSkuView = beanSearcher.searchFirst(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getSkuId, skuId).op(Operator.Equal).build());
  137. BigDecimal skuMoney = goodsDonSkuView.getMoney();
  138. if (goodsDonSkuView.getType() == 2) {
  139. goodsId = goodsDonSkuView.getGoodsId();
  140. }
  141. Page<CouponUserView> availableCouponList = couponUserMapper.getAvailableCouponList(skuId, couponId, goodsId, userId, isRenew, new Page<>(start, limit));
  142. Optional.ofNullable(availableCouponList).ifPresent(page->{
  143. page.getRecords().stream().forEach(data->{
  144. String str = couponSkuMapper.selectGoodsSkuStr(data.getCouponId());
  145. data.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
  146. List<CouponSkuView> couponSkus = couponSkuMapper.getGoodsSkuByCouponId(data.getCouponId());
  147. try {
  148. data.setGoodsSkuIds(Jsons.toJson(couponSkus));
  149. } catch (Exception e) {
  150. }
  151. if (!data.getIsAvailable()) {
  152. data.setReason(String.format("仅限%s", data.getGoodsSkuStr()));
  153. if (data.getUseScope() == Coupon.UseScope.renew) {
  154. data.setReason(String.format("%s续费时可使用", data.getReason()));
  155. } else if (data.getUseScope() == Coupon.UseScope.orders) {
  156. data.setReason(String.format("%s下单时可使用", data.getReason()));
  157. }
  158. }
  159. if (goodsDonSkuView.getType() == 2 && data.getType() == Coupon.Type.reduce) {
  160. if (data.getIsAvailable() && skuMoney.compareTo(data.getReduceCondition()) < 0) {
  161. data.setIsAvailable(false);
  162. data.setReason(String.format("满%s可用", data.getReduceCondition().divide(BigDecimal.valueOf(100))));
  163. }
  164. }
  165. if (isRenew != null && isRenew && data.getUseScope() == Coupon.UseScope.orders) {
  166. data.setReason("仅下单可使用");
  167. }
  168. //为了重新校验
  169. data.setCouponUserStatus(CouponUser.Status.unused);
  170. this.setCouponUserStatus(data, now);
  171. });
  172. });
  173. if (goodsDonSkuView.getType() == 2) {
  174. availableCouponList.getRecords().sort(Comparator.comparing(CouponUserView::getIsAvailable).reversed());
  175. }
  176. return availableCouponList;
  177. }
  178. /**
  179. * 设置用户优惠券状态
  180. */
  181. @Override
  182. public void setCouponUserStatus(CouponUserView data, DateTime now) {
  183. Boolean isFlag = false;
  184. if (data.getCouponUserStatus() == CouponUser.Status.unused) {
  185. if (data.getExpiryStatus() != null && !data.getExpiryStatus()) {
  186. data.setCouponUserStatus(CouponUser.Status.invalid);
  187. data.setIsAvailable(false);
  188. data.setReason("该优惠券已失效");
  189. isFlag = true;
  190. } else if (now.isAfter(data.getValidEndTime())) {
  191. data.setCouponUserStatus(CouponUser.Status.expired);
  192. data.setIsAvailable(false);
  193. data.setReason("该优惠券已过期");
  194. isFlag = true;
  195. } else if (now.isBefore(data.getValidStartTime())) {
  196. data.setCouponUserStatus(CouponUser.Status.notEffective);
  197. data.setIsAvailable(false);
  198. data.setReason("该优惠券未生效");
  199. isFlag = true;
  200. }
  201. }
  202. if (isFlag) {
  203. TASK_EXECUTOR.execute(() -> {
  204. if (data.getCouponUserStatus() != CouponUser.Status.notEffective) {
  205. CouponUser couponUser = new CouponUser();
  206. couponUser.setId(data.getId());
  207. couponUser.setStatus(data.getCouponUserStatus());
  208. couponUserMapper.updateById(couponUser);
  209. }
  210. });
  211. }
  212. }
  213. @Override
  214. public Integer collectRecommendCouponNum(Long userId, Boolean isNewUser) {
  215. List<CouponAvailableRecommendView> recommendCouponList = Optional.ofNullable(recommendMapper.getRecommendCouponList(userId, isNewUser)).orElse(new ArrayList<>());
  216. return recommendCouponList.size();
  217. }
  218. public void collectCommonCheck(CouponCollectReq couponCollectReq, Long userId) {
  219. Coupon coupon = couponMapper.selectById(couponCollectReq.getCouponId());
  220. if (coupon == null || !coupon.getDeleted()) {
  221. throw BusinessRuntimeException.getInstance("该优惠券不存在");
  222. }
  223. //是否可领取
  224. if (coupon.getScope() == Coupon.Scope.newUser) {
  225. //注册后从没产生过虚拟订单的用户,产生过退款后也不是新用户
  226. CouponNewUserView couponNewUserView = beanSearcher.searchFirst(CouponNewUserView.class, MapUtils.builder().field(CouponNewUserView::getUserId, userId).op(Operator.Equal).build());
  227. if (couponNewUserView != null) {
  228. throw BusinessRuntimeException.getInstance("该优惠券只能新用户才能领取");
  229. }
  230. }
  231. CouponUser couponUser = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponCollectReq.getCouponId()));
  232. if (couponUser != null) {
  233. if (couponUser.getChannel() == CouponUser.Channel.exCode) {
  234. throw BusinessRuntimeException.getInstance("您已兑换过该优惠券..");
  235. }
  236. throw BusinessRuntimeException.getInstance("您已领取该优惠券..");
  237. }
  238. }
  239. @Override
  240. public void updateCouponUserValidTime(Long couponId, CouponUser couponUser) {
  241. Coupon entity = couponMapper.getCouponById(couponId);
  242. Optional.ofNullable(entity).ifPresent(coupon -> {
  243. if (coupon.getIsValidTime()) {
  244. couponUser.setValidStartTime(coupon.getValidStartTime());
  245. couponUser.setValidEndTime(coupon.getValidEndTime());
  246. return;
  247. }
  248. couponUser.setValidStartTime(DateTime.now());
  249. if (coupon.getValidHours() > 0) {
  250. couponUser.setValidEndTime(DateUtil.offsetDay(couponUser.getValidStartTime(), coupon.getValidHours()));
  251. return;
  252. }
  253. couponUser.setValidEndTime(DateUtil.offsetDay(couponUser.getValidStartTime(), coupon.getValidDays()));
  254. });
  255. }
  256. @Override
  257. public void updateOrderDonCouponStatus(OrderDon orderDon) {
  258. log.info("订单:{}退款,优惠券返还start...", orderDon.getId());
  259. CouponUser couponUser = couponUserMapper.selectById(orderDon.getCouponUserId());
  260. if (CouponUser.Channel.exCode == couponUser.getChannel()) {
  261. //兑换码兑换的优惠券退款时,直接删除
  262. couponUserMapper.deleteById(couponUser.getId());
  263. } else {
  264. CouponUserView couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
  265. .field(CouponUserView::getCouponId, couponUser.getCouponId()).op(Operator.Equal)
  266. .field(CouponUserView::getUserId, orderDon.getUserId()).op(Operator.Equal)
  267. .build());
  268. couponUserView.setCouponUserStatus(CouponUser.Status.unused);
  269. this.setCouponUserStatus(couponUserView, DateTime.now());
  270. couponUser.setStatus(couponUserView.getCouponUserStatus());
  271. couponUserMapper.updateById(couponUser);
  272. }
  273. log.info("订单:{}退款,优惠券返还end...", orderDon.getId());
  274. }
  275. @Override
  276. public CouponUserView getNewUserWelfareCoupons(long userId, DateTime now) {
  277. return couponUserMapper.getNewUserWelfareCouponsByUserId(userId, CouponUser.Channel.new_welfare, now);
  278. }
  279. }