Commit 3e66a922bf37a0446aaa244620ef5e0b99644c77

Authored by puhui999
1 parent e0d9f7cf

【功能新增】商城: 满减送活动下单后,赠送优惠劵

yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java
@@ -3,9 +3,10 @@ package cn.iocoder.yudao.module.promotion.api.coupon; @@ -3,9 +3,10 @@ package cn.iocoder.yudao.module.promotion.api.coupon;
3 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; 3 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; 4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
5 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO; 5 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
6 -  
7 import jakarta.validation.Valid; 6 import jakarta.validation.Valid;
8 7
  8 +import java.util.List;
  9 +
9 /** 10 /**
10 * 优惠劵 API 接口 11 * 优惠劵 API 接口
11 * 12 *
@@ -35,4 +36,13 @@ public interface CouponApi { @@ -35,4 +36,13 @@ public interface CouponApi {
35 */ 36 */
36 CouponRespDTO validateCoupon(@Valid CouponValidReqDTO validReqDTO); 37 CouponRespDTO validateCoupon(@Valid CouponValidReqDTO validReqDTO);
37 38
  39 + /**
  40 + * 【管理员】给指定用户批量发送优惠券
  41 + *
  42 + * @param templateIds 优惠劵编号的数组
  43 + * @param counts 优惠券数量的数组
  44 + * @param userId 用户编号
  45 + */
  46 + void takeCouponsByAdmin(List<Long> templateIds, List<Integer> counts, Long userId);
  47 +
38 } 48 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
@@ -7,10 +7,11 @@ import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO; @@ -7,10 +7,11 @@ import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
7 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert; 7 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
9 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; 9 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
  10 +import jakarta.annotation.Resource;
10 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
11 import org.springframework.validation.annotation.Validated; 12 import org.springframework.validation.annotation.Validated;
12 13
13 -import jakarta.annotation.Resource; 14 +import java.util.List;
14 15
15 /** 16 /**
16 * 优惠劵 API 实现类 17 * 优惠劵 API 实现类
@@ -41,4 +42,9 @@ public class CouponApiImpl implements CouponApi { @@ -41,4 +42,9 @@ public class CouponApiImpl implements CouponApi {
41 return CouponConvert.INSTANCE.convert(coupon); 42 return CouponConvert.INSTANCE.convert(coupon);
42 } 43 }
43 44
  45 + @Override
  46 + public void takeCouponsByAdmin(List<Long> templateIds, List<Integer> counts, Long userId) {
  47 + couponService.takeCouponsByAdmin(templateIds, counts, userId);
  48 + }
  49 +
44 } 50 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
@@ -106,6 +106,15 @@ public interface CouponService { @@ -106,6 +106,15 @@ public interface CouponService {
106 } 106 }
107 107
108 /** 108 /**
  109 + * 【管理员】给指定用户批量发送优惠券
  110 + *
  111 + * @param templateIds 优惠劵编号的数组
  112 + * @param counts 优惠券数量的数组
  113 + * @param userId 用户编号
  114 + */
  115 + void takeCouponsByAdmin(List<Long> templateIds, List<Integer> counts, Long userId);
  116 +
  117 + /**
109 * 【会员】领取优惠券 118 * 【会员】领取优惠券
110 * 119 *
111 * @param templateId 优惠券模板编号 120 * @param templateId 优惠券模板编号
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
@@ -19,19 +19,19 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper; @@ -19,19 +19,19 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
19 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; 19 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
20 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 20 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
21 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum; 21 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
  22 +import jakarta.annotation.Resource;
22 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
23 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
24 import org.springframework.transaction.annotation.Transactional; 25 import org.springframework.transaction.annotation.Transactional;
25 import org.springframework.validation.annotation.Validated; 26 import org.springframework.validation.annotation.Validated;
26 27
27 -import jakarta.annotation.Resource;  
28 -  
29 import java.time.LocalDateTime; 28 import java.time.LocalDateTime;
30 import java.util.*; 29 import java.util.*;
31 import java.util.stream.Collectors; 30 import java.util.stream.Collectors;
32 31
33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
35 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 35 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
36 import static java.util.Arrays.asList; 36 import static java.util.Arrays.asList;
37 37
@@ -175,11 +175,35 @@ public class CouponServiceImpl implements CouponService { @@ -175,11 +175,35 @@ public class CouponServiceImpl implements CouponService {
175 // 3. 批量保存优惠劵 175 // 3. 批量保存优惠劵
176 couponMapper.insertBatch(convertList(userIds, userId -> CouponConvert.INSTANCE.convert(template, userId))); 176 couponMapper.insertBatch(convertList(userIds, userId -> CouponConvert.INSTANCE.convert(template, userId)));
177 177
178 - // 3. 增加优惠劵模板的领取数量 178 + // 4. 增加优惠劵模板的领取数量
179 couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size()); 179 couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size());
180 } 180 }
181 181
182 @Override 182 @Override
  183 + public void takeCouponsByAdmin(List<Long> templateIds, List<Integer> counts, Long userId) {
  184 + // 1. 获得优惠券模版
  185 + List<CouponTemplateDO> templateList = couponTemplateService.getCouponTemplateList(templateIds);
  186 + if (CollUtil.isEmpty(templateList)) {
  187 + return;
  188 + }
  189 +
  190 + Map<Long, CouponTemplateDO> templateMap = convertMap(templateList, CouponTemplateDO::getId);
  191 + // 2.1 批量构建优惠券
  192 + List<CouponDO> couponList = new ArrayList<>();
  193 + for (int i = 0; i < templateIds.size(); i++) {
  194 + int finalI = i;
  195 + findAndThen(templateMap, templateIds.get(i), template -> {
  196 + for (int j = 0; j < counts.get(finalI); j++) {
  197 + couponList.add(CouponConvert.INSTANCE.convert(template, userId)
  198 + .setTakeType(CouponTakeTypeEnum.ADMIN.getValue()));
  199 + }
  200 + });
  201 + }
  202 + // 2.2 批量保存优惠券
  203 + couponMapper.insertBatch(couponList);
  204 + }
  205 +
  206 + @Override
183 @Transactional(rollbackFor = Exception.class) 207 @Transactional(rollbackFor = Exception.class)
184 public void takeCouponByRegister(Long userId) { 208 public void takeCouponByRegister(Long userId) {
185 List<CouponTemplateDO> templates = couponTemplateService.getCouponTemplateListByTakeType(CouponTakeTypeEnum.REGISTER); 209 List<CouponTemplateDO> templates = couponTemplateService.getCouponTemplateListByTakeType(CouponTakeTypeEnum.REGISTER);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
@@ -16,6 +16,7 @@ import com.baomidou.mybatisplus.annotation.TableName; @@ -16,6 +16,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
16 import lombok.*; 16 import lombok.*;
17 17
18 import java.time.LocalDateTime; 18 import java.time.LocalDateTime;
  19 +import java.util.List;
19 20
20 /** 21 /**
21 * 交易订单 DO 22 * 交易订单 DO
@@ -290,6 +291,18 @@ public class TradeOrderDO extends BaseDO { @@ -290,6 +291,18 @@ public class TradeOrderDO extends BaseDO {
290 * VIP 减免金额,单位:分 291 * VIP 减免金额,单位:分
291 */ 292 */
292 private Integer vipPrice; 293 private Integer vipPrice;
  294 + /**
  295 + * 赠送的优惠劵编号的数组
  296 + *
  297 + * 目的:用于后续取消或者售后订单时,需要扣减赠送
  298 + */
  299 + private List<Long> couponIds;
  300 + /**
  301 + * 赠送的优惠券数量的数组
  302 + *
  303 + * 目的:用于后续取消或者售后订单时,需要扣减赠送
  304 + */
  305 + private List<Integer> couponCounts;
293 306
294 /** 307 /**
295 * 秒杀活动编号 308 * 秒杀活动编号
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java
@@ -160,19 +160,6 @@ public class TradeOrderItemDO extends BaseDO { @@ -160,19 +160,6 @@ public class TradeOrderItemDO extends BaseDO {
160 */ 160 */
161 private Integer vipPrice; 161 private Integer vipPrice;
162 162
163 - /**  
164 - * 赠送的优惠劵编号的数组  
165 - *  
166 - * 目的:用于后续取消或者售后订单时,需要扣减赠送  
167 - */  
168 - private List<Long> couponIds;  
169 - /**  
170 - * 赠送的优惠券数量的数组  
171 - *  
172 - * 目的:用于后续取消或者售后订单时,需要扣减赠送  
173 - */  
174 - private List<Integer> couponCounts;  
175 -  
176 // ========== 售后基本信息 ========== 163 // ========== 售后基本信息 ==========
177 164
178 /** 165 /**
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -201,6 +201,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -201,6 +201,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
201 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); 201 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
202 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum)); 202 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
203 order.setUserIp(getClientIP()).setTerminal(getTerminal()); 203 order.setUserIp(getClientIP()).setTerminal(getTerminal());
  204 + // 优惠券
  205 + order.setCouponIds(calculateRespBO.getCouponIds()).setCouponCounts(calculateRespBO.getCouponCounts());
204 // 支付 + 退款信息 206 // 支付 + 退款信息
205 order.setAdjustPrice(0).setPayStatus(false); 207 order.setAdjustPrice(0).setPayStatus(false);
206 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); 208 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCouponOrderHandler.java
1 package cn.iocoder.yudao.module.trade.service.order.handler; 1 package cn.iocoder.yudao.module.trade.service.order.handler;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; 4 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; 5 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@@ -32,16 +33,25 @@ public class TradeCouponOrderHandler implements TradeOrderHandler { @@ -32,16 +33,25 @@ public class TradeCouponOrderHandler implements TradeOrderHandler {
32 33
33 @Override 34 @Override
34 public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 35 public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
35 - 36 + if (CollUtil.isEmpty(order.getCouponIds())) {
  37 + return;
  38 + }
  39 + // 赠送优惠券
  40 + couponApi.takeCouponsByAdmin(order.getCouponIds(), order.getCouponCounts(), order.getUserId());
36 } 41 }
37 42
38 @Override 43 @Override
39 public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 44 public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
40 - if (order.getCouponId() == null || order.getCouponId() <= 0) { 45 + // 情况一:退还订单使用的优惠券
  46 + if (order.getCouponId() != null && order.getCouponId() > 0) {
  47 + // 退回优惠劵
  48 + couponApi.returnUsedCoupon(order.getCouponId());
  49 + }
  50 + // 情况二:收回赠送的优惠券
  51 + if (CollUtil.isEmpty(order.getCouponIds())) {
41 return; 52 return;
42 } 53 }
43 - // 退回优惠劵  
44 - couponApi.returnUsedCoupon(order.getCouponId()); 54 + // TODO @puhui999: 收回优惠券再考虑一下,是直接删除券还是改个状态
45 } 55 }
46 56
47 } 57 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
@@ -72,7 +72,6 @@ public class TradePriceCalculateRespBO { @@ -72,7 +72,6 @@ public class TradePriceCalculateRespBO {
72 */ 72 */
73 private Boolean freeDelivery; 73 private Boolean freeDelivery;
74 74
75 - // TODO @puhui999: 订单保存时需要保存  
76 /** 75 /**
77 * 赠送的优惠劵编号的数组 76 * 赠送的优惠劵编号的数组
78 */ 77 */
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
@@ -19,6 +19,7 @@ import org.springframework.stereotype.Component; @@ -19,6 +19,7 @@ import org.springframework.stereotype.Component;
19 import java.util.ArrayList; 19 import java.util.ArrayList;
20 import java.util.Comparator; 20 import java.util.Comparator;
21 import java.util.List; 21 import java.util.List;
  22 +import java.util.Objects;
22 23
23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
@@ -105,8 +106,18 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator @@ -105,8 +106,18 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
105 } 106 }
106 // 4.3 记录赠送的优惠券 107 // 4.3 记录赠送的优惠券
107 if (rule.getGiveCoupon()) { 108 if (rule.getGiveCoupon()) {
108 - // TODO @puhui999: 需要考虑赠送的优惠券是否重叠,重叠则对数量进行累加  
109 - result.setCouponIds(rule.getCouponIds()).setCouponCounts(rule.getCouponCounts()); 109 + for (int i = 0; i < rule.getCouponIds().size(); i++) {
  110 + Long couponId = result.getCouponIds().get(i);
  111 + Integer couponCount = result.getCouponCounts().get(i);
  112 + int index = CollUtil.indexOf(result.getCouponIds(), id -> Objects.equals(couponId, id));
  113 + if (index != -1) { // 情况一:别的满减活动送过同类优惠券,则直接增加数量
  114 + List<Integer> couponCounts = result.getCouponCounts();
  115 + couponCounts.set(index, couponCounts.get(index) + couponCount);
  116 + result.setCouponCounts(couponCounts);
  117 + } else { // 情况二:还没有赠送的优惠券
  118 + result.setCouponIds(rule.getCouponIds()).setCouponCounts(rule.getCouponCounts());
  119 + }
  120 + }
110 } 121 }
111 } 122 }
112 123