Commit 74a5e40d8d4306d48adaea4968a81e8f58a4ef92
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 | 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 | 245 | if (CollUtil.isEmpty(from)) { |
| 245 | 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<T> extends MPJBaseMapper<T> { |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | default Long selectCount() { |
| 59 | - return selectCount(new QueryWrapper<T>()); | |
| 59 | + return selectCount(new QueryWrapper<>()); | |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | 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 | 4 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; |
| 5 | 5 | |
| 6 | 6 | import javax.validation.Valid; |
| 7 | -import java.time.LocalDateTime; | |
| 8 | 7 | |
| 9 | 8 | // TODO @芋艿:后面也再撸撸这几个接口 |
| 10 | 9 | |
| ... | ... | @@ -18,19 +17,21 @@ public interface CombinationRecordApi { |
| 18 | 17 | /** |
| 19 | 18 | * 校验是否满足拼团条件 |
| 20 | 19 | * |
| 21 | - * @param activityId 活动编号 | |
| 22 | 20 | * @param userId 用户编号 |
| 21 | + * @param activityId 活动编号 | |
| 22 | + * @param headId 团长编号 | |
| 23 | 23 | * @param skuId sku 编号 |
| 24 | 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 | 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 | 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 | 48 | * @param userId 用户编号 |
| ... | ... | @@ -58,25 +51,18 @@ public interface CombinationRecordApi { |
| 58 | 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 | 58 | * @param userId 用户编号 |
| 59 | + * @param activityId 活动编号 | |
| 60 | + * @param headId 团长编号 | |
| 76 | 61 | * @param skuId sku 编号 |
| 77 | 62 | * @param count 数量 |
| 78 | 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 | 4 | |
| 5 | 5 | import javax.validation.constraints.NotNull; |
| 6 | 6 | |
| 7 | -// TODO @芋艿:这块要在看看 | |
| 8 | 7 | /** |
| 9 | 8 | * 拼团记录的创建 Request DTO |
| 10 | 9 | * |
| ... | ... | @@ -46,7 +45,6 @@ public class CombinationRecordCreateReqDTO { |
| 46 | 45 | /** |
| 47 | 46 | * 团长编号 |
| 48 | 47 | */ |
| 49 | - @NotNull(message = "团长编号不能为空") | |
| 50 | 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 | 80 | ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1_013_011_001, "拼团失败,已参与过该拼团"); |
| 81 | 81 | ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1_013_011_002, "拼团失败,父拼团不存在"); |
| 82 | 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 | 84 | ErrorCode COMBINATION_RECORD_FAILED_TIME_NOT_START = new ErrorCode(1_013_011_005, "拼团失败,活动未开始"); |
| 85 | 85 | ErrorCode COMBINATION_RECORD_FAILED_TIME_END = new ErrorCode(1_013_011_006, "拼团失败,活动已经结束"); |
| 86 | 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 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; |
| 4 | 4 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; |
| 5 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; | |
| 5 | 6 | import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; |
| 6 | 7 | import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; |
| 7 | 8 | import org.springframework.stereotype.Service; |
| 8 | 9 | |
| 9 | 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 | 16 | * 拼团活动 API 实现类 |
| ... | ... | @@ -21,23 +24,22 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { |
| 21 | 24 | private CombinationRecordService recordService; |
| 22 | 25 | |
| 23 | 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 | 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 | 36 | @Override |
| 34 | 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 | 45 | @Override |
| ... | ... | @@ -46,14 +48,8 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { |
| 46 | 48 | } |
| 47 | 49 | |
| 48 | 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 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 6 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| 7 | 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 | 9 | import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; |
| 13 | 10 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; |
| 14 | 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 | 14 | import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService; |
| 15 | +import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; | |
| 16 | 16 | import io.swagger.v3.oas.annotations.Operation; |
| 17 | 17 | import io.swagger.v3.oas.annotations.Parameter; |
| 18 | 18 | import io.swagger.v3.oas.annotations.tags.Tag; |
| ... | ... | @@ -23,6 +23,8 @@ import org.springframework.web.bind.annotation.*; |
| 23 | 23 | import javax.annotation.Resource; |
| 24 | 24 | import javax.validation.Valid; |
| 25 | 25 | import java.util.List; |
| 26 | +import java.util.Map; | |
| 27 | +import java.util.Set; | |
| 26 | 28 | |
| 27 | 29 | import static cn.hutool.core.collection.CollectionUtil.newArrayList; |
| 28 | 30 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| ... | ... | @@ -36,6 +38,8 @@ public class CombinationActivityController { |
| 36 | 38 | |
| 37 | 39 | @Resource |
| 38 | 40 | private CombinationActivityService combinationActivityService; |
| 41 | + @Resource | |
| 42 | + private CombinationRecordService combinationRecordService; | |
| 39 | 43 | |
| 40 | 44 | @Resource |
| 41 | 45 | private ProductSpuApi productSpuApi; |
| ... | ... | @@ -70,14 +74,14 @@ public class CombinationActivityController { |
| 70 | 74 | @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") |
| 71 | 75 | public CommonResult<CombinationActivityRespVO> getCombinationActivity(@RequestParam("id") Long id) { |
| 72 | 76 | CombinationActivityDO activity = combinationActivityService.getCombinationActivity(id); |
| 73 | - List<CombinationProductDO> products = combinationActivityService.getCombinationProductsByActivityIds(newArrayList(id)); | |
| 77 | + List<CombinationProductDO> products = combinationActivityService.getCombinationProductListByActivityIds(newArrayList(id)); | |
| 74 | 78 | return success(CombinationActivityConvert.INSTANCE.convert(activity, products)); |
| 75 | 79 | } |
| 76 | 80 | |
| 77 | 81 | @GetMapping("/page") |
| 78 | 82 | @Operation(summary = "获得拼团活动分页") |
| 79 | 83 | @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") |
| 80 | - public CommonResult<PageResult<CombinationActivityRespVO>> getCombinationActivityPage( | |
| 84 | + public CommonResult<PageResult<CombinationActivityPageItemRespVO>> getCombinationActivityPage( | |
| 81 | 85 | @Valid CombinationActivityPageReqVO pageVO) { |
| 82 | 86 | // 查询拼团活动 |
| 83 | 87 | PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO); |
| ... | ... | @@ -85,12 +89,21 @@ public class CombinationActivityController { |
| 85 | 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 | 102 | convertSet(pageResult.getList(), CombinationActivityDO::getId)); |
| 91 | 103 | List<ProductSpuRespDTO> spus = productSpuApi.getSpuList( |
| 92 | 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 | 48 | @NotNull(message = "开团人数不能为空") |
| 49 | 49 | private Integer userSize; |
| 50 | 50 | |
| 51 | + @Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") | |
| 52 | + @NotNull(message = "虚拟成团不能为空") | |
| 53 | + private Boolean virtualGroup; | |
| 54 | + | |
| 51 | 55 | @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 52 | 56 | @NotNull(message = "限制时长不能为空") |
| 53 | 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 | 15 | @Schema(description = "拼团名称", example = "赵六") |
| 16 | 16 | private String name; |
| 17 | 17 | |
| 18 | - @Schema(description = "活动状态:0开启 1关闭", example = "0") | |
| 18 | + @Schema(description = "活动状态", example = "0") | |
| 19 | 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 | 6 | import lombok.EqualsAndHashCode; |
| 7 | 7 | import lombok.ToString; |
| 8 | 8 | |
| 9 | -import javax.validation.Valid; | |
| 10 | -import javax.validation.constraints.NotNull; | |
| 11 | 9 | import java.time.LocalDateTime; |
| 12 | 10 | import java.util.List; |
| 13 | 11 | |
| ... | ... | @@ -20,30 +18,12 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO { |
| 20 | 18 | @Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901") |
| 21 | 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 | 21 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 30 | 22 | private LocalDateTime createTime; |
| 31 | 23 | |
| 32 | 24 | @Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") |
| 33 | 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 | 27 | @Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED) |
| 48 | 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 | 18 | @Schema(description = "秒杀活动 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 19 | 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 | 21 | @Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED) |
| 32 | 22 | private List<SeckillProductRespVO> products; |
| 33 | 23 | |
| ... | ... | @@ -52,4 +42,16 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO { |
| 52 | 42 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 53 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 6 | 7 | import cn.iocoder.yudao.module.promotion.controller.app.activity.vo.AppActivityRespVO; |
| 7 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; |
| ... | ... | @@ -21,12 +22,10 @@ import org.springframework.web.bind.annotation.RequestParam; |
| 21 | 22 | import org.springframework.web.bind.annotation.RestController; |
| 22 | 23 | |
| 23 | 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 | 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 | 30 | @Tag(name = "用户 APP - 营销活动") // 用于提供跨多个活动的 HTTP 接口 |
| 32 | 31 | @RestController |
| ... | ... | @@ -42,58 +41,60 @@ public class AppActivityController { |
| 42 | 41 | private BargainActivityService bargainActivityService; |
| 43 | 42 | |
| 44 | 43 | @GetMapping("/list-by-spu-id") |
| 45 | - @Operation(summary = "获得单个商品,近期参与的每个活动") // 每种活动,只返回一个 | |
| 44 | + @Operation(summary = "获得单个商品,近期参与的每个活动") | |
| 46 | 45 | @Parameter(name = "spuId", description = "商品编号", required = true) |
| 47 | 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 | 51 | @GetMapping("/list-by-spu-ids") |
| 52 | - @Operation(summary = "获得多个商品,近期参与的每个活动") // 每种活动,只返回一个;key 为 SPU 编号 | |
| 52 | + @Operation(summary = "获得多个商品,近期参与的每个活动") | |
| 53 | 53 | @Parameter(name = "spuIds", description = "商品编号数组", required = true) |
| 54 | 54 | public CommonResult<Map<Long, List<AppActivityRespVO>>> getActivityListBySpuIds(@RequestParam("spuIds") List<Long> spuIds) { |
| 55 | - | |
| 56 | 55 | if (CollUtil.isEmpty(spuIds)) { |
| 57 | 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 | 13 | private Long id; |
| 14 | 14 | |
| 15 | 15 | @Schema(description = "活动类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 16 | - // 对应 PromotionTypeEnum 枚举 | |
| 17 | - private Integer type; | |
| 16 | + private Integer type; // 对应 PromotionTypeEnum 枚举 | |
| 18 | 17 | |
| 19 | 18 | @Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618 大促") |
| 20 | 19 | private String name; |
| 21 | 20 | |
| 21 | + @Schema(description = "spu 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "618") | |
| 22 | + private Long spuId; | |
| 23 | + | |
| 22 | 24 | @Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 23 | 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 | 30 | import java.util.Collections; |
| 31 | 31 | import java.util.List; |
| 32 | 32 | |
| 33 | -import static cn.hutool.core.util.ObjectUtil.defaultIfNull; | |
| 34 | 33 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 35 | 34 | import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache; |
| 36 | 35 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| ... | ... | @@ -56,6 +55,7 @@ public class AppCombinationActivityController { |
| 56 | 55 | |
| 57 | 56 | @Resource |
| 58 | 57 | private CombinationActivityService activityService; |
| 58 | + | |
| 59 | 59 | @Resource |
| 60 | 60 | private ProductSpuApi spuApi; |
| 61 | 61 | |
| ... | ... | @@ -68,40 +68,45 @@ public class AppCombinationActivityController { |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | 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 | 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 | 82 | @GetMapping("/page") |
| 81 | 83 | @Operation(summary = "获得拼团活动分页") |
| 82 | 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 | 96 | @GetMapping("/get-detail") |
| 93 | 97 | @Operation(summary = "获得拼团活动明细") |
| 94 | 98 | @Parameter(name = "id", description = "活动编号", required = true, example = "1024") |
| 95 | 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 | 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 | 1 | package cn.iocoder.yudao.module.promotion.controller.app.combination; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.util.ObjectUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | |
| 6 | 4 | import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordDetailRespVO; |
| 7 | 5 | import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO; |
| 8 | 6 | import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordSummaryRespVO; |
| 9 | 7 | import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; |
| 10 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; |
| 9 | +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; | |
| 11 | 10 | import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; |
| 11 | +import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi; | |
| 12 | 12 | import io.swagger.v3.oas.annotations.Operation; |
| 13 | 13 | import io.swagger.v3.oas.annotations.Parameter; |
| 14 | 14 | import io.swagger.v3.oas.annotations.Parameters; |
| 15 | 15 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 16 | +import org.springframework.context.annotation.Lazy; | |
| 16 | 17 | import org.springframework.validation.annotation.Validated; |
| 17 | 18 | import org.springframework.web.bind.annotation.GetMapping; |
| 18 | 19 | import org.springframework.web.bind.annotation.RequestMapping; |
| ... | ... | @@ -23,6 +24,7 @@ import javax.annotation.Resource; |
| 23 | 24 | import javax.validation.constraints.Max; |
| 24 | 25 | import java.util.Collections; |
| 25 | 26 | import java.util.List; |
| 27 | +import java.util.Objects; | |
| 26 | 28 | |
| 27 | 29 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 28 | 30 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| ... | ... | @@ -36,24 +38,27 @@ public class AppCombinationRecordController { |
| 36 | 38 | |
| 37 | 39 | @Resource |
| 38 | 40 | private CombinationRecordService combinationRecordService; |
| 41 | + @Resource | |
| 42 | + @Lazy | |
| 43 | + private TradeOrderApi tradeOrderApi; | |
| 39 | 44 | |
| 40 | 45 | @GetMapping("/get-summary") |
| 41 | 46 | @Operation(summary = "获得拼团记录的概要信息", description = "用于小程序首页") |
| 42 | - // TODO 芋艿:增加 @Cache 缓存,1 分钟过期 | |
| 43 | 47 | public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() { |
| 44 | - // 获取所有拼团记录 | |
| 45 | - Long count = combinationRecordService.getRecordsCount(); | |
| 46 | 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 | 52 | summary.setAvatars(Collections.emptyList()); |
| 49 | 53 | summary.setUserCount(count); |
| 50 | 54 | return success(summary); |
| 51 | 55 | } |
| 52 | - | |
| 53 | 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 | 62 | return success(summary); |
| 58 | 63 | } |
| 59 | 64 | |
| ... | ... | @@ -61,7 +66,7 @@ public class AppCombinationRecordController { |
| 61 | 66 | @Operation(summary = "获得最近 n 条拼团记录(团长发起的)") |
| 62 | 67 | @Parameters({ |
| 63 | 68 | @Parameter(name = "activityId", description = "拼团活动编号"), |
| 64 | - @Parameter(name = "status", description = "状态"), | |
| 69 | + @Parameter(name = "status", description = "拼团状态"), // 对应 CombinationRecordStatusEnum 枚举 | |
| 65 | 70 | @Parameter(name = "count", description = "数量") |
| 66 | 71 | }) |
| 67 | 72 | public CommonResult<List<AppCombinationRecordRespVO>> getHeadCombinationRecordList( |
| ... | ... | @@ -69,44 +74,54 @@ public class AppCombinationRecordController { |
| 69 | 74 | @RequestParam("status") Integer status, |
| 70 | 75 | @RequestParam(value = "count", defaultValue = "20") @Max(20) Integer count) { |
| 71 | 76 | return success(CombinationActivityConvert.INSTANCE.convertList3( |
| 72 | - combinationRecordService.getRecordListWithHead(activityId, status, count))); | |
| 77 | + combinationRecordService.getHeadCombinationRecordList(activityId, status, count))); | |
| 73 | 78 | } |
| 74 | 79 | |
| 75 | 80 | @GetMapping("/get-detail") |
| 76 | 81 | @Operation(summary = "获得拼团记录明细") |
| 77 | 82 | @Parameter(name = "id", description = "拼团记录编号", required = true, example = "1024") |
| 78 | 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 | 86 | if (record == null) { |
| 82 | 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 | 9 | @Data |
| 10 | 10 | public class AppCombinationRecordSummaryRespVO { |
| 11 | 11 | |
| 12 | + /** | |
| 13 | + * 加载 {@link #avatars} 的数量 | |
| 14 | + */ | |
| 15 | + public static final Integer AVATAR_COUNT = 7; | |
| 16 | + | |
| 12 | 17 | @Schema(description = "拼团用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| 13 | 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 | 86 | |
| 87 | 87 | // 2.1 查询满足当前阶段的活动 |
| 88 | 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 | 91 | // 2.2 获取 spu 信息 |
| 91 | 92 | List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId)); |
| 92 | 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 | 1 | package cn.iocoder.yudao.module.promotion.convert.combination; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ObjectUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 5 | 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 | 9 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; |
| 9 | 10 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; |
| 10 | 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 | 13 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO; |
| 12 | 14 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO; |
| 13 | 15 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO; |
| 14 | 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 | 18 | import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO; |
| 16 | 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 | 21 | import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.record.AppCombinationRecordRespVO; |
| 18 | 22 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; |
| 19 | 23 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; |
| 20 | 24 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; |
| 25 | +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; | |
| 21 | 26 | import org.mapstruct.Mapper; |
| 22 | 27 | import org.mapstruct.Mapping; |
| 23 | 28 | import org.mapstruct.Mappings; |
| 24 | 29 | import org.mapstruct.factory.Mappers; |
| 25 | 30 | |
| 31 | +import java.time.LocalDateTime; | |
| 26 | 32 | import java.util.List; |
| 27 | 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 | 36 | import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; |
| 31 | 37 | |
| 32 | 38 | /** |
| ... | ... | @@ -53,20 +59,29 @@ public interface CombinationActivityConvert { |
| 53 | 59 | |
| 54 | 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 | 70 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 62 | - PageResult<CombinationActivityRespVO> pageResult = convertPage(page); | |
| 63 | 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 | 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 | 80 | return pageResult; |
| 68 | 81 | } |
| 69 | 82 | |
| 83 | + PageResult<CombinationActivityPageItemRespVO> convertPage(PageResult<CombinationActivityDO> page); | |
| 84 | + | |
| 70 | 85 | List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs); |
| 71 | 86 | |
| 72 | 87 | @Mappings({ |
| ... | ... | @@ -97,23 +112,31 @@ public interface CombinationActivityConvert { |
| 97 | 112 | default CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO, |
| 98 | 113 | CombinationActivityDO activity, MemberUserRespDTO user, |
| 99 | 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 | 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 | 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 | 133 | List<AppCombinationActivityRespVO> activityList = convertAppList(list); |
| 115 | 134 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 135 | + Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId); | |
| 116 | 136 | return CollectionUtils.convertList(activityList, item -> { |
| 137 | + // 设置 product 信息 | |
| 138 | + item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice)); | |
| 139 | + // 设置 SPU 信息 | |
| 117 | 140 | findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); |
| 118 | 141 | return item; |
| 119 | 142 | }); |
| ... | ... | @@ -121,13 +144,17 @@ public interface CombinationActivityConvert { |
| 121 | 144 | |
| 122 | 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 | 150 | PageResult<AppCombinationActivityRespVO> appPage = convertAppPage(result); |
| 126 | 151 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 152 | + Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId); | |
| 127 | 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 | 158 | return item; |
| 132 | 159 | }); |
| 133 | 160 | appPage.setList(list); |
| ... | ... | @@ -146,4 +173,31 @@ public interface CombinationActivityConvert { |
| 146 | 173 | |
| 147 | 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 | 60 | */ |
| 61 | 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 | 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 | 39 | @TableId |
| ... | ... | @@ -71,6 +76,7 @@ public class CombinationRecordDO extends BaseDO { |
| 71 | 76 | * 用户编号 |
| 72 | 77 | */ |
| 73 | 78 | private Long userId; |
| 79 | + | |
| 74 | 80 | /** |
| 75 | 81 | * 用户昵称 |
| 76 | 82 | */ |
| ... | ... | @@ -84,6 +90,8 @@ public class CombinationRecordDO extends BaseDO { |
| 84 | 90 | * 团长编号 |
| 85 | 91 | * |
| 86 | 92 | * 关联 {@link CombinationRecordDO#getId()} |
| 93 | + * | |
| 94 | + * 如果是团长,则它的值是 {@link #HEAD_ID_GROUP} | |
| 87 | 95 | */ |
| 88 | 96 | private Long headId; |
| 89 | 97 | /** |
| ... | ... | @@ -108,6 +116,9 @@ public class CombinationRecordDO extends BaseDO { |
| 108 | 116 | private Integer userCount; |
| 109 | 117 | /** |
| 110 | 118 | * 是否虚拟成团 |
| 119 | + * | |
| 120 | + * 默认为 false。 | |
| 121 | + * 拼团过期都还没有成功,如果 {@link CombinationActivityDO#getVirtualGroup()} 为 true,则执行虚拟成团的逻辑,才会更新该字段为 true | |
| 111 | 122 | */ |
| 112 | 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 | import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; |
| 9 | 9 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 10 | 10 | import org.apache.ibatis.annotations.Mapper; |
| 11 | +import org.apache.ibatis.annotations.Select; | |
| 11 | 12 | |
| 12 | 13 | import java.time.LocalDateTime; |
| 14 | +import java.util.Collection; | |
| 13 | 15 | import java.util.List; |
| 14 | 16 | |
| 15 | 17 | /** |
| ... | ... | @@ -83,11 +85,25 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> { |
| 83 | 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 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; |
| 8 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; |
| 9 | 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 | 14 | import java.util.List; |
| 12 | 15 | |
| 13 | 16 | /** |
| ... | ... | @@ -40,11 +43,24 @@ public interface CombinationActivityMapper extends BaseMapperX<CombinationActivi |
| 40 | 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 | 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 | 7 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 4 | 8 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 9 | +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO; | |
| 5 | 10 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; |
| 11 | +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |
| 6 | 12 | import org.apache.ibatis.annotations.Mapper; |
| 7 | 13 | |
| 14 | +import java.util.Collection; | |
| 15 | +import java.util.Collections; | |
| 8 | 16 | import java.util.List; |
| 17 | +import java.util.Map; | |
| 9 | 18 | |
| 10 | 19 | /** |
| 11 | 20 | * 拼团记录 Mapper |
| ... | ... | @@ -20,34 +29,18 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO |
| 20 | 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 | 35 | * @param headId 团长编号 |
| 37 | 36 | * @return 拼团记录 |
| 38 | 37 | */ |
| 39 | - default CombinationRecordDO selectOneByHeadId(Long headId, Integer status) { | |
| 38 | + default CombinationRecordDO selectByHeadId(Long headId, Integer status) { | |
| 40 | 39 | return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>() |
| 41 | 40 | .eq(CombinationRecordDO::getId, headId) |
| 42 | 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<CombinationRecordDO |
| 69 | 62 | */ |
| 70 | 63 | default List<CombinationRecordDO> selectLatestList(int count) { |
| 71 | 64 | return selectList(new LambdaQueryWrapperX<CombinationRecordDO>() |
| 72 | - .orderByDesc(CombinationRecordDO::getCreateTime) | |
| 65 | + .orderByDesc(CombinationRecordDO::getId) | |
| 73 | 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 | 71 | return selectList(new LambdaQueryWrapperX<CombinationRecordDO>() |
| 86 | 72 | .eqIfPresent(CombinationRecordDO::getActivityId, activityId) |
| 87 | 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 | 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 | 9 | import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO; |
| 10 | 10 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| 11 | 11 | import org.apache.ibatis.annotations.Mapper; |
| 12 | +import org.apache.ibatis.annotations.Select; | |
| 12 | 13 | |
| 14 | +import java.util.Collection; | |
| 13 | 15 | import java.util.List; |
| 14 | 16 | |
| 15 | 17 | /** |
| ... | ... | @@ -56,11 +58,24 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { |
| 56 | 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 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; |
| 9 | 9 | |
| 10 | 10 | import javax.validation.Valid; |
| 11 | +import java.util.Collection; | |
| 11 | 12 | import java.util.List; |
| 12 | 13 | import java.util.Set; |
| 13 | 14 | |
| ... | ... | @@ -99,11 +100,12 @@ public interface BargainActivityService { |
| 99 | 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 | 20 | |
| 21 | 21 | import javax.annotation.Resource; |
| 22 | 22 | import java.time.LocalDateTime; |
| 23 | +import java.util.Collection; | |
| 23 | 24 | import java.util.List; |
| 24 | 25 | import java.util.Set; |
| 25 | 26 | |
| ... | ... | @@ -176,8 +177,8 @@ public class BargainActivityServiceImpl implements BargainActivityService { |
| 176 | 177 | } |
| 177 | 178 | |
| 178 | 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 | 73 | * @return 拼团活动的商品列表 |
| 74 | 74 | */ |
| 75 | 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 | 82 | * @param activityIds 拼团活动 ids |
| 83 | 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 | 109 | PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam); |
| 102 | 110 | |
| 103 | 111 | /** |
| 104 | - * 获取指定活动指定 sku 编号的商品 | |
| 112 | + * 获取指定活动、指定 sku 编号的商品 | |
| 105 | 113 | * |
| 106 | 114 | * @param activityId 活动编号 |
| 107 | 115 | * @param skuId sku 编号 |
| ... | ... | @@ -110,11 +118,12 @@ public interface CombinationActivityService { |
| 110 | 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 | 65 | |
| 66 | 66 | // 插入拼团活动 |
| 67 | 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 | 69 | combinationActivityMapper.insert(activity); |
| 71 | 70 | // 插入商品 |
| 72 | 71 | List<CombinationProductDO> products = CombinationActivityConvert.INSTANCE.convertList(createReqVO.getProducts(), activity); |
| 73 | 72 | combinationProductMapper.insertBatch(products); |
| 74 | - // 返回 | |
| 75 | 73 | return activity.getId(); |
| 76 | 74 | } |
| 77 | 75 | |
| ... | ... | @@ -202,11 +200,16 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic |
| 202 | 200 | } |
| 203 | 201 | |
| 204 | 202 | @Override |
| 205 | - public List<CombinationProductDO> getCombinationProductsByActivityIds(Collection<Long> activityIds) { | |
| 203 | + public List<CombinationProductDO> getCombinationProductListByActivityIds(Collection<Long> activityIds) { | |
| 206 | 204 | return combinationProductMapper.selectListByActivityIds(activityIds); |
| 207 | 205 | } |
| 208 | 206 | |
| 209 | 207 | @Override |
| 208 | + public List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids) { | |
| 209 | + return combinationActivityMapper.selectList(CombinationActivityDO::getId, ids); | |
| 210 | + } | |
| 211 | + | |
| 212 | + @Override | |
| 210 | 213 | public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) { |
| 211 | 214 | return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count); |
| 212 | 215 | } |
| ... | ... | @@ -224,8 +227,8 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic |
| 224 | 227 | } |
| 225 | 228 | |
| 226 | 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 | 1 | package cn.iocoder.yudao.module.promotion.service.combination; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.core.KeyValue; |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 4 | 5 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; |
| 5 | 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 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; |
| 7 | 9 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; |
| 8 | 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 | 14 | import java.util.List; |
| 15 | +import java.util.Map; | |
| 12 | 16 | |
| 13 | 17 | /** |
| 14 | 18 | * 拼团记录 Service 接口 |
| ... | ... | @@ -27,40 +31,34 @@ public interface CombinationRecordService { |
| 27 | 31 | void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId); |
| 28 | 32 | |
| 29 | 33 | /** |
| 30 | - * 校验是否满足拼团条件 | |
| 31 | - * 如果不满足,会抛出异常 | |
| 34 | + * 【下单前】校验是否满足拼团活动条件 | |
| 35 | + * | |
| 36 | + * 如果校验失败,则抛出业务异常 | |
| 32 | 37 | * |
| 33 | - * @param activityId 活动编号 | |
| 34 | 38 | * @param userId 用户编号 |
| 39 | + * @param activityId 活动编号 | |
| 40 | + * @param headId 团长编号 | |
| 35 | 41 | * @param skuId sku 编号 |
| 36 | 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 | 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 | 59 | * @param userId 用户编号 |
| 62 | 60 | * @param orderId 订单编号 |
| 63 | - * @return 拼团状态 | |
| 61 | + * @return 拼团记录 | |
| 64 | 62 | */ |
| 65 | 63 | CombinationRecordDO getCombinationRecord(Long userId, Long orderId); |
| 66 | 64 | |
| ... | ... | @@ -71,27 +69,30 @@ public interface CombinationRecordService { |
| 71 | 69 | * @param activityId 活动 id |
| 72 | 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 | 79 | * @param userId 用户编号 |
| 80 | + * @param activityId 活动编号 | |
| 81 | + * @param headId 团长编号 | |
| 83 | 82 | * @param skuId sku 编号 |
| 84 | 83 | * @param count 数量 |
| 85 | 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 | 93 | * @return 记录数 |
| 93 | 94 | */ |
| 94 | - Long getRecordsCount(); | |
| 95 | + Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup); | |
| 95 | 96 | |
| 96 | 97 | /** |
| 97 | 98 | * 获取最近的 count 条拼团记录 |
| ... | ... | @@ -99,7 +100,7 @@ public interface CombinationRecordService { |
| 99 | 100 | * @param count 限制数量 |
| 100 | 101 | * @return 拼团记录列表 |
| 101 | 102 | */ |
| 102 | - List<CombinationRecordDO> getLatestRecordList(int count); | |
| 103 | + List<CombinationRecordDO> getLatestCombinationRecordList(int count); | |
| 103 | 104 | |
| 104 | 105 | /** |
| 105 | 106 | * 获得最近 n 条拼团记录(团长发起的) |
| ... | ... | @@ -109,7 +110,7 @@ public interface CombinationRecordService { |
| 109 | 110 | * @param count 数量 |
| 110 | 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 | 118 | * @param id 拼团记录编号 |
| 118 | 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 | 126 | * @param headId 团长编号 |
| 126 | 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 | 1 | package cn.iocoder.yudao.module.promotion.service.combination; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.util.ObjectUtil; | |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 5 | 5 | import cn.iocoder.yudao.framework.common.core.KeyValue; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 7 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 7 | 8 | import cn.iocoder.yudao.module.member.api.user.MemberUserApi; |
| 8 | 9 | import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; |
| 9 | 10 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
| ... | ... | @@ -12,6 +13,7 @@ import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| 12 | 13 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; |
| 13 | 14 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; |
| 14 | 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 | 17 | import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; |
| 16 | 18 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; |
| 17 | 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 | 21 | import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper; |
| 20 | 22 | import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; |
| 21 | 23 | import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi; |
| 22 | -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; | |
| 23 | 24 | import org.springframework.context.annotation.Lazy; |
| 24 | 25 | import org.springframework.stereotype.Service; |
| 25 | 26 | import org.springframework.transaction.annotation.Transactional; |
| 26 | 27 | import org.springframework.validation.annotation.Validated; |
| 27 | 28 | |
| 29 | +import javax.annotation.Nullable; | |
| 28 | 30 | import javax.annotation.Resource; |
| 29 | -import java.time.LocalDateTime; | |
| 30 | -import java.util.List; | |
| 31 | +import java.util.*; | |
| 31 | 32 | |
| 32 | 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 | 38 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; |
| 35 | 39 | |
| 36 | 40 | // TODO 芋艿:等拼团记录做完,完整 review 下 |
| ... | ... | @@ -61,6 +65,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { |
| 61 | 65 | @Resource |
| 62 | 66 | private TradeOrderApi tradeOrderApi; |
| 63 | 67 | |
| 68 | + // TODO @芋艿:在详细预览下; | |
| 64 | 69 | @Override |
| 65 | 70 | @Transactional(rollbackFor = Exception.class) |
| 66 | 71 | public void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId) { |
| ... | ... | @@ -72,30 +77,6 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { |
| 72 | 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 | 80 | private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) { |
| 100 | 81 | // 校验拼团是否存在 |
| 101 | 82 | CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId); |
| ... | ... | @@ -108,141 +89,247 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { |
| 108 | 89 | // TODO @芋艿:在详细预览下; |
| 109 | 90 | @Override |
| 110 | 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 | 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 | 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 | 104 | if (count > activity.getSingleLimitCount()) { |
| 120 | 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 | 131 | CombinationProductDO product = combinationActivityService.selectByActivityIdAndSkuId(activityId, skuId); |
| 124 | 132 | if (product == null) { |
| 125 | 133 | throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS); |
| 126 | 134 | } |
| 127 | - // 2.2、校验 sku 是否存在 | |
| 135 | + // 4.2 校验 sku 是否存在 | |
| 128 | 136 | ProductSkuRespDTO sku = productSkuApi.getSku(skuId); |
| 129 | 137 | if (sku == null) { |
| 130 | 138 | throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS); |
| 131 | 139 | } |
| 132 | - // 2.3、 校验库存是否充足 | |
| 140 | + // 4.3 校验库存是否充足 | |
| 133 | 141 | if (count > sku.getStock()) { |
| 134 | 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 | 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 | 162 | return new KeyValue<>(activity, product); |
| 161 | 163 | } |
| 162 | 164 | |
| 163 | - // TODO 芋艿:在详细 review 下; | |
| 164 | 165 | @Override |
| 165 | 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 | 173 | MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId()); |
| 199 | 174 | ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId()); |
| 200 | 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 | 224 | @Override |
| 205 | 225 | public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) { |
| 206 | - return validateCombinationRecord(userId, orderId); | |
| 226 | + return recordMapper.selectByUserIdAndOrderId(userId, orderId); | |
| 207 | 227 | } |
| 208 | 228 | |
| 209 | 229 | @Override |
| 210 | - public List<CombinationRecordDO> getRecordListByUserIdAndActivityId(Long userId, Long activityId) { | |
| 230 | + public List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId) { | |
| 211 | 231 | return recordMapper.selectListByUserIdAndActivityId(userId, activityId); |
| 212 | 232 | } |
| 213 | 233 | |
| 214 | 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 | 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 | 248 | @Override |
| 229 | - public List<CombinationRecordDO> getLatestRecordList(int count) { | |
| 249 | + public List<CombinationRecordDO> getLatestCombinationRecordList(int count) { | |
| 230 | 250 | return recordMapper.selectLatestList(count); |
| 231 | 251 | } |
| 232 | 252 | |
| 233 | 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 | 259 | @Override |
| 239 | - public CombinationRecordDO getRecordById(Long id) { | |
| 260 | + public CombinationRecordDO getCombinationRecordById(Long id) { | |
| 240 | 261 | return recordMapper.selectById(id); |
| 241 | 262 | } |
| 242 | 263 | |
| 243 | 264 | @Override |
| 244 | - public List<CombinationRecordDO> getRecordListByHeadId(Long headId) { | |
| 265 | + public List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId) { | |
| 245 | 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 | 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 | 311 | } |
| 312 | 312 | |
| 313 | 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 | 111 | o.setStartTime(null); |
| 112 | 112 | o.setEndTime(null); |
| 113 | 113 | o.setUserSize(null); |
| 114 | - o.setTotalCount(null); | |
| 115 | - o.setSuccessCount(null); | |
| 116 | - o.setOrderUserCount(null); | |
| 117 | 114 | o.setVirtualGroup(null); |
| 118 | 115 | o.setStatus(null); |
| 119 | 116 | o.setLimitDuration(null); |
| ... | ... | @@ -134,12 +131,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { |
| 134 | 131 | combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null))); |
| 135 | 132 | // 测试 userSize 不匹配 |
| 136 | 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 | 134 | // 测试 virtualGroup 不匹配 |
| 144 | 135 | combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null))); |
| 145 | 136 | // 测试 status 不匹配 |
| ... | ... | @@ -173,9 +164,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { |
| 173 | 164 | o.setStartTime(null); |
| 174 | 165 | o.setEndTime(null); |
| 175 | 166 | o.setUserSize(null); |
| 176 | - o.setTotalCount(null); | |
| 177 | - o.setSuccessCount(null); | |
| 178 | - o.setOrderUserCount(null); | |
| 179 | 167 | o.setVirtualGroup(null); |
| 180 | 168 | o.setStatus(null); |
| 181 | 169 | o.setLimitDuration(null); |
| ... | ... | @@ -196,12 +184,6 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest { |
| 196 | 184 | combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setEndTime(null))); |
| 197 | 185 | // 测试 userSize 不匹配 |
| 198 | 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 | 187 | // 测试 virtualGroup 不匹配 |
| 206 | 188 | combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setVirtualGroup(null))); |
| 207 | 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 | 28 | */ |
| 29 | 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 | 33 | ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元"); |
| 34 | 34 | ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, "交易订单删除失败,订单不是【已取消】状态"); |
| 35 | 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 | 38 | // ========== After Sale 模块 1-011-000-100 ========== |
| 38 | 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 | 16 | MEMBER_CREATE(1, "用户下单"), |
| 17 | 17 | ADMIN_UPDATE_PRICE(2, "订单价格 {oldPayPrice} 修改,实际支付金额为 {newPayPrice} 元"), |
| 18 | 18 | MEMBER_PAY(10, "用户付款成功"), |
| 19 | - SYSTEM_UP_ADDRESS(11, "订单 {orderId} 收货地址修改"), | |
| 19 | + ADMIN_UPDATE_ADDRESS(11, "收货地址修改"), | |
| 20 | 20 | ADMIN_DELIVERY(20, "已发货,快递公司:{deliveryName},快递单号:{logisticsNo}"), |
| 21 | 21 | MEMBER_RECEIVE(30, "用户已收货"), |
| 22 | 22 | SYSTEM_RECEIVE(31, "到期未收货,系统自动确认收货"), | ... | ... |
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.enums.order; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ObjectUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.core.IntArrayValuable; |
| 4 | 5 | import lombok.Getter; |
| 5 | 6 | import lombok.RequiredArgsConstructor; |
| ... | ... | @@ -37,4 +38,20 @@ public enum TradeOrderTypeEnum implements IntArrayValuable { |
| 37 | 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 | 4 | import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; |
| 5 | 5 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 6 | 6 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; |
| 7 | +import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; | |
| 7 | 8 | import org.springframework.stereotype.Service; |
| 8 | 9 | import org.springframework.validation.annotation.Validated; |
| 9 | 10 | |
| ... | ... | @@ -25,6 +26,8 @@ public class TradeOrderApiImpl implements TradeOrderApi { |
| 25 | 26 | |
| 26 | 27 | @Resource |
| 27 | 28 | private TradeOrderQueryService tradeOrderQueryService; |
| 29 | + @Resource | |
| 30 | + private TradeOrderUpdateService tradeOrderUpdateService; | |
| 28 | 31 | |
| 29 | 32 | @Override |
| 30 | 33 | public List<TradeOrderRespDTO> getOrderList(Collection<Long> ids) { |
| ... | ... | @@ -37,6 +40,16 @@ public class TradeOrderApiImpl implements TradeOrderApi { |
| 37 | 40 | } |
| 38 | 41 | |
| 39 | 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 | 53 | public Integer getOrderStatus(Long id) { |
| 41 | 54 | TradeOrderDO order = tradeOrderQueryService.getOrder(id); |
| 42 | 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 | 52 | private Long seckillActivityId; |
| 53 | 53 | |
| 54 | 54 | // ========== 拼团活动相关字段 ========== |
| 55 | - // TODO @puhui999:是不是拼团记录的编号哈? | |
| 56 | 55 | @Schema(description = "拼团活动编号", example = "1024") |
| 57 | 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 | 207 | default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO, |
| 208 | 208 | List<CartDO> cartList) { |
| 209 | 209 | TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId) |
| 210 | + .setItems(new ArrayList<>(settlementReqVO.getItems().size())) | |
| 210 | 211 | .setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus()) |
| 212 | + // 物流信息 | |
| 211 | 213 | .setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId()) |
| 212 | 214 | .setPickUpStoreId(settlementReqVO.getPickUpStoreId()) |
| 213 | - .setItems(new ArrayList<>(settlementReqVO.getItems().size())) | |
| 215 | + // 各种活动 | |
| 214 | 216 | .setSeckillActivityId(settlementReqVO.getSeckillActivityId()) |
| 215 | - .setBargainRecordId(settlementReqVO.getBargainRecordId()); | |
| 217 | + .setBargainRecordId(settlementReqVO.getBargainRecordId()) | |
| 218 | + .setCombinationActivityId(settlementReqVO.getCombinationActivityId()) | |
| 219 | + .setCombinationHeadId(settlementReqVO.getCombinationHeadId()); | |
| 216 | 220 | // 商品项的构建 |
| 217 | 221 | Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId); |
| 218 | 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 | 318 | */ |
| 319 | 319 | private Long combinationActivityId; |
| 320 | 320 | /** |
| 321 | - * 团长编号 | |
| 321 | + * 拼团团长编号 | |
| 322 | 322 | * |
| 323 | - * 关联 CombinationRecordDO 的 id 字段 | |
| 323 | + * 关联 CombinationRecordDO 的 headId 字段 | |
| 324 | 324 | */ |
| 325 | 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 | 15 | */ |
| 16 | 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 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 5 | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.hutool.extra.spring.SpringUtil; | |
| 6 | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | 8 | import cn.iocoder.yudao.module.member.api.user.MemberUserApi; |
| 8 | 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 | 14 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; |
| 14 | 15 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; |
| 15 | 16 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; |
| 17 | +import cn.iocoder.yudao.module.trade.dal.redis.RedisKeyConstants; | |
| 16 | 18 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; |
| 17 | 19 | import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory; |
| 18 | 20 | import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; |
| 19 | 21 | import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; |
| 20 | 22 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; |
| 23 | +import org.springframework.cache.annotation.Cacheable; | |
| 21 | 24 | import org.springframework.stereotype.Service; |
| 22 | 25 | |
| 23 | 26 | import javax.annotation.Resource; |
| ... | ... | @@ -141,7 +144,6 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { |
| 141 | 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 | 151 | * @return 物流轨迹 |
| 150 | 152 | */ |
| 151 | 153 | private List<ExpressTrackRespDTO> getExpressTrackList(TradeOrderDO order) { |
| 152 | - // 查询物流公司 | |
| 153 | 154 | if (order.getLogisticsId() == null) { |
| 154 | 155 | return Collections.emptyList(); |
| 155 | 156 | } |
| 157 | + // 查询物流公司 | |
| 156 | 158 | DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(order.getLogisticsId()); |
| 157 | 159 | if (express == null) { |
| 158 | 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 | 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 | 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 | 170 | */ |
| 171 | 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 | 250 | /** |
| 251 | 251 | * 订单创建前,执行前置逻辑 |
| 252 | 252 | * |
| 253 | - * @param order 订单 | |
| 253 | + * @param order 订单 | |
| 254 | 254 | * @param orderItems 订单项 |
| 255 | 255 | */ |
| 256 | 256 | private void beforeCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { |
| ... | ... | @@ -267,9 +267,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 267 | 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 | 274 | private void afterCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems, |
| 275 | 275 | AppTradeOrderCreateReqVO createReqVO) { |
| ... | ... | @@ -331,7 +331,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 331 | 331 | } |
| 332 | 332 | |
| 333 | 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 | 337 | // 4.1 增加用户积分(赠送) |
| 337 | 338 | addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId()); |
| ... | ... | @@ -624,12 +625,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 624 | 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 | 628 | List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id); |
| 629 | + // 3. 回滚库存 | |
| 632 | 630 | productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems)); |
| 631 | + // 3.1、 活动相关的回滚 | |
| 632 | + tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, orderItems)); | |
| 633 | 633 | |
| 634 | 634 | // 4. 回滚优惠券 |
| 635 | 635 | if (order.getCouponId() != null && order.getCouponId() > 0) { |
| ... | ... | @@ -738,19 +738,21 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 738 | 738 | } |
| 739 | 739 | |
| 740 | 740 | @Override |
| 741 | - @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_UP_ADDRESS) | |
| 741 | + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_ADDRESS) | |
| 742 | 742 | public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) { |
| 743 | 743 | // 校验交易订单 |
| 744 | 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 | 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 | 758 | @Override |
| ... | ... | @@ -806,10 +808,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 806 | 808 | // 2.2 如果全部退款,则进行取消订单 |
| 807 | 809 | getSelf().cancelOrderByAfterSale(order, orderRefundPrice); |
| 808 | 810 | |
| 809 | - // TODO @puhui999:活动相关的回滚 | |
| 810 | 811 | |
| 811 | 812 | // 3. 回滚库存 |
| 812 | 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 | 817 | // 4.1 回滚积分:扣减用户积分(赠送的) |
| 815 | 818 | reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId()); |
| ... | ... | @@ -909,6 +912,25 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 909 | 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 | 1 | package cn.iocoder.yudao.module.trade.service.order.handler; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.util.ObjectUtil; | |
| 4 | 3 | import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi; |
| 5 | 4 | import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; |
| 6 | 5 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| ... | ... | @@ -26,9 +25,10 @@ public class TradeBargainHandler implements TradeOrderHandler { |
| 26 | 25 | |
| 27 | 26 | @Override |
| 28 | 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 | 29 | return; |
| 31 | 30 | } |
| 31 | + | |
| 32 | 32 | // 扣减砍价活动的库存 |
| 33 | 33 | bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(), |
| 34 | 34 | -orderItems.get(0).getCount()); |
| ... | ... | @@ -36,13 +36,20 @@ public class TradeBargainHandler implements TradeOrderHandler { |
| 36 | 36 | |
| 37 | 37 | @Override |
| 38 | 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 | 40 | return; |
| 41 | 41 | } |
| 42 | + | |
| 42 | 43 | // 记录砍价记录对应的订单编号 |
| 43 | 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 | 1 | package cn.iocoder.yudao.module.trade.service.order.handler; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.lang.Assert; |
| 4 | -import cn.hutool.core.util.ObjectUtil; | |
| 5 | 4 | import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; |
| 6 | 5 | import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; |
| 7 | 6 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| ... | ... | @@ -26,40 +25,37 @@ public class TradeCombinationHandler implements TradeOrderHandler { |
| 26 | 25 | @Override |
| 27 | 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 | 29 | return; |
| 31 | 30 | } |
| 32 | 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 | 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 | 44 | return; |
| 45 | 45 | } |
| 46 | 46 | Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品"); |
| 47 | 47 | |
| 48 | 48 | // 获取商品信息 |
| 49 | 49 | TradeOrderItemDO item = orderItems.get(0); |
| 50 | - // 创建砍价记录 | |
| 50 | + // 创建拼团记录 | |
| 51 | 51 | combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, item)); |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 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 | 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 | 33 | * 支付订单后 |
| 34 | 34 | * |
| 35 | 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 | 1 | package cn.iocoder.yudao.module.trade.service.order.handler; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.util.ObjectUtil; | |
| 4 | 3 | import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; |
| 5 | 4 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 6 | 5 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; |
| ... | ... | @@ -23,12 +22,21 @@ public class TradeSeckillHandler implements TradeOrderHandler { |
| 23 | 22 | |
| 24 | 23 | @Override |
| 25 | 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 | 26 | return; |
| 28 | 27 | } |
| 28 | + | |
| 29 | 29 | // 扣减秒杀活动的库存 |
| 30 | 30 | seckillActivityApi.updateSeckillStock(order.getSeckillActivityId(), |
| 31 | 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
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 | 12 | |
| 13 | 13 | import javax.annotation.Resource; |
| 14 | 14 | |
| 15 | +// TODO @puhui999:单测可以后补下 | |
| 16 | + | |
| 15 | 17 | /** |
| 16 | 18 | * 拼团活动的 {@link TradePriceCalculator} 实现类 |
| 17 | 19 | * |
| ... | ... | @@ -30,12 +32,11 @@ public class TradeCombinationActivityPriceCalculator implements TradePriceCalcul |
| 30 | 32 | if (param.getCombinationActivityId() == null) { |
| 31 | 33 | return; |
| 32 | 34 | } |
| 33 | - | |
| 34 | 35 | Assert.isTrue(param.getItems().size() == 1, "拼团时,只允许选择一个商品"); |
| 35 | 36 | // 2. 校验是否可以参与拼团 |
| 36 | 37 | TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); |
| 37 | 38 | CombinationValidateJoinRespDTO combinationActivity = combinationRecordApi.validateJoinCombination( |
| 38 | - param.getCombinationActivityId(), param.getUserId(), | |
| 39 | + param.getUserId(), param.getCombinationActivityId(), param.getCombinationHeadId(), | |
| 39 | 40 | orderItem.getSkuId(), orderItem.getCount()); |
| 40 | 41 | |
| 41 | 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 | 176 | } |
| 177 | 177 | |
| 178 | 178 | @Override |
| 179 | - @DataPermission(enable = false) // 禁用数据权限,避免简历不正确的缓存 | |
| 179 | + @DataPermission(enable = false) // 禁用数据权限,避免建立不正确的缓存 | |
| 180 | 180 | @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id") |
| 181 | 181 | public Set<Long> getChildDeptIdListFromCache(Long id) { |
| 182 | 182 | List<DeptDO> children = getChildDeptList(id); | ... | ... |