Commit 0b35d4a7e3c55b29b2153f00285a31c6b5ecad8c

Authored by puhui999
1 parent 5ecd0d94

订单:完善拼团活动部分 TODO

Showing 16 changed files with 179 additions and 102 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
@@ -93,4 +93,44 @@ public class LocalDateTimeUtils { @@ -93,4 +93,44 @@ public class LocalDateTimeUtils {
93 LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2)); 93 LocalDateTime.of(nowDate, startTime2), LocalDateTime.of(nowDate, endTime2));
94 } 94 }
95 95
  96 + /**
  97 + * 构建日期时间 TODO 后面有需要的话再继续扩展
  98 + *
  99 + * @author HUIHUI
  100 + */
  101 + public static class BuilderDateTime {
  102 +
  103 + /**
  104 + * 日期;2023-10-01
  105 + */
  106 + private String localDate;
  107 + /**
  108 + * 时间;10:01:00
  109 + */
  110 + private String localTime;
  111 +
  112 + public BuilderDateTime() {
  113 + }
  114 +
  115 + public BuilderDateTime withDate(String date) {
  116 + this.localDate = date;
  117 + return this;
  118 + }
  119 +
  120 + public BuilderDateTime withDate(LocalDateTime date) {
  121 + this.localDate = LocalDateTimeUtil.format(date, "yyyy-MM-dd");
  122 + return this;
  123 + }
  124 +
  125 + public BuilderDateTime withTime(String time) {
  126 + this.localTime = time;
  127 + return this;
  128 + }
  129 +
  130 + public LocalDateTime build() {
  131 + return LocalDateTimeUtil.parse(this.localDate + " " + this.localTime, "yyyy-MM-dd HH:mm:ss");
  132 + }
  133 +
  134 + }
  135 +
96 } 136 }
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImplTest.java
@@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper; @@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.product.dal.mysql.comment.ProductCommentMapper;
16 import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum; 16 import cn.iocoder.yudao.module.product.enums.comment.ProductCommentScoresEnum;
17 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; 17 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
18 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; 18 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
19 -import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;  
20 import org.junit.jupiter.api.Test; 19 import org.junit.jupiter.api.Test;
21 import org.springframework.boot.test.mock.mockito.MockBean; 20 import org.springframework.boot.test.mock.mockito.MockBean;
22 import org.springframework.context.annotation.Import; 21 import org.springframework.context.annotation.Import;
@@ -49,8 +48,6 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest { @@ -49,8 +48,6 @@ public class ProductCommentServiceImplTest extends BaseDbUnitTest {
49 private ProductCommentServiceImpl productCommentService; 48 private ProductCommentServiceImpl productCommentService;
50 49
51 @MockBean 50 @MockBean
52 - private TradeOrderApi tradeOrderApi;  
53 - @MockBean  
54 private ProductSpuService productSpuService; 51 private ProductSpuService productSpuService;
55 @MockBean 52 @MockBean
56 private ProductSkuService productSkuService; 53 private ProductSkuService productSkuService;
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationActivityApi.java
@@ -7,14 +7,4 @@ package cn.iocoder.yudao.module.promotion.api.combination; @@ -7,14 +7,4 @@ package cn.iocoder.yudao.module.promotion.api.combination;
7 */ 7 */
8 public interface CombinationActivityApi { 8 public interface CombinationActivityApi {
9 9
10 - /**  
11 - * 校验是否满足拼团条件  
12 - *  
13 - * @param activityId 活动编号  
14 - * @param userId 用户编号  
15 - * @param skuId sku 编号  
16 - * @param count 数量  
17 - */  
18 - void validateCombination(Long activityId, Long userId, Long skuId, Integer count);  
19 -  
20 } 10 }
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
@@ -15,6 +15,16 @@ import java.time.LocalDateTime; @@ -15,6 +15,16 @@ import java.time.LocalDateTime;
15 public interface CombinationRecordApi { 15 public interface CombinationRecordApi {
16 16
17 /** 17 /**
  18 + * 校验是否满足拼团条件
  19 + *
  20 + * @param activityId 活动编号
  21 + * @param userId 用户编号
  22 + * @param skuId sku 编号
  23 + * @param count 数量
  24 + */
  25 + void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count);
  26 +
  27 + /**
18 * 创建开团记录 28 * 创建开团记录
19 * 29 *
20 * @param reqDTO 请求 DTO 30 * @param reqDTO 请求 DTO
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
@@ -75,9 +75,10 @@ public interface ErrorCodeConstants { @@ -75,9 +75,10 @@ public interface ErrorCodeConstants {
75 ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013011002, "拼团失败,父拼团不存在"); 75 ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013011002, "拼团失败,父拼团不存在");
76 ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1013011003, "拼团失败,拼团人数已满"); 76 ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1013011003, "拼团失败,拼团人数已满");
77 ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1013011004, "拼团失败,已参与其它拼团"); 77 ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1013011004, "拼团失败,已参与其它拼团");
78 - ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1013011005, "拼团失败,活动已经结束");  
79 - ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1013011006, "拼团失败,原因:单次限购超出");  
80 - ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1013011007, "拼团失败,原因:超出总购买次数"); 78 + ErrorCode COMBINATION_RECORD_FAILED_TIME_NOT_START = new ErrorCode(1013011005, "拼团失败,活动未开始");
  79 + ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1013011006, "拼团失败,活动已经结束");
  80 + ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1013011007, "拼团失败,原因:单次限购超出");
  81 + ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1013011008, "拼团失败,原因:超出总购买次数");
81 82
82 // ========== 砍价活动 1013012000 ========== 83 // ========== 砍价活动 1013012000 ==========
83 ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1013012000, "砍价活动不存在"); 84 ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1013012000, "砍价活动不存在");
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationActivityApiImpl.java
1 package cn.iocoder.yudao.module.promotion.api.combination; 1 package cn.iocoder.yudao.module.promotion.api.combination;
2 2
3 -import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;  
4 import org.springframework.stereotype.Service; 3 import org.springframework.stereotype.Service;
5 4
6 -import javax.annotation.Resource;  
7 -  
8 /** 5 /**
9 * 拼团活动 Api 接口实现类 6 * 拼团活动 Api 接口实现类
10 * 7 *
@@ -13,12 +10,4 @@ import javax.annotation.Resource; @@ -13,12 +10,4 @@ import javax.annotation.Resource;
13 @Service 10 @Service
14 public class CombinationActivityApiImpl implements CombinationActivityApi { 11 public class CombinationActivityApiImpl implements CombinationActivityApi {
15 12
16 - @Resource  
17 - private CombinationActivityService activityService;  
18 -  
19 - @Override  
20 - public void validateCombination(Long activityId, Long userId, Long skuId, Integer count) {  
21 - activityService.validateCombination(activityId, userId, skuId, count);  
22 - }  
23 -  
24 } 13 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
@@ -20,6 +20,11 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { @@ -20,6 +20,11 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
20 private CombinationRecordService recordService; 20 private CombinationRecordService recordService;
21 21
22 @Override 22 @Override
  23 + public void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count) {
  24 + recordService.validateCombinationRecord(activityId, userId, skuId, count);
  25 + }
  26 +
  27 + @Override
23 public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) { 28 public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
24 recordService.createCombinationRecord(reqDTO); 29 recordService.createCombinationRecord(reqDTO);
25 } 30 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java
1 package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity; 1 package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity;
2 2
3 -import cn.hutool.core.date.LocalDateTimeUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
7 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; 7 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
8 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 8 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
9 import cn.iocoder.yudao.module.product.enums.DictTypeConstants; 9 import cn.iocoder.yudao.module.product.enums.DictTypeConstants;
@@ -129,11 +129,14 @@ public interface SeckillActivityConvert { @@ -129,11 +129,14 @@ public interface SeckillActivityConvert {
129 default AppSeckillActivityDetailRespVO convert3(SeckillActivityDO seckillActivity, List<SeckillProductDO> products, SeckillConfigDO filteredConfig) { 129 default AppSeckillActivityDetailRespVO convert3(SeckillActivityDO seckillActivity, List<SeckillProductDO> products, SeckillConfigDO filteredConfig) {
130 return convert2(seckillActivity) 130 return convert2(seckillActivity)
131 .setProducts(convertList1(products)) 131 .setProducts(convertList1(products))
132 - // TODO @puhui999:要不要在里面 default 一个方法,处理这个事件;简洁一点;  
133 - .setStartTime(LocalDateTimeUtil.parse(LocalDateTimeUtil.format(seckillActivity.getStartTime(), "yyyy-MM-dd") + " " + filteredConfig.getStartTime(),  
134 - "yyyy-MM-dd HH:mm:ss")) // 活动开始日期和时段结合  
135 - .setEndTime(LocalDateTimeUtil.parse(LocalDateTimeUtil.format(seckillActivity.getEndTime(), "yyyy-MM-dd") + " " + filteredConfig.getEndTime(),  
136 - "yyyy-MM-dd HH:mm:ss")); // 活动结束日期和时段结合 132 + .setStartTime(new LocalDateTimeUtils.BuilderDateTime()
  133 + .withDate(seckillActivity.getStartTime())
  134 + .withTime(filteredConfig.getStartTime())
  135 + .build())// 活动开始日期和时段结合
  136 + .setEndTime(new LocalDateTimeUtils.BuilderDateTime()
  137 + .withDate(seckillActivity.getEndTime())
  138 + .withTime(filteredConfig.getEndTime())
  139 + .build()); // 活动结束日期和时段结合
137 } 140 }
138 141
139 List<SeckillActivityProductRespDTO> convertList4(List<SeckillProductDO> seckillActivityProductList); 142 List<SeckillActivityProductRespDTO> convertList4(List<SeckillProductDO> seckillActivityProductList);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java
@@ -20,6 +20,10 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO @@ -20,6 +20,10 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO
20 CombinationRecordDO::getOrderId, orderId); 20 CombinationRecordDO::getOrderId, orderId);
21 } 21 }
22 22
  23 + default List<CombinationRecordDO> selectListByUserId(Long userId) {
  24 + return selectList(CombinationRecordDO::getUserId, userId);
  25 + }
  26 +
23 default List<CombinationRecordDO> selectListByUserIdAndStatus(Long userId, Integer status) { 27 default List<CombinationRecordDO> selectListByUserIdAndStatus(Long userId, Integer status) {
24 return selectList(new LambdaQueryWrapperX<CombinationRecordDO>() 28 return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
25 .eq(CombinationRecordDO::getUserId, userId) 29 .eq(CombinationRecordDO::getUserId, userId)
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
@@ -85,17 +85,6 @@ public interface CombinationActivityService { @@ -85,17 +85,6 @@ public interface CombinationActivityService {
85 List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds); 85 List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds);
86 86
87 /** 87 /**
88 - * 校验是否满足拼团条件  
89 - * 如果不满足,会抛出异常  
90 - *  
91 - * @param activityId 活动编号  
92 - * @param userId 用户编号  
93 - * @param skuId sku 编号  
94 - * @param count 数量  
95 - */  
96 - void validateCombination(Long activityId, Long userId, Long skuId, Integer count);  
97 -  
98 - /**  
99 * 获取正在进行的活动分页数据 88 * 获取正在进行的活动分页数据
100 * 89 *
101 * @param count 需要的数量 90 * @param count 需要的数量
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
@@ -17,12 +17,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product @@ -17,12 +17,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product
17 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; 17 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
18 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 18 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
19 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; 19 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
20 -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;  
21 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper; 20 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper;
22 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper; 21 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper;
23 -import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;  
24 -import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;  
25 -import org.springframework.context.annotation.Lazy;  
26 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
27 import org.springframework.transaction.annotation.Transactional; 23 import org.springframework.transaction.annotation.Transactional;
28 import org.springframework.validation.annotation.Validated; 24 import org.springframework.validation.annotation.Validated;
@@ -33,7 +29,8 @@ import java.util.List; @@ -33,7 +29,8 @@ import java.util.List;
33 import java.util.Map; 29 import java.util.Map;
34 30
35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
36 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
37 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; 34 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
38 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; 35 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
39 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 36 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
@@ -54,15 +51,9 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic @@ -54,15 +51,9 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
54 private CombinationProductMapper combinationProductMapper; 51 private CombinationProductMapper combinationProductMapper;
55 52
56 @Resource 53 @Resource
57 - @Lazy // TODO @puhui999:我感觉 validateCombination 可以挪到 CombinationRecordServiceImpl 中,因为它更偏向能不能创建拼团记录;  
58 - private CombinationRecordService combinationRecordService;  
59 -  
60 - @Resource  
61 private ProductSpuApi productSpuApi; 54 private ProductSpuApi productSpuApi;
62 @Resource 55 @Resource
63 private ProductSkuApi productSkuApi; 56 private ProductSkuApi productSkuApi;
64 - @Resource  
65 - private TradeOrderApi tradeOrderApi;  
66 57
67 @Override 58 @Override
68 @Transactional(rollbackFor = Exception.class) 59 @Transactional(rollbackFor = Exception.class)
@@ -216,36 +207,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic @@ -216,36 +207,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
216 } 207 }
217 208
218 @Override 209 @Override
219 - public void validateCombination(Long activityId, Long userId, Long skuId, Integer count) {  
220 - // 1.1 校验拼团活动是否存在  
221 - CombinationActivityDO activity = validateCombinationActivityExists(activityId);  
222 - // 1.2 校验活动是否开启  
223 - if (ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {  
224 - throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);  
225 - }  
226 - // 1.3 校验是否超出单次限购数量  
227 - if (count > activity.getSingleLimitCount()) {  
228 - throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);  
229 - }  
230 -  
231 - // 2. 校验是否超出总限购数量  
232 - List<CombinationRecordDO> recordList = combinationRecordService.getRecordListByUserIdAndActivityId(userId, activityId);  
233 - if (CollUtil.isEmpty(recordList)) {  
234 - return;  
235 - }  
236 - // 过滤出拼团成功的  
237 - // TODO @puhui999:count 要不存一个在 record 里?  
238 - List<Long> skuIds = convertList(recordList, CombinationRecordDO::getSkuId,  
239 - item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus()));  
240 - Integer countSum = tradeOrderApi.getOrderItemCountSumByOrderIdAndSkuId(convertList(recordList,  
241 - CombinationRecordDO::getOrderId,  
242 - item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), skuIds);  
243 - if (activity.getTotalLimitCount() < countSum) {  
244 - throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);  
245 - }  
246 - }  
247 -  
248 - @Override  
249 public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) { 210 public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) {
250 return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count); 211 return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count);
251 } 212 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java
@@ -23,6 +23,17 @@ public interface CombinationRecordService { @@ -23,6 +23,17 @@ public interface CombinationRecordService {
23 void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId); 23 void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId);
24 24
25 /** 25 /**
  26 + * 校验是否满足拼团条件
  27 + * 如果不满足,会抛出异常
  28 + *
  29 + * @param activityId 活动编号
  30 + * @param userId 用户编号
  31 + * @param skuId sku 编号
  32 + * @param count 数量
  33 + */
  34 + void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count);
  35 +
  36 + /**
26 * 创建拼团记录 37 * 创建拼团记录
27 * 38 *
28 * @param reqDTO 创建信息 39 * @param reqDTO 创建信息
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.service.combination; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.service.combination;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
  5 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 6 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 7 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
7 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 8 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
@@ -14,6 +15,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationA @@ -14,6 +15,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationA
14 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 15 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
15 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper; 16 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
16 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; 17 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
  18 +import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
17 import org.springframework.context.annotation.Lazy; 19 import org.springframework.context.annotation.Lazy;
18 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
19 import org.springframework.transaction.annotation.Transactional; 21 import org.springframework.transaction.annotation.Transactional;
@@ -24,6 +26,7 @@ import java.time.LocalDateTime; @@ -24,6 +26,7 @@ import java.time.LocalDateTime;
24 import java.util.List; 26 import java.util.List;
25 27
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 28 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
27 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 30 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
28 31
29 // TODO 芋艿:等拼团记录做完,完整 review 下 32 // TODO 芋艿:等拼团记录做完,完整 review 下
@@ -51,6 +54,8 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -51,6 +54,8 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
51 @Resource 54 @Resource
52 @Lazy 55 @Lazy
53 private ProductSkuApi productSkuApi; 56 private ProductSkuApi productSkuApi;
  57 + @Resource
  58 + private TradeOrderApi tradeOrderApi;
54 59
55 @Override 60 @Override
56 @Transactional(rollbackFor = Exception.class) 61 @Transactional(rollbackFor = Exception.class)
@@ -97,6 +102,35 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -97,6 +102,35 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
97 } 102 }
98 103
99 // TODO @puhui999:有一个应该在创建那要做下;就是当前 activityId 已经有未支付的订单,不允许在发起新的;要么支付,要么去掉先; 104 // TODO @puhui999:有一个应该在创建那要做下;就是当前 activityId 已经有未支付的订单,不允许在发起新的;要么支付,要么去掉先;
  105 + @Override
  106 + public void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count) {
  107 + // 1.1 校验拼团活动是否存在
  108 + CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId);
  109 + // 1.2 校验活动是否开启
  110 + if (ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
  111 + throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);
  112 + }
  113 + // 1.3 校验是否超出单次限购数量
  114 + if (count > activity.getSingleLimitCount()) {
  115 + throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
  116 + }
  117 +
  118 + // 2. 校验是否超出总限购数量
  119 + List<CombinationRecordDO> recordList = getRecordListByUserIdAndActivityId(userId, activityId);
  120 + if (CollUtil.isEmpty(recordList)) {
  121 + return;
  122 + }
  123 + // 过滤出拼团成功的
  124 + // TODO @puhui999:count 要不存一个在 record 里?
  125 + List<Long> skuIds = convertList(recordList, CombinationRecordDO::getSkuId,
  126 + item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus()));
  127 + Integer countSum = tradeOrderApi.getOrderItemCountSumByOrderIdAndSkuId(convertList(recordList,
  128 + CombinationRecordDO::getOrderId,
  129 + item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), skuIds);
  130 + if (activity.getTotalLimitCount() < countSum) {
  131 + throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);
  132 + }
  133 + }
100 134
101 @Override 135 @Override
102 @Transactional(rollbackFor = Exception.class) 136 @Transactional(rollbackFor = Exception.class)
@@ -105,21 +139,26 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -105,21 +139,26 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
105 CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(reqDTO.getActivityId()); 139 CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(reqDTO.getActivityId());
106 // 1.2 需要校验下,他当前是不是已经参加了该拼团; 140 // 1.2 需要校验下,他当前是不是已经参加了该拼团;
107 // TODO @puhui999:拼团应该可以重复参加;应该去校验总共的上限哈,就是 activity.totalLimitCount 141 // TODO @puhui999:拼团应该可以重复参加;应该去校验总共的上限哈,就是 activity.totalLimitCount
108 - CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(reqDTO.getUserId(), reqDTO.getOrderId());  
109 - if (recordDO != null) {  
110 - throw exception(COMBINATION_RECORD_EXISTS); 142 + List<CombinationRecordDO> records = recordMapper.selectListByUserId(reqDTO.getUserId());
  143 + List<Long> orderIds = convertList(records, CombinationRecordDO::getOrderId);
  144 + // 1.2.1 如果存在订单才去校验
  145 + if (CollUtil.isNotEmpty(orderIds)) {
  146 +
111 } 147 }
112 // 1.3 校验用户是否参加了其它拼团 148 // 1.3 校验用户是否参加了其它拼团
113 List<CombinationRecordDO> recordDOList = recordMapper.selectListByUserIdAndStatus(reqDTO.getUserId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); 149 List<CombinationRecordDO> recordDOList = recordMapper.selectListByUserIdAndStatus(reqDTO.getUserId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
114 if (CollUtil.isNotEmpty(recordDOList)) { 150 if (CollUtil.isNotEmpty(recordDOList)) {
115 throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED); 151 throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED);
116 } 152 }
117 - // TODO @puhui999:有个开始时间未校验  
118 - // 1.4 校验当前活动是否过期 153 + // 1、4 校验活动是否开启
  154 + if (LocalDateTime.now().isAfter(activity.getStartTime())) {
  155 + throw exception(COMBINATION_RECORD_FAILED_TIME_NOT_START);
  156 + }
  157 + // 1.5 校验当前活动是否过期
119 if (LocalDateTime.now().isAfter(activity.getEndTime())) { 158 if (LocalDateTime.now().isAfter(activity.getEndTime())) {
120 throw exception(COMBINATION_RECORD_FAILED_TIME_END); 159 throw exception(COMBINATION_RECORD_FAILED_TIME_END);
121 } 160 }
122 - // 1.5 父拼团是否存在,是否已经满了 161 + // 1.6 父拼团是否存在,是否已经满了
123 if (reqDTO.getHeadId() != null) { 162 if (reqDTO.getHeadId() != null) {
124 // 查询进行中的父拼团 163 // 查询进行中的父拼团
125 CombinationRecordDO record = recordMapper.selectOneByHeadId(reqDTO.getHeadId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); 164 CombinationRecordDO record = recordMapper.selectOneByHeadId(reqDTO.getHeadId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -56,6 +56,7 @@ import cn.iocoder.yudao.module.trade.service.cart.CartService; @@ -56,6 +56,7 @@ import cn.iocoder.yudao.module.trade.service.cart.CartService;
56 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; 56 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
57 import cn.iocoder.yudao.module.trade.service.message.TradeMessageService; 57 import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
58 import cn.iocoder.yudao.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO; 58 import cn.iocoder.yudao.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO;
  59 +import cn.iocoder.yudao.module.trade.service.order.bo.TradeAfterPayOrderReqBO;
59 import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO; 60 import cn.iocoder.yudao.module.trade.service.order.bo.TradeBeforeOrderCreateReqBO;
60 import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler; 61 import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler;
61 import cn.iocoder.yudao.module.trade.service.price.TradePriceService; 62 import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
@@ -333,13 +334,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -333,13 +334,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
333 if (updateCount == 0) { 334 if (updateCount == 0) {
334 throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); 335 throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
335 } 336 }
336 - // 校验活动  
337 - // 1、拼团活动  
338 - // TODO @puhui999:这块也抽象到 handler 里  
339 - if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {  
340 - // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录  
341 - combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now());  
342 - } 337 + // 订单支付成功后
  338 + tradeOrderHandlers.forEach(tradeOrderHandler -> tradeOrderHandler.afterPayOrder(new TradeAfterPayOrderReqBO()
  339 + .setOrderId(order.getId()).setOrderType(order.getType()).setUserId(order.getUserId()).setPayTime(LocalDateTime.now())));
343 // TODO 芋艿:发送订单变化的消息 340 // TODO 芋艿:发送订单变化的消息
344 341
345 // TODO 芋艿:发送站内信 342 // TODO 芋艿:发送站内信
@@ -742,7 +739,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -742,7 +739,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
742 } 739 }
743 740
744 // 3. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀 741 // 3. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀
745 - tradeOrderHandlers.forEach(handler -> handler.rollback()); 742 + tradeOrderHandlers.forEach(handler -> handler.cancelOrder());
746 743
747 // 4. 回滚库存 744 // 4. 回滚库存
748 List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id); 745 List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/bo/TradeAfterPayOrderReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.order.bo;
  2 +
  3 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +/**
  10 + * 订单支付后 Request BO
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@Data
  15 +public class TradeAfterPayOrderReqBO {
  16 +
  17 + /**
  18 + * 订单编号
  19 + */
  20 + @Schema(description = "订单编号", example = "6")
  21 + private Long orderId;
  22 +
  23 + /**
  24 + * 订单类型
  25 + *
  26 + * 枚举 {@link TradeOrderTypeEnum}
  27 + */
  28 + @Schema(description = "订单类型", example = "3")
  29 + private Integer orderType;
  30 +
  31 + /**
  32 + * 用户编号
  33 + */
  34 + @Schema(description = "用户编号", example = "11")
  35 + private Long userId;
  36 +
  37 + /**
  38 + * 订单支付时间
  39 + */
  40 + @Schema(description = "订单支付时间", example = "2023-08-15 10:00:00")
  41 + private LocalDateTime payTime;
  42 +
  43 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java
1 package cn.iocoder.yudao.module.trade.service.price.calculator; 1 package cn.iocoder.yudao.module.trade.service.price.calculator;
2 2
3 -import cn.hutool.core.util.ObjectUtil;  
4 import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; 3 import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi;
5 import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityProductRespDTO; 4 import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillActivityProductRespDTO;
6 -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;  
7 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 5 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
8 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 6 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
9 import org.springframework.core.annotation.Order; 7 import org.springframework.core.annotation.Order;
@@ -31,7 +29,7 @@ public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator @@ -31,7 +29,7 @@ public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator
31 @Override 29 @Override
32 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { 30 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
33 // 1、判断订单类型和是否具有秒杀活动编号 31 // 1、判断订单类型和是否具有秒杀活动编号
34 - if (ObjectUtil.notEqual(param.getType(), TradeOrderTypeEnum.SECKILL.getType()) && param.getSeckillActivityId() == null) { 32 + if (param.getSeckillActivityId() == null) {
35 return; 33 return;
36 } 34 }
37 // 2、获取秒杀活动商品信息 35 // 2、获取秒杀活动商品信息