Commit ebf5693255ca571c1d96cf02b9d6847404c50ce8

Authored by 芋道源码
Committed by Gitee
2 parents f00b3005 81e38666

!1069 【代码优化】商城: 满减送活动

Merge pull request !1069 from puhui999/develop
Showing 18 changed files with 256 additions and 222 deletions
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; @@ -5,6 +5,7 @@ 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 import jakarta.validation.Valid; 6 import jakarta.validation.Valid;
7 7
  8 +import java.util.List;
8 import java.util.Map; 9 import java.util.Map;
9 10
10 /** 11 /**
@@ -36,23 +37,21 @@ public interface CouponApi { @@ -36,23 +37,21 @@ public interface CouponApi {
36 */ 37 */
37 CouponRespDTO validateCoupon(@Valid CouponValidReqDTO validReqDTO); 38 CouponRespDTO validateCoupon(@Valid CouponValidReqDTO validReqDTO);
38 39
39 - // TODO @puhui999:可能需要根据 TradeOrderDO 的建议,进行修改;需要返回优惠劵编号  
40 /** 40 /**
41 * 【管理员】给指定用户批量发送优惠券 41 * 【管理员】给指定用户批量发送优惠券
42 * 42 *
43 - * @param giveCouponsMap key: 优惠劵编号,value:对应的优惠券数量 43 + * @param giveCoupons key: 优惠劵模版编号,value:对应的数量
44 * @param userId 用户编号 44 * @param userId 用户编号
  45 + * @return 优惠券编号列表
45 */ 46 */
46 - // TODO @puhui999:giveCouponsMap 可能改成 giveCoupons 更合适?优惠劵模版编号、数量  
47 - void takeCouponsByAdmin(Map<Long, Integer> giveCouponsMap, Long userId); 47 + List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId);
48 48
49 - // TODO @puhui999:可能需要根据 TradeOrderDO 的建议,进行修改 giveCouponsMap 参数  
50 /** 49 /**
51 * 【管理员】作废指定用户的指定优惠劵 50 * 【管理员】作废指定用户的指定优惠劵
52 * 51 *
53 - * @param giveCouponsMap key: 优惠劵编号,value:对应的优惠券数量 52 + * @param giveCouponIds 赠送的优惠券编号
54 * @param userId 用户编号 53 * @param userId 用户编号
55 */ 54 */
56 - void invalidateCouponsByAdmin(Map<Long, Integer> giveCouponsMap, Long userId); 55 + void invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId);
57 56
58 } 57 }
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/dto/RewardActivityMatchRespDTO.java
@@ -86,27 +86,17 @@ public class RewardActivityMatchRespDTO { @@ -86,27 +86,17 @@ public class RewardActivityMatchRespDTO {
86 * 是否包邮 86 * 是否包邮
87 */ 87 */
88 private Boolean freeDelivery; 88 private Boolean freeDelivery;
89 - // TODO @puhui999:建议不返回 + 去掉 givePoint、giveCoupon 字段哈。  
90 - /**  
91 - * 是否赠送积分  
92 - */  
93 - private Boolean givePoint;  
94 /** 89 /**
95 * 赠送的积分 90 * 赠送的积分
96 */ 91 */
97 private Integer point; 92 private Integer point;
98 /** 93 /**
99 - * 是否赠送优惠券  
100 - */  
101 - private Boolean giveCoupon;  
102 - // TODO @puhui999:giveCoupons 即可  
103 - /**  
104 * 赠送的优惠劵 94 * 赠送的优惠劵
105 * 95 *
106 * key: 优惠劵模版编号 96 * key: 优惠劵模版编号
107 * value:对应的优惠券数量 97 * value:对应的优惠券数量
108 */ 98 */
109 - private Map<Long, Integer> giveCouponsMap; 99 + private Map<Long, Integer> giveCoupons;
110 100
111 } 101 }
112 102
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponStatusEnum.java
@@ -17,9 +17,7 @@ public enum CouponStatusEnum implements IntArrayValuable { @@ -17,9 +17,7 @@ public enum CouponStatusEnum implements IntArrayValuable {
17 17
18 UNUSED(1, "未使用"), 18 UNUSED(1, "未使用"),
19 USED(2, "已使用"), 19 USED(2, "已使用"),
20 - EXPIRE(3, "已过期"),  
21 - // TODO @puhui999:捉摸了下,貌似搞成逻辑删除好了?不然好多地方的 status 都要做一些变动。可能未来加个 invalidateType 来标识,是管理后台删除,还是取消回收。或者优惠劵的 change log 可能更好。  
22 - INVALID(4, "已作废"); 20 + EXPIRE(3, "已过期");
23 21
24 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponStatusEnum::getStatus).toArray(); 22 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponStatusEnum::getStatus).toArray();
25 23
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
@@ -11,6 +11,7 @@ import jakarta.annotation.Resource; @@ -11,6 +11,7 @@ import jakarta.annotation.Resource;
11 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
12 import org.springframework.validation.annotation.Validated; 12 import org.springframework.validation.annotation.Validated;
13 13
  14 +import java.util.List;
14 import java.util.Map; 15 import java.util.Map;
15 16
16 /** 17 /**
@@ -43,13 +44,13 @@ public class CouponApiImpl implements CouponApi { @@ -43,13 +44,13 @@ public class CouponApiImpl implements CouponApi {
43 } 44 }
44 45
45 @Override 46 @Override
46 - public void takeCouponsByAdmin(Map<Long, Integer> giveCouponsMap, Long userId) {  
47 - couponService.takeCouponsByAdmin(giveCouponsMap, userId); 47 + public List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId) {
  48 + return couponService.takeCouponsByAdmin(giveCoupons, userId);
48 } 49 }
49 50
50 @Override 51 @Override
51 - public void invalidateCouponsByAdmin(Map<Long, Integer> giveCouponsMap, Long userId) {  
52 - couponService.invalidateCouponsByAdmin(giveCouponsMap, userId); 52 + public void invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId) {
  53 + couponService.invalidateCouponsByAdmin(giveCouponIds, userId);
53 } 54 }
54 55
55 } 56 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityBaseVO.java
1 package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo; 1 package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.hutool.core.util.BooleanUtil;  
5 import cn.iocoder.yudao.framework.common.validation.InEnum; 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
6 import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum; 5 import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
7 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; 6 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
@@ -77,24 +76,16 @@ public class RewardActivityBaseVO { @@ -77,24 +76,16 @@ public class RewardActivityBaseVO {
77 @NotNull(message = "规则是否包邮不能为空") 76 @NotNull(message = "规则是否包邮不能为空")
78 private Boolean freeDelivery; 77 private Boolean freeDelivery;
79 78
80 - @Schema(description = "是否赠送积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")  
81 - @NotNull(message = "规则是否赠送积分不能为空")  
82 - private Boolean givePoint;  
83 -  
84 @Schema(description = "赠送的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") 79 @Schema(description = "赠送的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
85 private Integer point; 80 private Integer point;
86 81
87 - @Schema(description = "是否赠送优惠券", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")  
88 - @NotNull(message = "规则是否赠送优惠券不能为空")  
89 - private Boolean giveCoupon;  
90 -  
91 @Schema(description = "赠送的优惠劵编号的数组", example = "1,2,3") 82 @Schema(description = "赠送的优惠劵编号的数组", example = "1,2,3")
92 private Map<Long, Integer> giveCouponsMap; 83 private Map<Long, Integer> giveCouponsMap;
93 84
94 @AssertTrue(message = "赠送的积分不能小于 1") 85 @AssertTrue(message = "赠送的积分不能小于 1")
95 @JsonIgnore 86 @JsonIgnore
96 public boolean isPointValid() { 87 public boolean isPointValid() {
97 - return BooleanUtil.isFalse(givePoint) || (point != null && point >= 1); 88 + return point == null || point >= 1;
98 } 89 }
99 90
100 } 91 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/reward/RewardActivityDO.java
@@ -100,20 +100,10 @@ public class RewardActivityDO extends BaseDO { @@ -100,20 +100,10 @@ public class RewardActivityDO extends BaseDO {
100 * 是否包邮 100 * 是否包邮
101 */ 101 */
102 private Boolean freeDelivery; 102 private Boolean freeDelivery;
103 - // TODO @puhui999:是不是大于零,就认为赠送积分哈;简洁一点;  
104 - /**  
105 - * 是否赠送积分  
106 - */  
107 - private Boolean givePoint;  
108 /** 103 /**
109 * 赠送的积分 104 * 赠送的积分
110 */ 105 */
111 private Integer point; 106 private Integer point;
112 - // TODO @puhui999:非空,就认为赠送优惠劵  
113 - /**  
114 - * 是否赠送优惠券  
115 - */  
116 - private Boolean giveCoupon;  
117 /** 107 /**
118 * 赠送的优惠劵 108 * 赠送的优惠劵
119 * 109 *
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
10 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; 10 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  11 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
11 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 12 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
12 import com.github.yulichang.toolkit.MPJWrappers; 13 import com.github.yulichang.toolkit.MPJWrappers;
13 import org.apache.ibatis.annotations.Mapper; 14 import org.apache.ibatis.annotations.Mapper;
@@ -72,15 +73,6 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; { @@ -72,15 +73,6 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; {
72 ); 73 );
73 } 74 }
74 75
75 - default List<CouponDO> selectListByTemplateIdAndUserIdAndTakeType(Long templateId, Collection<Long> userIds,  
76 - Integer takeType) {  
77 - return selectList(new LambdaQueryWrapperX<CouponDO>()  
78 - .eq(CouponDO::getTemplateId, templateId)  
79 - .eq(CouponDO::getTakeType, takeType)  
80 - .in(CouponDO::getUserId, userIds)  
81 - );  
82 - }  
83 -  
84 default Map<Long, Integer> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) { 76 default Map<Long, Integer> selectCountByUserIdAndTemplateIdIn(Long userId, Collection<Long> templateIds) {
85 String templateIdAlias = "templateId"; 77 String templateIdAlias = "templateId";
86 String countAlias = "count"; 78 String countAlias = "count";
@@ -116,4 +108,11 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; { @@ -116,4 +108,11 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; {
116 ); 108 );
117 } 109 }
118 110
  111 + default List<CouponDO> selectListByIdAndUserIdAndTakeType(Long couponId, Long userId, Integer takeType) {
  112 + return selectList(new LambdaQueryWrapper<CouponDO>()
  113 + .eq(CouponDO::getId, couponId)
  114 + .eq(CouponDO::getUserId, userId)
  115 + .eq(CouponDO::getTakeType, takeType));
  116 + }
  117 +
119 } 118 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
@@ -39,14 +39,6 @@ public interface CouponService { @@ -39,14 +39,6 @@ public interface CouponService {
39 void validCoupon(CouponDO coupon); 39 void validCoupon(CouponDO coupon);
40 40
41 /** 41 /**
42 - * 获得优惠劵分页  
43 - *  
44 - * @param pageReqVO 分页查询  
45 - * @return 优惠劵分页  
46 - */  
47 - PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO);  
48 -  
49 - /**  
50 * 使用优惠劵 42 * 使用优惠劵
51 * 43 *
52 * @param id 优惠劵编号 44 * @param id 优惠劵编号
@@ -70,56 +62,42 @@ public interface CouponService { @@ -70,56 +62,42 @@ public interface CouponService {
70 void deleteCoupon(Long id); 62 void deleteCoupon(Long id);
71 63
72 /** 64 /**
73 - * 获得用户的优惠劵列表  
74 - *  
75 - * @param userId 用户编号  
76 - * @param status 优惠劵状态  
77 - * @return 优惠劵列表  
78 - */  
79 - List<CouponDO> getCouponList(Long userId, Integer status);  
80 -  
81 - /**  
82 - * 获得未使用的优惠劵数量  
83 - *  
84 - * @param userId 用户编号  
85 - * @return 未使用的优惠劵数量  
86 - */  
87 - Long getUnusedCouponCount(Long userId);  
88 -  
89 - /**  
90 * 领取优惠券 65 * 领取优惠券
91 * 66 *
92 * @param templateId 优惠券模板编号 67 * @param templateId 优惠券模板编号
93 * @param userIds 用户编号列表 68 * @param userIds 用户编号列表
94 * @param takeType 领取方式 69 * @param takeType 领取方式
  70 + * @return key: userId, value: 优惠券编号列表
95 */ 71 */
96 - void takeCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType); 72 + Map<Long, List<Long>> takeCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType);
97 73
98 /** 74 /**
99 * 【管理员】给用户发送优惠券 75 * 【管理员】给用户发送优惠券
100 * 76 *
101 * @param templateId 优惠券模板编号 77 * @param templateId 优惠券模板编号
102 * @param userIds 用户编号列表 78 * @param userIds 用户编号列表
  79 + * @return key: userId, value: 优惠券编号列表
103 */ 80 */
104 - default void takeCouponByAdmin(Long templateId, Set<Long> userIds) {  
105 - takeCoupon(templateId, userIds, CouponTakeTypeEnum.ADMIN); 81 + default Map<Long, List<Long>> takeCouponByAdmin(Long templateId, Set<Long> userIds) {
  82 + return takeCoupon(templateId, userIds, CouponTakeTypeEnum.ADMIN);
106 } 83 }
107 84
108 /** 85 /**
109 * 【管理员】给指定用户批量发送优惠券 86 * 【管理员】给指定用户批量发送优惠券
110 * 87 *
111 - * @param giveCouponsMap key: 优惠劵编号,value:对应的优惠券数量 88 + * @param giveCoupons key: 优惠劵模版编号,value:对应的数量
112 * @param userId 用户编号 89 * @param userId 用户编号
  90 + * @return 优惠券编号列表
113 */ 91 */
114 - void takeCouponsByAdmin(Map<Long, Integer> giveCouponsMap, Long userId); 92 + List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId);
115 93
116 /** 94 /**
117 - * 【管理员】收回给指定用户批量发送优惠券 95 + * 【管理员】作废指定用户的指定优惠劵
118 * 96 *
119 - * @param giveCouponsMap key: 优惠劵编号,value:对应的优惠券数量 97 + * @param giveCouponIds 赠送的优惠券编号
120 * @param userId 用户编号 98 * @param userId 用户编号
121 */ 99 */
122 - void invalidateCouponsByAdmin(Map<Long, Integer> giveCouponsMap, Long userId); 100 + void invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId);
123 101
124 /** 102 /**
125 * 【会员】领取优惠券 103 * 【会员】领取优惠券
@@ -139,16 +117,38 @@ public interface CouponService { @@ -139,16 +117,38 @@ public interface CouponService {
139 void takeCouponByRegister(Long userId); 117 void takeCouponByRegister(Long userId);
140 118
141 /** 119 /**
142 - * 获取会员领取指定优惠券的数量 120 + * 过期优惠券
143 * 121 *
144 - * @param templateId 优惠券模板编号  
145 - * @param userId 用户编号  
146 - * @return 领取优惠券的数量 122 + * @return 过期数量
147 */ 123 */
148 - default Integer getTakeCount(Long templateId, Long userId) {  
149 - Map<Long, Integer> map = getTakeCountMapByTemplateIds(Collections.singleton(templateId), userId);  
150 - return MapUtil.getInt(map, templateId, 0);  
151 - } 124 + int expireCoupon();
  125 +
  126 + //======================= 查询相关 =======================
  127 +
  128 + /**
  129 + * 获得未使用的优惠劵数量
  130 + *
  131 + * @param userId 用户编号
  132 + * @return 未使用的优惠劵数量
  133 + */
  134 + Long getUnusedCouponCount(Long userId);
  135 +
  136 + /**
  137 + * 获得优惠劵分页
  138 + *
  139 + * @param pageReqVO 分页查询
  140 + * @return 优惠劵分页
  141 + */
  142 + PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO);
  143 +
  144 + /**
  145 + * 获得用户的优惠劵列表
  146 + *
  147 + * @param userId 用户编号
  148 + * @param status 优惠劵状态
  149 + * @return 优惠劵列表
  150 + */
  151 + List<CouponDO> getCouponList(Long userId, Integer status);
152 152
153 /** 153 /**
154 * 统计会员领取优惠券的数量 154 * 统计会员领取优惠券的数量
@@ -160,20 +160,25 @@ public interface CouponService { @@ -160,20 +160,25 @@ public interface CouponService {
160 Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId); 160 Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId);
161 161
162 /** 162 /**
163 - * 获取用户匹配的优惠券列表 163 + * 获取会员领取指定优惠券的数量
164 * 164 *
  165 + * @param templateId 优惠券模板编号
165 * @param userId 用户编号 166 * @param userId 用户编号
166 - * @param matchReqVO 匹配参数  
167 - * @return 优惠券列表 167 + * @return 领取优惠券的数量
168 */ 168 */
169 - List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO); 169 + default Integer getTakeCount(Long templateId, Long userId) {
  170 + Map<Long, Integer> map = getTakeCountMapByTemplateIds(Collections.singleton(templateId), userId);
  171 + return MapUtil.getInt(map, templateId, 0);
  172 + }
170 173
171 /** 174 /**
172 - * 过期优惠券 175 + * 获取用户匹配的优惠券列表
173 * 176 *
174 - * @return 过期数量 177 + * @param userId 用户编号
  178 + * @param matchReqVO 匹配参数
  179 + * @return 优惠券列表
175 */ 180 */
176 - int expireCoupon(); 181 + List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
177 182
178 /** 183 /**
179 * 获取用户是否可以领取优惠券 184 * 获取用户是否可以领取优惠券
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.service.coupon; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.service.coupon;
3 import cn.hutool.core.collection.CollStreamUtil; 3 import cn.hutool.core.collection.CollStreamUtil;
4 import cn.hutool.core.collection.CollUtil; 4 import cn.hutool.core.collection.CollUtil;
5 import cn.hutool.core.map.MapUtil; 5 import cn.hutool.core.map.MapUtil;
  6 +import cn.hutool.core.util.ObjUtil;
6 import cn.hutool.core.util.ObjectUtil; 7 import cn.hutool.core.util.ObjectUtil;
7 import cn.hutool.core.util.StrUtil; 8 import cn.hutool.core.util.StrUtil;
8 import cn.hutool.extra.spring.SpringUtil; 9 import cn.hutool.extra.spring.SpringUtil;
@@ -31,6 +32,7 @@ import java.util.stream.Collectors; @@ -31,6 +32,7 @@ import java.util.stream.Collectors;
31 32
32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 34 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
34 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 36 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
35 import static java.util.Arrays.asList; 37 import static java.util.Arrays.asList;
36 38
@@ -76,20 +78,6 @@ public class CouponServiceImpl implements CouponService { @@ -76,20 +78,6 @@ public class CouponServiceImpl implements CouponService {
76 } 78 }
77 79
78 @Override 80 @Override
79 - public PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO) {  
80 - // 获得用户编号  
81 - if (StrUtil.isNotEmpty(pageReqVO.getNickname())) {  
82 - List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(pageReqVO.getNickname());  
83 - if (CollUtil.isEmpty(users)) {  
84 - return PageResult.empty();  
85 - }  
86 - pageReqVO.setUserIds(convertSet(users, MemberUserRespDTO::getId));  
87 - }  
88 - // 分页查询  
89 - return couponMapper.selectPage(pageReqVO);  
90 - }  
91 -  
92 - @Override  
93 public void useCoupon(Long id, Long userId, Long orderId) { 81 public void useCoupon(Long id, Long userId, Long orderId) {
94 // 校验优惠劵 82 // 校验优惠劵
95 validCoupon(id, userId); 83 validCoupon(id, userId);
@@ -146,26 +134,8 @@ public class CouponServiceImpl implements CouponService { @@ -146,26 +134,8 @@ public class CouponServiceImpl implements CouponService {
146 } 134 }
147 135
148 @Override 136 @Override
149 - public List<CouponDO> getCouponList(Long userId, Integer status) {  
150 - return couponMapper.selectListByUserIdAndStatus(userId, status);  
151 - }  
152 -  
153 - private CouponDO validateCouponExists(Long id) {  
154 - CouponDO coupon = couponMapper.selectById(id);  
155 - if (coupon == null) {  
156 - throw exception(COUPON_NOT_EXISTS);  
157 - }  
158 - return coupon;  
159 - }  
160 -  
161 - @Override  
162 - public Long getUnusedCouponCount(Long userId) {  
163 - return couponMapper.selectCountByUserIdAndStatus(userId, CouponStatusEnum.UNUSED.getStatus());  
164 - }  
165 -  
166 - @Override  
167 @Transactional(rollbackFor = Exception.class) 137 @Transactional(rollbackFor = Exception.class)
168 - public void takeCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType) { 138 + public Map<Long, List<Long>> takeCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType) {
169 CouponTemplateDO template = couponTemplateService.getCouponTemplate(templateId); 139 CouponTemplateDO template = couponTemplateService.getCouponTemplate(templateId);
170 // 1. 过滤掉达到领取限制的用户 140 // 1. 过滤掉达到领取限制的用户
171 removeTakeLimitUser(userIds, template); 141 removeTakeLimitUser(userIds, template);
@@ -173,40 +143,45 @@ public class CouponServiceImpl implements CouponService { @@ -173,40 +143,45 @@ public class CouponServiceImpl implements CouponService {
173 validateCouponTemplateCanTake(template, userIds, takeType); 143 validateCouponTemplateCanTake(template, userIds, takeType);
174 144
175 // 3. 批量保存优惠劵 145 // 3. 批量保存优惠劵
176 - couponMapper.insertBatch(convertList(userIds, userId -> CouponConvert.INSTANCE.convert(template, userId))); 146 + List<CouponDO> couponList = convertList(userIds, userId -> CouponConvert.INSTANCE.convert(template, userId));
  147 + couponMapper.insertBatch(couponList);
177 148
178 // 4. 增加优惠劵模板的领取数量 149 // 4. 增加优惠劵模板的领取数量
179 couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size()); 150 couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size());
  151 +
  152 + return convertMultiMap(couponList, CouponDO::getUserId, CouponDO::getId);
180 } 153 }
181 154
182 @Override 155 @Override
183 - public void takeCouponsByAdmin(Map<Long, Integer> giveCouponsMap, Long userId) {  
184 - if (CollUtil.isEmpty(giveCouponsMap)) {  
185 - return; 156 + public List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId) {
  157 + if (CollUtil.isEmpty(giveCoupons)) {
  158 + return Collections.emptyList();
186 } 159 }
187 160
  161 + List<Long> couponIds = new ArrayList<>();
188 // 循环发放 162 // 循环发放
189 - for (Map.Entry<Long, Integer> entry : giveCouponsMap.entrySet()) { 163 + for (Map.Entry<Long, Integer> entry : giveCoupons.entrySet()) {
190 try { 164 try {
191 for (int i = 0; i < entry.getValue(); i++) { 165 for (int i = 0; i < entry.getValue(); i++) {
192 - getSelf().takeCoupon(entry.getKey(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.ADMIN); 166 + Map<Long, List<Long>> userCouponIdsMap = getSelf().takeCoupon(entry.getKey(), CollUtil.newHashSet(userId),
  167 + CouponTakeTypeEnum.ADMIN);
  168 + findAndThen(userCouponIdsMap, userId, couponIds::addAll);
193 } 169 }
194 } catch (Exception e) { 170 } catch (Exception e) {
195 log.error("[takeCouponsByAdmin][coupon({}) 优惠券发放失败]", entry, e); 171 log.error("[takeCouponsByAdmin][coupon({}) 优惠券发放失败]", entry, e);
196 } 172 }
197 } 173 }
  174 + return couponIds;
198 } 175 }
199 176
200 @Override 177 @Override
201 - public void invalidateCouponsByAdmin(Map<Long, Integer> giveCouponsMap, Long userId) { 178 + public void invalidateCouponsByAdmin(List<Long> giveCouponIds, Long userId) {
202 // 循环收回 179 // 循环收回
203 - for (Map.Entry<Long, Integer> entry : giveCouponsMap.entrySet()) { 180 + for (Long couponId : giveCouponIds) {
204 try { 181 try {
205 - for (int i = 0; i < entry.getValue(); i++) {  
206 - getSelf().takeBackCoupon(entry.getKey(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.ADMIN);  
207 - } 182 + getSelf().takeBackCoupon(couponId, userId, CouponTakeTypeEnum.ADMIN);
208 } catch (Exception e) { 183 } catch (Exception e) {
209 - log.error("[takeBackCouponsByAdmin][coupon({}) 收回优惠券失败]", entry, e); 184 + log.error("[invalidateCouponsByAdmin][couponId({}) 收回优惠券失败]", couponId, e);
210 } 185 }
211 } 186 }
212 } 187 }
@@ -214,32 +189,36 @@ public class CouponServiceImpl implements CouponService { @@ -214,32 +189,36 @@ public class CouponServiceImpl implements CouponService {
214 /** 189 /**
215 * 【管理员】收回优惠券 190 * 【管理员】收回优惠券
216 * 191 *
217 - * @param templateId 模版编号  
218 - * @param userIds 用户编号列表 192 + * @param couponId 模版编号
  193 + * @param userId 用户编号
219 * @param takeType 领取方式 194 * @param takeType 领取方式
220 */ 195 */
221 @Transactional(rollbackFor = Exception.class) 196 @Transactional(rollbackFor = Exception.class)
222 - public void takeBackCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType) {  
223 - CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(templateId);  
224 - // 1.1 校验模板 197 + public void takeBackCoupon(Long couponId, Long userId, CouponTakeTypeEnum takeType) {
  198 + // 1.1 校验优惠券
  199 + CouponDO coupon = couponMapper.selectByIdAndUserId(couponId, userId);
  200 + if (coupon == null) {
  201 + throw exception(COUPON_NOT_EXISTS);
  202 + }
  203 + // 1.2 校验模板
  204 + CouponTemplateDO couponTemplate = couponTemplateService.getCouponTemplate(coupon.getTemplateId());
225 if (couponTemplate == null) { 205 if (couponTemplate == null) {
226 throw exception(COUPON_TEMPLATE_NOT_EXISTS); 206 throw exception(COUPON_TEMPLATE_NOT_EXISTS);
227 } 207 }
228 - // 1.2 校验领取方式 208 + // 1.3 校验领取方式
229 if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getValue())) { 209 if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getValue())) {
230 throw exception(COUPON_TEMPLATE_CANNOT_TAKE); 210 throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
231 } 211 }
232 212
233 - // 2.1 过滤出还未使用的赠送的优惠券  
234 - List<CouponDO> couponList = couponMapper.selectListByTemplateIdAndUserIdAndTakeType(templateId, userIds,  
235 - takeType.getValue());  
236 - List<CouponDO> unUsedCouponList = filterList(couponList, item -> !CouponStatusEnum.USED.getStatus().equals(item.getStatus())); 213 + // 2.1 校验优惠券是否已经使用,如若使用则先不管
  214 + if (ObjUtil.equal(coupon.getStatus(), CouponStatusEnum.USED.getStatus())) {
  215 + return;
  216 + }
237 // 2.2 减少优惠劵模板的领取数量 217 // 2.2 减少优惠劵模板的领取数量
238 - couponTemplateService.updateCouponTemplateTakeCount(templateId, unUsedCouponList.size() * -1);  
239 - // 2.3 批量更新优惠劵状态  
240 - couponMapper.updateById(convertList(unUsedCouponList, item -> new CouponDO().setId(item.getId())  
241 - .setStatus(CouponStatusEnum.INVALID.getStatus())));  
242 - 218 + couponTemplateService.updateCouponTemplateTakeCount(couponTemplate.getId(), -1);
  219 + // 2.3 批量作废优惠劵
  220 + // TODO @puhui999:捉摸了下,貌似搞成逻辑删除好了?不然好多地方的 status 都要做一些变动。可能未来加个 invalidateType 来标识,是管理后台删除,还是取消回收。或者优惠劵的 change log 可能更好。
  221 + couponMapper.deleteById(couponId);
243 } 222 }
244 223
245 @Override 224 @Override
@@ -252,24 +231,6 @@ public class CouponServiceImpl implements CouponService { @@ -252,24 +231,6 @@ public class CouponServiceImpl implements CouponService {
252 } 231 }
253 232
254 @Override 233 @Override
255 - public Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {  
256 - if (CollUtil.isEmpty(templateIds)) {  
257 - return Collections.emptyMap();  
258 - }  
259 - return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);  
260 - }  
261 -  
262 - @Override  
263 - public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {  
264 - List<CouponDO> list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,  
265 - CouponStatusEnum.UNUSED.getStatus(),  
266 - matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());  
267 - // 兜底逻辑:如果 CouponExpireJob 未执行,status 未变成 EXPIRE ,但是 validEndTime 已经过期了,需要进行过滤  
268 - list.removeIf(coupon -> !LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime()));  
269 - return list;  
270 - }  
271 -  
272 - @Override  
273 public int expireCoupon() { 234 public int expireCoupon() {
274 // 1. 查询待过期的优惠券 235 // 1. 查询待过期的优惠券
275 List<CouponDO> list = couponMapper.selectListByStatusAndValidEndTimeLe( 236 List<CouponDO> list = couponMapper.selectListByStatusAndValidEndTimeLe(
@@ -293,27 +254,6 @@ public class CouponServiceImpl implements CouponService { @@ -293,27 +254,6 @@ public class CouponServiceImpl implements CouponService {
293 return count; 254 return count;
294 } 255 }
295 256
296 - @Override  
297 - public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) {  
298 - // 1. 未登录时,都显示可以领取  
299 - Map<Long, Boolean> userCanTakeMap = convertMap(templates, CouponTemplateDO::getId, templateId -> true);  
300 - if (userId == null) {  
301 - return userCanTakeMap;  
302 - }  
303 -  
304 - // 2.1 过滤领取数量无限制的  
305 - Set<Long> templateIds = convertSet(templates, CouponTemplateDO::getId, template -> template.getTakeLimitCount() != -1);  
306 - // 2.2 检查用户领取的数量是否超过限制  
307 - if (CollUtil.isNotEmpty(templateIds)) {  
308 - Map<Long, Integer> couponTakeCountMap = this.getTakeCountMapByTemplateIds(templateIds, userId);  
309 - for (CouponTemplateDO template : templates) {  
310 - Integer takeCount = couponTakeCountMap.get(template.getId());  
311 - userCanTakeMap.put(template.getId(), takeCount == null || takeCount < template.getTakeLimitCount());  
312 - }  
313 - }  
314 - return userCanTakeMap;  
315 - }  
316 -  
317 /** 257 /**
318 * 过期单个优惠劵 258 * 过期单个优惠劵
319 * 259 *
@@ -385,11 +325,84 @@ public class CouponServiceImpl implements CouponService { @@ -385,11 +325,84 @@ public class CouponServiceImpl implements CouponService {
385 userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount()); 325 userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount());
386 } 326 }
387 327
  328 + //======================= 查询相关 =======================
  329 +
  330 + @Override
  331 + public Long getUnusedCouponCount(Long userId) {
  332 + return couponMapper.selectCountByUserIdAndStatus(userId, CouponStatusEnum.UNUSED.getStatus());
  333 + }
  334 +
  335 + @Override
  336 + public PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO) {
  337 + // 获得用户编号
  338 + if (StrUtil.isNotEmpty(pageReqVO.getNickname())) {
  339 + List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(pageReqVO.getNickname());
  340 + if (CollUtil.isEmpty(users)) {
  341 + return PageResult.empty();
  342 + }
  343 + pageReqVO.setUserIds(convertSet(users, MemberUserRespDTO::getId));
  344 + }
  345 + // 分页查询
  346 + return couponMapper.selectPage(pageReqVO);
  347 + }
  348 +
  349 + @Override
  350 + public List<CouponDO> getCouponList(Long userId, Integer status) {
  351 + return couponMapper.selectListByUserIdAndStatus(userId, status);
  352 + }
  353 +
  354 + @Override
  355 + public Map<Long, Integer> getTakeCountMapByTemplateIds(Collection<Long> templateIds, Long userId) {
  356 + if (CollUtil.isEmpty(templateIds)) {
  357 + return Collections.emptyMap();
  358 + }
  359 + return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);
  360 + }
  361 +
  362 + @Override
  363 + public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
  364 + List<CouponDO> list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,
  365 + CouponStatusEnum.UNUSED.getStatus(),
  366 + matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());
  367 + // 兜底逻辑:如果 CouponExpireJob 未执行,status 未变成 EXPIRE ,但是 validEndTime 已经过期了,需要进行过滤
  368 + list.removeIf(coupon -> !LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime()));
  369 + return list;
  370 + }
  371 +
  372 + @Override
  373 + public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) {
  374 + // 1. 未登录时,都显示可以领取
  375 + Map<Long, Boolean> userCanTakeMap = convertMap(templates, CouponTemplateDO::getId, templateId -> true);
  376 + if (userId == null) {
  377 + return userCanTakeMap;
  378 + }
  379 +
  380 + // 2.1 过滤领取数量无限制的
  381 + Set<Long> templateIds = convertSet(templates, CouponTemplateDO::getId, template -> template.getTakeLimitCount() != -1);
  382 + // 2.2 检查用户领取的数量是否超过限制
  383 + if (CollUtil.isNotEmpty(templateIds)) {
  384 + Map<Long, Integer> couponTakeCountMap = this.getTakeCountMapByTemplateIds(templateIds, userId);
  385 + for (CouponTemplateDO template : templates) {
  386 + Integer takeCount = couponTakeCountMap.get(template.getId());
  387 + userCanTakeMap.put(template.getId(), takeCount == null || takeCount < template.getTakeLimitCount());
  388 + }
  389 + }
  390 + return userCanTakeMap;
  391 + }
  392 +
388 @Override 393 @Override
389 public CouponDO getCoupon(Long userId, Long id) { 394 public CouponDO getCoupon(Long userId, Long id) {
390 return couponMapper.selectByIdAndUserId(id, userId); 395 return couponMapper.selectByIdAndUserId(id, userId);
391 } 396 }
392 397
  398 + private CouponDO validateCouponExists(Long id) {
  399 + CouponDO coupon = couponMapper.selectById(id);
  400 + if (coupon == null) {
  401 + throw exception(COUPON_NOT_EXISTS);
  402 + }
  403 + return coupon;
  404 + }
  405 +
393 /** 406 /**
394 * 获得自身的代理对象,解决 AOP 生效问题 407 * 获得自身的代理对象,解决 AOP 生效问题
395 * 408 *
@@ -398,4 +411,5 @@ public class CouponServiceImpl implements CouponService { @@ -398,4 +411,5 @@ public class CouponServiceImpl implements CouponService {
398 private CouponServiceImpl getSelf() { 411 private CouponServiceImpl getSelf() {
399 return SpringUtil.getBean(getClass()); 412 return SpringUtil.getBean(getClass());
400 } 413 }
  414 +
401 } 415 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java
@@ -159,7 +159,6 @@ public class RewardActivityServiceImpl implements RewardActivityService { @@ -159,7 +159,6 @@ public class RewardActivityServiceImpl implements RewardActivityService {
159 159
160 @Override 160 @Override
161 public List<RewardActivityMatchRespDTO> getMatchRewardActivityList(Collection<Long> spuIds) { 161 public List<RewardActivityMatchRespDTO> getMatchRewardActivityList(Collection<Long> spuIds) {
162 - // TODO 芋艿:待实现;先指定,然后再全局的;  
163 List<RewardActivityDO> list = rewardActivityMapper.selectListBySpuIdsAndStatus(spuIds, CommonStatusEnum.ENABLE.getStatus()); 162 List<RewardActivityDO> list = rewardActivityMapper.selectListBySpuIdsAndStatus(spuIds, CommonStatusEnum.ENABLE.getStatus());
164 return BeanUtils.toBean(list, RewardActivityMatchRespDTO.class); 163 return BeanUtils.toBean(list, RewardActivityMatchRespDTO.class);
165 } 164 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.order; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.order;
2 2
3 import cn.iocoder.yudao.framework.common.enums.TerminalEnum; 3 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
5 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
6 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
@@ -18,6 +19,7 @@ import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; @@ -18,6 +19,7 @@ import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
18 import lombok.*; 19 import lombok.*;
19 20
20 import java.time.LocalDateTime; 21 import java.time.LocalDateTime;
  22 +import java.util.List;
21 import java.util.Map; 23 import java.util.Map;
22 24
23 /** 25 /**
@@ -294,17 +296,23 @@ public class TradeOrderDO extends BaseDO { @@ -294,17 +296,23 @@ public class TradeOrderDO extends BaseDO {
294 */ 296 */
295 private Integer vipPrice; 297 private Integer vipPrice;
296 298
297 - // TODO @puhui999:项了下,貌似这里存储 List<Long> giveCouponIds 更合适。因为优惠劵赠送到最后是对应的编号,然后从而进行取消?  
298 /** 299 /**
299 * 赠送的优惠劵 300 * 赠送的优惠劵
300 * 301 *
301 * key: 优惠劵编号 302 * key: 优惠劵编号
302 * value:对应的优惠券数量 303 * value:对应的优惠券数量
303 * 304 *
304 - * 目的:用于后续取消或者售后订单时,需要扣减赠送 305 + * 目的:用于订单支付后赠送优惠券
305 */ 306 */
306 @TableField(typeHandler = JacksonTypeHandler.class) 307 @TableField(typeHandler = JacksonTypeHandler.class)
307 private Map<Long, Integer> giveCouponsMap; 308 private Map<Long, Integer> giveCouponsMap;
  309 + /**
  310 + * 赠送的优惠劵编号
  311 + *
  312 + * 目的:用于后续取消或者售后订单时,需要扣减赠送
  313 + */
  314 + @TableField(typeHandler = LongListTypeHandler.class)
  315 + private List<Long> giveCouponIds;
308 316
309 /** 317 /**
310 * 秒杀活动编号 318 * 秒杀活动编号
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
@@ -11,6 +11,8 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderI @@ -11,6 +11,8 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderI
11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
12 import jakarta.validation.constraints.NotNull; 12 import jakarta.validation.constraints.NotNull;
13 13
  14 +import java.util.List;
  15 +
14 /** 16 /**
15 * 交易订单【写】Service 接口 17 * 交易订单【写】Service 接口
16 * 18 *
@@ -194,4 +196,13 @@ public interface TradeOrderUpdateService { @@ -194,4 +196,13 @@ public interface TradeOrderUpdateService {
194 */ 196 */
195 void cancelPaidOrder(Long userId, Long orderId, Integer cancelType); 197 void cancelPaidOrder(Long userId, Long orderId, Integer cancelType);
196 198
  199 + /**
  200 + * 更新下单赠送的优惠券编号到订单
  201 + *
  202 + * @param userId 用户编号
  203 + * @param orderId 订单编号
  204 + * @param giveCouponIds 赠送的优惠券编号列表
  205 + */
  206 + void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds);
  207 +
197 } 208 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -202,7 +202,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -202,7 +202,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
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 // 使用 + 赠送优惠券 204 // 使用 + 赠送优惠券
205 - order.setGiveCouponsMap(calculateRespBO.getGiveCouponsMap()); 205 + order.setGiveCouponsMap(calculateRespBO.getGiveCoupons());
206 // 支付 + 退款信息 206 // 支付 + 退款信息
207 order.setAdjustPrice(0).setPayStatus(false); 207 order.setAdjustPrice(0).setPayStatus(false);
208 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); 208 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
@@ -890,6 +890,22 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -890,6 +890,22 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
890 .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice()));// 价格信息 890 .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice()));// 价格信息
891 } 891 }
892 892
  893 + @Override
  894 + public void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds) {
  895 + // 1.1 检验订单存在
  896 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
  897 + if (order == null) {
  898 + throw exception(ORDER_NOT_FOUND);
  899 + }
  900 + // 1.2 校验订单是否支付
  901 + if (!order.getPayStatus()) {
  902 + throw exception(ORDER_CANCEL_PAID_FAIL, "已支付");
  903 + }
  904 +
  905 + // 2. 更新订单赠送的优惠券编号列表
  906 + tradeOrderMapper.updateById(new TradeOrderDO().setId(orderId).setGiveCouponIds(giveCouponIds));
  907 + }
  908 +
893 /** 909 /**
894 * 创建单个订单的评论 910 * 创建单个订单的评论
895 * 911 *
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCouponOrderHandler.java
@@ -5,7 +5,10 @@ import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; @@ -5,7 +5,10 @@ import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
5 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; 5 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
  8 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
  9 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
8 import jakarta.annotation.Resource; 10 import jakarta.annotation.Resource;
  11 +import org.springframework.context.annotation.Lazy;
9 import org.springframework.stereotype.Component; 12 import org.springframework.stereotype.Component;
10 13
11 import java.util.List; 14 import java.util.List;
@@ -19,6 +22,12 @@ import java.util.List; @@ -19,6 +22,12 @@ import java.util.List;
19 public class TradeCouponOrderHandler implements TradeOrderHandler { 22 public class TradeCouponOrderHandler implements TradeOrderHandler {
20 23
21 @Resource 24 @Resource
  25 + @Lazy // 延迟加载,避免循环依赖
  26 + private TradeOrderUpdateService orderUpdateService;
  27 + @Resource
  28 + private TradeOrderQueryService orderQueryService;
  29 +
  30 + @Resource
22 private CouponApi couponApi; 31 private CouponApi couponApi;
23 32
24 @Override 33 @Override
@@ -37,7 +46,11 @@ public class TradeCouponOrderHandler implements TradeOrderHandler { @@ -37,7 +46,11 @@ public class TradeCouponOrderHandler implements TradeOrderHandler {
37 return; 46 return;
38 } 47 }
39 // 赠送优惠券 48 // 赠送优惠券
40 - couponApi.takeCouponsByAdmin(order.getGiveCouponsMap(), order.getUserId()); 49 + List<Long> couponIds = couponApi.takeCouponsByAdmin(order.getGiveCouponsMap(), order.getUserId());
  50 + if (CollUtil.isEmpty(couponIds)) {
  51 + return;
  52 + }
  53 + orderUpdateService.updateOrderGiveCouponIds(order.getUserId(), order.getId(), couponIds);
41 } 54 }
42 55
43 @Override 56 @Override
@@ -48,10 +61,10 @@ public class TradeCouponOrderHandler implements TradeOrderHandler { @@ -48,10 +61,10 @@ public class TradeCouponOrderHandler implements TradeOrderHandler {
48 couponApi.returnUsedCoupon(order.getCouponId()); 61 couponApi.returnUsedCoupon(order.getCouponId());
49 } 62 }
50 // 情况二:收回赠送的优惠券 63 // 情况二:收回赠送的优惠券
51 - if (CollUtil.isEmpty(order.getGiveCouponsMap())) { 64 + if (CollUtil.isEmpty(order.getGiveCouponIds())) {
52 return; 65 return;
53 } 66 }
54 - couponApi.invalidateCouponsByAdmin(order.getGiveCouponsMap(), order.getUserId()); 67 + couponApi.invalidateCouponsByAdmin(order.getGiveCouponIds(), order.getUserId());
55 } 68 }
56 69
57 } 70 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
@@ -79,7 +79,7 @@ public class TradePriceCalculateRespBO { @@ -79,7 +79,7 @@ public class TradePriceCalculateRespBO {
79 * key: 优惠劵编号,value:对应的优惠券数量 79 * key: 优惠劵编号,value:对应的优惠券数量
80 * 目的:用于后续取消或者售后订单时,需要扣减赠送 80 * 目的:用于后续取消或者售后订单时,需要扣减赠送
81 */ 81 */
82 - private Map<Long, Integer> giveCouponsMap; 82 + private Map<Long, Integer> giveCoupons;
83 83
84 /** 84 /**
85 * 订单价格 85 * 订单价格
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
@@ -32,7 +32,7 @@ public class TradePriceCalculatorHelper { @@ -32,7 +32,7 @@ public class TradePriceCalculatorHelper {
32 List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) { 32 List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) {
33 // 创建 PriceCalculateRespDTO 对象 33 // 创建 PriceCalculateRespDTO 对象
34 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO(); 34 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO();
35 - result.setType(getOrderType(param)).setPromotions(new ArrayList<>()).setGiveCouponsMap(new LinkedHashMap<>()); 35 + result.setType(getOrderType(param)).setPromotions(new ArrayList<>()).setGiveCoupons(new LinkedHashMap<>());
36 36
37 // 创建它的 OrderItem 属性 37 // 创建它的 OrderItem 属性
38 result.setItems(new ArrayList<>(param.getItems().size())); 38 result.setItems(new ArrayList<>(param.getItems().size()));
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
@@ -93,7 +93,7 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator @@ -93,7 +93,7 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
93 TradePriceCalculatorHelper.recountAllPrice(result); 93 TradePriceCalculatorHelper.recountAllPrice(result);
94 94
95 // 4.1 记录赠送的积分 95 // 4.1 记录赠送的积分
96 - if (Boolean.TRUE.equals(rule.getGivePoint())) { 96 + if (rule.getPoint() != null && rule.getPoint() > 0) {
97 List<Integer> dividePoints = TradePriceCalculatorHelper.dividePrice(orderItems, rule.getPoint()); 97 List<Integer> dividePoints = TradePriceCalculatorHelper.dividePrice(orderItems, rule.getPoint());
98 for (int i = 0; i < orderItems.size(); i++) { 98 for (int i = 0; i < orderItems.size(); i++) {
99 // 商品可能赠送了积分,所以这里要加上 99 // 商品可能赠送了积分,所以这里要加上
@@ -107,13 +107,13 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator @@ -107,13 +107,13 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
107 result.setFreeDelivery(true); 107 result.setFreeDelivery(true);
108 } 108 }
109 // 4.3 记录赠送的优惠券 109 // 4.3 记录赠送的优惠券
110 - if (Boolean.TRUE.equals(rule.getGiveCoupon())) {  
111 - for (Map.Entry<Long, Integer> entry : rule.getGiveCouponsMap().entrySet()) {  
112 - Map<Long, Integer> giveCouponsMap = result.getGiveCouponsMap();  
113 - if (giveCouponsMap.get(entry.getKey()) == null) { // 情况一:还没有赠送的优惠券  
114 - result.setGiveCouponsMap(rule.getGiveCouponsMap()); 110 + if (CollUtil.isNotEmpty(rule.getGiveCoupons())) {
  111 + for (Map.Entry<Long, Integer> entry : rule.getGiveCoupons().entrySet()) {
  112 + Map<Long, Integer> giveCoupons = result.getGiveCoupons();
  113 + if (giveCoupons.get(entry.getKey()) == null) { // 情况一:还没有赠送的优惠券
  114 + result.setGiveCoupons(rule.getGiveCoupons());
115 } else { // 情况二:别的满减活动送过同类优惠券,则直接增加数量 115 } else { // 情况二:别的满减活动送过同类优惠券,则直接增加数量
116 - giveCouponsMap.put(entry.getKey(), giveCouponsMap.get(entry.getKey()) + entry.getValue()); 116 + giveCoupons.put(entry.getKey(), giveCoupons.get(entry.getKey()) + entry.getValue());
117 } 117 }
118 } 118 }
119 } 119 }
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculatorTest.java
@@ -49,7 +49,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest @@ -49,7 +49,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
49 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO() 49 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
50 .setType(TradeOrderTypeEnum.NORMAL.getType()) 50 .setType(TradeOrderTypeEnum.NORMAL.getType())
51 .setPrice(new TradePriceCalculateRespBO.Price()) 51 .setPrice(new TradePriceCalculateRespBO.Price())
52 - .setPromotions(new ArrayList<>()).setGiveCouponsMap(new LinkedHashMap<>()) 52 + .setPromotions(new ArrayList<>()).setGiveCoupons(new LinkedHashMap<>())
53 .setItems(asList( 53 .setItems(asList(
54 new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true) 54 new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
55 .setPrice(100).setSpuId(1L), 55 .setPrice(100).setSpuId(1L),
@@ -68,16 +68,16 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest @@ -68,16 +68,16 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
68 .setConditionType(PromotionConditionTypeEnum.PRICE.getType()) 68 .setConditionType(PromotionConditionTypeEnum.PRICE.getType())
69 .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L)) 69 .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
70 .setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(20).setDiscountPrice(70) 70 .setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(20).setDiscountPrice(70)
71 - .setGivePoint(false).setFreeDelivery(false)))), 71 + .setFreeDelivery(false)))),
72 randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(2000L).setName("活动 2000 号") 72 randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(2000L).setName("活动 2000 号")
73 .setConditionType(PromotionConditionTypeEnum.COUNT.getType()) 73 .setConditionType(PromotionConditionTypeEnum.COUNT.getType())
74 .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(singletonList(3L)) 74 .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(singletonList(3L))
75 .setRules(asList(new RewardActivityMatchRespDTO.Rule().setLimit(1).setDiscountPrice(10) 75 .setRules(asList(new RewardActivityMatchRespDTO.Rule().setLimit(1).setDiscountPrice(10)
76 - .setGivePoint(true).setPoint(50).setFreeDelivery(false),  
77 - new RewardActivityMatchRespDTO.Rule().setLimit(2).setDiscountPrice(60).setGivePoint(true) 76 + .setPoint(50).setFreeDelivery(false),
  77 + new RewardActivityMatchRespDTO.Rule().setLimit(2).setDiscountPrice(60)
78 .setPoint(100).setFreeDelivery(false), // 最大可满足,因为是 4 个 78 .setPoint(100).setFreeDelivery(false), // 最大可满足,因为是 4 个
79 new RewardActivityMatchRespDTO.Rule().setLimit(10).setDiscountPrice(100) 79 new RewardActivityMatchRespDTO.Rule().setLimit(10).setDiscountPrice(100)
80 - .setGivePoint(false).setFreeDelivery(false)))) 80 + .setFreeDelivery(false))))
81 )); 81 ));
82 82
83 // 调用 83 // 调用