Commit 74a5e40d8d4306d48adaea4968a81e8f58a4ef92

Authored by owen
2 parents df16278a 7b727d5c

Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

# Conflicts:
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java
Showing 59 changed files with 1164 additions and 495 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -240,7 +240,8 @@ public class CollectionUtils { @@ -240,7 +240,8 @@ public class CollectionUtils {
240 return valueFunc.apply(t); 240 return valueFunc.apply(t);
241 } 241 }
242 242
243 - public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc, BinaryOperator<V> accumulator) { 243 + public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc,
  244 + BinaryOperator<V> accumulator) {
244 if (CollUtil.isEmpty(from)) { 245 if (CollUtil.isEmpty(from)) {
245 return null; 246 return null;
246 } 247 }
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
@@ -56,7 +56,7 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; { @@ -56,7 +56,7 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
56 } 56 }
57 57
58 default Long selectCount() { 58 default Long selectCount() {
59 - return selectCount(new QueryWrapper<T>()); 59 + return selectCount(new QueryWrapper<>());
60 } 60 }
61 61
62 default Long selectCount(String field, Object value) { 62 default Long selectCount(String field, Object value) {
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
@@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCr @@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCr
4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
5 5
6 import javax.validation.Valid; 6 import javax.validation.Valid;
7 -import java.time.LocalDateTime;  
8 7
9 // TODO @芋艿:后面也再撸撸这几个接口 8 // TODO @芋艿:后面也再撸撸这几个接口
10 9
@@ -18,19 +17,21 @@ public interface CombinationRecordApi { @@ -18,19 +17,21 @@ public interface CombinationRecordApi {
18 /** 17 /**
19 * 校验是否满足拼团条件 18 * 校验是否满足拼团条件
20 * 19 *
21 - * @param activityId 活动编号  
22 * @param userId 用户编号 20 * @param userId 用户编号
  21 + * @param activityId 活动编号
  22 + * @param headId 团长编号
23 * @param skuId sku 编号 23 * @param skuId sku 编号
24 * @param count 数量 24 * @param count 数量
25 */ 25 */
26 - void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count); 26 + void validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count);
27 27
28 /** 28 /**
29 * 创建开团记录 29 * 创建开团记录
30 * 30 *
31 * @param reqDTO 请求 DTO 31 * @param reqDTO 请求 DTO
  32 + * @return 开团记录编号
32 */ 33 */
33 - void createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO); 34 + Long createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
34 35
35 /** 36 /**
36 * 查询拼团记录是否成功 37 * 查询拼团记录是否成功
@@ -42,14 +43,6 @@ public interface CombinationRecordApi { @@ -42,14 +43,6 @@ public interface CombinationRecordApi {
42 boolean isCombinationRecordSuccess(Long userId, Long orderId); 43 boolean isCombinationRecordSuccess(Long userId, Long orderId);
43 44
44 /** 45 /**
45 - * 更新拼团状态为【成功】  
46 - *  
47 - * @param userId 用户编号  
48 - * @param orderId 订单编号  
49 - */  
50 - void updateRecordStatusToSuccess(Long userId, Long orderId);  
51 -  
52 - /**  
53 * 更新拼团状态为【失败】 46 * 更新拼团状态为【失败】
54 * 47 *
55 * @param userId 用户编号 48 * @param userId 用户编号
@@ -58,25 +51,18 @@ public interface CombinationRecordApi { @@ -58,25 +51,18 @@ public interface CombinationRecordApi {
58 void updateRecordStatusToFailed(Long userId, Long orderId); 51 void updateRecordStatusToFailed(Long userId, Long orderId);
59 52
60 /** 53 /**
61 - * 更新拼团状态为 进行中  
62 - *  
63 - * @param userId 用户编号  
64 - * @param orderId 订单编号  
65 - * @param startTime 开始时间  
66 - */  
67 - void updateRecordStatusToInProgress(Long userId, Long orderId, LocalDateTime startTime);  
68 -  
69 - /**  
70 * 【下单前】校验是否满足拼团活动条件 54 * 【下单前】校验是否满足拼团活动条件
71 * 55 *
72 * 如果校验失败,则抛出业务异常 56 * 如果校验失败,则抛出业务异常
73 * 57 *
74 - * @param activityId 活动编号  
75 * @param userId 用户编号 58 * @param userId 用户编号
  59 + * @param activityId 活动编号
  60 + * @param headId 团长编号
76 * @param skuId sku 编号 61 * @param skuId sku 编号
77 * @param count 数量 62 * @param count 数量
78 * @return 拼团信息 63 * @return 拼团信息
79 */ 64 */
80 - CombinationValidateJoinRespDTO validateJoinCombination(Long activityId, Long userId, Long skuId, Integer count); 65 + CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId,
  66 + Long skuId, Integer count);
81 67
82 } 68 }
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordCreateReqDTO.java
@@ -4,7 +4,6 @@ import lombok.Data; @@ -4,7 +4,6 @@ import lombok.Data;
4 4
5 import javax.validation.constraints.NotNull; 5 import javax.validation.constraints.NotNull;
6 6
7 -// TODO @芋艿:这块要在看看  
8 /** 7 /**
9 * 拼团记录的创建 Request DTO 8 * 拼团记录的创建 Request DTO
10 * 9 *
@@ -46,7 +45,6 @@ public class CombinationRecordCreateReqDTO { @@ -46,7 +45,6 @@ public class CombinationRecordCreateReqDTO {
46 /** 45 /**
47 * 团长编号 46 * 团长编号
48 */ 47 */
49 - @NotNull(message = "团长编号不能为空")  
50 private Long headId; 48 private Long headId;
51 /** 49 /**
52 * 拼团商品单价 50 * 拼团商品单价
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
@@ -80,7 +80,7 @@ public interface ErrorCodeConstants { @@ -80,7 +80,7 @@ public interface ErrorCodeConstants {
80 ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1_013_011_001, "拼团失败,已参与过该拼团"); 80 ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1_013_011_001, "拼团失败,已参与过该拼团");
81 ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1_013_011_002, "拼团失败,父拼团不存在"); 81 ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1_013_011_002, "拼团失败,父拼团不存在");
82 ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1_013_011_003, "拼团失败,拼团人数已满"); 82 ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1_013_011_003, "拼团失败,拼团人数已满");
83 - ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1_013_011_004, "拼团失败,已参与其它拼团"); 83 + ErrorCode COMBINATION_RECORD_FAILED_HAVE_JOINED = new ErrorCode(1_013_011_004, "拼团失败,原因:存在该活动正在进行的拼团记录");
84 ErrorCode COMBINATION_RECORD_FAILED_TIME_NOT_START = new ErrorCode(1_013_011_005, "拼团失败,活动未开始"); 84 ErrorCode COMBINATION_RECORD_FAILED_TIME_NOT_START = new ErrorCode(1_013_011_005, "拼团失败,活动未开始");
85 ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1_013_011_006, "拼团失败,活动已经结束"); 85 ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1_013_011_006, "拼团失败,活动已经结束");
86 ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_007, "拼团失败,原因:单次限购超出"); 86 ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_007, "拼团失败,原因:单次限购超出");
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java
@@ -40,4 +40,12 @@ public enum CombinationRecordStatusEnum implements IntArrayValuable { @@ -40,4 +40,12 @@ public enum CombinationRecordStatusEnum implements IntArrayValuable {
40 return ObjectUtil.equal(status, SUCCESS.getStatus()); 40 return ObjectUtil.equal(status, SUCCESS.getStatus());
41 } 41 }
42 42
  43 + public static boolean isInProgress(Integer status) {
  44 + return ObjectUtil.equal(status, IN_PROGRESS.getStatus());
  45 + }
  46 +
  47 + public static boolean isFailed(Integer status) {
  48 + return ObjectUtil.equal(status, FAILED.getStatus());
  49 + }
  50 +
43 } 51 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
@@ -2,12 +2,15 @@ package cn.iocoder.yudao.module.promotion.api.combination; @@ -2,12 +2,15 @@ package cn.iocoder.yudao.module.promotion.api.combination;
2 2
3 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 3 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
  5 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
5 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; 6 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
6 import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; 7 import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
7 import org.springframework.stereotype.Service; 8 import org.springframework.stereotype.Service;
8 9
9 import javax.annotation.Resource; 10 import javax.annotation.Resource;
10 -import java.time.LocalDateTime; 11 +
  12 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  13 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_RECORD_NOT_EXISTS;
11 14
12 /** 15 /**
13 * 拼团活动 API 实现类 16 * 拼团活动 API 实现类
@@ -21,23 +24,22 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { @@ -21,23 +24,22 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
21 private CombinationRecordService recordService; 24 private CombinationRecordService recordService;
22 25
23 @Override 26 @Override
24 - public void validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count) {  
25 - recordService.validateCombinationRecord(activityId, userId, skuId, count); 27 + public void validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count) {
  28 + recordService.validateCombinationRecord(userId, activityId, headId, skuId, count);
26 } 29 }
27 30
28 @Override 31 @Override
29 - public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {  
30 - recordService.createCombinationRecord(reqDTO); 32 + public Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
  33 + return recordService.createCombinationRecord(reqDTO);
31 } 34 }
32 35
33 @Override 36 @Override
34 public boolean isCombinationRecordSuccess(Long userId, Long orderId) { 37 public boolean isCombinationRecordSuccess(Long userId, Long orderId) {
35 - return CombinationRecordStatusEnum.isSuccess(recordService.getCombinationRecord(userId, orderId).getStatus());  
36 - }  
37 -  
38 - @Override  
39 - public void updateRecordStatusToSuccess(Long userId, Long orderId) {  
40 - recordService.updateCombinationRecordStatusByUserIdAndOrderId(CombinationRecordStatusEnum.SUCCESS.getStatus(), userId, orderId); 38 + CombinationRecordDO record = recordService.getCombinationRecord(userId, orderId);
  39 + if (record == null) {
  40 + throw exception(COMBINATION_RECORD_NOT_EXISTS);
  41 + }
  42 + return CombinationRecordStatusEnum.isSuccess(record.getStatus());
41 } 43 }
42 44
43 @Override 45 @Override
@@ -46,14 +48,8 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { @@ -46,14 +48,8 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
46 } 48 }
47 49
48 @Override 50 @Override
49 - public void updateRecordStatusToInProgress(Long userId, Long orderId, LocalDateTime startTime) {  
50 - recordService.updateRecordStatusAndStartTimeByUserIdAndOrderId(CombinationRecordStatusEnum.IN_PROGRESS.getStatus(),  
51 - userId, orderId, startTime);  
52 - }  
53 -  
54 - @Override  
55 - public CombinationValidateJoinRespDTO validateJoinCombination(Long activityId, Long userId, Long skuId, Integer count) {  
56 - return recordService.validateJoinCombination(activityId, userId, skuId, count); 51 + public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) {
  52 + return recordService.validateJoinCombination(userId, activityId, headId, skuId, count);
57 } 53 }
58 54
59 } 55 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java
@@ -5,14 +5,14 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -5,14 +5,14 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; 6 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
7 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 7 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
8 -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;  
9 -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;  
10 -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;  
11 -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO; 8 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.*;
12 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; 9 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
13 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
14 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; 11 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
  12 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
  13 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
15 import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService; 14 import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
  15 +import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
16 import io.swagger.v3.oas.annotations.Operation; 16 import io.swagger.v3.oas.annotations.Operation;
17 import io.swagger.v3.oas.annotations.Parameter; 17 import io.swagger.v3.oas.annotations.Parameter;
18 import io.swagger.v3.oas.annotations.tags.Tag; 18 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -23,6 +23,8 @@ import org.springframework.web.bind.annotation.*; @@ -23,6 +23,8 @@ import org.springframework.web.bind.annotation.*;
23 import javax.annotation.Resource; 23 import javax.annotation.Resource;
24 import javax.validation.Valid; 24 import javax.validation.Valid;
25 import java.util.List; 25 import java.util.List;
  26 +import java.util.Map;
  27 +import java.util.Set;
26 28
27 import static cn.hutool.core.collection.CollectionUtil.newArrayList; 29 import static cn.hutool.core.collection.CollectionUtil.newArrayList;
28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -36,6 +38,8 @@ public class CombinationActivityController { @@ -36,6 +38,8 @@ public class CombinationActivityController {
36 38
37 @Resource 39 @Resource
38 private CombinationActivityService combinationActivityService; 40 private CombinationActivityService combinationActivityService;
  41 + @Resource
  42 + private CombinationRecordService combinationRecordService;
39 43
40 @Resource 44 @Resource
41 private ProductSpuApi productSpuApi; 45 private ProductSpuApi productSpuApi;
@@ -70,14 +74,14 @@ public class CombinationActivityController { @@ -70,14 +74,14 @@ public class CombinationActivityController {
70 @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") 74 @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
71 public CommonResult<CombinationActivityRespVO> getCombinationActivity(@RequestParam("id") Long id) { 75 public CommonResult<CombinationActivityRespVO> getCombinationActivity(@RequestParam("id") Long id) {
72 CombinationActivityDO activity = combinationActivityService.getCombinationActivity(id); 76 CombinationActivityDO activity = combinationActivityService.getCombinationActivity(id);
73 - List<CombinationProductDO> products = combinationActivityService.getCombinationProductsByActivityIds(newArrayList(id)); 77 + List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(newArrayList(id));
74 return success(CombinationActivityConvert.INSTANCE.convert(activity, products)); 78 return success(CombinationActivityConvert.INSTANCE.convert(activity, products));
75 } 79 }
76 80
77 @GetMapping("/page") 81 @GetMapping("/page")
78 @Operation(summary = "获得拼团活动分页") 82 @Operation(summary = "获得拼团活动分页")
79 @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") 83 @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
80 - public CommonResult<PageResult<CombinationActivityRespVO>> getCombinationActivityPage( 84 + public CommonResult<PageResult<CombinationActivityPageItemRespVO>> getCombinationActivityPage(
81 @Valid CombinationActivityPageReqVO pageVO) { 85 @Valid CombinationActivityPageReqVO pageVO) {
82 // 查询拼团活动 86 // 查询拼团活动
83 PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO); 87 PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO);
@@ -85,12 +89,21 @@ public class CombinationActivityController { @@ -85,12 +89,21 @@ public class CombinationActivityController {
85 return success(PageResult.empty(pageResult.getTotal())); 89 return success(PageResult.empty(pageResult.getTotal()));
86 } 90 }
87 91
  92 + // 统计数据
  93 + Set<Long> activityIds = convertSet(pageResult.getList(), CombinationActivityDO::getId);
  94 + Map<Long, Integer> groupCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
  95 + activityIds, null, CombinationRecordDO.HEAD_ID_GROUP);
  96 + Map<Long, Integer> groupSuccessCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
  97 + activityIds, CombinationRecordStatusEnum.SUCCESS.getStatus(), CombinationRecordDO.HEAD_ID_GROUP);
  98 + Map<Long, Integer> recordCountMap = combinationRecordService.getCombinationRecordCountMapByActivity(
  99 + activityIds, null, null);
88 // 拼接数据 100 // 拼接数据
89 - List<CombinationProductDO> products = combinationActivityService.getCombinationProductsByActivityIds( 101 + List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(
90 convertSet(pageResult.getList(), CombinationActivityDO::getId)); 102 convertSet(pageResult.getList(), CombinationActivityDO::getId));
91 List<ProductSpuRespDTO> spus = productSpuApi.getSpuList( 103 List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(
92 convertSet(pageResult.getList(), CombinationActivityDO::getSpuId)); 104 convertSet(pageResult.getList(), CombinationActivityDO::getSpuId));
93 - return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products, spus)); 105 + return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products,
  106 + groupCountMap, groupSuccessCountMap, recordCountMap, spus));
94 } 107 }
95 108
96 } 109 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationRecordController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordPageItemRespVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordSummaryVO;
  8 +import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
  9 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
  10 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
  11 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
  12 +import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
  13 +import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
  14 +import io.swagger.v3.oas.annotations.Operation;
  15 +import io.swagger.v3.oas.annotations.tags.Tag;
  16 +import org.springframework.context.annotation.Lazy;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.GetMapping;
  20 +import org.springframework.web.bind.annotation.RequestMapping;
  21 +import org.springframework.web.bind.annotation.RestController;
  22 +
  23 +import javax.annotation.Resource;
  24 +import javax.validation.Valid;
  25 +import java.util.List;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  29 +
  30 +@Tag(name = "管理后台 - 拼团记录")
  31 +@RestController
  32 +@RequestMapping("/promotion/combination-record")
  33 +@Validated
  34 +public class CombinationRecordController {
  35 +
  36 + @Resource
  37 + private CombinationActivityService combinationActivityService;
  38 + @Resource
  39 + @Lazy
  40 + private CombinationRecordService combinationRecordService;
  41 +
  42 + @GetMapping("/page")
  43 + @Operation(summary = "获得拼团记录分页")
  44 + @PreAuthorize("@ss.hasPermission('promotion:combination-record:query')")
  45 + public CommonResult<PageResult<CombinationRecordPageItemRespVO>> getBargainRecordPage(@Valid CombinationRecordReqPageVO pageVO) {
  46 + PageResult<CombinationRecordDO> recordPage = combinationRecordService.getCombinationRecordPage(pageVO);
  47 + List<CombinationActivityDO> activities = combinationActivityService.getCombinationActivityListByIds(
  48 + convertSet(recordPage.getList(), CombinationRecordDO::getActivityId));
  49 + return success(CombinationActivityConvert.INSTANCE.convert(recordPage, activities));
  50 + }
  51 +
  52 + @GetMapping("/get-summary")
  53 + @Operation(summary = "获得拼团记录的概要信息", description = "用于拼团记录页面展示")
  54 + @PreAuthorize("@ss.hasPermission('promotion:combination-record:query')")
  55 + public CommonResult<CombinationRecordSummaryVO> getCombinationRecordSummary() {
  56 + CombinationRecordSummaryVO summaryVO = new CombinationRecordSummaryVO();
  57 + summaryVO.setUserCount(combinationRecordService.getCombinationRecordCount(null, null)); // 获取所有拼团记录
  58 + summaryVO.setSuccessCount(combinationRecordService.getCombinationRecordCount( // 获取成团记录
  59 + CombinationRecordStatusEnum.SUCCESS.getStatus(), null));
  60 + summaryVO.setVirtualGroupCount(combinationRecordService.getCombinationRecordCount(null, Boolean.TRUE));// 获取虚拟成团记录
  61 + return success(summaryVO);
  62 + }
  63 +
  64 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityBaseVO.java
@@ -48,6 +48,10 @@ public class CombinationActivityBaseVO { @@ -48,6 +48,10 @@ public class CombinationActivityBaseVO {
48 @NotNull(message = "开团人数不能为空") 48 @NotNull(message = "开团人数不能为空")
49 private Integer userSize; 49 private Integer userSize;
50 50
  51 + @Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  52 + @NotNull(message = "虚拟成团不能为空")
  53 + private Boolean virtualGroup;
  54 +
51 @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 55 @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
52 @NotNull(message = "限制时长不能为空") 56 @NotNull(message = "限制时长不能为空")
53 private Integer limitDuration; 57 private Integer limitDuration;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageItemRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.time.LocalDateTime;
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - 拼团活动的分页项 Response VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class CombinationActivityPageItemRespVO extends CombinationActivityBaseVO {
  17 +
  18 + @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
  19 + private Long id;
  20 +
  21 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private LocalDateTime createTime;
  23 +
  24 + @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  25 + private Integer status;
  26 +
  27 + @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + private List<CombinationProductRespVO> products;
  29 +
  30 + // ========== 商品字段 ==========
  31 +
  32 + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取
  33 + example = "618大促")
  34 + private String spuName;
  35 + @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
  36 + example = "https://www.iocoder.cn/xx.png")
  37 + private String picUrl;
  38 + @Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取
  39 + example = "50")
  40 + private Integer marketPrice;
  41 +
  42 + // ========== 统计字段 ==========
  43 +
  44 + @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "33")
  45 + private Integer groupCount;
  46 +
  47 + @Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
  48 + private Integer groupSuccessCount;
  49 +
  50 + @Schema(description = "购买次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  51 + private Integer recordCount;
  52 +
  53 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityPageReqVO.java
@@ -15,8 +15,7 @@ public class CombinationActivityPageReqVO extends PageParam { @@ -15,8 +15,7 @@ public class CombinationActivityPageReqVO extends PageParam {
15 @Schema(description = "拼团名称", example = "赵六") 15 @Schema(description = "拼团名称", example = "赵六")
16 private String name; 16 private String name;
17 17
18 - @Schema(description = "活动状态:0开启 1关闭", example = "0") 18 + @Schema(description = "活动状态", example = "0")
19 private Integer status; 19 private Integer status;
20 20
21 -  
22 } 21 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityRespVO.java
@@ -6,8 +6,6 @@ import lombok.Data; @@ -6,8 +6,6 @@ import lombok.Data;
6 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
7 import lombok.ToString; 7 import lombok.ToString;
8 8
9 -import javax.validation.Valid;  
10 -import javax.validation.constraints.NotNull;  
11 import java.time.LocalDateTime; 9 import java.time.LocalDateTime;
12 import java.util.List; 10 import java.util.List;
13 11
@@ -20,30 +18,12 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO { @@ -20,30 +18,12 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO {
20 @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901") 18 @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
21 private Long id; 19 private Long id;
22 20
23 - @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大促")  
24 - private String spuName;  
25 -  
26 - @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png")  
27 - private String picUrl;  
28 -  
29 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 21 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
30 private LocalDateTime createTime; 22 private LocalDateTime createTime;
31 23
32 @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") 24 @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
33 private Integer userSize; 25 private Integer userSize;
34 26
35 - @Schema(description = "开团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "33")  
36 - private Integer totalCount;  
37 -  
38 - @Schema(description = "成团组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")  
39 - private Integer successCount;  
40 -  
41 - @Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")  
42 - private Integer virtualGroup;  
43 -  
44 - @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")  
45 - private Integer status;  
46 -  
47 @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) 27 @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
48 private List<CombinationProductRespVO> products; 28 private List<CombinationProductRespVO> products;
49 29
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordBaseVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import org.springframework.format.annotation.DateTimeFormat;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +/**
  13 + * 拼团记录 Base VO,提供给添加、修改、详细的子 VO 使用
  14 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  15 + *
  16 + * @author HUIHUI
  17 + */
  18 +@Data
  19 +public class CombinationRecordBaseVO {
  20 +
  21 + @Schema(description = "拼团记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  22 + private Long id;
  23 +
  24 + @Schema(description = "拼团活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  25 + private Long activityId;
  26 +
  27 + @Schema(description = "团长编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  28 + private Long headId;
  29 +
  30 + // ========== 用户相关 ==========
  31 +
  32 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9430")
  33 + @NotNull(message = "用户编号不能为空")
  34 + private Long userId;
  35 +
  36 + @Schema(description = "用户昵称", example = "老芋艿")
  37 + private String nickname;
  38 +
  39 + @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xxx.jpg")
  40 + private String avatar;
  41 +
  42 + // ========== 商品相关 ==========
  43 +
  44 + @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23622")
  45 + @NotNull(message = "商品 SPU 编号不能为空")
  46 + private Long spuId;
  47 +
  48 + @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29950")
  49 + @NotNull(message = "商品 SKU 编号不能为空")
  50 + private Long skuId;
  51 +
  52 + @Schema(description = "商品名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是大黄豆")
  53 + private String spuName;
  54 +
  55 + @Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  56 + private String picUrl;
  57 +
  58 + @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
  59 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  60 + private LocalDateTime expireTime;
  61 +
  62 + @Schema(description = "可参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  63 + private Integer userSize;
  64 +
  65 + @Schema(description = "已参团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  66 + private Integer userCount;
  67 +
  68 + @Schema(description = "拼团状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  69 + private Integer status;
  70 +
  71 + @Schema(description = "是否虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  72 + private Boolean virtualGroup;
  73 +
  74 + @Schema(description = "开始时间 (订单付款后开始的时间)", requiredMode = Schema.RequiredMode.REQUIRED)
  75 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  76 + private LocalDateTime startTime;
  77 +
  78 + @Schema(description = "结束时间(成团时间/失败时间)", requiredMode = Schema.RequiredMode.REQUIRED)
  79 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  80 + private LocalDateTime endTime;
  81 +
  82 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordPageItemRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +@Schema(description = "管理后台 - 拼团记录的分页项 Response VO")
  10 +@Data
  11 +@EqualsAndHashCode(callSuper = true)
  12 +@ToString(callSuper = true)
  13 +public class CombinationRecordPageItemRespVO extends CombinationRecordBaseVO {
  14 +
  15 + // ========== 活动相关 ==========
  16 +
  17 + private CombinationActivityRespVO activity;
  18 +
  19 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordReqPageVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +import lombok.ToString;
  10 +import org.springframework.format.annotation.DateTimeFormat;
  11 +
  12 +import java.time.LocalDateTime;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  15 +
  16 +@Schema(description = "管理后台 - 拼团记录分页 Request VO")
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +public class CombinationRecordReqPageVO extends PageParam {
  21 +
  22 + @Schema(description = "活动状态", example = "1")
  23 + @InEnum(BargainRecordStatusEnum.class)
  24 + private Integer status;
  25 +
  26 + @Schema(description = "创建时间")
  27 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  28 + private LocalDateTime[] createTime;
  29 +
  30 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordSummaryVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - 拼团记录信息统计 Response VO")
  7 +@Data
  8 +public class CombinationRecordSummaryVO {
  9 +
  10 + @Schema(description = "所有拼团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  11 + private Long userCount;
  12 +
  13 + @Schema(description = "成团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Long successCount;
  15 +
  16 + @Schema(description = "虚拟成团记录", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + private Long virtualGroupCount;
  18 +
  19 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/vo/activity/SeckillActivityRespVO.java
@@ -18,16 +18,6 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO { @@ -18,16 +18,6 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO {
18 @Schema(description = "秒杀活动 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 18 @Schema(description = "秒杀活动 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
19 private Long id; 19 private Long id;
20 20
21 - @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取  
22 - example = "618大促")  
23 - private String spuName;  
24 - @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取  
25 - example = "https://www.iocoder.cn/xx.png")  
26 - private String picUrl;  
27 - @Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取  
28 - example = "50")  
29 - private Integer marketPrice;  
30 -  
31 @Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED) 21 @Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
32 private List<SeckillProductRespVO> products; 22 private List<SeckillProductRespVO> products;
33 23
@@ -52,4 +42,16 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO { @@ -52,4 +42,16 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO {
52 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 42 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
53 private LocalDateTime createTime; 43 private LocalDateTime createTime;
54 44
  45 + // ========== 商品字段 ==========
  46 +
  47 + @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取
  48 + example = "618大促")
  49 + private String spuName;
  50 + @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
  51 + example = "https://www.iocoder.cn/xx.png")
  52 + private String picUrl;
  53 + @Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取
  54 + example = "50")
  55 + private Integer marketPrice;
  56 +
55 } 57 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.app.activity; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.app.activity;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
  5 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.module.promotion.controller.app.activity.vo.AppActivityRespVO; 7 import cn.iocoder.yudao.module.promotion.controller.app.activity.vo.AppActivityRespVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
@@ -21,12 +22,10 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -21,12 +22,10 @@ import org.springframework.web.bind.annotation.RequestParam;
21 import org.springframework.web.bind.annotation.RestController; 22 import org.springframework.web.bind.annotation.RestController;
22 23
23 import javax.annotation.Resource; 24 import javax.annotation.Resource;
24 -import java.util.ArrayList;  
25 -import java.util.HashMap;  
26 -import java.util.List;  
27 -import java.util.Map; 25 +import java.util.*;
28 26
29 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 27 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
30 29
31 @Tag(name = "用户 APP - 营销活动") // 用于提供跨多个活动的 HTTP 接口 30 @Tag(name = "用户 APP - 营销活动") // 用于提供跨多个活动的 HTTP 接口
32 @RestController 31 @RestController
@@ -42,58 +41,60 @@ public class AppActivityController { @@ -42,58 +41,60 @@ public class AppActivityController {
42 private BargainActivityService bargainActivityService; 41 private BargainActivityService bargainActivityService;
43 42
44 @GetMapping("/list-by-spu-id") 43 @GetMapping("/list-by-spu-id")
45 - @Operation(summary = "获得单个商品,近期参与的每个活动") // 每种活动,只返回一个 44 + @Operation(summary = "获得单个商品,近期参与的每个活动")
46 @Parameter(name = "spuId", description = "商品编号", required = true) 45 @Parameter(name = "spuId", description = "商品编号", required = true)
47 public CommonResult<List<AppActivityRespVO>> getActivityListBySpuId(@RequestParam("spuId") Long spuId) { 46 public CommonResult<List<AppActivityRespVO>> getActivityListBySpuId(@RequestParam("spuId") Long spuId) {
48 - return success(getAppActivityRespVOList(spuId)); 47 + // 每种活动,只返回一个
  48 + return success(getAppActivityRespVOList(Collections.singletonList(spuId)));
49 } 49 }
50 50
51 @GetMapping("/list-by-spu-ids") 51 @GetMapping("/list-by-spu-ids")
52 - @Operation(summary = "获得多个商品,近期参与的每个活动") // 每种活动,只返回一个;key 为 SPU 编号 52 + @Operation(summary = "获得多个商品,近期参与的每个活动")
53 @Parameter(name = "spuIds", description = "商品编号数组", required = true) 53 @Parameter(name = "spuIds", description = "商品编号数组", required = true)
54 public CommonResult<Map<Long, List<AppActivityRespVO>>> getActivityListBySpuIds(@RequestParam("spuIds") List<Long> spuIds) { 54 public CommonResult<Map<Long, List<AppActivityRespVO>>> getActivityListBySpuIds(@RequestParam("spuIds") List<Long> spuIds) {
55 -  
56 if (CollUtil.isEmpty(spuIds)) { 55 if (CollUtil.isEmpty(spuIds)) {
57 return success(MapUtil.empty()); 56 return success(MapUtil.empty());
58 } 57 }
59 -  
60 - Map<Long, List<AppActivityRespVO>> map = new HashMap<>(spuIds.size());  
61 - spuIds.forEach(spuId -> {  
62 - map.put(spuId, getAppActivityRespVOList(spuId));  
63 - });  
64 - return success(map); 58 + // 每种活动,只返回一个;key 为 SPU 编号
  59 + return success(convertMultiMap(getAppActivityRespVOList(spuIds), AppActivityRespVO::getSpuId));
65 } 60 }
66 61
67 - private List<AppActivityRespVO> getAppActivityRespVOList(Long spuId) {  
68 - List<AppActivityRespVO> respList = new ArrayList<>();  
69 - CombinationActivityDO combination = combinationActivityService.getCombinationActivityBySpuId(spuId);  
70 - if (combination != null) {  
71 - respList.add(new AppActivityRespVO()  
72 - .setId(combination.getId())  
73 - .setType(PromotionTypeEnum.COMBINATION_ACTIVITY.getType())  
74 - .setName(combination.getName())  
75 - .setStartTime(combination.getStartTime())  
76 - .setEndTime(combination.getEndTime())); 62 + private List<AppActivityRespVO> getAppActivityRespVOList(Collection<Long> spuIds) {
  63 + if (CollUtil.isEmpty(spuIds)) {
  64 + return new ArrayList<>();
  65 + }
  66 + List<AppActivityRespVO> activityList = new ArrayList<>();
  67 + // 拼团活动
  68 + List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatus(
  69 + spuIds, CommonStatusEnum.ENABLE.getStatus());
  70 + if (CollUtil.isNotEmpty(combinationActivities)) {
  71 + combinationActivities.forEach(item -> {
  72 + activityList.add(new AppActivityRespVO().setId(item.getId())
  73 + .setType(PromotionTypeEnum.COMBINATION_ACTIVITY.getType()).setName(item.getName())
  74 + .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
  75 + });
77 } 76 }
78 - SeckillActivityDO seckill = seckillActivityService.getSeckillActivityBySpuId(spuId);  
79 - if (seckill != null) {  
80 - respList.add(new AppActivityRespVO()  
81 - .setId(seckill.getId())  
82 - .setType(PromotionTypeEnum.SECKILL_ACTIVITY.getType())  
83 - .setName(seckill.getName())  
84 - .setStartTime(seckill.getStartTime())  
85 - .setEndTime(seckill.getEndTime())); 77 + // 秒杀活动
  78 + List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatus(
  79 + spuIds, CommonStatusEnum.ENABLE.getStatus());
  80 + if (CollUtil.isNotEmpty(seckillActivities)) {
  81 + seckillActivities.forEach(item -> {
  82 + activityList.add(new AppActivityRespVO().setId(item.getId())
  83 + .setType(PromotionTypeEnum.SECKILL_ACTIVITY.getType()).setName(item.getName())
  84 + .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
  85 + });
86 } 86 }
87 - BargainActivityDO bargain = bargainActivityService.getBargainActivityBySpuId(spuId);  
88 - if (bargain != null) {  
89 - respList.add(new AppActivityRespVO()  
90 - .setId(bargain.getId())  
91 - .setType(PromotionTypeEnum.BARGAIN_ACTIVITY.getType())  
92 - .setName(bargain.getName())  
93 - .setStartTime(bargain.getStartTime())  
94 - .setEndTime(bargain.getEndTime())); 87 + // 砍价活动
  88 + List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatus(
  89 + spuIds, CommonStatusEnum.ENABLE.getStatus());
  90 + if (CollUtil.isNotEmpty(bargainActivities)) {
  91 + bargainActivities.forEach(item -> {
  92 + activityList.add(new AppActivityRespVO().setId(item.getId())
  93 + .setType(PromotionTypeEnum.BARGAIN_ACTIVITY.getType()).setName(item.getName())
  94 + .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
  95 + });
95 } 96 }
96 - return respList; 97 + return activityList;
97 } 98 }
98 99
99 } 100 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/vo/AppActivityRespVO.java
@@ -13,12 +13,14 @@ public class AppActivityRespVO { @@ -13,12 +13,14 @@ public class AppActivityRespVO {
13 private Long id; 13 private Long id;
14 14
15 @Schema(description = "活动类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 15 @Schema(description = "活动类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
16 - // 对应 PromotionTypeEnum 枚举  
17 - private Integer type; 16 + private Integer type; // 对应 PromotionTypeEnum 枚举
18 17
19 @Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大促") 18 @Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大促")
20 private String name; 19 private String name;
21 20
  21 + @Schema(description = "spu 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "618")
  22 + private Long spuId;
  23 +
22 @Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED) 24 @Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
23 private LocalDateTime startTime; 25 private LocalDateTime startTime;
24 26
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java
@@ -30,7 +30,6 @@ import java.time.Duration; @@ -30,7 +30,6 @@ import java.time.Duration;
30 import java.util.Collections; 30 import java.util.Collections;
31 import java.util.List; 31 import java.util.List;
32 32
33 -import static cn.hutool.core.util.ObjectUtil.defaultIfNull;  
34 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 33 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
35 import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache; 34 import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 35 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -56,6 +55,7 @@ public class AppCombinationActivityController { @@ -56,6 +55,7 @@ public class AppCombinationActivityController {
56 55
57 @Resource 56 @Resource
58 private CombinationActivityService activityService; 57 private CombinationActivityService activityService;
  58 +
59 @Resource 59 @Resource
60 private ProductSpuApi spuApi; 60 private ProductSpuApi spuApi;
61 61
@@ -68,40 +68,45 @@ public class AppCombinationActivityController { @@ -68,40 +68,45 @@ public class AppCombinationActivityController {
68 } 68 }
69 69
70 private List<AppCombinationActivityRespVO> getCombinationActivityList0(Integer count) { 70 private List<AppCombinationActivityRespVO> getCombinationActivityList0(Integer count) {
71 - List<CombinationActivityDO> list = activityService.getCombinationActivityListByCount(defaultIfNull(count, 6));  
72 - if (CollUtil.isEmpty(list)) { 71 + List<CombinationActivityDO> activityList = activityService.getCombinationActivityListByCount(count);
  72 + if (CollUtil.isEmpty(activityList)) {
73 return Collections.emptyList(); 73 return Collections.emptyList();
74 } 74 }
75 // 拼接返回 75 // 拼接返回
76 - List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(list, CombinationActivityDO::getSpuId));  
77 - return CombinationActivityConvert.INSTANCE.convertAppList(list, spuList); 76 + List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
  77 + convertList(activityList, CombinationActivityDO::getId));
  78 + List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
  79 + return CombinationActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList);
78 } 80 }
79 81
80 @GetMapping("/page") 82 @GetMapping("/page")
81 @Operation(summary = "获得拼团活动分页") 83 @Operation(summary = "获得拼团活动分页")
82 public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) { 84 public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) {
83 - PageResult<CombinationActivityDO> result = activityService.getCombinationActivityPage(pageParam);  
84 - if (CollUtil.isEmpty(result.getList())) {  
85 - return success(PageResult.empty(result.getTotal())); 85 + PageResult<CombinationActivityDO> pageResult = activityService.getCombinationActivityPage(pageParam);
  86 + if (CollUtil.isEmpty(pageResult.getList())) {
  87 + return success(PageResult.empty(pageResult.getTotal()));
86 } 88 }
87 // 拼接返回 89 // 拼接返回
88 - List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(result.getList(), CombinationActivityDO::getSpuId));  
89 - return success(CombinationActivityConvert.INSTANCE.convertAppPage(result, spuList)); 90 + List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
  91 + convertList(pageResult.getList(), CombinationActivityDO::getId));
  92 + List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), CombinationActivityDO::getSpuId));
  93 + return success(CombinationActivityConvert.INSTANCE.convertAppPage(pageResult, productList, spuList));
90 } 94 }
91 95
92 @GetMapping("/get-detail") 96 @GetMapping("/get-detail")
93 @Operation(summary = "获得拼团活动明细") 97 @Operation(summary = "获得拼团活动明细")
94 @Parameter(name = "id", description = "活动编号", required = true, example = "1024") 98 @Parameter(name = "id", description = "活动编号", required = true, example = "1024")
95 public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivityDetail(@RequestParam("id") Long id) { 99 public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivityDetail(@RequestParam("id") Long id) {
96 - // 1、获取活动  
97 - CombinationActivityDO combinationActivity = activityService.getCombinationActivity(id);  
98 - if (combinationActivity == null  
99 - || ObjectUtil.equal(combinationActivity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { 100 + // 1. 获取活动
  101 + CombinationActivityDO activity = activityService.getCombinationActivity(id);
  102 + if (activity == null
  103 + || ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
100 return success(null); 104 return success(null);
101 } 105 }
102 - // 2、获取活动商品  
103 - List<CombinationProductDO> products = activityService.getCombinationProductsByActivityId(combinationActivity.getId());  
104 - return success(CombinationActivityConvert.INSTANCE.convert3(combinationActivity, products)); 106 +
  107 + // 2. 获取活动商品
  108 + List<CombinationProductDO> products = activityService.getCombinationProductsByActivityId(activity.getId());
  109 + return success(CombinationActivityConvert.INSTANCE.convert3(activity, products));
105 } 110 }
106 111
107 } 112 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java
1 package cn.iocoder.yudao.module.promotion.controller.app.combination; 1 package cn.iocoder.yudao.module.promotion.controller.app.combination;
2 2
3 -import cn.hutool.core.util.ObjectUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
6 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO; 4 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;
7 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO; 5 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;
8 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordSummaryRespVO; 6 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordSummaryRespVO;
9 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; 7 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
  9 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
11 import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; 10 import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
  11 +import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
12 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
13 import io.swagger.v3.oas.annotations.Parameter; 13 import io.swagger.v3.oas.annotations.Parameter;
14 import io.swagger.v3.oas.annotations.Parameters; 14 import io.swagger.v3.oas.annotations.Parameters;
15 import io.swagger.v3.oas.annotations.tags.Tag; 15 import io.swagger.v3.oas.annotations.tags.Tag;
  16 +import org.springframework.context.annotation.Lazy;
16 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
17 import org.springframework.web.bind.annotation.GetMapping; 18 import org.springframework.web.bind.annotation.GetMapping;
18 import org.springframework.web.bind.annotation.RequestMapping; 19 import org.springframework.web.bind.annotation.RequestMapping;
@@ -23,6 +24,7 @@ import javax.annotation.Resource; @@ -23,6 +24,7 @@ import javax.annotation.Resource;
23 import javax.validation.constraints.Max; 24 import javax.validation.constraints.Max;
24 import java.util.Collections; 25 import java.util.Collections;
25 import java.util.List; 26 import java.util.List;
  27 +import java.util.Objects;
26 28
27 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 29 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 30 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -36,24 +38,27 @@ public class AppCombinationRecordController { @@ -36,24 +38,27 @@ public class AppCombinationRecordController {
36 38
37 @Resource 39 @Resource
38 private CombinationRecordService combinationRecordService; 40 private CombinationRecordService combinationRecordService;
  41 + @Resource
  42 + @Lazy
  43 + private TradeOrderApi tradeOrderApi;
39 44
40 @GetMapping("/get-summary") 45 @GetMapping("/get-summary")
41 @Operation(summary = "获得拼团记录的概要信息", description = "用于小程序首页") 46 @Operation(summary = "获得拼团记录的概要信息", description = "用于小程序首页")
42 - // TODO 芋艿:增加 @Cache 缓存,1 分钟过期  
43 public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() { 47 public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() {
44 - // 获取所有拼团记录  
45 - Long count = combinationRecordService.getRecordsCount();  
46 AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO(); 48 AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO();
47 - if (count == null || count == 0L) { 49 + // 1. 获得拼团记录数量
  50 + Long count = combinationRecordService.getCombinationRecordCount(null, null);
  51 + if (count == 0) {
48 summary.setAvatars(Collections.emptyList()); 52 summary.setAvatars(Collections.emptyList());
49 summary.setUserCount(count); 53 summary.setUserCount(count);
50 return success(summary); 54 return success(summary);
51 } 55 }
52 -  
53 summary.setUserCount(count); 56 summary.setUserCount(count);
54 - // TODO 只返回最近的 7 个  
55 - int num = 7;  
56 - summary.setAvatars(convertList(combinationRecordService.getLatestRecordList(num), CombinationRecordDO::getAvatar)); 57 +
  58 + // 2. 获得拼团记录头像
  59 + List<CombinationRecordDO> records = combinationRecordService.getLatestCombinationRecordList(
  60 + AppCombinationRecordSummaryRespVO.AVATAR_COUNT);
  61 + summary.setAvatars(convertList(records, CombinationRecordDO::getAvatar));
57 return success(summary); 62 return success(summary);
58 } 63 }
59 64
@@ -61,7 +66,7 @@ public class AppCombinationRecordController { @@ -61,7 +66,7 @@ public class AppCombinationRecordController {
61 @Operation(summary = "获得最近 n 条拼团记录(团长发起的)") 66 @Operation(summary = "获得最近 n 条拼团记录(团长发起的)")
62 @Parameters({ 67 @Parameters({
63 @Parameter(name = "activityId", description = "拼团活动编号"), 68 @Parameter(name = "activityId", description = "拼团活动编号"),
64 - @Parameter(name = "status", description = "状态"), 69 + @Parameter(name = "status", description = "拼团状态"), // 对应 CombinationRecordStatusEnum 枚举
65 @Parameter(name = "count", description = "数量") 70 @Parameter(name = "count", description = "数量")
66 }) 71 })
67 public CommonResult<List<AppCombinationRecordRespVO>> getHeadCombinationRecordList( 72 public CommonResult<List<AppCombinationRecordRespVO>> getHeadCombinationRecordList(
@@ -69,44 +74,54 @@ public class AppCombinationRecordController { @@ -69,44 +74,54 @@ public class AppCombinationRecordController {
69 @RequestParam("status") Integer status, 74 @RequestParam("status") Integer status,
70 @RequestParam(value = "count", defaultValue = "20") @Max(20) Integer count) { 75 @RequestParam(value = "count", defaultValue = "20") @Max(20) Integer count) {
71 return success(CombinationActivityConvert.INSTANCE.convertList3( 76 return success(CombinationActivityConvert.INSTANCE.convertList3(
72 - combinationRecordService.getRecordListWithHead(activityId, status, count))); 77 + combinationRecordService.getHeadCombinationRecordList(activityId, status, count)));
73 } 78 }
74 79
75 @GetMapping("/get-detail") 80 @GetMapping("/get-detail")
76 @Operation(summary = "获得拼团记录明细") 81 @Operation(summary = "获得拼团记录明细")
77 @Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024") 82 @Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024")
78 public CommonResult<AppCombinationRecordDetailRespVO> getCombinationRecordDetail(@RequestParam("id") Long id) { 83 public CommonResult<AppCombinationRecordDetailRespVO> getCombinationRecordDetail(@RequestParam("id") Long id) {
79 - // 1、查询这条记录  
80 - CombinationRecordDO record = combinationRecordService.getRecordById(id); 84 + // 1. 查找这条拼团记录
  85 + CombinationRecordDO record = combinationRecordService.getCombinationRecordById(id);
81 if (record == null) { 86 if (record == null) {
82 return success(null); 87 return success(null);
83 } 88 }
84 89
85 - AppCombinationRecordDetailRespVO detail = new AppCombinationRecordDetailRespVO();  
86 - List<CombinationRecordDO> records;  
87 - // 2、判断是否为团长  
88 - if (record.getHeadId() == null) {  
89 - detail.setHeadRecord(CombinationActivityConvert.INSTANCE.convert(record));  
90 - // 2.1、查找团员拼团记录  
91 - records = combinationRecordService.getRecordListByHeadId(record.getId());  
92 - } else {  
93 - // 2.2、查找团长拼团记录  
94 - CombinationRecordDO headRecord = combinationRecordService.getRecordById(record.getHeadId());  
95 - if (headRecord == null) {  
96 - return success(null);  
97 - } 90 + // 2. 查找该拼团的参团记录
  91 + CombinationRecordDO headRecord;
  92 + List<CombinationRecordDO> memberRecords;
  93 + if (Objects.equals(record.getHeadId(), CombinationRecordDO.HEAD_ID_GROUP)) { // 情况一:团长
  94 + headRecord = record;
  95 + memberRecords = combinationRecordService.getCombinationRecordListByHeadId(record.getId());
  96 + } else { // 情况二:团员
  97 + headRecord = combinationRecordService.getCombinationRecordById(record.getHeadId());
  98 + memberRecords = combinationRecordService.getCombinationRecordListByHeadId(headRecord.getId());
  99 + }
98 100
99 - detail.setHeadRecord(CombinationActivityConvert.INSTANCE.convert(headRecord));  
100 - // 2.3、查找团员拼团记录  
101 - records = combinationRecordService.getRecordListByHeadId(headRecord.getId()); 101 + // 3. 拼接数据
  102 + return success(CombinationActivityConvert.INSTANCE.convert(getLoginUserId(), headRecord, memberRecords));
  103 + }
102 104
  105 + @GetMapping("/cancel")
  106 + @Operation(summary = "取消拼团")
  107 + @Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024")
  108 + public CommonResult<Boolean> cancelCombinationRecord(@RequestParam("id") Long id) {
  109 + Long userId = getLoginUserId();
  110 + // 1、查找这条拼团记录
  111 + CombinationRecordDO record = combinationRecordService.getCombinationRecordByIdAndUser(userId, id);
  112 + if (record == null) {
  113 + return success(Boolean.FALSE);
  114 + }
  115 + // 1.1、需要先校验拼团记录未完成;
  116 + if (!CombinationRecordStatusEnum.isInProgress(record.getStatus())) {
  117 + return success(Boolean.FALSE);
103 } 118 }
104 - detail.setMemberRecords(CombinationActivityConvert.INSTANCE.convertList3(records));  
105 119
106 - // 3、获取当前用户参团记录订单编号  
107 - CombinationRecordDO userRecord = CollectionUtils.findFirst(records, r -> ObjectUtil.equal(r.getUserId(), getLoginUserId()));  
108 - detail.setOrderId(userRecord == null ? null : userRecord.getOrderId()); // 如果没参团,返回 null  
109 - return success(detail); 120 + // 2. 取消已支付的订单
  121 + tradeOrderApi.cancelPaidOrder(userId, record.getOrderId());
  122 + // 3. 取消拼团记录
  123 + combinationRecordService.cancelCombinationRecord(userId, record.getId(), record.getHeadId());
  124 + return success(Boolean.TRUE);
110 } 125 }
111 126
112 } 127 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/record/AppCombinationRecordSummaryRespVO.java
@@ -9,6 +9,11 @@ import java.util.List; @@ -9,6 +9,11 @@ import java.util.List;
9 @Data 9 @Data
10 public class AppCombinationRecordSummaryRespVO { 10 public class AppCombinationRecordSummaryRespVO {
11 11
  12 + /**
  13 + * 加载 {@link #avatars} 的数量
  14 + */
  15 + public static final Integer AVATAR_COUNT = 7;
  16 +
12 @Schema(description = "拼团用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 17 @Schema(description = "拼团用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
13 private Long userCount; 18 private Long userCount;
14 19
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java
@@ -86,7 +86,8 @@ public class AppSeckillActivityController { @@ -86,7 +86,8 @@ public class AppSeckillActivityController {
86 86
87 // 2.1 查询满足当前阶段的活动 87 // 2.1 查询满足当前阶段的活动
88 List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus()); 88 List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus());
89 - List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(convertList(activityList, SeckillActivityDO::getId)); 89 + List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(
  90 + convertList(activityList, SeckillActivityDO::getId));
90 // 2.2 获取 spu 信息 91 // 2.2 获取 spu 信息
91 List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId)); 92 List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
92 return SeckillActivityConvert.INSTANCE.convert(config, activityList, productList, spuList); 93 return SeckillActivityConvert.INSTANCE.convert(config, activityList, productList, spuList);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
1 package cn.iocoder.yudao.module.promotion.convert.combination; 1 package cn.iocoder.yudao.module.promotion.convert.combination;
2 2
  3 +import cn.hutool.core.util.ObjectUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
@@ -8,25 +9,30 @@ import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; @@ -8,25 +9,30 @@ import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
8 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 9 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
9 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 10 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
10 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; 11 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
  12 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageItemRespVO;
11 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO; 13 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
12 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO; 14 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
13 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO; 15 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;
14 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO; 16 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;
  17 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordPageItemRespVO;
15 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO; 18 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO;
16 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO; 19 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO;
  20 +import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO;
17 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO; 21 import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO;
18 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 22 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
19 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; 23 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
20 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 24 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
  25 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
21 import org.mapstruct.Mapper; 26 import org.mapstruct.Mapper;
22 import org.mapstruct.Mapping; 27 import org.mapstruct.Mapping;
23 import org.mapstruct.Mappings; 28 import org.mapstruct.Mappings;
24 import org.mapstruct.factory.Mappers; 29 import org.mapstruct.factory.Mappers;
25 30
  31 +import java.time.LocalDateTime;
26 import java.util.List; 32 import java.util.List;
27 import java.util.Map; 33 import java.util.Map;
28 34
29 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 35 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
30 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; 36 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
31 37
32 /** 38 /**
@@ -53,20 +59,29 @@ public interface CombinationActivityConvert { @@ -53,20 +59,29 @@ public interface CombinationActivityConvert {
53 59
54 List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list); 60 List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list);
55 61
56 - PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page);  
57 62
58 - default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page,  
59 - List<CombinationProductDO> productList,  
60 - List<ProductSpuRespDTO> spuList) { 63 + default PageResult<CombinationActivityPageItemRespVO> convertPage(PageResult<CombinationActivityDO> page,
  64 + List<CombinationProductDO> productList,
  65 + Map<Long, Integer> groupCountMap,
  66 + Map<Long, Integer> groupSuccessCountMap,
  67 + Map<Long, Integer> recordCountMap,
  68 + List<ProductSpuRespDTO> spuList) {
  69 + PageResult<CombinationActivityPageItemRespVO> pageResult = convertPage(page);
61 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); 70 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
62 - PageResult<CombinationActivityRespVO> pageResult = convertPage(page);  
63 pageResult.getList().forEach(item -> { 71 pageResult.getList().forEach(item -> {
64 - MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())); 72 + MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())
  73 + .setMarketPrice(spu.getMarketPrice()));
65 item.setProducts(convertList2(productList)); 74 item.setProducts(convertList2(productList));
  75 + // 设置统计字段
  76 + item.setGroupCount(groupCountMap.getOrDefault(item.getId(), 0))
  77 + .setGroupSuccessCount(groupSuccessCountMap.getOrDefault(item.getId(), 0))
  78 + .setRecordCount(recordCountMap.getOrDefault(item.getId(), 0));
66 }); 79 });
67 return pageResult; 80 return pageResult;
68 } 81 }
69 82
  83 + PageResult<CombinationActivityPageItemRespVO> convertPage(PageResult<CombinationActivityDO> page);
  84 +
70 List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs); 85 List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs);
71 86
72 @Mappings({ 87 @Mappings({
@@ -97,23 +112,31 @@ public interface CombinationActivityConvert { @@ -97,23 +112,31 @@ public interface CombinationActivityConvert {
97 default CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO, 112 default CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO,
98 CombinationActivityDO activity, MemberUserRespDTO user, 113 CombinationActivityDO activity, MemberUserRespDTO user,
99 ProductSpuRespDTO spu, ProductSkuRespDTO sku) { 114 ProductSpuRespDTO spu, ProductSkuRespDTO sku) {
100 - return convert(reqDTO)  
101 - .setCount(reqDTO.getCount())  
102 - .setVirtualGroup(false) 115 + return convert(reqDTO).setVirtualGroup(false)
  116 + .setStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus()) // 创建后默认状态为进行中
  117 + .setStartTime(LocalDateTime.now()) // TODO @puhui999:想了下,这个 startTime 应该是团长的;
  118 + // TODO @puhui999:有团长的情况下,expireTime 应该是团长的;
103 .setExpireTime(activity.getStartTime().plusHours(activity.getLimitDuration())) 119 .setExpireTime(activity.getStartTime().plusHours(activity.getLimitDuration()))
104 - .setUserSize(activity.getUserSize())  
105 - .setNickname(user.getNickname())  
106 - .setAvatar(user.getAvatar())  
107 - .setSpuName(spu.getName())  
108 - .setPicUrl(sku.getPicUrl()); 120 + .setUserSize(activity.getUserSize()).setUserCount(1) // 默认就是 1 插入后会接着更新一次所有的拼团记录
  121 + // 用户信息
  122 + .setNickname(user.getNickname()).setAvatar(user.getAvatar())
  123 + // 商品信息
  124 + .setSpuName(spu.getName()).setPicUrl(sku.getPicUrl());
  125 +
109 } 126 }
110 127
111 List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list); 128 List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list);
112 129
113 - default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list, List<ProductSpuRespDTO> spuList) { 130 + default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list,
  131 + List<CombinationProductDO> productList,
  132 + List<ProductSpuRespDTO> spuList) {
114 List<AppCombinationActivityRespVO> activityList = convertAppList(list); 133 List<AppCombinationActivityRespVO> activityList = convertAppList(list);
115 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); 134 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
  135 + Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
116 return CollectionUtils.convertList(activityList, item -> { 136 return CollectionUtils.convertList(activityList, item -> {
  137 + // 设置 product 信息
  138 + item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
  139 + // 设置 SPU 信息
117 findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); 140 findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
118 return item; 141 return item;
119 }); 142 });
@@ -121,13 +144,17 @@ public interface CombinationActivityConvert { @@ -121,13 +144,17 @@ public interface CombinationActivityConvert {
121 144
122 PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result); 145 PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result);
123 146
124 - default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result, List<ProductSpuRespDTO> spuList) { 147 + default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result,
  148 + List<CombinationProductDO> productList,
  149 + List<ProductSpuRespDTO> spuList) {
125 PageResult<AppCombinationActivityRespVO> appPage = convertAppPage(result); 150 PageResult<AppCombinationActivityRespVO> appPage = convertAppPage(result);
126 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); 151 Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
  152 + Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
127 List<AppCombinationActivityRespVO> list = CollectionUtils.convertList(appPage.getList(), item -> { 153 List<AppCombinationActivityRespVO> list = CollectionUtils.convertList(appPage.getList(), item -> {
128 - findAndThen(spuMap, item.getSpuId(), spu -> {  
129 - item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice());  
130 - }); 154 + // 设置 product 信息
  155 + item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
  156 + // 设置 SPU 信息
  157 + findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
131 return item; 158 return item;
132 }); 159 });
133 appPage.setList(list); 160 appPage.setList(list);
@@ -146,4 +173,31 @@ public interface CombinationActivityConvert { @@ -146,4 +173,31 @@ public interface CombinationActivityConvert {
146 173
147 AppCombinationRecordRespVO convert(CombinationRecordDO record); 174 AppCombinationRecordRespVO convert(CombinationRecordDO record);
148 175
  176 + PageResult<CombinationRecordPageItemRespVO> convert(PageResult<CombinationRecordDO> result);
  177 +
  178 + default PageResult<CombinationRecordPageItemRespVO> convert(PageResult<CombinationRecordDO> recordPage, List<CombinationActivityDO> activities) {
  179 + PageResult<CombinationRecordPageItemRespVO> result = convert(recordPage);
  180 + Map<Long, CombinationActivityDO> activityMap = convertMap(activities, CombinationActivityDO::getId);
  181 + result.setList(CollectionUtils.convertList(result.getList(), item -> {
  182 + findAndThen(activityMap, item.getActivityId(), activity -> {
  183 + item.setActivity(convert(activity));
  184 + });
  185 + return item;
  186 + }));
  187 + return result;
  188 + }
  189 +
  190 +
  191 + default AppCombinationRecordDetailRespVO convert(Long userId, CombinationRecordDO headRecord, List<CombinationRecordDO> memberRecords) {
  192 + AppCombinationRecordDetailRespVO respVO = new AppCombinationRecordDetailRespVO()
  193 + .setHeadRecord(convert(headRecord)).setMemberRecords(convertList3(memberRecords));
  194 + // 处理自己参与拼团的 orderId
  195 + CombinationRecordDO userRecord = CollectionUtils.findFirst(memberRecords, r -> ObjectUtil.equal(r.getUserId(), userId));
  196 + if (userRecord == null && ObjectUtil.equal(headRecord.getUserId(), userId)) {
  197 + userRecord = headRecord;
  198 + }
  199 + respVO.setOrderId(userRecord == null ? null : userRecord.getOrderId());
  200 + return respVO;
  201 + }
  202 +
149 } 203 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationActivityDO.java
@@ -60,21 +60,9 @@ public class CombinationActivityDO extends BaseDO { @@ -60,21 +60,9 @@ public class CombinationActivityDO extends BaseDO {
60 */ 60 */
61 private Integer userSize; 61 private Integer userSize;
62 /** 62 /**
63 - * 开团组数  
64 - */  
65 - private Integer totalCount;  
66 - /**  
67 - * 成团组数  
68 - */  
69 - private Integer successCount;  
70 - /**  
71 - * 参与人数  
72 - */  
73 - private Integer orderUserCount;  
74 - /**  
75 * 虚拟成团 63 * 虚拟成团
76 */ 64 */
77 - private Integer virtualGroup; 65 + private Boolean virtualGroup;
78 /** 66 /**
79 * 活动状态 67 * 活动状态
80 * 68 *
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationRecordDO.java
@@ -29,6 +29,11 @@ import java.time.LocalDateTime; @@ -29,6 +29,11 @@ import java.time.LocalDateTime;
29 public class CombinationRecordDO extends BaseDO { 29 public class CombinationRecordDO extends BaseDO {
30 30
31 /** 31 /**
  32 + * 团长编号 - 团长
  33 + */
  34 + public static final Long HEAD_ID_GROUP = 0L;
  35 +
  36 + /**
32 * 编号,主键自增 37 * 编号,主键自增
33 */ 38 */
34 @TableId 39 @TableId
@@ -71,6 +76,7 @@ public class CombinationRecordDO extends BaseDO { @@ -71,6 +76,7 @@ public class CombinationRecordDO extends BaseDO {
71 * 用户编号 76 * 用户编号
72 */ 77 */
73 private Long userId; 78 private Long userId;
  79 +
74 /** 80 /**
75 * 用户昵称 81 * 用户昵称
76 */ 82 */
@@ -84,6 +90,8 @@ public class CombinationRecordDO extends BaseDO { @@ -84,6 +90,8 @@ public class CombinationRecordDO extends BaseDO {
84 * 团长编号 90 * 团长编号
85 * 91 *
86 * 关联 {@link CombinationRecordDO#getId()} 92 * 关联 {@link CombinationRecordDO#getId()}
  93 + *
  94 + * 如果是团长,则它的值是 {@link #HEAD_ID_GROUP}
87 */ 95 */
88 private Long headId; 96 private Long headId;
89 /** 97 /**
@@ -108,6 +116,9 @@ public class CombinationRecordDO extends BaseDO { @@ -108,6 +116,9 @@ public class CombinationRecordDO extends BaseDO {
108 private Integer userCount; 116 private Integer userCount;
109 /** 117 /**
110 * 是否虚拟成团 118 * 是否虚拟成团
  119 + *
  120 + * 默认为 false。
  121 + * 拼团过期都还没有成功,如果 {@link CombinationActivityDO#getVirtualGroup()} 为 true,则执行虚拟成团的逻辑,才会更新该字段为 true
111 */ 122 */
112 private Boolean virtualGroup; 123 private Boolean virtualGroup;
113 124
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
@@ -8,8 +8,10 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba @@ -8,8 +8,10 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
10 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
  11 +import org.apache.ibatis.annotations.Select;
11 12
12 import java.time.LocalDateTime; 13 import java.time.LocalDateTime;
  14 +import java.util.Collection;
13 import java.util.List; 15 import java.util.List;
14 16
15 /** 17 /**
@@ -83,11 +85,25 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; { @@ -83,11 +85,25 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; {
83 .last("LIMIT " + count)); 85 .last("LIMIT " + count));
84 } 86 }
85 87
86 - default BargainActivityDO selectOne(Long spuId) {  
87 - return selectOne(new LambdaQueryWrapperX<BargainActivityDO>()  
88 - .eq(BargainActivityDO::getSpuId, spuId)  
89 - .orderByDesc(BargainActivityDO::getCreateTime)  
90 - , false);  
91 - } 88 + // TODO @puhui999:一个商品,在统一时间,不会参与多个活动;so 是不是不用 inner join 哈?
  89 + // PS:如果可以参与多个,其实可以这样写 select * from promotion_bargain_activity group by spu_id ORDER BY create_time DESC;通过 group 来过滤
  90 + /**
  91 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
  92 + *
  93 + * @param spuIds spu 编号
  94 + * @param status 状态
  95 + * @return 砍价活动列表
  96 + */
  97 + @Select("SELECT p1.* " +
  98 + "FROM promotion_bargain_activity p1 " +
  99 + "INNER JOIN ( " +
  100 + " SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +
  101 + " FROM promotion_bargain_activity " +
  102 + " WHERE spu_id IN #{spuIds} " +
  103 + " GROUP BY spu_id " +
  104 + ") p2 " +
  105 + "ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +
  106 + "ORDER BY p1.create_time DESC;")
  107 + List<BargainActivityDO> selectListBySpuIds(Collection<Long> spuIds, Integer status);
92 108
93 } 109 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationActivityMapper.java
@@ -7,7 +7,10 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -7,7 +7,10 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
9 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
  10 +import org.apache.ibatis.annotations.Param;
  11 +import org.apache.ibatis.annotations.Select;
10 12
  13 +import java.util.Collection;
11 import java.util.List; 14 import java.util.List;
12 15
13 /** 16 /**
@@ -40,11 +43,24 @@ public interface CombinationActivityMapper extends BaseMapperX&lt;CombinationActivi @@ -40,11 +43,24 @@ public interface CombinationActivityMapper extends BaseMapperX&lt;CombinationActivi
40 .last("LIMIT " + count)); 43 .last("LIMIT " + count));
41 } 44 }
42 45
43 - default CombinationActivityDO selectOne(Long spuId) {  
44 - return selectOne(new LambdaQueryWrapperX<CombinationActivityDO>()  
45 - .eq(CombinationActivityDO::getSpuId, spuId)  
46 - .orderByDesc(CombinationActivityDO::getCreateTime)  
47 - , false);  
48 - } 46 + // TODO @puhui999:类似 BargainActivityMapper
  47 + /**
  48 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
  49 + *
  50 + * @param spuIds spu 编号
  51 + * @param status 状态
  52 + * @return 拼团活动列表
  53 + */
  54 + @Select("SELECT p1.* " +
  55 + "FROM promotion_combination_activity p1 " +
  56 + "INNER JOIN ( " +
  57 + " SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +
  58 + " FROM promotion_combination_activity " +
  59 + " WHERE spu_id IN #{spuIds} " +
  60 + " GROUP BY spu_id " +
  61 + ") p2 " +
  62 + "ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +
  63 + "ORDER BY p1.create_time DESC;")
  64 + List<CombinationActivityDO> selectListBySpuIds(@Param("spuIds") Collection<Long> spuIds, @Param("status") Integer status);
49 65
50 } 66 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.combination; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.combination;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 8 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  9 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
5 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
  11 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
6 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
7 13
  14 +import java.util.Collection;
  15 +import java.util.Collections;
8 import java.util.List; 16 import java.util.List;
  17 +import java.util.Map;
9 18
10 /** 19 /**
11 * 拼团记录 Mapper 20 * 拼团记录 Mapper
@@ -20,34 +29,18 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO @@ -20,34 +29,18 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO
20 CombinationRecordDO::getOrderId, orderId); 29 CombinationRecordDO::getOrderId, orderId);
21 } 30 }
22 31
23 - default List<CombinationRecordDO> selectListByUserId(Long userId) {  
24 - return selectList(CombinationRecordDO::getUserId, userId);  
25 - }  
26 -  
27 - default List<CombinationRecordDO> selectListByUserIdAndStatus(Long userId, Integer status) {  
28 - return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()  
29 - .eq(CombinationRecordDO::getUserId, userId)  
30 - .eq(CombinationRecordDO::getStatus, status));  
31 - }  
32 -  
33 /** 32 /**
34 * 查询拼团记录 33 * 查询拼团记录
35 * 34 *
36 * @param headId 团长编号 35 * @param headId 团长编号
37 * @return 拼团记录 36 * @return 拼团记录
38 */ 37 */
39 - default CombinationRecordDO selectOneByHeadId(Long headId, Integer status) { 38 + default CombinationRecordDO selectByHeadId(Long headId, Integer status) {
40 return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>() 39 return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>()
41 .eq(CombinationRecordDO::getId, headId) 40 .eq(CombinationRecordDO::getId, headId)
42 .eq(CombinationRecordDO::getStatus, status)); 41 .eq(CombinationRecordDO::getStatus, status));
43 } 42 }
44 43
45 - default List<CombinationRecordDO> selectListByHeadIdAndStatus(Long headId, Integer status) {  
46 - return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()  
47 - .eq(CombinationRecordDO::getHeadId, headId)  
48 - .eq(CombinationRecordDO::getStatus, status));  
49 - }  
50 -  
51 /** 44 /**
52 * 查询拼团记录 45 * 查询拼团记录
53 * 46 *
@@ -69,25 +62,51 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO @@ -69,25 +62,51 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO
69 */ 62 */
70 default List<CombinationRecordDO> selectLatestList(int count) { 63 default List<CombinationRecordDO> selectLatestList(int count) {
71 return selectList(new LambdaQueryWrapperX<CombinationRecordDO>() 64 return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
72 - .orderByDesc(CombinationRecordDO::getCreateTime) 65 + .orderByDesc(CombinationRecordDO::getId)
73 .last("LIMIT " + count)); 66 .last("LIMIT " + count));
74 } 67 }
75 68
76 - /**  
77 - * 获得最近 count 条拼团记录(团长发起的)  
78 - *  
79 - * @param activityId 拼团活动编号  
80 - * @param status 记录状态  
81 - * @param count 数量  
82 - * @return 拼团记录列表  
83 - */  
84 - default List<CombinationRecordDO> selectList(Long activityId, Integer status, Integer count) { 69 + default List<CombinationRecordDO> selectListByActivityIdAndStatusAndHeadId(Long activityId, Integer status,
  70 + Long headId, Integer count) {
85 return selectList(new LambdaQueryWrapperX<CombinationRecordDO>() 71 return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
86 .eqIfPresent(CombinationRecordDO::getActivityId, activityId) 72 .eqIfPresent(CombinationRecordDO::getActivityId, activityId)
87 .eqIfPresent(CombinationRecordDO::getStatus, status) 73 .eqIfPresent(CombinationRecordDO::getStatus, status)
88 - .eq(CombinationRecordDO::getHeadId, null) // TODO 团长的 headId 是不是 null 还是自己的记录编号来着?  
89 - .orderByDesc(CombinationRecordDO::getCreateTime) 74 + .eq(CombinationRecordDO::getHeadId, headId)
  75 + .orderByDesc(CombinationRecordDO::getId)
90 .last("LIMIT " + count)); 76 .last("LIMIT " + count));
91 } 77 }
92 78
  79 + default Map<Long, Integer> selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(Collection<Long> activityIds,
  80 + Integer status, Long headId) {
  81 + // SQL count 查询
  82 + List<Map<String, Object>> result = selectMaps(new QueryWrapper<CombinationRecordDO>()
  83 + .select("COUNT(DISTINCT(user_id)) AS recordCount, activity_id AS activityId")
  84 + .in("activity_id", activityIds)
  85 + .eq(status != null, "status", status)
  86 + .eq(headId != null, "head_id", headId)
  87 + .groupBy("activity_id"));
  88 + if (CollUtil.isEmpty(result)) {
  89 + return Collections.emptyMap();
  90 + }
  91 + // 转换数据
  92 + return CollectionUtils.convertMap(result,
  93 + record -> MapUtil.getLong(record, "activityId"),
  94 + record -> MapUtil.getInt(record, "recordCount"));
  95 + }
  96 +
  97 + default PageResult<CombinationRecordDO> selectPage(CombinationRecordReqPageVO pageVO) {
  98 + return selectPage(pageVO, new LambdaQueryWrapperX<CombinationRecordDO>()
  99 + .eqIfPresent(CombinationRecordDO::getStatus, pageVO.getStatus())
  100 + .betweenIfPresent(CombinationRecordDO::getCreateTime, pageVO.getCreateTime()));
  101 + }
  102 +
  103 + // TODO @puhui999:这个最好把 headId 也作为一个参数;因为有个要求 userCount,它要 DISTINCT 下;整体可以参考 selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId
  104 + default Long selectCountByHeadAndStatusAndVirtualGroup(Integer status, Boolean virtualGroup) {
  105 + return selectCount(new LambdaQueryWrapperX<CombinationRecordDO>()
  106 + .eq(status != null || virtualGroup != null,
  107 + CombinationRecordDO::getHeadId, CombinationRecordDO.HEAD_ID_GROUP) // 统计团信息则指定团长
  108 + .eqIfPresent(CombinationRecordDO::getStatus, status)
  109 + .eqIfPresent(CombinationRecordDO::getVirtualGroup, virtualGroup));
  110 + }
  111 +
93 } 112 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
@@ -9,7 +9,9 @@ import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppS @@ -9,7 +9,9 @@ import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppS
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
10 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 10 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
11 import org.apache.ibatis.annotations.Mapper; 11 import org.apache.ibatis.annotations.Mapper;
  12 +import org.apache.ibatis.annotations.Select;
12 13
  14 +import java.util.Collection;
13 import java.util.List; 15 import java.util.List;
14 16
15 /** 17 /**
@@ -56,11 +58,24 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -56,11 +58,24 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
56 .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0")); 58 .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0"));
57 } 59 }
58 60
59 - default SeckillActivityDO selectOne(Long spuId) {  
60 - return selectOne(new LambdaQueryWrapperX<SeckillActivityDO>()  
61 - .eq(SeckillActivityDO::getSpuId, spuId)  
62 - .orderByDesc(SeckillActivityDO::getCreateTime)  
63 - , false);  
64 - } 61 + // TODO @puhui999:类似 BargainActivityMapper
  62 + /**
  63 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
  64 + *
  65 + * @param spuIds spu 编号
  66 + * @param status 状态
  67 + * @return 秒杀活动列表
  68 + */
  69 + @Select("SELECT p1.* " +
  70 + "FROM promotion_seckill_activity p1 " +
  71 + "INNER JOIN ( " +
  72 + " SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +
  73 + " FROM promotion_seckill_activity " +
  74 + " WHERE spu_id IN #{spuIds} " +
  75 + " GROUP BY spu_id " +
  76 + ") p2 " +
  77 + "ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +
  78 + "ORDER BY p1.create_time DESC;")
  79 + List<SeckillActivityDO> selectListBySpuIds(Collection<Long> spuIds, Integer status);
65 80
66 } 81 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
9 9
10 import javax.validation.Valid; 10 import javax.validation.Valid;
  11 +import java.util.Collection;
11 import java.util.List; 12 import java.util.List;
12 import java.util.Set; 13 import java.util.Set;
13 14
@@ -99,11 +100,12 @@ public interface BargainActivityService { @@ -99,11 +100,12 @@ public interface BargainActivityService {
99 List<BargainActivityDO> getBargainActivityListByCount(Integer count); 100 List<BargainActivityDO> getBargainActivityListByCount(Integer count);
100 101
101 /** 102 /**
102 - * 获取指定 spu 编号的活动 103 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
103 * 104 *
104 - * @param spuId spu 编号  
105 - * @return 砍价活动 105 + * @param spuIds spu 编号
  106 + * @param status 状态
  107 + * @return 砍价活动列表
106 */ 108 */
107 - BargainActivityDO getBargainActivityBySpuId(Long spuId); 109 + List<BargainActivityDO> getBargainActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
108 110
109 } 111 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
@@ -20,6 +20,7 @@ import org.springframework.validation.annotation.Validated; @@ -20,6 +20,7 @@ import org.springframework.validation.annotation.Validated;
20 20
21 import javax.annotation.Resource; 21 import javax.annotation.Resource;
22 import java.time.LocalDateTime; 22 import java.time.LocalDateTime;
  23 +import java.util.Collection;
23 import java.util.List; 24 import java.util.List;
24 import java.util.Set; 25 import java.util.Set;
25 26
@@ -176,8 +177,8 @@ public class BargainActivityServiceImpl implements BargainActivityService { @@ -176,8 +177,8 @@ public class BargainActivityServiceImpl implements BargainActivityService {
176 } 177 }
177 178
178 @Override 179 @Override
179 - public BargainActivityDO getBargainActivityBySpuId(Long spuId) {  
180 - return bargainActivityMapper.selectOne(spuId); 180 + public List<BargainActivityDO> getBargainActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
  181 + return bargainActivityMapper.selectListBySpuIds(spuIds, status);
181 } 182 }
182 183
183 } 184 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
@@ -73,7 +73,7 @@ public interface CombinationActivityService { @@ -73,7 +73,7 @@ public interface CombinationActivityService {
73 * @return 拼团活动的商品列表 73 * @return 拼团活动的商品列表
74 */ 74 */
75 default List<CombinationProductDO> getCombinationProductsByActivityId(Long activityId) { 75 default List<CombinationProductDO> getCombinationProductsByActivityId(Long activityId) {
76 - return getCombinationProductsByActivityIds(Collections.singletonList(activityId)); 76 + return getCombinationProductListByActivityIds(Collections.singletonList(activityId));
77 } 77 }
78 78
79 /** 79 /**
@@ -82,7 +82,15 @@ public interface CombinationActivityService { @@ -82,7 +82,15 @@ public interface CombinationActivityService {
82 * @param activityIds 拼团活动 ids 82 * @param activityIds 拼团活动 ids
83 * @return 拼团活动的商品列表 83 * @return 拼团活动的商品列表
84 */ 84 */
85 - List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds); 85 + List<CombinationProductDO> getCombinationProductListByActivityIds(Collection<Long> activityIds);
  86 +
  87 + /**
  88 + * 获得拼团活动列表
  89 + *
  90 + * @param ids 拼团活动 ids
  91 + * @return 拼团活动的列表
  92 + */
  93 + List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids);
86 94
87 /** 95 /**
88 * 获取正在进行的活动分页数据 96 * 获取正在进行的活动分页数据
@@ -101,7 +109,7 @@ public interface CombinationActivityService { @@ -101,7 +109,7 @@ public interface CombinationActivityService {
101 PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam); 109 PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam);
102 110
103 /** 111 /**
104 - * 获取指定活动指定 sku 编号的商品 112 + * 获取指定活动、指定 sku 编号的商品
105 * 113 *
106 * @param activityId 活动编号 114 * @param activityId 活动编号
107 * @param skuId sku 编号 115 * @param skuId sku 编号
@@ -110,11 +118,12 @@ public interface CombinationActivityService { @@ -110,11 +118,12 @@ public interface CombinationActivityService {
110 CombinationProductDO selectByActivityIdAndSkuId(Long activityId, Long skuId); 118 CombinationProductDO selectByActivityIdAndSkuId(Long activityId, Long skuId);
111 119
112 /** 120 /**
113 - * 获取指定 spu 编号的活动 121 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
114 * 122 *
115 - * @param spuId spu 编号  
116 - * @return 拼团活动 123 + * @param spuIds spu 编号
  124 + * @param status 状态
  125 + * @return 拼团活动列表
117 */ 126 */
118 - CombinationActivityDO getCombinationActivityBySpuId(Long spuId); 127 + List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
119 128
120 } 129 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
@@ -65,13 +65,11 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic @@ -65,13 +65,11 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
65 65
66 // 插入拼团活动 66 // 插入拼团活动
67 CombinationActivityDO activity = CombinationActivityConvert.INSTANCE.convert(createReqVO) 67 CombinationActivityDO activity = CombinationActivityConvert.INSTANCE.convert(createReqVO)
68 - .setStatus(CommonStatusEnum.ENABLE.getStatus())  
69 - .setTotalCount(0).setSuccessCount(0).setOrderUserCount(0).setVirtualGroup(0); 68 + .setStatus(CommonStatusEnum.ENABLE.getStatus());
70 combinationActivityMapper.insert(activity); 69 combinationActivityMapper.insert(activity);
71 // 插入商品 70 // 插入商品
72 List<CombinationProductDO> products = CombinationActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activity); 71 List<CombinationProductDO> products = CombinationActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activity);
73 combinationProductMapper.insertBatch(products); 72 combinationProductMapper.insertBatch(products);
74 - // 返回  
75 return activity.getId(); 73 return activity.getId();
76 } 74 }
77 75
@@ -202,11 +200,16 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic @@ -202,11 +200,16 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
202 } 200 }
203 201
204 @Override 202 @Override
205 - public List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds) { 203 + public List<CombinationProductDO> getCombinationProductListByActivityIds(Collection<Long> activityIds) {
206 return combinationProductMapper.selectListByActivityIds(activityIds); 204 return combinationProductMapper.selectListByActivityIds(activityIds);
207 } 205 }
208 206
209 @Override 207 @Override
  208 + public List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids) {
  209 + return combinationActivityMapper.selectList(CombinationActivityDO::getId, ids);
  210 + }
  211 +
  212 + @Override
210 public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) { 213 public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) {
211 return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count); 214 return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count);
212 } 215 }
@@ -224,8 +227,8 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic @@ -224,8 +227,8 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
224 } 227 }
225 228
226 @Override 229 @Override
227 - public CombinationActivityDO getCombinationActivityBySpuId(Long spuId) {  
228 - return combinationActivityMapper.selectOne(spuId); 230 + public List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
  231 + return combinationActivityMapper.selectListBySpuIds(spuIds, status);
229 } 232 }
230 233
231 } 234 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java
1 package cn.iocoder.yudao.module.promotion.service.combination; 1 package cn.iocoder.yudao.module.promotion.service.combination;
2 2
3 import cn.iocoder.yudao.framework.common.core.KeyValue; 3 import cn.iocoder.yudao.framework.common.core.KeyValue;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 6 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
6 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
9 11
10 -import java.time.LocalDateTime; 12 +import javax.annotation.Nullable;
  13 +import java.util.Collection;
11 import java.util.List; 14 import java.util.List;
  15 +import java.util.Map;
12 16
13 /** 17 /**
14 * 拼团记录 Service 接口 18 * 拼团记录 Service 接口
@@ -27,40 +31,34 @@ public interface CombinationRecordService { @@ -27,40 +31,34 @@ public interface CombinationRecordService {
27 void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId); 31 void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId);
28 32
29 /** 33 /**
30 - * 校验是否满足拼团条件  
31 - * 如果不满足,会抛出异常 34 + * 【下单前】校验是否满足拼团活动条件
  35 + *
  36 + * 如果校验失败,则抛出业务异常
32 * 37 *
33 - * @param activityId 活动编号  
34 * @param userId 用户编号 38 * @param userId 用户编号
  39 + * @param activityId 活动编号
  40 + * @param headId 团长编号
35 * @param skuId sku 编号 41 * @param skuId sku 编号
36 * @param count 数量 42 * @param count 数量
37 - * @return 返回拼团活动和拼团活动商品 43 + * @return 拼团信息
38 */ 44 */
39 - KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(Long activityId, Long userId, Long skuId, Integer count); 45 + KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(Long userId, Long activityId, Long headId,
  46 + Long skuId, Integer count);
40 47
41 /** 48 /**
42 * 创建拼团记录 49 * 创建拼团记录
43 * 50 *
44 * @param reqDTO 创建信息 51 * @param reqDTO 创建信息
  52 + * @return 开团记录编号
45 */ 53 */
46 - void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO); 54 + Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO);
47 55
48 /** 56 /**
49 - * 更新拼团状态和开始时间  
50 - *  
51 - * @param status 状态  
52 - * @param userId 用户编号  
53 - * @param orderId 订单编号  
54 - * @param startTime 开始时间  
55 - */  
56 - void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime);  
57 -  
58 - /**  
59 - * 获得拼团状态 57 + * 获得拼团记录
60 * 58 *
61 * @param userId 用户编号 59 * @param userId 用户编号
62 * @param orderId 订单编号 60 * @param orderId 订单编号
63 - * @return 拼团状态 61 + * @return 拼团记录
64 */ 62 */
65 CombinationRecordDO getCombinationRecord(Long userId, Long orderId); 63 CombinationRecordDO getCombinationRecord(Long userId, Long orderId);
66 64
@@ -71,27 +69,30 @@ public interface CombinationRecordService { @@ -71,27 +69,30 @@ public interface CombinationRecordService {
71 * @param activityId 活动 id 69 * @param activityId 活动 id
72 * @return 拼团记录列表 70 * @return 拼团记录列表
73 */ 71 */
74 - List<CombinationRecordDO> getRecordListByUserIdAndActivityId(Long userId, Long activityId); 72 + List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId);
75 73
76 /** 74 /**
77 * 【下单前】校验是否满足拼团活动条件 75 * 【下单前】校验是否满足拼团活动条件
78 * 76 *
79 * 如果校验失败,则抛出业务异常 77 * 如果校验失败,则抛出业务异常
80 * 78 *
81 - * @param activityId 活动编号  
82 * @param userId 用户编号 79 * @param userId 用户编号
  80 + * @param activityId 活动编号
  81 + * @param headId 团长编号
83 * @param skuId sku 编号 82 * @param skuId sku 编号
84 * @param count 数量 83 * @param count 数量
85 * @return 拼团信息 84 * @return 拼团信息
86 */ 85 */
87 - CombinationValidateJoinRespDTO validateJoinCombination(Long activityId, Long userId, Long skuId, Integer count); 86 + CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count);
88 87
89 /** 88 /**
90 - * 获取所有拼团记录数 89 + * 获取拼团记录数
91 * 90 *
  91 + * @param status 状态-允许为空
  92 + * @param virtualGroup 是否虚拟成团-允许为空
92 * @return 记录数 93 * @return 记录数
93 */ 94 */
94 - Long getRecordsCount(); 95 + Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup);
95 96
96 /** 97 /**
97 * 获取最近的 count 条拼团记录 98 * 获取最近的 count 条拼团记录
@@ -99,7 +100,7 @@ public interface CombinationRecordService { @@ -99,7 +100,7 @@ public interface CombinationRecordService {
99 * @param count 限制数量 100 * @param count 限制数量
100 * @return 拼团记录列表 101 * @return 拼团记录列表
101 */ 102 */
102 - List<CombinationRecordDO> getLatestRecordList(int count); 103 + List<CombinationRecordDO> getLatestCombinationRecordList(int count);
103 104
104 /** 105 /**
105 * 获得最近 n 条拼团记录(团长发起的) 106 * 获得最近 n 条拼团记录(团长发起的)
@@ -109,7 +110,7 @@ public interface CombinationRecordService { @@ -109,7 +110,7 @@ public interface CombinationRecordService {
109 * @param count 数量 110 * @param count 数量
110 * @return 拼团记录列表 111 * @return 拼团记录列表
111 */ 112 */
112 - List<CombinationRecordDO> getRecordListWithHead(Long activityId, Integer status, Integer count); 113 + List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count);
113 114
114 /** 115 /**
115 * 获取指定编号的拼团记录 116 * 获取指定编号的拼团记录
@@ -117,7 +118,7 @@ public interface CombinationRecordService { @@ -117,7 +118,7 @@ public interface CombinationRecordService {
117 * @param id 拼团记录编号 118 * @param id 拼团记录编号
118 * @return 拼团记录 119 * @return 拼团记录
119 */ 120 */
120 - CombinationRecordDO getRecordById(Long id); 121 + CombinationRecordDO getCombinationRecordById(Long id);
121 122
122 /** 123 /**
123 * 获取指定团长编号的拼团记录 124 * 获取指定团长编号的拼团记录
@@ -125,6 +126,46 @@ public interface CombinationRecordService { @@ -125,6 +126,46 @@ public interface CombinationRecordService {
125 * @param headId 团长编号 126 * @param headId 团长编号
126 * @return 拼团记录列表 127 * @return 拼团记录列表
127 */ 128 */
128 - List<CombinationRecordDO> getRecordListByHeadId(Long headId); 129 + List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId);
  130 +
  131 + /**
  132 + * 获取拼团记录分页数据
  133 + *
  134 + * @param pageVO 分页请求
  135 + * @return 拼团记录分页数据
  136 + */
  137 + PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO);
  138 +
  139 + /**
  140 + * 【拼团活动】获得拼团记录数量 Map
  141 + *
  142 + * @param activityIds 活动记录编号数组
  143 + * @param status 拼团状态,允许空
  144 + * @param headId 团长编号,允许空。目的 headId 设置为 {@link CombinationRecordDO#HEAD_ID_GROUP} 时,可以设置
  145 + * @return 拼团记录数量 Map
  146 + */
  147 + Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,
  148 + @Nullable Integer status,
  149 + @Nullable Long headId);
  150 +
  151 +
  152 + /**
  153 + * 获取拼团记录
  154 + *
  155 + * @param userId 用户编号
  156 + * @param id 拼团记录编号
  157 + * @return 拼团记录
  158 + */
  159 + CombinationRecordDO getCombinationRecordByIdAndUser(Long userId, Long id);
  160 +
  161 + /**
  162 + * 取消拼团
  163 + *
  164 + * @param userId 用户编号
  165 + * @param id 拼团记录编号
  166 + * @param headId 团长编号
  167 + */
  168 + void cancelCombinationRecord(Long userId, Long id, Long headId);
  169 +
129 170
130 } 171 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
1 package cn.iocoder.yudao.module.promotion.service.combination; 1 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.ObjUtil;
5 import cn.iocoder.yudao.framework.common.core.KeyValue; 5 import cn.iocoder.yudao.framework.common.core.KeyValue;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 8 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
8 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 9 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
9 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 10 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
@@ -12,6 +13,7 @@ import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; @@ -12,6 +13,7 @@ import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
12 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 13 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
13 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 14 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
14 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 15 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
  16 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
15 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; 17 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
16 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 18 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
17 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; 19 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
@@ -19,18 +21,20 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR @@ -19,18 +21,20 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR
19 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper; 21 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
20 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; 22 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
21 import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi; 23 import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
22 -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;  
23 import org.springframework.context.annotation.Lazy; 24 import org.springframework.context.annotation.Lazy;
24 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
25 import org.springframework.transaction.annotation.Transactional; 26 import org.springframework.transaction.annotation.Transactional;
26 import org.springframework.validation.annotation.Validated; 27 import org.springframework.validation.annotation.Validated;
27 28
  29 +import javax.annotation.Nullable;
28 import javax.annotation.Resource; 30 import javax.annotation.Resource;
29 -import java.time.LocalDateTime;  
30 -import java.util.List; 31 +import java.util.*;
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.findFirst;
  35 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
  36 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.afterNow;
  37 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeNow;
34 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 38 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
35 39
36 // TODO 芋艿:等拼团记录做完,完整 review 下 40 // TODO 芋艿:等拼团记录做完,完整 review 下
@@ -61,6 +65,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -61,6 +65,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
61 @Resource 65 @Resource
62 private TradeOrderApi tradeOrderApi; 66 private TradeOrderApi tradeOrderApi;
63 67
  68 + // TODO @芋艿:在详细预览下;
64 @Override 69 @Override
65 @Transactional(rollbackFor = Exception.class) 70 @Transactional(rollbackFor = Exception.class)
66 public void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId) { 71 public void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId) {
@@ -72,30 +77,6 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -72,30 +77,6 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
72 recordMapper.updateById(record); 77 recordMapper.updateById(record);
73 } 78 }
74 79
75 - @Override  
76 - @Transactional(rollbackFor = Exception.class)  
77 - public void updateRecordStatusAndStartTimeByUserIdAndOrderId(Integer status, Long userId, Long orderId, LocalDateTime startTime) {  
78 - CombinationRecordDO record = validateCombinationRecord(userId, orderId);  
79 - // 更新状态  
80 - record.setStatus(status);  
81 - // 更新开始时间  
82 - record.setStartTime(startTime);  
83 - recordMapper.updateById(record);  
84 -  
85 - // 更新拼团参入人数  
86 - List<CombinationRecordDO> records = recordMapper.selectListByHeadIdAndStatus(record.getHeadId(), status);  
87 - if (CollUtil.isNotEmpty(records)) {  
88 - records.forEach(item -> {  
89 - item.setUserCount(records.size());  
90 - // 校验拼团是否满足要求  
91 - if (ObjectUtil.equal(records.size(), record.getUserSize())) {  
92 - item.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());  
93 - }  
94 - });  
95 - }  
96 - recordMapper.updateBatch(records);  
97 - }  
98 -  
99 private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) { 80 private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) {
100 // 校验拼团是否存在 81 // 校验拼团是否存在
101 CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId); 82 CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId);
@@ -108,141 +89,247 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -108,141 +89,247 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
108 // TODO @芋艿:在详细预览下; 89 // TODO @芋艿:在详细预览下;
109 @Override 90 @Override
110 public KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord( 91 public KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(
111 - Long activityId, Long userId, Long skuId, Integer count) {  
112 - // 1.1 校验拼团活动是否存在 92 + Long userId, Long activityId, Long headId, Long skuId, Integer count) {
  93 + // 1. 校验拼团活动是否存在
113 CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId); 94 CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId);
114 - // 1.2 校验活动是否开启  
115 - if (ObjectUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { 95 + // 1.1 校验活动是否开启
  96 + if (ObjUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
116 throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE); 97 throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);
117 } 98 }
118 - // 2 校验是否超出单次限购数量 99 + // 1.2. 校验活动开始时间
  100 + if (afterNow(activity.getStartTime())) {
  101 + throw exception(COMBINATION_RECORD_FAILED_TIME_NOT_START);
  102 + }
  103 + // 1.3 校验是否超出单次限购数量
119 if (count > activity.getSingleLimitCount()) { 104 if (count > activity.getSingleLimitCount()) {
120 throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED); 105 throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
121 } 106 }
122 - // 2.1、校验活动商品是否存在 107 +
  108 + // 2. 父拼团是否存在,是否已经满了
  109 + if (headId != null) {
  110 + // 2.1. 查询进行中的父拼团
  111 + CombinationRecordDO record = recordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
  112 + if (record == null) {
  113 + throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
  114 + }
  115 + // 2.2. 校验拼团是否已满
  116 + if (ObjUtil.equal(record.getUserCount(), record.getUserSize())) {
  117 + throw exception(COMBINATION_RECORD_USER_FULL);
  118 + }
  119 + // 2.3 校验拼团是否过期(有父拼团的时候只校验父拼团的过期时间)
  120 + if (beforeNow(record.getExpireTime())) {
  121 + throw exception(COMBINATION_RECORD_FAILED_TIME_END);
  122 + }
  123 + } else {
  124 + // 3. 校验当前活动是否结束(自己是父拼团的时候才校验活动是否结束)
  125 + if (beforeNow(activity.getEndTime())) {
  126 + throw exception(COMBINATION_RECORD_FAILED_TIME_END);
  127 + }
  128 + }
  129 +
  130 + // 4.1 校验活动商品是否存在
123 CombinationProductDO product = combinationActivityService.selectByActivityIdAndSkuId(activityId, skuId); 131 CombinationProductDO product = combinationActivityService.selectByActivityIdAndSkuId(activityId, skuId);
124 if (product == null) { 132 if (product == null) {
125 throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS); 133 throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
126 } 134 }
127 - // 2.2、校验 sku 是否存在 135 + // 4.2 校验 sku 是否存在
128 ProductSkuRespDTO sku = productSkuApi.getSku(skuId); 136 ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
129 if (sku == null) { 137 if (sku == null) {
130 throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS); 138 throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
131 } 139 }
132 - // 2.3、 校验库存是否充足 140 + // 4.3 校验库存是否充足
133 if (count > sku.getStock()) { 141 if (count > sku.getStock()) {
134 throw exception(COMBINATION_ACTIVITY_UPDATE_STOCK_FAIL); 142 throw exception(COMBINATION_ACTIVITY_UPDATE_STOCK_FAIL);
135 } 143 }
136 - // 3、校验是否有拼团记录  
137 - List<CombinationRecordDO> recordList = getRecordListByUserIdAndActivityId(userId, activityId);  
138 - if (CollUtil.isEmpty(recordList)) { 144 +
  145 + // 6.1 校验是否有拼团记录
  146 + List<CombinationRecordDO> recordList = recordMapper.selectListByUserIdAndActivityId(userId, activityId);
  147 + recordList.removeIf(record -> CombinationRecordStatusEnum.isFailed(record.getStatus())); // 取消的订单,不算数
  148 + if (CollUtil.isEmpty(recordList)) { // 如果为空,说明可以参与,直接返回
139 return new KeyValue<>(activity, product); 149 return new KeyValue<>(activity, product);
140 } 150 }
141 - // 4、校验是否超出总限购数量  
142 - Integer sumValue = getSumValue(convertList(recordList, CombinationRecordDO::getCount,  
143 - item -> ObjectUtil.equals(item.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus())), i -> i, Integer::sum);  
144 - if ((sumValue + count) > activity.getTotalLimitCount()) {  
145 - throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);  
146 - }  
147 - // 5、校验拼团记录是否存在未支付的订单(如果存在未支付的订单则不允许发起新的拼团)  
148 - CombinationRecordDO record = findFirst(recordList, item -> ObjectUtil.equals(item.getStatus(), null));  
149 - if (record == null) {  
150 - return new KeyValue<>(activity, product); 151 + // 6.2 校验用户是否有该活动正在进行的拼团
  152 + CombinationRecordDO inProgressRecord = findFirst(recordList,
  153 + record -> CombinationRecordStatusEnum.isInProgress(record.getStatus()));
  154 + if (inProgressRecord != null) {
  155 + throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED);
151 } 156 }
152 - // 5.1、查询关联的订单是否已经支付  
153 - // 当前 activityId 已经有未支付的订单,不允许在发起新的;要么支付,要么去掉先;  
154 - // TODO 芋艿:看看是不是可以删除掉;  
155 - Integer orderStatus = tradeOrderApi.getOrderStatus(record.getOrderId());  
156 - if (ObjectUtil.equal(orderStatus, TradeOrderStatusEnum.UNPAID.getStatus())) {  
157 - throw exception(COMBINATION_RECORD_FAILED_ORDER_STATUS_UNPAID); 157 + // 6.3 校验是否超出总限购数量
  158 + Integer sumValue = getSumValue(recordList, CombinationRecordDO::getCount, Integer::sum);
  159 + if (sumValue != null && sumValue + count > activity.getTotalLimitCount()) {
  160 + throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);
158 } 161 }
159 -  
160 return new KeyValue<>(activity, product); 162 return new KeyValue<>(activity, product);
161 } 163 }
162 164
163 - // TODO 芋艿:在详细 review 下;  
164 @Override 165 @Override
165 @Transactional(rollbackFor = Exception.class) 166 @Transactional(rollbackFor = Exception.class)
166 - public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {  
167 - // 1、校验拼团活动  
168 - KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(  
169 - reqDTO.getActivityId(), reqDTO.getUserId(), reqDTO.getSkuId(), reqDTO.getCount());  
170 - CombinationActivityDO activity = keyValue.getKey();  
171 - // 2、校验用户是否参加了其它拼团  
172 - List<CombinationRecordDO> recordDOList = recordMapper.selectListByUserIdAndStatus(reqDTO.getUserId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());  
173 - if (CollUtil.isNotEmpty(recordDOList)) {  
174 - throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED);  
175 - }  
176 - // 3、校验活动是否开启  
177 - if (LocalDateTime.now().isAfter(activity.getStartTime())) {  
178 - throw exception(COMBINATION_RECORD_FAILED_TIME_NOT_START);  
179 - }  
180 - // 4、校验当前活动是否过期  
181 - if (LocalDateTime.now().isAfter(activity.getEndTime())) {  
182 - throw exception(COMBINATION_RECORD_FAILED_TIME_END);  
183 - }  
184 - // 5、父拼团是否存在,是否已经满了  
185 - if (reqDTO.getHeadId() != null) {  
186 - // 5.1、查询进行中的父拼团  
187 - CombinationRecordDO record = recordMapper.selectOneByHeadId(reqDTO.getHeadId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());  
188 - if (record == null) {  
189 - throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);  
190 - }  
191 - // 5.2、校验拼团是否满足要求  
192 - if (ObjectUtil.equal(record.getUserCount(), record.getUserSize())) {  
193 - throw exception(COMBINATION_RECORD_USER_FULL);  
194 - }  
195 - } 167 + public Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
  168 + // 1. 校验拼团活动
  169 + KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(),
  170 + reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount());
196 171
197 - // 6. 创建拼团记录 172 + // 2.1 组合数据创建拼团记录
198 MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId()); 173 MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId());
199 ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId()); 174 ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId());
200 ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId()); 175 ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId());
201 - recordMapper.insert(CombinationActivityConvert.INSTANCE.convert(reqDTO, activity, user, spu, sku)); 176 + CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku);
  177 + // 2.2 如果是团长需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP
  178 + if (record.getHeadId() == null) {
  179 + record.setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
  180 + }
  181 + recordMapper.insert(record);
  182 +
  183 + if (ObjUtil.equal(CombinationRecordDO.HEAD_ID_GROUP, record.getHeadId())) {
  184 + return record.getId();
  185 + }
  186 +
  187 + // TODO @puhui:是不是这里的更新,放到 order 模块那;支付完成后;
  188 + // 4、更新拼团相关信息到订单
  189 + tradeOrderApi.updateOrderCombinationInfo(record.getOrderId(), record.getActivityId(), record.getId(), record.getHeadId());
  190 + // 4、更新拼团记录
  191 + updateCombinationRecordWhenCreate(reqDTO.getHeadId(), keyValue.getKey());
  192 + return record.getId();
  193 + }
  194 +
  195 + /**
  196 + * 当新增拼团时,更新拼团记录的进展
  197 + *
  198 + * @param headId 团长编号
  199 + * @param activity 活动
  200 + */
  201 + private void updateCombinationRecordWhenCreate(Long headId, CombinationActivityDO activity) {
  202 + // 1. 团长 + 团员
  203 + List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);
  204 + if (CollUtil.isEmpty(records)) {
  205 + return;
  206 + }
  207 + CombinationRecordDO headRecord = recordMapper.selectById(headId);
  208 +
  209 + // 2. 批量更新记录
  210 + List<CombinationRecordDO> updateRecords = new ArrayList<>();
  211 + records.add(headRecord); // 加入团长,团长也需要更新
  212 + boolean isFull = records.size() >= activity.getUserSize();
  213 + records.forEach(item -> {
  214 + CombinationRecordDO updateRecord = new CombinationRecordDO();
  215 + updateRecord.setId(item.getId()).setUserCount(records.size());
  216 + if (isFull) {
  217 + updateRecord.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
  218 + }
  219 + updateRecords.add(updateRecord);
  220 + });
  221 + recordMapper.updateBatch(updateRecords);
202 } 222 }
203 223
204 @Override 224 @Override
205 public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) { 225 public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) {
206 - return validateCombinationRecord(userId, orderId); 226 + return recordMapper.selectByUserIdAndOrderId(userId, orderId);
207 } 227 }
208 228
209 @Override 229 @Override
210 - public List<CombinationRecordDO> getRecordListByUserIdAndActivityId(Long userId, Long activityId) { 230 + public List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId) {
211 return recordMapper.selectListByUserIdAndActivityId(userId, activityId); 231 return recordMapper.selectListByUserIdAndActivityId(userId, activityId);
212 } 232 }
213 233
214 @Override 234 @Override
215 - public CombinationValidateJoinRespDTO validateJoinCombination(Long activityId, Long userId, Long skuId, Integer count) {  
216 - KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(activityId, userId, skuId, count);  
217 - return new CombinationValidateJoinRespDTO()  
218 - .setActivityId(keyValue.getKey().getId())  
219 - .setName(keyValue.getKey().getName())  
220 - .setCombinationPrice(keyValue.getValue().getCombinationPrice()); 235 + public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId,
  236 + Long skuId, Integer count) {
  237 + KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(userId, activityId,
  238 + headId, skuId, count);
  239 + return new CombinationValidateJoinRespDTO().setActivityId(keyValue.getKey().getId())
  240 + .setName(keyValue.getKey().getName()).setCombinationPrice(keyValue.getValue().getCombinationPrice());
221 } 241 }
222 242
223 @Override 243 @Override
224 - public Long getRecordsCount() {  
225 - return recordMapper.selectCount(); 244 + public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup) {
  245 + return recordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup);
226 } 246 }
227 247
228 @Override 248 @Override
229 - public List<CombinationRecordDO> getLatestRecordList(int count) { 249 + public List<CombinationRecordDO> getLatestCombinationRecordList(int count) {
230 return recordMapper.selectLatestList(count); 250 return recordMapper.selectLatestList(count);
231 } 251 }
232 252
233 @Override 253 @Override
234 - public List<CombinationRecordDO> getRecordListWithHead(Long activityId, Integer status, Integer count) {  
235 - return recordMapper.selectList(activityId, status, count); 254 + public List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count) {
  255 + return recordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status,
  256 + CombinationRecordDO.HEAD_ID_GROUP, count);
236 } 257 }
237 258
238 @Override 259 @Override
239 - public CombinationRecordDO getRecordById(Long id) { 260 + public CombinationRecordDO getCombinationRecordById(Long id) {
240 return recordMapper.selectById(id); 261 return recordMapper.selectById(id);
241 } 262 }
242 263
243 @Override 264 @Override
244 - public List<CombinationRecordDO> getRecordListByHeadId(Long headId) { 265 + public List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId) {
245 return recordMapper.selectList(CombinationRecordDO::getHeadId, headId); 266 return recordMapper.selectList(CombinationRecordDO::getHeadId, headId);
246 } 267 }
247 268
  269 + @Override
  270 + public PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO) {
  271 + return recordMapper.selectPage(pageVO);
  272 + }
  273 +
  274 + @Override
  275 + public Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,
  276 + @Nullable Integer status, @Nullable Long headId) {
  277 + return recordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId);
  278 + }
  279 +
  280 + @Override
  281 + public CombinationRecordDO getCombinationRecordByIdAndUser(Long userId, Long id) {
  282 + return recordMapper.selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getId, id);
  283 + }
  284 +
  285 + @Override
  286 + @Transactional(rollbackFor = Exception.class)
  287 + public void cancelCombinationRecord(Long userId, Long id, Long headId) {
  288 + // 删除记录
  289 + recordMapper.deleteById(id);
  290 +
  291 + // 需要更新的记录
  292 + List<CombinationRecordDO> updateRecords = new ArrayList<>();
  293 + // 如果它是团长,则顺序(下单时间)继承
  294 + if (Objects.equals(headId, CombinationRecordDO.HEAD_ID_GROUP)) { // 情况一:团长
  295 + // 团员
  296 + List<CombinationRecordDO> list = getCombinationRecordListByHeadId(id);
  297 + if (CollUtil.isEmpty(list)) {
  298 + return;
  299 + }
  300 + // 按照创建时间升序排序
  301 + list.sort(Comparator.comparing(CombinationRecordDO::getCreateTime)); // 影响原 list
  302 + CombinationRecordDO newHead = list.get(0); // 新团长继位
  303 + list.forEach(item -> {
  304 + CombinationRecordDO recordDO = new CombinationRecordDO();
  305 + recordDO.setId(item.getId());
  306 + if (ObjUtil.equal(item.getId(), newHead.getId())) { // 新团长
  307 + recordDO.setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
  308 + } else {
  309 + recordDO.setHeadId(newHead.getId());
  310 + }
  311 + recordDO.setUserCount(list.size());
  312 + updateRecords.add(recordDO);
  313 + });
  314 + } else { // 情况二:团员
  315 + // 团长
  316 + CombinationRecordDO recordHead = recordMapper.selectById(headId);
  317 + // 团员
  318 + List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);
  319 + if (CollUtil.isEmpty(records)) {
  320 + return;
  321 + }
  322 + records.add(recordHead); // 加入团长,团长数据也需要更新
  323 + records.forEach(item -> {
  324 + CombinationRecordDO recordDO = new CombinationRecordDO();
  325 + recordDO.setId(item.getId());
  326 + recordDO.setUserCount(records.size());
  327 + updateRecords.add(recordDO);
  328 + });
  329 + }
  330 +
  331 + // 更新拼团记录
  332 + recordMapper.updateBatch(updateRecords);
  333 + }
  334 +
248 } 335 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
@@ -120,11 +120,12 @@ public interface SeckillActivityService { @@ -120,11 +120,12 @@ public interface SeckillActivityService {
120 SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count); 120 SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count);
121 121
122 /** 122 /**
123 - * 获取指定 spu 编号的活动 123 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
124 * 124 *
125 - * @param spuId spu 编号  
126 - * @return 秒杀活动 125 + * @param spuIds spu 编号
  126 + * @param status 状态
  127 + * @return 秒杀活动列表
127 */ 128 */
128 - SeckillActivityDO getSeckillActivityBySpuId(Long spuId); 129 + List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
129 130
130 } 131 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
@@ -311,8 +311,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { @@ -311,8 +311,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
311 } 311 }
312 312
313 @Override 313 @Override
314 - public SeckillActivityDO getSeckillActivityBySpuId(Long spuId) {  
315 - return seckillActivityMapper.selectOne(spuId); 314 + public List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
  315 + return seckillActivityMapper.selectListBySpuIds(spuIds, status);
316 } 316 }
317 317
318 } 318 }
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImplTest.java
@@ -111,9 +111,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { @@ -111,9 +111,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
111 o.setStartTime(null); 111 o.setStartTime(null);
112 o.setEndTime(null); 112 o.setEndTime(null);
113 o.setUserSize(null); 113 o.setUserSize(null);
114 - o.setTotalCount(null);  
115 - o.setSuccessCount(null);  
116 - o.setOrderUserCount(null);  
117 o.setVirtualGroup(null); 114 o.setVirtualGroup(null);
118 o.setStatus(null); 115 o.setStatus(null);
119 o.setLimitDuration(null); 116 o.setLimitDuration(null);
@@ -134,12 +131,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { @@ -134,12 +131,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
134 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null))); 131 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null)));
135 // 测试 userSize 不匹配 132 // 测试 userSize 不匹配
136 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null))); 133 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null)));
137 - // 测试 totalNum 不匹配  
138 - combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalCount(null)));  
139 - // 测试 successNum 不匹配  
140 - combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessCount(null)));  
141 - // 测试 orderUserCount 不匹配  
142 - combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null)));  
143 // 测试 virtualGroup 不匹配 134 // 测试 virtualGroup 不匹配
144 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null))); 135 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null)));
145 // 测试 status 不匹配 136 // 测试 status 不匹配
@@ -173,9 +164,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { @@ -173,9 +164,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
173 o.setStartTime(null); 164 o.setStartTime(null);
174 o.setEndTime(null); 165 o.setEndTime(null);
175 o.setUserSize(null); 166 o.setUserSize(null);
176 - o.setTotalCount(null);  
177 - o.setSuccessCount(null);  
178 - o.setOrderUserCount(null);  
179 o.setVirtualGroup(null); 167 o.setVirtualGroup(null);
180 o.setStatus(null); 168 o.setStatus(null);
181 o.setLimitDuration(null); 169 o.setLimitDuration(null);
@@ -196,12 +184,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { @@ -196,12 +184,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
196 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null))); 184 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null)));
197 // 测试 userSize 不匹配 185 // 测试 userSize 不匹配
198 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null))); 186 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setUserSize(null)));
199 - // 测试 totalNum 不匹配  
200 - combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setTotalCount(null)));  
201 - // 测试 successNum 不匹配  
202 - combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setSuccessCount(null)));  
203 - // 测试 orderUserCount 不匹配  
204 - combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setOrderUserCount(null)));  
205 // 测试 virtualGroup 不匹配 187 // 测试 virtualGroup 不匹配
206 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null))); 188 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null)));
207 // 测试 status 不匹配 189 // 测试 status 不匹配
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java
@@ -28,13 +28,22 @@ public interface TradeOrderApi { @@ -28,13 +28,22 @@ public interface TradeOrderApi {
28 */ 28 */
29 TradeOrderRespDTO getOrder(Long id); 29 TradeOrderRespDTO getOrder(Long id);
30 30
31 - // TODO 芋艿:看看是不是可以删除掉;  
32 /** 31 /**
33 - * 获取订单状态 32 + * 更新拼团相关信息到订单
34 * 33 *
35 - * @param id 订单编号  
36 - * @return 订单状态 34 + * @param orderId 订单编号
  35 + * @param activityId 拼团活动编号
  36 + * @param combinationRecordId 拼团记录编号
  37 + * @param headId 团长编号
  38 + */
  39 + void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId);
  40 +
  41 + /**
  42 + * 取消支付订单
  43 + *
  44 + * @param userId 用户编号
  45 + * @param orderId 订单编号
37 */ 46 */
38 - Integer getOrderStatus(Long id); 47 + void cancelPaidOrder(Long userId, Long orderId);
39 48
40 } 49 }
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -33,6 +33,7 @@ public interface ErrorCodeConstants { @@ -33,6 +33,7 @@ public interface ErrorCodeConstants {
33 ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元"); 33 ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元");
34 ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, "交易订单删除失败,订单不是【已取消】状态"); 34 ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, "交易订单删除失败,订单不是【已取消】状态");
35 ErrorCode ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP = new ErrorCode(1_011_000_030, "交易订单自提失败,收货方式不是【用户自提】"); 35 ErrorCode ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP = new ErrorCode(1_011_000_030, "交易订单自提失败,收货方式不是【用户自提】");
  36 + ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单已发货");
36 37
37 // ========== After Sale 模块 1-011-000-100 ========== 38 // ========== After Sale 模块 1-011-000-100 ==========
38 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在"); 39 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java
@@ -16,7 +16,7 @@ public enum TradeOrderOperateTypeEnum { @@ -16,7 +16,7 @@ public enum TradeOrderOperateTypeEnum {
16 MEMBER_CREATE(1, "用户下单"), 16 MEMBER_CREATE(1, "用户下单"),
17 ADMIN_UPDATE_PRICE(2, "订单价格 {oldPayPrice} 修改,实际支付金额为 {newPayPrice} 元"), 17 ADMIN_UPDATE_PRICE(2, "订单价格 {oldPayPrice} 修改,实际支付金额为 {newPayPrice} 元"),
18 MEMBER_PAY(10, "用户付款成功"), 18 MEMBER_PAY(10, "用户付款成功"),
19 - SYSTEM_UP_ADDRESS(11, "订单 {orderId} 收货地址修改"), 19 + ADMIN_UPDATE_ADDRESS(11, "收货地址修改"),
20 ADMIN_DELIVERY(20, "已发货,快递公司:{deliveryName},快递单号:{logisticsNo}"), 20 ADMIN_DELIVERY(20, "已发货,快递公司:{deliveryName},快递单号:{logisticsNo}"),
21 MEMBER_RECEIVE(30, "用户已收货"), 21 MEMBER_RECEIVE(30, "用户已收货"),
22 SYSTEM_RECEIVE(31, "到期未收货,系统自动确认收货"), 22 SYSTEM_RECEIVE(31, "到期未收货,系统自动确认收货"),
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java
1 package cn.iocoder.yudao.module.trade.enums.order; 1 package cn.iocoder.yudao.module.trade.enums.order;
2 2
  3 +import cn.hutool.core.util.ObjectUtil;
3 import cn.iocoder.yudao.framework.common.core.IntArrayValuable; 4 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4 import lombok.Getter; 5 import lombok.Getter;
5 import lombok.RequiredArgsConstructor; 6 import lombok.RequiredArgsConstructor;
@@ -37,4 +38,20 @@ public enum TradeOrderTypeEnum implements IntArrayValuable { @@ -37,4 +38,20 @@ public enum TradeOrderTypeEnum implements IntArrayValuable {
37 return ARRAYS; 38 return ARRAYS;
38 } 39 }
39 40
  41 + public static boolean isNormal(Integer type) {
  42 + return ObjectUtil.equal(type, NORMAL.getType());
  43 + }
  44 +
  45 + public static boolean isSeckill(Integer type) {
  46 + return ObjectUtil.equal(type, SECKILL.getType());
  47 + }
  48 +
  49 + public static boolean isBargain(Integer type) {
  50 + return ObjectUtil.equal(type, BARGAIN.getType());
  51 + }
  52 +
  53 + public static boolean isCombination(Integer type) {
  54 + return ObjectUtil.equal(type, COMBINATION.getType());
  55 + }
  56 +
40 } 57 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
4 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 4 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
6 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 6 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
  7 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
7 import org.springframework.stereotype.Service; 8 import org.springframework.stereotype.Service;
8 import org.springframework.validation.annotation.Validated; 9 import org.springframework.validation.annotation.Validated;
9 10
@@ -25,6 +26,8 @@ public class TradeOrderApiImpl implements TradeOrderApi { @@ -25,6 +26,8 @@ public class TradeOrderApiImpl implements TradeOrderApi {
25 26
26 @Resource 27 @Resource
27 private TradeOrderQueryService tradeOrderQueryService; 28 private TradeOrderQueryService tradeOrderQueryService;
  29 + @Resource
  30 + private TradeOrderUpdateService tradeOrderUpdateService;
28 31
29 @Override 32 @Override
30 public List<TradeOrderRespDTO> getOrderList(Collection<Long> ids) { 33 public List<TradeOrderRespDTO> getOrderList(Collection<Long> ids) {
@@ -37,6 +40,16 @@ public class TradeOrderApiImpl implements TradeOrderApi { @@ -37,6 +40,16 @@ public class TradeOrderApiImpl implements TradeOrderApi {
37 } 40 }
38 41
39 @Override 42 @Override
  43 + public void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId) {
  44 + tradeOrderUpdateService.updateOrderCombinationInfo(orderId, activityId, combinationRecordId, headId);
  45 + }
  46 +
  47 + @Override
  48 + public void cancelPaidOrder(Long userId, Long orderId) {
  49 + tradeOrderUpdateService.cancelPaidOrder(userId, orderId);
  50 + }
  51 +
  52 + @Override
40 public Integer getOrderStatus(Long id) { 53 public Integer getOrderStatus(Long id) {
41 TradeOrderDO order = tradeOrderQueryService.getOrder(id); 54 TradeOrderDO order = tradeOrderQueryService.getOrder(id);
42 if (order == null) { 55 if (order == null) {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java
@@ -52,7 +52,6 @@ public class AppTradeOrderSettlementReqVO { @@ -52,7 +52,6 @@ public class AppTradeOrderSettlementReqVO {
52 private Long seckillActivityId; 52 private Long seckillActivityId;
53 53
54 // ========== 拼团活动相关字段 ========== 54 // ========== 拼团活动相关字段 ==========
55 - // TODO @puhui999:是不是拼团记录的编号哈?  
56 @Schema(description = "拼团活动编号", example = "1024") 55 @Schema(description = "拼团活动编号", example = "1024")
57 private Long combinationActivityId; 56 private Long combinationActivityId;
58 57
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
@@ -207,12 +207,16 @@ public interface TradeOrderConvert { @@ -207,12 +207,16 @@ public interface TradeOrderConvert {
207 default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO, 207 default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
208 List<CartDO> cartList) { 208 List<CartDO> cartList) {
209 TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId) 209 TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId)
  210 + .setItems(new ArrayList<>(settlementReqVO.getItems().size()))
210 .setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus()) 211 .setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus())
  212 + // 物流信息
211 .setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId()) 213 .setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId())
212 .setPickUpStoreId(settlementReqVO.getPickUpStoreId()) 214 .setPickUpStoreId(settlementReqVO.getPickUpStoreId())
213 - .setItems(new ArrayList<>(settlementReqVO.getItems().size())) 215 + // 各种活动
214 .setSeckillActivityId(settlementReqVO.getSeckillActivityId()) 216 .setSeckillActivityId(settlementReqVO.getSeckillActivityId())
215 - .setBargainRecordId(settlementReqVO.getBargainRecordId()); 217 + .setBargainRecordId(settlementReqVO.getBargainRecordId())
  218 + .setCombinationActivityId(settlementReqVO.getCombinationActivityId())
  219 + .setCombinationHeadId(settlementReqVO.getCombinationHeadId());
216 // 商品项的构建 220 // 商品项的构建
217 Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId); 221 Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId);
218 for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) { 222 for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
@@ -318,10 +318,16 @@ public class TradeOrderDO extends BaseDO { @@ -318,10 +318,16 @@ public class TradeOrderDO extends BaseDO {
318 */ 318 */
319 private Long combinationActivityId; 319 private Long combinationActivityId;
320 /** 320 /**
321 - * 团长编号 321 + * 拼团团长编号
322 * 322 *
323 - * 关联 CombinationRecordDO 的 id 字段 323 + * 关联 CombinationRecordDO 的 headId 字段
324 */ 324 */
325 private Long combinationHeadId; 325 private Long combinationHeadId;
  326 + /**
  327 + * 拼团记录编号
  328 + *
  329 + * 关联 CombinationRecordDO 的 id 字段
  330 + */
  331 + private Long combinationRecordId;
326 332
327 } 333 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/redis/RedisKeyConstants.java
@@ -15,4 +15,12 @@ public interface RedisKeyConstants { @@ -15,4 +15,12 @@ public interface RedisKeyConstants {
15 */ 15 */
16 String TRADE_NO = "trade_no:"; 16 String TRADE_NO = "trade_no:";
17 17
  18 + /**
  19 + * 交易序号的缓存
  20 + *
  21 + * KEY 格式:express_track:{code-logisticsNo-receiverMobile}
  22 + * VALUE 数据格式 String, 物流信息集合
  23 + */
  24 + String EXPRESS_TRACK = "express_track";
  25 +
18 } 26 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.order; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.order;
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.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 8 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
8 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 9 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
@@ -13,11 +14,13 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; @@ -13,11 +14,13 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
13 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 14 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
14 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; 15 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
15 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; 16 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
  17 +import cn.iocoder.yudao.module.trade.dal.redis.RedisKeyConstants;
16 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; 18 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
17 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory; 19 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
18 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; 20 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
19 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; 21 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
20 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; 22 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
  23 +import org.springframework.cache.annotation.Cacheable;
21 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
22 25
23 import javax.annotation.Resource; 26 import javax.annotation.Resource;
@@ -141,7 +144,6 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { @@ -141,7 +144,6 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
141 return tradeOrderItemMapper.selectProductSumByOrderId(convertSet(orders, TradeOrderDO::getId)); 144 return tradeOrderItemMapper.selectProductSumByOrderId(convertSet(orders, TradeOrderDO::getId));
142 } 145 }
143 146
144 - // TODO @puhui999:可以加个 spring 缓存,30 分钟;主要考虑及时性要求不高,但是每次调用需要钱;  
145 /** 147 /**
146 * 获得订单的物流轨迹 148 * 获得订单的物流轨迹
147 * 149 *
@@ -149,19 +151,34 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { @@ -149,19 +151,34 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
149 * @return 物流轨迹 151 * @return 物流轨迹
150 */ 152 */
151 private List<ExpressTrackRespDTO> getExpressTrackList(TradeOrderDO order) { 153 private List<ExpressTrackRespDTO> getExpressTrackList(TradeOrderDO order) {
152 - // 查询物流公司  
153 if (order.getLogisticsId() == null) { 154 if (order.getLogisticsId() == null) {
154 return Collections.emptyList(); 155 return Collections.emptyList();
155 } 156 }
  157 + // 查询物流公司
156 DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(order.getLogisticsId()); 158 DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(order.getLogisticsId());
157 if (express == null) { 159 if (express == null) {
158 throw exception(EXPRESS_NOT_EXISTS); 160 throw exception(EXPRESS_NOT_EXISTS);
159 } 161 }
  162 + // 查询物流轨迹
  163 + return getSelf().getExpressTrackList(express.getCode(), order.getLogisticsNo(), order.getReceiverMobile());
  164 + }
160 165
  166 + /**
  167 + * 查询物流轨迹
  168 + * 加个 spring 缓存,30 分钟;主要考虑及时性要求不高,但是每次调用需要钱;TODO @艿艿:这个时间不会搞了。。。交给你了哈哈哈
  169 + *
  170 + * @param code 快递公司编码
  171 + * @param logisticsNo 发货快递单号
  172 + * @param receiverMobile 收、寄件人的电话号码
  173 + * @return 物流轨迹
  174 + */
  175 + @Cacheable(cacheNames = RedisKeyConstants.EXPRESS_TRACK, key = "#code + '-' + #logisticsNo + '-' + #receiverMobile",
  176 + condition = "#result != null")
  177 + public List<ExpressTrackRespDTO> getExpressTrackList(String code, String logisticsNo, String receiverMobile) {
161 // 查询物流轨迹 178 // 查询物流轨迹
162 return expressClientFactory.getDefaultExpressClient().getExpressTrackList( 179 return expressClientFactory.getDefaultExpressClient().getExpressTrackList(
163 - new ExpressTrackQueryReqDTO().setExpressCode(express.getCode()).setLogisticsNo(order.getLogisticsNo())  
164 - .setPhone(order.getReceiverMobile())); 180 + new ExpressTrackQueryReqDTO().setExpressCode(code).setLogisticsNo(logisticsNo)
  181 + .setPhone(receiverMobile));
165 } 182 }
166 183
167 184
@@ -190,4 +207,13 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { @@ -190,4 +207,13 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
190 return tradeOrderItemMapper.selectListByOrderId(orderIds); 207 return tradeOrderItemMapper.selectListByOrderId(orderIds);
191 } 208 }
192 209
  210 + /**
  211 + * 获得自身的代理对象,解决 AOP 生效问题
  212 + *
  213 + * @return 自己
  214 + */
  215 + private TradeOrderQueryServiceImpl getSelf() {
  216 + return SpringUtil.getBean(getClass());
  217 + }
  218 +
193 } 219 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
@@ -170,4 +170,23 @@ public interface TradeOrderUpdateService { @@ -170,4 +170,23 @@ public interface TradeOrderUpdateService {
170 */ 170 */
171 int createOrderItemCommentBySystem(); 171 int createOrderItemCommentBySystem();
172 172
  173 + /**
  174 + * 更新拼团相关信息到订单
  175 + *
  176 + * @param orderId 订单编号
  177 + * @param activityId 拼团活动编号
  178 + * @param combinationRecordId 拼团记录编号
  179 + * @param headId 团长编号
  180 + */
  181 + // TODO 芋艿:再 review 拼团
  182 + void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId);
  183 +
  184 + /**
  185 + * 取消支付订单
  186 + *
  187 + * @param userId 用户编号
  188 + * @param orderId 订单编号
  189 + */
  190 + void cancelPaidOrder(Long userId, Long orderId);
  191 +
173 } 192 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -250,7 +250,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -250,7 +250,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
250 /** 250 /**
251 * 订单创建前,执行前置逻辑 251 * 订单创建前,执行前置逻辑
252 * 252 *
253 - * @param order 订单 253 + * @param order 订单
254 * @param orderItems 订单项 254 * @param orderItems 订单项
255 */ 255 */
256 private void beforeCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 256 private void beforeCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
@@ -267,9 +267,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -267,9 +267,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
267 * <p> 267 * <p>
268 * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等 268 * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
269 * 269 *
270 - * @param order 订单  
271 - * @param orderItems 订单项  
272 - * @param createReqVO 创建订单请求 270 + * @param order 订单
  271 + * @param orderItems 订单项
  272 + * @param createReqVO 创建订单请求
273 */ 273 */
274 private void afterCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems, 274 private void afterCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
275 AppTradeOrderCreateReqVO createReqVO) { 275 AppTradeOrderCreateReqVO createReqVO) {
@@ -331,7 +331,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -331,7 +331,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
331 } 331 }
332 332
333 // 3、订单支付成功后 333 // 3、订单支付成功后
334 - tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order)); 334 + List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
  335 + tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems));
335 336
336 // 4.1 增加用户积分(赠送) 337 // 4.1 增加用户积分(赠送)
337 addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId()); 338 addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId());
@@ -624,12 +625,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -624,12 +625,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
624 throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID); 625 throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
625 } 626 }
626 627
627 - // 2. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀  
628 - tradeOrderHandlers.forEach(handler -> handler.cancelOrder());  
629 -  
630 - // 3. 回滚库存  
631 List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id); 628 List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
  629 + // 3. 回滚库存
632 productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems)); 630 productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
  631 + // 3.1、 活动相关的回滚
  632 + tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, orderItems));
633 633
634 // 4. 回滚优惠券 634 // 4. 回滚优惠券
635 if (order.getCouponId() != null && order.getCouponId() > 0) { 635 if (order.getCouponId() != null && order.getCouponId() > 0) {
@@ -738,19 +738,21 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -738,19 +738,21 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
738 } 738 }
739 739
740 @Override 740 @Override
741 - @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_UP_ADDRESS) 741 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_ADDRESS)
742 public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) { 742 public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) {
743 // 校验交易订单 743 // 校验交易订单
744 TradeOrderDO order = validateOrderExists(reqVO.getId()); 744 TradeOrderDO order = validateOrderExists(reqVO.getId());
745 - // TODO @puhui999:是否需要校验订单是否发货  
746 - // TODO 发货后是否支持修改收货地址 745 + // 发货后,不允许修改;
  746 + // TODO @puhui999:只有待发货,可以执行 update
  747 + if (TradeOrderStatusEnum.isDelivered(order.getStatus())) {
  748 + throw exception(ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED);
  749 + }
747 750
748 // 更新 751 // 更新
749 tradeOrderMapper.updateById(TradeOrderConvert.INSTANCE.convert(reqVO)); 752 tradeOrderMapper.updateById(TradeOrderConvert.INSTANCE.convert(reqVO));
750 753
751 // 记录订单日志 754 // 记录订单日志
752 - TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus(),  
753 - MapUtil.<String, Object>builder().put("orderId", order.getId()).build()); 755 + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
754 } 756 }
755 757
756 @Override 758 @Override
@@ -806,10 +808,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -806,10 +808,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
806 // 2.2 如果全部退款,则进行取消订单 808 // 2.2 如果全部退款,则进行取消订单
807 getSelf().cancelOrderByAfterSale(order, orderRefundPrice); 809 getSelf().cancelOrderByAfterSale(order, orderRefundPrice);
808 810
809 - // TODO @puhui999:活动相关的回滚  
810 811
811 // 3. 回滚库存 812 // 3. 回滚库存
812 productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem))); 813 productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));
  814 + // 3.1、 活动相关的回滚
  815 + tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, Collections.singletonList(orderItem)));
813 816
814 // 4.1 回滚积分:扣减用户积分(赠送的) 817 // 4.1 回滚积分:扣减用户积分(赠送的)
815 reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId()); 818 reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());
@@ -909,6 +912,25 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -909,6 +912,25 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
909 return count; 912 return count;
910 } 913 }
911 914
  915 + @Override
  916 + @Transactional(rollbackFor = Exception.class)
  917 + public void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId) {
  918 + tradeOrderMapper.updateById(
  919 + new TradeOrderDO().setId(orderId).setCombinationActivityId(activityId)
  920 + .setCombinationRecordId(combinationRecordId).setCombinationHeadId(headId));
  921 + }
  922 +
  923 + @Override
  924 + @Transactional(rollbackFor = Exception.class)
  925 + public void cancelPaidOrder(Long userId, Long orderId) {
  926 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
  927 + if (order == null) {
  928 + throw exception(ORDER_NOT_FOUND);
  929 + }
  930 +
  931 + cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
  932 + }
  933 +
912 /** 934 /**
913 * 创建单个订单的评论 935 * 创建单个订单的评论
914 * 936 *
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBargainHandler.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.util.ObjectUtil;  
4 import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi; 3 import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi;
5 import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; 4 import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi;
6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@@ -26,9 +25,10 @@ public class TradeBargainHandler implements TradeOrderHandler { @@ -26,9 +25,10 @@ public class TradeBargainHandler implements TradeOrderHandler {
26 25
27 @Override 26 @Override
28 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 27 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
29 - if (ObjectUtil.notEqual(TradeOrderTypeEnum.BARGAIN.getType(), order.getType())) { 28 + if (TradeOrderTypeEnum.isBargain(order.getType())) {
30 return; 29 return;
31 } 30 }
  31 +
32 // 扣减砍价活动的库存 32 // 扣减砍价活动的库存
33 bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(), 33 bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(),
34 -orderItems.get(0).getCount()); 34 -orderItems.get(0).getCount());
@@ -36,13 +36,20 @@ public class TradeBargainHandler implements TradeOrderHandler { @@ -36,13 +36,20 @@ public class TradeBargainHandler implements TradeOrderHandler {
36 36
37 @Override 37 @Override
38 public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 38 public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
39 - if (ObjectUtil.notEqual(TradeOrderTypeEnum.BARGAIN.getType(), order.getType())) { 39 + if (TradeOrderTypeEnum.isBargain(order.getType())) {
40 return; 40 return;
41 } 41 }
  42 +
42 // 记录砍价记录对应的订单编号 43 // 记录砍价记录对应的订单编号
43 bargainRecordApi.updateBargainRecordOrderId(order.getBargainRecordId(), order.getId()); 44 bargainRecordApi.updateBargainRecordOrderId(order.getBargainRecordId(), order.getId());
44 } 45 }
45 46
46 - // TODO 芋艿:取消订单时,需要增加库存 47 + @Override
  48 + public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  49 + if (TradeOrderTypeEnum.isBargain(order.getType())) {
  50 + return;
  51 + }
  52 + // TODO 芋艿:取消订单时,需要增加库存
  53 + }
47 54
48 } 55 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationHandler.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.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 -import cn.hutool.core.util.ObjectUtil;  
5 import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; 4 import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
6 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 5 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@@ -26,40 +25,37 @@ public class TradeCombinationHandler implements TradeOrderHandler { @@ -26,40 +25,37 @@ public class TradeCombinationHandler implements TradeOrderHandler {
26 @Override 25 @Override
27 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 26 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
28 // 如果不是拼团订单则结束 27 // 如果不是拼团订单则结束
29 - if (ObjectUtil.notEqual(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { 28 + if (TradeOrderTypeEnum.isCombination(order.getType())) {
30 return; 29 return;
31 } 30 }
32 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品"); 31 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
33 32
34 - // 获取商品信息  
35 - TradeOrderItemDO item = orderItems.get(0);  
36 // 校验是否满足拼团活动相关限制 33 // 校验是否满足拼团活动相关限制
37 - combinationRecordApi.validateCombinationRecord(order.getCombinationActivityId(), order.getUserId(), item.getSkuId(), item.getCount()); 34 + TradeOrderItemDO item = orderItems.get(0);
  35 + combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(),
  36 + order.getCombinationHeadId(), item.getSkuId(), item.getCount());
  37 + // TODO @puhui999:这里还要限制下,是不是已经 createOrder;就是还没支付的时候,重复下单了;需要校验下;不然的话,一个拼团可以下多个单子了;
38 } 38 }
39 39
40 @Override 40 @Override
41 - public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 41 + public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
42 // 如果不是拼团订单则结束 42 // 如果不是拼团订单则结束
43 - if (ObjectUtil.notEqual(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { 43 + if (TradeOrderTypeEnum.isCombination(order.getType())) {
44 return; 44 return;
45 } 45 }
46 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品"); 46 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
47 47
48 // 获取商品信息 48 // 获取商品信息
49 TradeOrderItemDO item = orderItems.get(0); 49 TradeOrderItemDO item = orderItems.get(0);
50 - // 创建砍价记录 50 + // 创建拼团记录
51 combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, item)); 51 combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, item));
52 } 52 }
53 53
54 @Override 54 @Override
55 - public void afterPayOrder(TradeOrderDO order) {  
56 - // 如果不是拼团订单则结束  
57 - if (ObjectUtil.notEqual(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { 55 + public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  56 + if (TradeOrderTypeEnum.isCombination(order.getType())) {
58 return; 57 return;
59 } 58 }
60 -  
61 - // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录  
62 - combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), order.getPayTime());  
63 } 59 }
64 60
65 } 61 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeOrderHandler.java
@@ -33,12 +33,18 @@ public interface TradeOrderHandler { @@ -33,12 +33,18 @@ public interface TradeOrderHandler {
33 * 支付订单后 33 * 支付订单后
34 * 34 *
35 * @param order 订单 35 * @param order 订单
  36 + * @param orderItems 订单项
36 */ 37 */
37 - default void afterPayOrder(TradeOrderDO order) {} 38 + default void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  39 + }
38 40
39 /** 41 /**
40 * 订单取消 42 * 订单取消
  43 + *
  44 + * @param order 订单
  45 + * @param orderItems 订单项
41 */ 46 */
42 - default void cancelOrder() {} 47 + default void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  48 + }
43 49
44 } 50 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeSeckillHandler.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.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.trade.dal.dataobject.order.TradeOrderDO; 4 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@@ -23,12 +22,21 @@ public class TradeSeckillHandler implements TradeOrderHandler { @@ -23,12 +22,21 @@ public class TradeSeckillHandler implements TradeOrderHandler {
23 22
24 @Override 23 @Override
25 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 24 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
26 - if (ObjectUtil.notEqual(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) { 25 + if (TradeOrderTypeEnum.isSeckill(order.getType())) {
27 return; 26 return;
28 } 27 }
  28 +
29 // 扣减秒杀活动的库存 29 // 扣减秒杀活动的库存
30 seckillActivityApi.updateSeckillStock(order.getSeckillActivityId(), 30 seckillActivityApi.updateSeckillStock(order.getSeckillActivityId(),
31 orderItems.get(0).getSkuId(), orderItems.get(0).getCount()); 31 orderItems.get(0).getSkuId(), orderItems.get(0).getCount());
32 } 32 }
33 33
  34 + @Override
  35 + public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  36 + if (TradeOrderTypeEnum.isSeckill(order.getType())) {
  37 + return;
  38 + }
  39 +
  40 + }
  41 +
34 } 42 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java
@@ -68,7 +68,6 @@ public class TradePriceCalculateReqBO { @@ -68,7 +68,6 @@ public class TradePriceCalculateReqBO {
68 private Long seckillActivityId; 68 private Long seckillActivityId;
69 69
70 // ========== 拼团活动相关字段 ========== 70 // ========== 拼团活动相关字段 ==========
71 - // TODO @puhui999:是不是拼团记录的编号哈?  
72 /** 71 /**
73 * 拼团活动编号 72 * 拼团活动编号
74 */ 73 */
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCombinationActivityPriceCalculator.java
@@ -12,6 +12,8 @@ import org.springframework.stereotype.Component; @@ -12,6 +12,8 @@ import org.springframework.stereotype.Component;
12 12
13 import javax.annotation.Resource; 13 import javax.annotation.Resource;
14 14
  15 +// TODO @puhui999:单测可以后补下
  16 +
15 /** 17 /**
16 * 拼团活动的 {@link TradePriceCalculator} 实现类 18 * 拼团活动的 {@link TradePriceCalculator} 实现类
17 * 19 *
@@ -30,12 +32,11 @@ public class TradeCombinationActivityPriceCalculator implements TradePriceCalcul @@ -30,12 +32,11 @@ public class TradeCombinationActivityPriceCalculator implements TradePriceCalcul
30 if (param.getCombinationActivityId() == null) { 32 if (param.getCombinationActivityId() == null) {
31 return; 33 return;
32 } 34 }
33 -  
34 Assert.isTrue(param.getItems().size() == 1, "拼团时,只允许选择一个商品"); 35 Assert.isTrue(param.getItems().size() == 1, "拼团时,只允许选择一个商品");
35 // 2. 校验是否可以参与拼团 36 // 2. 校验是否可以参与拼团
36 TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); 37 TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0);
37 CombinationValidateJoinRespDTO combinationActivity = combinationRecordApi.validateJoinCombination( 38 CombinationValidateJoinRespDTO combinationActivity = combinationRecordApi.validateJoinCombination(
38 - param.getCombinationActivityId(), param.getUserId(), 39 + param.getUserId(), param.getCombinationActivityId(), param.getCombinationHeadId(),
39 orderItem.getSkuId(), orderItem.getCount()); 40 orderItem.getSkuId(), orderItem.getCount());
40 41
41 // 3.1 记录优惠明细 42 // 3.1 记录优惠明细
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java
@@ -176,7 +176,7 @@ public class DeptServiceImpl implements DeptService { @@ -176,7 +176,7 @@ public class DeptServiceImpl implements DeptService {
176 } 176 }
177 177
178 @Override 178 @Override
179 - @DataPermission(enable = false) // 禁用数据权限,避免简历不正确的缓存 179 + @DataPermission(enable = false) // 禁用数据权限,避免建立不正确的缓存
180 @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id") 180 @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id")
181 public Set<Long> getChildDeptIdListFromCache(Long id) { 181 public Set<Long> getChildDeptIdListFromCache(Long id) {
182 List<DeptDO> children = getChildDeptList(id); 182 List<DeptDO> children = getChildDeptList(id);