Commit d156d43d63c2b0d576ca1703a8049a28da53a19a

Authored by puhui999
1 parent 446951bd

fix:完善 review 拼团、秒杀活动的实现提到的问题

Showing 43 changed files with 391 additions and 695 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
... ... @@ -27,6 +27,10 @@ public class CollectionUtils {
27 27 return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
28 28 }
29 29  
  30 + public static <T> boolean isAny(Collection<T> from, Predicate<T> predicate) {
  31 + return from.stream().anyMatch(predicate);
  32 + }
  33 +
30 34 public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
31 35 if (CollUtil.isEmpty(from)) {
32 36 return new ArrayList<>();
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.date;
3 3 import cn.hutool.core.date.LocalDateTimeUtil;
4 4  
5 5 import java.time.Duration;
  6 +import java.time.LocalDate;
6 7 import java.time.LocalDateTime;
7 8 import java.time.LocalTime;
8 9  
... ... @@ -70,7 +71,7 @@ public class LocalDateTimeUtils {
70 71 * @param endTime2 校验所需的结束时间
71 72 * @return 是否重叠
72 73 */
73   - // TODO @puhui999:LocalDateTimeUtil.isOverlap() 是不是可以满足呀?
  74 + @Deprecated
74 75 public static boolean checkTimeOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
75 76 // 判断时间是否重叠
76 77 // 开始时间在已配置时段的结束时间之前 且 结束时间在已配置时段的开始时间之后 []
... ... @@ -81,4 +82,14 @@ public class LocalDateTimeUtils {
81 82 || startTime1.isBefore(endTime2) && endTime1.isAfter(endTime2);
82 83 }
83 84  
  85 + public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
  86 + // 日期部分使用了当前日期LocalDate.now()
  87 + LocalDateTime startDateTime1 = LocalDateTime.of(LocalDate.now(), startTime1);
  88 + LocalDateTime endDateTime1 = LocalDateTime.of(LocalDate.now(), endTime1);
  89 + LocalDateTime startDateTime2 = LocalDateTime.of(LocalDate.now(), startTime2);
  90 + LocalDateTime endDateTime2 = LocalDateTime.of(LocalDate.now(), endTime2);
  91 +
  92 + return LocalDateTimeUtil.isOverlap(startDateTime1, endDateTime1, startDateTime2, endDateTime2);
  93 + }
  94 +
84 95 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/comment/AppProductCommentController.java
1 1 package cn.iocoder.yudao.module.product.controller.app.comment;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
... ... @@ -9,10 +8,8 @@ import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStati
9 8 import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
10 9 import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
11 10 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
12   -import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
13 11 import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
14 12 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
15   -import com.google.common.collect.Maps;
16 13 import io.swagger.v3.oas.annotations.Operation;
17 14 import io.swagger.v3.oas.annotations.Parameter;
18 15 import io.swagger.v3.oas.annotations.Parameters;
... ... @@ -27,7 +24,6 @@ import org.springframework.web.bind.annotation.RestController;
27 24 import javax.annotation.Resource;
28 25 import javax.validation.Valid;
29 26 import java.util.List;
30   -import java.util.Map;
31 27 import java.util.Set;
32 28  
33 29 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
... ... @@ -60,15 +56,9 @@ public class AppProductCommentController {
60 56 @GetMapping("/page")
61 57 @Operation(summary = "获得商品评价分页")
62 58 public CommonResult<PageResult<AppProductCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {
63   - // TODO @puhui999:写到 convert 里,可以更简洁哈。
64 59 PageResult<ProductCommentDO> commentDOPage = productCommentService.getCommentPage(pageVO, Boolean.TRUE);
65 60 Set<Long> skuIds = CollectionUtils.convertSet(commentDOPage.getList(), ProductCommentDO::getSkuId);
66   - List<ProductSkuDO> skuList = productSkuService.getSkuList(skuIds);
67   - Map<Long, ProductSkuDO> skuDOMap = Maps.newLinkedHashMapWithExpectedSize(skuIds.size());
68   - if (CollUtil.isNotEmpty(skuList)) {
69   - skuDOMap.putAll(CollectionUtils.convertMap(skuList, ProductSkuDO::getId, c -> c));
70   - }
71   - PageResult<AppProductCommentRespVO> page = ProductCommentConvert.INSTANCE.convertPage02(commentDOPage, skuDOMap);
  61 + PageResult<AppProductCommentRespVO> page = ProductCommentConvert.INSTANCE.convertPage02(commentDOPage, productSkuService.getSkuList(skuIds));
72 62 return success(page);
73 63 }
74 64  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/comment/ProductCommentConvert.java
1 1 package cn.iocoder.yudao.module.product.convert.comment;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.util.ObjectUtil;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 7 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
6 8 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
7 9 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
... ... @@ -13,6 +15,7 @@ import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProdu
13 15 import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
14 16 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
15 17 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
  18 +import com.google.common.collect.Maps;
16 19 import org.mapstruct.Mapper;
17 20 import org.mapstruct.Mapping;
18 21 import org.mapstruct.Named;
... ... @@ -37,19 +40,23 @@ public interface ProductCommentConvert {
37 40  
38 41 @Mapping(target = "scores", expression = "java(calculateOverallScore(goodCount, mediocreCount, negativeCount))")
39 42 AppCommentStatisticsRespVO convert(Long goodCount, Long mediocreCount, Long negativeCount);
  43 +
40 44 @Named("calculateOverallScore")
41 45 default double calculateOverallScore(long goodCount, long mediocreCount, long negativeCount) {
42 46 return (goodCount * 5 + mediocreCount * 3 + negativeCount) / (double) (goodCount + mediocreCount + negativeCount);
43 47 }
44   -
  48 +
45 49 List<ProductCommentRespVO> convertList(List<ProductCommentDO> list);
46 50  
47 51 PageResult<ProductCommentRespVO> convertPage(PageResult<ProductCommentDO> page);
48 52  
49 53 PageResult<AppProductCommentRespVO> convertPage01(PageResult<ProductCommentDO> pageResult);
50 54  
51   - default PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult,
52   - Map<Long, ProductSkuDO> skuMap) {
  55 + default PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult, List<ProductSkuDO> skuList) {
  56 + Map<Long, ProductSkuDO> skuMap = Maps.newLinkedHashMapWithExpectedSize(skuList.size());
  57 + if (CollUtil.isNotEmpty(skuList)) {
  58 + skuMap.putAll(CollectionUtils.convertMap(skuList, ProductSkuDO::getId));
  59 + }
53 60 PageResult<AppProductCommentRespVO> page = convertPage01(pageResult);
54 61 page.getList().forEach(item -> {
55 62 // 判断用户是否选择匿名
... ... @@ -61,6 +68,7 @@ public interface ProductCommentConvert {
61 68 });
62 69 return page;
63 70 }
  71 +
64 72 List<AppProductPropertyValueDetailRespVO> convertList01(List<ProductSkuDO.Property> properties);
65 73  
66 74 /**
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
1 1 package cn.iocoder.yudao.module.product.convert.spu;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 5 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
5 6 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
6 7 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
... ... @@ -81,6 +82,7 @@ public interface ProductSpuConvert {
81 82 }
82 83 return voList;
83 84 }
  85 +
84 86 @Named("convertListForGetSpuList0")
85 87 List<AppProductSpuPageRespVO> convertListForGetSpuList0(List<ProductSpuDO> list);
86 88  
... ... @@ -112,12 +114,7 @@ public interface ProductSpuConvert {
112 114 default List<ProductSpuDetailRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) {
113 115 List<ProductSpuDetailRespVO> vos = new ArrayList<>(spus.size());
114 116 Map<Long, List<ProductSkuDO>> skuMultiMap = convertMultiMap(skus, ProductSkuDO::getSpuId);
115   - // TODO @puhui999:可以直接使用 CollUtils.convertList
116   - spus.forEach(spu -> {
117   - ProductSpuDetailRespVO detailRespVO = convert03(spu);
118   - detailRespVO.setSkus(ProductSkuConvert.INSTANCE.convertList(skuMultiMap.get(spu.getId())));
119   - vos.add(detailRespVO);
120   - });
  117 + CollectionUtils.convertList(spus, spu -> vos.add(convert03(spu).setSkus(ProductSkuConvert.INSTANCE.convertList(skuMultiMap.get(spu.getId())))));
121 118 return vos;
122 119 }
123 120  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/comment/ProductCommentMapper.java
... ... @@ -26,18 +26,19 @@ public interface ProductCommentMapper extends BaseMapperX&lt;ProductCommentDO&gt; {
26 26 }
27 27  
28 28 static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) {
29   - // TODO @puhui999:是不是不用 apply 拉?直接用 mybatis 的方法就好啦
  29 + LambdaQueryWrapperX<ProductCommentDO> queryWrapperX = new LambdaQueryWrapperX<>();
30 30 // 构建好评查询语句:好评计算 总评 >= 4
31 31 if (ObjectUtil.equal(type, AppCommentPageReqVO.GOOD_COMMENT)) {
32   - queryWrapper.apply("scores >= 4");
  32 + queryWrapperX.ge(ProductCommentDO::getScores, 4);
33 33 }
34 34 // 构建中评查询语句:中评计算 总评 >= 3 且 总评 < 4
35 35 if (ObjectUtil.equal(type, AppCommentPageReqVO.MEDIOCRE_COMMENT)) {
36   - queryWrapper.apply("scores >=3 and scores < 4");
  36 + queryWrapperX.ge(ProductCommentDO::getScores, 3);
  37 + queryWrapperX.lt(ProductCommentDO::getScores, 4);
37 38 }
38 39 // 构建差评查询语句:差评计算 总评 < 3
39 40 if (ObjectUtil.equal(type, AppCommentPageReqVO.NEGATIVE_COMMENT)) {
40   - queryWrapper.apply("scores < 3");
  41 + queryWrapperX.lt(ProductCommentDO::getScores, 3);
41 42 }
42 43 }
43 44  
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationActivityApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.combination;
  2 +
  3 +/**
  4 + * 拼团活动 API 接口
  5 + *
  6 + * @author HUIHUI
  7 + */
  8 +public interface CombinationActivityApi {
  9 +
  10 +}
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApi.java renamed to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
1 1 package cn.iocoder.yudao.module.promotion.api.combination;
2 2  
3   -import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
  3 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
4 4  
5 5 import javax.validation.Valid;
6   -
7   -// TODO @puhui999:CombinationRecordApi 分成活动、记录哈
8 6 // TODO @芋艿:后面也再撸撸这几个接口
  7 +
9 8 /**
10   - * 拼团活动 API 接口
  9 + * 拼团记录 API 接口
11 10 *
12 11 * @author HUIHUI
13 12 */
14   -public interface CombinationApi {
  13 +public interface CombinationRecordApi {
15 14  
16 15 /**
17 16 * 创建开团记录
18 17 *
19 18 * @param reqDTO 请求 DTO
20 19 */
21   - void createRecord(@Valid CombinationRecordReqDTO reqDTO);
  20 + void createRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
22 21  
23 22 /**
24   - * 获取开团记录状态
  23 + * 校验拼团是否成功
25 24 *
26 25 * @param userId 用户编号
27 26 * @param orderId 订单编号
  27 + * @return 拼团是否成功
28 28 */
29 29 boolean validateRecordStatusIsSuccess(Long userId, Long orderId);
30 30  
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordReqDTO.java renamed to yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordCreateReqDTO.java
... ... @@ -5,14 +5,13 @@ import lombok.Data;
5 5 import javax.validation.constraints.NotEmpty;
6 6 import javax.validation.constraints.NotNull;
7 7  
8   -// TODO @puhui999:CombinationRecordCreateReqDTO,这样更容易知道是创建噢
9 8 /**
10 9 * 拼团记录 Request DTO
11 10 *
12 11 * @author HUIHUI
13 12 */
14 13 @Data
15   -public class CombinationRecordReqDTO {
  14 +public class CombinationRecordCreateReqDTO {
16 15  
17 16 /**
18 17 * 拼团活动编号
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
... ... @@ -65,5 +65,8 @@ public interface ErrorCodeConstants {
65 65 ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
66 66 ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
67 67 ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在");
  68 + ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1013010005, "拼团失败,已参与过该拼团");
  69 + ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013010006, "拼团失败,父拼团不存在");
  70 + ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1013010006, "拼团失败,拼团人数已满");
68 71  
69 72 }
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java
1 1 package cn.iocoder.yudao.module.promotion.enums.combination;
2 2  
  3 +import cn.hutool.core.util.ObjectUtil;
3 4 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4 5 import lombok.AllArgsConstructor;
5 6 import lombok.Getter;
... ... @@ -36,4 +37,8 @@ public enum CombinationRecordStatusEnum implements IntArrayValuable {
36 37 return ARRAYS;
37 38 }
38 39  
  40 + public static boolean isSuccess(Integer status) {
  41 + return ObjectUtil.equal(status, SUCCESS.getStatus());
  42 + }
  43 +
39 44 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationApiImpl.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
1 1 package cn.iocoder.yudao.module.promotion.api.combination;
2 2  
3   -import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
4   -import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
  3 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
  4 +import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
  5 +import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
5 6 import org.springframework.stereotype.Service;
6 7  
7 8 import javax.annotation.Resource;
... ... @@ -13,30 +14,29 @@ import java.time.LocalDateTime;
13 14 * @author HUIHUI
14 15 */
15 16 @Service
16   -public class CombinationApiImpl implements CombinationApi {
  17 +public class CombinationRecordApiImpl implements CombinationRecordApi {
17 18  
18 19 @Resource
19   - private CombinationActivityService activityService;
  20 + private CombinationRecordService recordService;
20 21  
21 22 @Override
22   - public void createRecord(CombinationRecordReqDTO reqDTO) {
23   - activityService.createRecord(reqDTO);
  23 + public void createRecord(CombinationRecordCreateReqDTO reqDTO) {
  24 + recordService.createRecord(reqDTO);
24 25 }
25 26  
26 27 @Override
27 28 public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) {
28   - return activityService.validateRecordStatusIsSuccess(userId, orderId);
  29 + return CombinationRecordStatusEnum.isSuccess(recordService.getRecord(userId, orderId).getStatus());
29 30 }
30 31  
31 32 @Override
32 33 public void updateRecordStatus(Long userId, Long orderId, Integer status) {
33   - activityService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status);
  34 + recordService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status);
34 35 }
35 36  
36 37 @Override
37 38 public void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status) {
38   - activityService.updateRecordStatusAndStartTimeByUserIdAndOrderId(userId, orderId, status, LocalDateTime.now());
  39 + recordService.updateRecordStatusAndStartTimeByUserIdAndOrderId(userId, orderId, status, LocalDateTime.now());
39 40 }
40 41  
41   -
42 42 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationActivityController.java
... ... @@ -3,14 +3,14 @@ package cn.iocoder.yudao.module.promotion.controller.admin.combination;
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6   -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
7   -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
8 6 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
9   -import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
10   -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.*;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
  8 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
  9 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
  10 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
11 11 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
12   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
13   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
  12 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
  13 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
14 14 import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
15 15 import io.swagger.v3.oas.annotations.Operation;
16 16 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -20,16 +20,12 @@ import org.springframework.validation.annotation.Validated;
20 20 import org.springframework.web.bind.annotation.*;
21 21  
22 22 import javax.annotation.Resource;
23   -import javax.servlet.http.HttpServletResponse;
24 23 import javax.validation.Valid;
25   -import java.io.IOException;
26 24 import java.util.Collection;
27 25 import java.util.List;
28   -import java.util.Set;
29 26  
30 27 import static cn.hutool.core.collection.CollectionUtil.newArrayList;
31 28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
32   -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
33 29  
34 30 @Tag(name = "管理后台 - 拼团活动")
35 31 @RestController
... ... @@ -82,7 +78,7 @@ public class CombinationActivityController {
82 78 @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
83 79 public CommonResult<List<CombinationActivityRespVO>> getCombinationActivityList(@RequestParam("ids") Collection<Long> ids) {
84 80 List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(ids);
85   - return success(CombinationActivityConvert.INSTANCE.convertList(list));
  81 + return success(CombinationActivityConvert.INSTANCE.complementList(list));
86 82 }
87 83  
88 84 @GetMapping("/page")
... ... @@ -91,24 +87,9 @@ public class CombinationActivityController {
91 87 public CommonResult<PageResult<CombinationActivityRespVO>> getCombinationActivityPage(
92 88 @Valid CombinationActivityPageReqVO pageVO) {
93 89 PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO);
94   - // TODO @puhui999:可以不一定 aIds,直接批量查询结果出来;下面也是类似;
95   - Set<Long> aIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getId);
96   - List<CombinationProductDO> products = combinationActivityService.getProductsByActivityIds(aIds);
97   - Set<Long> spuIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getSpuId);
98   - List<ProductSpuRespDTO> spus = spuApi.getSpuList(spuIds);
99   - return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products, spus));
100   - }
101   -
102   - @GetMapping("/export-excel")
103   - @Operation(summary = "导出拼团活动 Excel")
104   - @PreAuthorize("@ss.hasPermission('promotion:combination-activity:export')")
105   - @OperateLog(type = EXPORT)
106   - public void exportCombinationActivityExcel(@Valid CombinationActivityExportReqVO exportReqVO,
107   - HttpServletResponse response) throws IOException {
108   - List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(exportReqVO);
109   - // 导出 Excel
110   - List<CombinationActivityExcelVO> datas = CombinationActivityConvert.INSTANCE.convertList02(list);
111   - ExcelUtils.write(response, "拼团活动.xls", "数据", CombinationActivityExcelVO.class, datas);
  90 + return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult,
  91 + combinationActivityService.getProductsByActivityIds(CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getId)),
  92 + spuApi.getSpuList(CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getSpuId))));
112 93 }
113 94  
114 95 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExcelVO.java deleted
1   -package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
2   -
3   -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
4   -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
5   -import com.alibaba.excel.annotation.ExcelProperty;
6   -import lombok.Data;
7   -
8   -import java.time.LocalDateTime;
9   -
10   -// TODO @puhui999:如无必要,导出都可以删除哈
11   -/**
12   - * 拼团活动 Excel VO
13   - *
14   - * @author HUIHUI
15   - */
16   -@Data
17   -public class CombinationActivityExcelVO {
18   -
19   - @ExcelProperty("活动编号")
20   - private Long id;
21   -
22   - @ExcelProperty("拼团名称")
23   - private String name;
24   -
25   - @ExcelProperty("商品 SPU 编号关联 ProductSpuDO 的 id")
26   - private Long spuId;
27   -
28   - @ExcelProperty("总限购数量")
29   - private Integer totalLimitCount;
30   -
31   - @ExcelProperty("单次限购数量")
32   - private Integer singleLimitCount;
33   -
34   - @ExcelProperty("开始时间")
35   - private LocalDateTime startTime;
36   -
37   - @ExcelProperty("结束时间")
38   - private LocalDateTime endTime;
39   -
40   - @ExcelProperty("开团人数")
41   - private Integer userSize;
42   -
43   - @ExcelProperty("开团组数")
44   - private Integer totalNum;
45   -
46   - @ExcelProperty("成团组数")
47   - private Integer successNum;
48   -
49   - @ExcelProperty("参与人数")
50   - private Integer orderUserCount;
51   -
52   - @ExcelProperty("虚拟成团")
53   - private Integer virtualGroup;
54   -
55   - @ExcelProperty(value = "活动状态:0开启 1关闭", converter = DictConvert.class)
56   - @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
57   - private Integer status;
58   -
59   - @ExcelProperty("限制时长(小时)")
60   - private Integer limitDuration;
61   -
62   - @ExcelProperty("创建时间")
63   - private LocalDateTime createTime;
64   -
65   -}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/activity/CombinationActivityExportReqVO.java deleted
1   -package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
2   -
3   -import io.swagger.v3.oas.annotations.media.Schema;
4   -import lombok.Data;
5   -import org.springframework.format.annotation.DateTimeFormat;
6   -
7   -import java.time.LocalDateTime;
8   -
9   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
10   -
11   -// TODO @puhui999:如无必要,导出都可以删除哈
12   -@Schema(description = "管理后台 - 拼团活动 Excel 导出 Request VO,参数和 CombinationActivityPageReqVO 是一致的")
13   -@Data
14   -public class CombinationActivityExportReqVO {
15   -
16   - @Schema(description = "拼团名称", example = "赵六")
17   - private String name;
18   -
19   - @Schema(description = "商品 SPU 编号关联 ProductSpuDO 的 id", example = "14016")
20   - private Long spuId;
21   -
22   - @Schema(description = "总限购数量", example = "16218")
23   - private Integer totalLimitCount;
24   -
25   - @Schema(description = "单次限购数量", example = "28265")
26   - private Integer singleLimitCount;
27   -
28   - @Schema(description = "开始时间")
29   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
30   - private LocalDateTime[] startTime;
31   -
32   - @Schema(description = "结束时间")
33   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
34   - private LocalDateTime[] endTime;
35   -
36   - @Schema(description = "开团人数")
37   - private Integer userSize;
38   -
39   - @Schema(description = "开团组数")
40   - private Integer totalNum;
41   -
42   - @Schema(description = "成团组数")
43   - private Integer successNum;
44   -
45   - @Schema(description = "参与人数", example = "25222")
46   - private Integer orderUserCount;
47   -
48   - @Schema(description = "虚拟成团")
49   - private Integer virtualGroup;
50   -
51   - @Schema(description = "活动状态:0开启 1关闭", example = "0")
52   - private Integer status;
53   -
54   - @Schema(description = "限制时长(小时)")
55   - private Integer limitDuration;
56   -
57   - @Schema(description = "创建时间")
58   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
59   - private LocalDateTime[] createTime;
60   -
61   -}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExcelVO.java deleted
1   -package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
2   -
3   -import com.alibaba.excel.annotation.ExcelProperty;
4   -import lombok.Data;
5   -
6   -import java.time.LocalDateTime;
7   -
8   -// TODO @puhui999:可以考虑删除 excel 导出哈
9   -/**
10   - * 拼团商品 Excel VO
11   - *
12   - * @author HUIHUI
13   - */
14   -@Data
15   -public class CombinationProductExcelVO {
16   -
17   - @ExcelProperty("编号")
18   - private Long id;
19   -
20   - @ExcelProperty("拼团活动编号")
21   - private Long activityId;
22   -
23   - @ExcelProperty("商品 SPU 编号")
24   - private Long spuId;
25   -
26   - @ExcelProperty("商品 SKU 编号")
27   - private Long skuId;
28   -
29   - @ExcelProperty("拼团商品状态")
30   - private Integer activityStatus;
31   -
32   - @ExcelProperty("活动开始时间点")
33   - private LocalDateTime activityStartTime;
34   -
35   - @ExcelProperty("活动结束时间点")
36   - private LocalDateTime activityEndTime;
37   -
38   - @ExcelProperty("拼团价格,单位分")
39   - private Integer activePrice;
40   -
41   - @ExcelProperty("创建时间")
42   - private LocalDateTime createTime;
43   -
44   -}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/product/CombinationProductExportReqVO.java deleted
1   -package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
2   -
3   -import io.swagger.v3.oas.annotations.media.Schema;
4   -import lombok.Data;
5   -import org.springframework.format.annotation.DateTimeFormat;
6   -
7   -import java.time.LocalDateTime;
8   -
9   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
10   -
11   -// TODO @puhui999:可以考虑删除 excel 导出哈
12   -@Schema(description = "管理后台 - 拼团商品 Excel 导出 Request VO,参数和 CombinationProductPageReqVO 是一致的")
13   -@Data
14   -public class CombinationProductExportReqVO {
15   -
16   - @Schema(description = "拼团活动编号", example = "6829")
17   - private Long activityId;
18   -
19   - @Schema(description = "商品 SPU 编号", example = "18731")
20   - private Long spuId;
21   -
22   - @Schema(description = "商品 SKU 编号", example = "31675")
23   - private Long skuId;
24   -
25   - @Schema(description = "拼团商品状态", example = "2")
26   - private Integer activityStatus;
27   -
28   - @Schema(description = "活动开始时间点")
29   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
30   - private LocalDateTime[] activityStartTime;
31   -
32   - @Schema(description = "活动结束时间点")
33   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
34   - private LocalDateTime[] activityEndTime;
35   -
36   - @Schema(description = "拼团价格,单位分", example = "27682")
37   - private Integer activePrice;
38   -
39   - @Schema(description = "创建时间")
40   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
41   - private LocalDateTime[] createTime;
42   -
43   -}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillActivityController.java
... ... @@ -85,7 +85,7 @@ public class SeckillActivityController {
85 85 @PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
86 86 public CommonResult<List<SeckillActivityRespVO>> getSeckillActivityList(@RequestParam("ids") Collection<Long> ids) {
87 87 List<SeckillActivityDO> list = seckillActivityService.getSeckillActivityList(ids);
88   - return success(SeckillActivityConvert.INSTANCE.convertList(list));
  88 + return success(SeckillActivityConvert.INSTANCE.complementList(list));
89 89 }
90 90  
91 91 @GetMapping("/page")
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/seckill/SeckillConfigController.java
1 1 package cn.iocoder.yudao.module.promotion.controller.admin.seckill;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 6 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.*;
... ... @@ -80,7 +81,7 @@ public class SeckillConfigController {
80 81 @GetMapping("/list-all-simple")
81 82 @Operation(summary = "获得所有开启状态的秒杀时段精简列表", description = "主要用于前端的下拉选项")
82 83 public CommonResult<List<SeckillConfigSimpleRespVO>> getListAllSimple() {
83   - List<SeckillConfigDO> list = seckillConfigService.getListAllSimple();
  84 + List<SeckillConfigDO> list = seckillConfigService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());
84 85 return success(SeckillConfigConvert.INSTANCE.convertList1(list));
85 86 }
86 87  
... ...
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.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 5 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
6   -import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
  6 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
7 7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
8   -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExcelVO;
9 8 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
10 9 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
11 10 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;
12 11 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;
13 12 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO;
14   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
15   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
16   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
  13 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
  14 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
  15 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
17 16 import org.mapstruct.Mapper;
18 17 import org.mapstruct.Mapping;
19 18 import org.mapstruct.Mappings;
... ... @@ -71,20 +70,18 @@ public interface CombinationActivityConvert {
71 70 return respVO;
72 71 }
73 72  
74   - List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list);
  73 + List<CombinationActivityRespVO> complementList(List<CombinationActivityDO> list);
75 74  
76 75 PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page);
77 76  
78 77 default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page,
79 78 List<CombinationProductDO> productList,
80 79 List<ProductSpuRespDTO> spuList) {
81   - // TODO @puhui999:c -> c 可以去掉哈
82   - Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId, c -> c);
  80 + Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
83 81 PageResult<CombinationActivityRespVO> pageResult = convertPage(page);
84 82 pageResult.getList().forEach(item -> {
85   - // TODO @puhui999:最好 MapUtils.findAndThen,万一没找到呢,啊哈哈。
86   - item.setSpuName(spuMap.get(item.getSpuId()).getName());
87   - item.setPicUrl(spuMap.get(item.getSpuId()).getPicUrl());
  83 + MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()));
  84 + MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()));
88 85 item.setProducts(convertList2(productList));
89 86 });
90 87 return pageResult;
... ... @@ -92,8 +89,6 @@ public interface CombinationActivityConvert {
92 89  
93 90 List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs);
94 91  
95   - List<CombinationActivityExcelVO> convertList02(List<CombinationActivityDO> list);
96   -
97 92 @Mappings({
98 93 @Mapping(target = "id", ignore = true),
99 94 @Mapping(target = "activityId", source = "activityDO.id"),
... ... @@ -105,34 +100,28 @@ public interface CombinationActivityConvert {
105 100 })
106 101 CombinationProductDO convert(CombinationActivityDO activityDO, CombinationProductBaseVO vo);
107 102  
108   - default List<CombinationProductDO> convertList(CombinationActivityDO activityDO, List<? extends CombinationProductBaseVO> products) {
  103 + default List<CombinationProductDO> complementList(List<? extends CombinationProductBaseVO> products, CombinationActivityDO activityDO) {
109 104 List<CombinationProductDO> list = new ArrayList<>();
110 105 products.forEach(sku -> {
111 106 CombinationProductDO productDO = convert(activityDO, sku);
112   - // TODO 状态设置
113   - productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
  107 + productDO.setActivityStatus(activityDO.getStatus());
114 108 list.add(productDO);
115 109 });
116 110 return list;
117 111 }
118 112  
119   - // TODO @puhui999:这个方法的参数,调整成 productDOs、vos、activityDO;因为 productDOs 是主角;
120   - // 然后,这个方法,感觉不是为了 convert,而是为了补全;
121   - default List<CombinationProductDO> convertList1(CombinationActivityDO activityDO,
122   - List<CombinationProductUpdateReqVO> vos,
123   - List<CombinationProductDO> productDOs) {
  113 + default List<CombinationProductDO> complementList(List<CombinationProductDO> productDOs, List<CombinationProductUpdateReqVO> vos, CombinationActivityDO activityDO) {
124 114 Map<Long, Long> longMap = convertMap(productDOs, CombinationProductDO::getSkuId, CombinationProductDO::getId);
125 115 List<CombinationProductDO> list = new ArrayList<>();
126 116 vos.forEach(sku -> {
127 117 CombinationProductDO productDO = convert(activityDO, sku);
128 118 productDO.setId(longMap.get(sku.getSkuId()));
129   - // TODO @puhui999:是是不是用 activityDO 的状态;
130   - productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
  119 + productDO.setActivityStatus(activityDO.getStatus());
131 120 list.add(productDO);
132 121 });
133 122 return list;
134 123 }
135 124  
136   - CombinationRecordDO convert(CombinationRecordReqDTO reqDTO);
  125 + CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO);
137 126  
138 127 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java
1 1 package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity;
2 2  
3   -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 5 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
... ... @@ -38,7 +37,7 @@ public interface SeckillActivityConvert {
38 37  
39 38 SeckillActivityRespVO convert(SeckillActivityDO bean);
40 39  
41   - List<SeckillActivityRespVO> convertList(List<SeckillActivityDO> list);
  40 + List<SeckillActivityRespVO> complementList(List<SeckillActivityDO> list);
42 41  
43 42 PageResult<SeckillActivityRespVO> convertPage(PageResult<SeckillActivityDO> page);
44 43  
... ... @@ -74,24 +73,23 @@ public interface SeckillActivityConvert {
74 73 })
75 74 SeckillProductDO convert(SeckillActivityDO activityDO, SeckillProductBaseVO vo);
76 75  
77   - default List<SeckillProductDO> convertList(SeckillActivityDO activityDO, List<? extends SeckillProductBaseVO> products) {
  76 + default List<SeckillProductDO> complementList(List<? extends SeckillProductBaseVO> products, SeckillActivityDO activityDO) {
78 77 List<SeckillProductDO> list = new ArrayList<>();
79 78 products.forEach(sku -> {
80 79 SeckillProductDO productDO = convert(activityDO, sku);
81   - productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
  80 + productDO.setActivityStatus(activityDO.getStatus());
82 81 list.add(productDO);
83 82 });
84 83 return list;
85 84 }
86 85  
87   - // TODO @puhui999:同拼团那个 convert 想通的情况哈。
88   - default List<SeckillProductDO> convertList1(SeckillActivityDO activityDO, List<SeckillProductUpdateReqVO> vos, List<SeckillProductDO> productDOs) {
  86 + default List<SeckillProductDO> complementList(List<SeckillProductDO> productDOs, List<SeckillProductUpdateReqVO> vos, SeckillActivityDO activityDO) {
89 87 Map<Long, Long> longMap = CollectionUtils.convertMap(productDOs, SeckillProductDO::getSkuId, SeckillProductDO::getId);
90 88 List<SeckillProductDO> list = new ArrayList<>();
91 89 vos.forEach(sku -> {
92 90 SeckillProductDO productDO = convert(activityDO, sku);
93 91 productDO.setId(longMap.get(sku.getSkuId()));
94   - productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
  92 + productDO.setActivityStatus(activityDO.getStatus());
95 93 list.add(productDO);
96 94 });
97 95 return list;
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationActivityDO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationActivityDO.java
1   -package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity;
  1 +package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
... ... @@ -9,7 +9,6 @@ import lombok.*;
9 9  
10 10 import java.time.LocalDateTime;
11 11  
12   -// TODO @puhui999:是不是应该在 combination 哈?
13 12 /**
14 13 * 拼团活动 DO
15 14 *
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationProductDO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationProductDO.java
1   -package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity;
  1 +package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -42,6 +42,8 @@ public class CombinationProductDO extends BaseDO {
42 42 private Long skuId;
43 43 /**
44 44 * 拼团商品状态
  45 + *
  46 + * 关联 {@link CombinationActivityDO#getStatus()}
45 47 */
46 48 private Integer activityStatus;
47 49 /**
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/combinationactivity/CombinationRecordDO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationRecordDO.java
1   -package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity;
  1 +package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationActivityMapper.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationActivityMapper.java
1   -package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity;
  1 +package cn.iocoder.yudao.module.promotion.dal.mysql.combination;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
7 6 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
8   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
9 8 import org.apache.ibatis.annotations.Mapper;
10 9  
11 10 import java.util.List;
12 11  
13   -// TODO @puhui999:是不是应该在 combination 哈?
14 12 /**
15 13 * 拼团活动 Mapper
16 14 *
... ... @@ -25,13 +23,6 @@ public interface CombinationActivityMapper extends BaseMapperX&lt;CombinationActivi
25 23 .orderByDesc(CombinationActivityDO::getId));
26 24 }
27 25  
28   - default List<CombinationActivityDO> selectList(CombinationActivityExportReqVO reqVO) {
29   - return selectList(new LambdaQueryWrapperX<CombinationActivityDO>()
30   - .likeIfPresent(CombinationActivityDO::getName, reqVO.getName())
31   - .eqIfPresent(CombinationActivityDO::getStatus, reqVO.getStatus())
32   - .orderByDesc(CombinationActivityDO::getId));
33   - }
34   -
35 26 default List<CombinationActivityDO> selectListByStatus(Integer status) {
36 27 return selectList(CombinationActivityDO::getStatus, status);
37 28 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationProductMapper.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationProductMapper.java
1   -package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity;
  1 +package cn.iocoder.yudao.module.promotion.dal.mysql.combination;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductExportReqVO;
7 6 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductPageReqVO;
8   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
9 8 import org.apache.ibatis.annotations.Mapper;
10 9  
11 10 import java.util.Collection;
... ... @@ -32,19 +31,6 @@ public interface CombinationProductMapper extends BaseMapperX&lt;CombinationProduct
32 31 .orderByDesc(CombinationProductDO::getId));
33 32 }
34 33  
35   - default List<CombinationProductDO> selectList(CombinationProductExportReqVO reqVO) {
36   - return selectList(new LambdaQueryWrapperX<CombinationProductDO>()
37   - .eqIfPresent(CombinationProductDO::getActivityId, reqVO.getActivityId())
38   - .eqIfPresent(CombinationProductDO::getSpuId, reqVO.getSpuId())
39   - .eqIfPresent(CombinationProductDO::getSkuId, reqVO.getSkuId())
40   - .eqIfPresent(CombinationProductDO::getActivityStatus, reqVO.getActivityStatus())
41   - .betweenIfPresent(CombinationProductDO::getActivityStartTime, reqVO.getActivityStartTime())
42   - .betweenIfPresent(CombinationProductDO::getActivityEndTime, reqVO.getActivityEndTime())
43   - .eqIfPresent(CombinationProductDO::getActivePrice, reqVO.getActivePrice())
44   - .betweenIfPresent(CombinationProductDO::getCreateTime, reqVO.getCreateTime())
45   - .orderByDesc(CombinationProductDO::getId));
46   - }
47   -
48 34 default List<CombinationProductDO> selectListByActivityIds(Collection<Long> ids) {
49 35 return selectList(CombinationProductDO::getActivityId, ids);
50 36 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/combinationactivity/CombinationRecordMapper.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java
1   -package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity;
  1 +package cn.iocoder.yudao.module.promotion.dal.mysql.combination;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
5   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
  5 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
6 6 import org.apache.ibatis.annotations.Mapper;
7 7  
8 8 import java.util.List;
... ... @@ -20,6 +20,18 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO
20 20 CombinationRecordDO::getOrderId, orderId);
21 21 }
22 22  
  23 + /**
  24 + * 查询拼团记录
  25 + *
  26 + * @param headId 团长编号
  27 + * @param activityId 活动编号
  28 + * @return 拼团记录
  29 + */
  30 + default CombinationRecordDO selectRecordByHeadId(Long headId, Long activityId, Integer status) {
  31 + return selectOne(CombinationRecordDO::getUserId, headId, CombinationRecordDO::getActivityId, activityId,
  32 + CombinationRecordDO::getStatus, status);
  33 + }
  34 +
23 35 default List<CombinationRecordDO> selectListByHeadIdAndStatus(Long headId, Integer status) {
24 36 return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
25 37 .eq(CombinationRecordDO::getHeadId, headId)
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
1 1 package cn.iocoder.yudao.module.promotion.service.combination;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
5 4 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
7 5 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
8 6 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
9   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
10   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
  8 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
11 9  
12 10 import javax.validation.Valid;
13   -import java.time.LocalDateTime;
14 11 import java.util.Collection;
15 12 import java.util.List;
16 13  
... ... @@ -68,14 +65,6 @@ public interface CombinationActivityService {
68 65 PageResult<CombinationActivityDO> getCombinationActivityPage(CombinationActivityPageReqVO pageReqVO);
69 66  
70 67 /**
71   - * 获得拼团活动列表, 用于 Excel 导出
72   - *
73   - * @param exportReqVO 查询条件
74   - * @return 拼团活动列表
75   - */
76   - List<CombinationActivityDO> getCombinationActivityList(CombinationActivityExportReqVO exportReqVO);
77   -
78   - /**
79 68 * 获得拼团活动商品列表
80 69 *
81 70 * @param ids 拼团活动 ids
... ... @@ -83,41 +72,4 @@ public interface CombinationActivityService {
83 72 */
84 73 List<CombinationProductDO> getProductsByActivityIds(Collection<Long> ids);
85 74  
86   - // TODO @puhui999:拆一个 CombinationRecordService 里,方法名可以简洁成 updateRecordStatusByOrderId;service 方法可以稍微简单一点,如果是 update 方法
87   - /**
88   - * 更新拼团状态
89   - *
90   - * @param userId 用户编号
91   - * @param orderId 订单编号
92   - * @param status 状态
93   - */
94   - void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status);
95   -
96   - /**
97   - * 更新拼团状态和开始时间
98   - *
99   - * @param userId 用户编号
100   - * @param orderId 订单编号
101   - * @param status 状态
102   - * @param startTime 开始时间
103   - */
104   - void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime);
105   -
106   - // TODO @puhui999:拆一个 CombinationRecordService 里
107   - /**
108   - * 创建拼团记录
109   - *
110   - * @param reqDTO 创建信息
111   - */
112   - void createRecord(CombinationRecordReqDTO reqDTO);
113   -
114   - /**
115   - * 获得拼团状态
116   - *
117   - * @param userId 用户编号
118   - * @param orderId 订单编号
119   - * @return 拼团状态
120   - */
121   - boolean validateRecordStatusIsSuccess(Long userId, Long orderId);
122   -
123 75 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.service.combination;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
  4 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +/**
  9 + * 商品活动记录 service
  10 + *
  11 + * @author HUIHUI
  12 + */
  13 +public interface CombinationRecordService {
  14 +
  15 + /**
  16 + * 更新拼团状态
  17 + *
  18 + * @param userId 用户编号
  19 + * @param orderId 订单编号
  20 + * @param status 状态
  21 + */
  22 + void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status);
  23 +
  24 + /**
  25 + * 创建拼团记录
  26 + *
  27 + * @param reqDTO 创建信息
  28 + */
  29 + void createRecord(CombinationRecordCreateReqDTO reqDTO);
  30 +
  31 + /**
  32 + * 更新拼团状态和开始时间
  33 + *
  34 + * @param userId 用户编号
  35 + * @param orderId 订单编号
  36 + * @param status 状态
  37 + * @param startTime 开始时间
  38 + */
  39 + void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime);
  40 +
  41 + /**
  42 + * 获得拼团状态
  43 + *
  44 + * @param userId 用户编号
  45 + * @param orderId 订单编号
  46 + * @return 拼团状态
  47 + */
  48 + CombinationRecordDO getRecord(Long userId, Long orderId);
  49 +
  50 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationServiceImpl.java
... ... @@ -12,20 +12,19 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
12 12 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
13 13 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
14 14 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
15   -import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
  15 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
16 16 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
17   -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
18 17 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
19 18 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
20 19 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductCreateReqVO;
21 20 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO;
22 21 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
23   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
24   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
25   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
26   -import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationActivityMapper;
27   -import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationProductMapper;
28   -import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationRecordMapper;
  22 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
  23 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
  24 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
  25 +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper;
  26 +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper;
  27 +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
29 28 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
30 29 import org.springframework.stereotype.Service;
31 30 import org.springframework.transaction.annotation.Transactional;
... ... @@ -47,7 +46,7 @@ import static cn.iocoder.yudao.module.promotion.util.PromotionUtils.validateProd
47 46 */
48 47 @Service
49 48 @Validated
50   -public class CombinationActivityServiceImpl implements CombinationActivityService {
  49 +public class CombinationServiceImpl implements CombinationActivityService, CombinationRecordService {
51 50  
52 51 @Resource
53 52 private CombinationActivityMapper combinationActivityMapper;
... ... @@ -81,7 +80,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
81 80 activityDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
82 81 combinationActivityMapper.insert(activityDO);
83 82 // 插入商品
84   - List<CombinationProductDO> productDOs = CombinationActivityConvert.INSTANCE.convertList(activityDO, createReqVO.getProducts());
  83 + List<CombinationProductDO> productDOs = CombinationActivityConvert.INSTANCE.complementList(createReqVO.getProducts(), activityDO);
85 84 combinationProductMapper.insertBatch(productDOs);
86 85 // 返回
87 86 return activityDO.getId();
... ... @@ -144,15 +143,14 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
144 143 Map<String, List<CombinationProductDO>> data = CollectionUtils.convertCDUMap(convertSet1, convertSet, mapData -> {
145 144 HashMap<String, List<CombinationProductDO>> cdu = MapUtil.newHashMap(3);
146 145 MapUtils.findAndThen(mapData, "create", list -> {
147   - cdu.put("create", CombinationActivityConvert.INSTANCE.convertList(updateObj,
148   - CollectionUtils.filterList(products, item -> list.contains(item.getSkuId()))));
  146 + cdu.put("create", CombinationActivityConvert.INSTANCE.complementList(CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
149 147 });
150 148 MapUtils.findAndThen(mapData, "delete", list -> {
151 149 cdu.put("create", CollectionUtils.filterList(combinationProductDOs, item -> list.contains(item.getSkuId())));
152 150 });
153 151 MapUtils.findAndThen(mapData, "update", list -> {
154   - cdu.put("update", CombinationActivityConvert.INSTANCE.convertList1(updateObj,
155   - CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), combinationProductDOs));
  152 + cdu.put("update", CombinationActivityConvert.INSTANCE.complementList(combinationProductDOs,
  153 + CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
156 154 });
157 155 return cdu;
158 156 });
... ... @@ -201,11 +199,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
201 199 }
202 200  
203 201 @Override
204   - public List<CombinationActivityDO> getCombinationActivityList(CombinationActivityExportReqVO exportReqVO) {
205   - return combinationActivityMapper.selectList(exportReqVO);
206   - }
207   -
208   - @Override
209 202 public List<CombinationProductDO> getProductsByActivityIds(Collection<Long> ids) {
210 203 return combinationProductMapper.selectListByActivityIds(ids);
211 204 }
... ... @@ -236,7 +229,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
236 229 recordDOs.forEach(item -> {
237 230 item.setUserCount(recordDOs.size());
238 231 // 校验拼团是否满足要求
239   - if (recordDOs.size() >= recordDO.getUserSize()) {
  232 + if (ObjectUtil.equal(recordDOs.size(), recordDO.getUserSize())) {
240 233 item.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
241 234 }
242 235 });
... ... @@ -254,13 +247,30 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
254 247 }
255 248  
256 249 @Override
257   - public void createRecord(CombinationRecordReqDTO reqDTO) {
  250 + public void createRecord(CombinationRecordCreateReqDTO reqDTO) {
258 251 // 校验拼团活动
259 252 CombinationActivityDO activity = validateCombinationActivityExists(reqDTO.getActivityId());
260   - // TODO @puhui999:需要校验下,它当前是不是已经参加了该拼团;
261   - // TODO @puhui999: 父拼团是否存在,是否已经满了
  253 + // 需要校验下,它当前是不是已经参加了该拼团;
  254 + CombinationRecordDO recordDO = recordMapper.selectRecord(reqDTO.getUserId(), reqDTO.getOrderId());
  255 + if (recordDO != null) {
  256 + throw exception(COMBINATION_RECORD_EXISTS);
  257 + }
  258 + // 父拼团是否存在,是否已经满了
  259 + if (reqDTO.getHeadId() != null) {
  260 + CombinationRecordDO recordDO1 = recordMapper.selectRecordByHeadId(reqDTO.getHeadId(), reqDTO.getActivityId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
  261 + if (recordDO1 == null) {
  262 + throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
  263 + }
  264 + // 校验拼团是否满足要求
  265 + if (ObjectUtil.equal(recordDO1.getUserCount(), recordDO1.getUserSize())) {
  266 + throw exception(COMBINATION_RECORD_USER_FULL);
  267 + }
  268 + }
262 269  
263 270 CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO);
  271 + if (reqDTO.getHeadId() == null) {
  272 + record.setHeadId(reqDTO.getUserId());
  273 + }
264 274 record.setVirtualGroup(false);
265 275 // TODO @puhui999:过期时间,应该是 Date 哈;
266 276 record.setExpireTime(activity.getLimitDuration());
... ... @@ -269,21 +279,17 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
269 279 }
270 280  
271 281 @Override
272   - public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) {
273   - CombinationRecordDO record = validateCombinationRecord(userId, orderId);
274   - // TODO @puhui999:可以搞个 getRecrod 方法,然后业务通过 CombinationRecordStatusEnum.isSuccess 方法校验
275   - return ObjectUtil.equal(record.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus());
  282 + public CombinationRecordDO getRecord(Long userId, Long orderId) {
  283 + return validateCombinationRecord(userId, orderId);
276 284 }
277 285  
278   - // TODO @puhui999:status 传入进来搞哈;
279   -
280 286 /**
281 287 * APP 端获取开团记录
282 288 *
283 289 * @return 开团记录
284 290 */
285   - public List<CombinationRecordDO> getRecordList() {
286   - return recordMapper.selectListByStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
  291 + public List<CombinationRecordDO> getRecordListByStatus(Integer status) {
  292 + return recordMapper.selectListByStatus(status);
287 293 }
288 294  
289 295 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillactivity/SeckillActivityServiceImpl.java
1 1 package cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
4 5 import cn.hutool.core.util.ObjectUtil;
5 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  9 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
8 10 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
9 11 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
10 12 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
... ... @@ -26,11 +28,10 @@ import org.springframework.transaction.annotation.Transactional;
26 28 import org.springframework.validation.annotation.Validated;
27 29  
28 30 import javax.annotation.Resource;
29   -import java.util.Collection;
30   -import java.util.List;
31   -import java.util.Set;
  31 +import java.util.*;
32 32  
33 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  34 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
34 35 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
35 36 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
36 37 import static cn.iocoder.yudao.module.promotion.util.PromotionUtils.validateProductSkuExistence;
... ... @@ -63,8 +64,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
63 64 // 获取所选 spu 下的所有 sku
64 65 List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(createReqVO.getSpuId()));
65 66 // 校验商品 sku 是否存在
66   - // TODO @puhui999:直接校验 sku 数量,是不是就完事啦,不需要校验的特别严谨哈;
67   - validateProductSkuExistence(createReqVO.getProducts(), skus, SeckillProductCreateReqVO::getSkuId);
  67 + if (skus.size() != createReqVO.getProducts().size()) {
  68 + throw exception(SKU_NOT_EXISTS);
  69 + }
68 70  
69 71 // 插入秒杀活动
70 72 SeckillActivityDO activity = SeckillActivityConvert.INSTANCE.convert(createReqVO)
... ... @@ -72,9 +74,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
72 74 .setTotalStock(CollectionUtils.getSumValue(createReqVO.getProducts(), SeckillProductCreateReqVO::getStock, Integer::sum));
73 75 seckillActivityMapper.insert(activity);
74 76 // 插入商品
75   - // TODO @puhui999:products 要注意复数哈
76   - List<SeckillProductDO> product = SeckillActivityConvert.INSTANCE.convertList(activity, createReqVO.getProducts());
77   - seckillProductMapper.insertBatch(product);
  77 + List<SeckillProductDO> products = SeckillActivityConvert.INSTANCE.complementList(createReqVO.getProducts(), activity);
  78 + seckillProductMapper.insertBatch(products);
78 79 return activity.getId();
79 80 }
80 81  
... ... @@ -99,11 +100,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
99 100 }
100 101 List<SeckillActivityDO> activityDOs2 = CollectionUtils.convertList(activityDOs, c -> c, s -> {
101 102 // 判断秒杀时段是否有交集
102   - // TODO @puhui999:是不是 containsAny 就是有交集呀
103   - List<Long> configIdsClone = CollUtil.newArrayList(s.getConfigIds());
104   - configIdsClone.retainAll(configIds);
105   - return CollUtil.isNotEmpty(configIdsClone);
  103 + return CollectionUtils.containsAny(s.getConfigIds(), configIds);
106 104 });
  105 +
107 106 if (CollUtil.isNotEmpty(activityDOs2)) {
108 107 throw exception(SECKILL_TIME_CONFLICTS);
109 108 }
... ... @@ -147,31 +146,31 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
147 146 // 数据库中的活动商品
148 147 List<SeckillProductDO> seckillProductDOs = seckillProductMapper.selectListByActivityId(updateObj.getId());
149 148 Set<Long> dbSkuIds = CollectionUtils.convertSet(seckillProductDOs, SeckillProductDO::getSkuId);
150   - // 1. 删除后台存在的前端不存在的商品
151   - // TODO @puhui999:delete 应该是 id,不是 skuId 哈
152 149 Set<Long> voSkuIds = CollectionUtils.convertSet(products, SeckillProductUpdateReqVO::getSkuId);
153   - List<Long> d = CollectionUtils.filterList(dbSkuIds, item -> !voSkuIds.contains(item));
154   - if (CollUtil.isNotEmpty(d)) {
155   - seckillProductMapper.deleteBatchIds(d);
156   - }
157   - // 2. 前端存在的后端不存在的商品
158   - List<Long> c = CollectionUtils.filterList(voSkuIds, item -> !dbSkuIds.contains(item));
159   - if (CollUtil.isNotEmpty(c)) {
160   - List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> c.contains(item.getSkuId()));
161   - List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList(updateObj, vos);
162   - seckillProductMapper.insertBatch(productDOs);
163   - }
164   - // 3. 更新已存在的商品
165   - List<Long> u = CollectionUtils.filterList(voSkuIds, dbSkuIds::contains);
166   - if (CollUtil.isNotEmpty(u)) {
167   - List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> u.contains(item.getSkuId()));
168   - List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList1(updateObj, vos, seckillProductDOs);
169   - seckillProductMapper.updateBatch(productDOs);
170   - }
  150 + Map<String, List<SeckillProductDO>> data = CollectionUtils.convertCDUMap(voSkuIds, dbSkuIds, mapData -> {
  151 + HashMap<String, List<SeckillProductDO>> cdu = MapUtil.newHashMap(3);
  152 + MapUtils.findAndThen(mapData, "create", list -> {
  153 + cdu.put("create", SeckillActivityConvert.INSTANCE.complementList(
  154 + CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
  155 + });
  156 + MapUtils.findAndThen(mapData, "delete", list -> {
  157 + cdu.put("create", CollectionUtils.filterList(seckillProductDOs, item -> list.contains(item.getSkuId())));
  158 + });
  159 + MapUtils.findAndThen(mapData, "update", list -> {
  160 + cdu.put("update", SeckillActivityConvert.INSTANCE.complementList(seckillProductDOs,
  161 + CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
  162 + });
  163 + return cdu;
  164 + });
  165 +
  166 + // 执行增删改
  167 + MapUtils.findAndThen(data, "create", item -> seckillProductMapper.insertBatch(item));
  168 + MapUtils.findAndThen(data, "delete", item -> seckillProductMapper.deleteBatchIds(
  169 + CollectionUtils.convertSet(item, SeckillProductDO::getId)));
  170 + MapUtils.findAndThen(data, "update", item -> seckillProductMapper.updateBatch(item));
171 171 }
172 172  
173 173 @Override
174   - @Transactional(rollbackFor = Exception.class) // TODO @puhui999:这个不用加事务哈
175 174 public void closeSeckillActivity(Long id) {
176 175 // TODO 待验证没使用过
177 176 // 校验存在
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillconfig/SeckillConfigService.java
... ... @@ -73,9 +73,10 @@ public interface SeckillConfigService {
73 73 /**
74 74 * 获得所有正常状态的时段配置列表
75 75 *
  76 + * @param status 状态
76 77 * @return 秒杀时段列表
77 78 */
78   - List<SeckillConfigDO> getListAllSimple();
  79 + List<SeckillConfigDO> getSeckillConfigListByStatus(Integer status);
79 80  
80 81 /**
81 82 * 更新秒杀时段配置状态
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/seckillconfig/SeckillConfigServiceImpl.java
... ... @@ -62,7 +62,7 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
62 62 seckillConfigMapper.updateById(updateObj);
63 63 }
64 64  
65   - // TODO @puhui999: 这个要不合并到更新操作里? 不单独有个操作咧; 更新状态不用那么多必须的参数,更新的时候需要校验时间段
  65 + // TODO @puhui999: 这个要不合并到更新操作里? 不单独有个操作咧; fix: 更新状态不用那么多必须的参数,更新的时候需要校验时间段
66 66 @Override
67 67 public void updateSeckillConfigStatus(Long id, Integer status) {
68 68 // 校验秒杀时段是否存在
... ... @@ -108,7 +108,7 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
108 108 LocalTime startTime2 = LocalTime.parse(config.getStartTime());
109 109 LocalTime endTime2 = LocalTime.parse(config.getEndTime());
110 110 // 判断时间是否重叠
111   - return LocalDateTimeUtils.checkTimeOverlap(startTime1, endTime1, startTime2, endTime2);
  111 + return LocalDateTimeUtils.isOverlap(startTime1, endTime1, startTime2, endTime2);
112 112 });
113 113  
114 114 if (hasConflict) {
... ... @@ -151,10 +151,9 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
151 151 return seckillConfigMapper.selectPage(pageVO);
152 152 }
153 153  
154   - // TODO @puhui999:改成传入 enable 状态哈。一个通用的 getSeckillConfigList 方法
155 154 @Override
156   - public List<SeckillConfigDO> getListAllSimple() {
157   - return seckillConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
  155 + public List<SeckillConfigDO> getSeckillConfigListByStatus(Integer status) {
  156 + return seckillConfigMapper.selectListByStatus(status);
158 157 }
159 158  
160 159 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImplTest.java
... ... @@ -3,17 +3,15 @@ package cn.iocoder.yudao.module.promotion.service.combination;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
5 5 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
7 6 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
8 7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
9   -import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
10   -import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationActivityMapper;
  8 +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
  9 +import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper;
11 10 import org.junit.jupiter.api.Disabled;
12 11 import org.junit.jupiter.api.Test;
13 12 import org.springframework.context.annotation.Import;
14 13  
15 14 import javax.annotation.Resource;
16   -import java.util.List;
17 15  
18 16 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
19 17 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
... ... @@ -26,15 +24,15 @@ import static org.junit.jupiter.api.Assertions.*;
26 24  
27 25 // TODO 芋艿:等完成后,在补全单测
28 26 /**
29   - * {@link CombinationActivityServiceImpl} 的单元测试类
  27 + * {@link CombinationServiceImpl} 的单元测试类
30 28 *
31 29 * @author HUIHUI
32 30 */
33   -@Import(CombinationActivityServiceImpl.class)
  31 +@Import(CombinationServiceImpl.class)
34 32 public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
35 33  
36 34 @Resource
37   - private CombinationActivityServiceImpl combinationActivityService;
  35 + private CombinationServiceImpl combinationActivityService;
38 36  
39 37 @Resource
40 38 private CombinationActivityMapper combinationActivityMapper;
... ... @@ -225,28 +223,7 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
225 223 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setLimitDuration(null)));
226 224 // 测试 createTime 不匹配
227 225 combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setCreateTime(null)));
228   - // 准备参数
229   - CombinationActivityExportReqVO reqVO = new CombinationActivityExportReqVO();
230   - reqVO.setName(null);
231   - reqVO.setSpuId(null);
232   - reqVO.setTotalLimitCount(null);
233   - reqVO.setSingleLimitCount(null);
234   - reqVO.setStartTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
235   - reqVO.setEndTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
236   - reqVO.setUserSize(null);
237   - reqVO.setTotalNum(null);
238   - reqVO.setSuccessNum(null);
239   - reqVO.setOrderUserCount(null);
240   - reqVO.setVirtualGroup(null);
241   - reqVO.setStatus(null);
242   - reqVO.setLimitDuration(null);
243   - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
244 226  
245   - // 调用
246   - List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(reqVO);
247   - // 断言
248   - assertEquals(1, list.size());
249   - assertPojoEquals(dbCombinationActivity, list.get(0));
250 227 }
251 228  
252 229 }
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
... ... @@ -31,6 +31,10 @@ public interface ErrorCodeConstants {
31 31 ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1011000020, "创建交易订单项的评价失败,订单已评价");
32 32 ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1011000021, "交易订单发货失败,订单已退款或部分退款");
33 33 ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1011000022, "交易订单发货失败,拼团未成功");
  34 + // TODO 已移除订单单独发货
  35 + ErrorCode ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY = new ErrorCode(1011000023, "订单发货失败,请选择发货商品");
  36 + ErrorCode ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS = new ErrorCode(1011000024, "订单发货失败,所选发货商品不存在");
  37 + ErrorCode ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY = new ErrorCode(1011000025, "订单发货失败,所选商品已发货");
34 38  
35 39 // ========== After Sale 模块 1011000100 ==========
36 40 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
... ... @@ -69,10 +73,4 @@ public interface ErrorCodeConstants {
69 73 // ========== 物流 PICK_UP 模块 1011006000 ==========
70 74 ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
71 75  
72   - // ========== 物流 PICK_UP 模块 1011007000 ==========
73   - // TODO @puhui999:这几个错误码,应该属于订单哈
74   - ErrorCode ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY = new ErrorCode(1011007000, "订单发货失败,请选择发货商品");
75   - ErrorCode ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS = new ErrorCode(1011007001, "订单发货失败,所选发货商品不存在");
76   - ErrorCode ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY = new ErrorCode(1011007002, "订单发货失败,所选商品已发货");
77   -
78 76 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDeliveryReqVO.java
... ... @@ -5,8 +5,8 @@ import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
5 5 import io.swagger.v3.oas.annotations.media.Schema;
6 6 import lombok.Data;
7 7  
  8 +import javax.validation.constraints.NotEmpty;
8 9 import javax.validation.constraints.NotNull;
9   -import java.util.List;
10 10  
11 11 @Schema(description = "管理后台 - 订单发货 Request VO")
12 12 @Data
... ... @@ -21,20 +21,14 @@ public class TradeOrderDeliveryReqVO {
21 21 @NotNull(message = "发货类型不能为空")
22 22 private Integer type;
23 23  
24   - // TODO @puhui999:还是要校验下
25   -
26 24 @Schema(description = "发货物流公司编号", example = "1")
  25 + @NotNull(message = "发货物流公司不能为空")
27 26 private Long logisticsId;
28 27  
29 28 @Schema(description = "发货物流单号", example = "SF123456789")
  29 + @NotEmpty(message = "发货物流单号不能为空")
30 30 private String logisticsNo;
31 31  
32   - // TODO 订单项商品单独发货;不做单独发
33   -
34   - @Schema(description = "发货订单项", example = "[1,2,3]")
35   - @NotNull(message = "发货订单项不能为空")
36   - private List<Long> orderItemIds;
37   -
38 32 // =============== 同城配送 ================
39 33 // TODO
40 34  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
... ... @@ -13,12 +13,11 @@ import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
13 13 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
14 14 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
15 15 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
16   -import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
  16 +import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
17 17 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
18 18 import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
19 19 import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
20 20 import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
21   -import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
22 21 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
23 22 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
24 23 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
... ... @@ -27,7 +26,6 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderI
27 26 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
28 27 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
29 28 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
30   -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
31 29 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
32 30 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
33 31 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
... ... @@ -334,17 +332,6 @@ public interface TradeOrderConvert {
334 332 @Mapping(target = "avatar", source = "user.avatar"),
335 333 @Mapping(target = "status", ignore = true)
336 334 })
337   - CombinationRecordReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem, AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user);
338   -
339   - TradeOrderDeliveryDO covert(Long orderId, Long orderItemId, Long userId, Integer deliveryType, Long logisticsId, String logisticsNo);
340   -
341   - default List<TradeOrderDeliveryDO> covert(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
342   - ArrayList<TradeOrderDeliveryDO> arrayList = new ArrayList<>();
343   - deliveryReqVO.getOrderItemIds().forEach(item -> {
344   - arrayList.add(covert(order.getId(), item, order.getUserId(), deliveryReqVO.getType(),
345   - deliveryReqVO.getLogisticsId(), deliveryReqVO.getLogisticsNo()));
346   - });
347   - return arrayList;
348   - }
  335 + CombinationRecordCreateReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem, AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user);
349 336  
350 337 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDeliveryDO.java deleted
1   -package cn.iocoder.yudao.module.trade.dal.dataobject.order;
2   -
3   -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4   -import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
5   -import com.baomidou.mybatisplus.annotation.KeySequence;
6   -import com.baomidou.mybatisplus.annotation.TableName;
7   -import lombok.*;
8   -
9   -// TODO @puhui999:
10   -/**
11   - * 交易订单发货记录 DO
12   - *
13   - * @author HUIHUI
14   - */
15   -@TableName("trade_order_delivery")
16   -@KeySequence("trade_order_delivery_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
17   -@Data
18   -@EqualsAndHashCode(callSuper = true)
19   -@ToString(callSuper = true)
20   -@Builder
21   -@NoArgsConstructor
22   -@AllArgsConstructor
23   -public class TradeOrderDeliveryDO extends BaseDO {
24   -
25   - /**
26   - * 订单发货记录 id
27   - */
28   - private Long id;
29   - /**
30   - * 订单 id
31   - */
32   - private Long orderId;
33   - /**
34   - * 订单项 id TODO 要不要一个发货记录可对应多个订单项?
35   - */
36   - private Long orderItemId;
37   - /**
38   - * 用户编号
39   - *
40   - * 关联 MemberUserDO 的 id 编号
41   - */
42   - private Long userId;
43   - /**
44   - * 配送方式
45   - *
46   - * 枚举 {@link DeliveryTypeEnum}
47   - */
48   - private Integer deliveryType;
49   - /**
50   - * 发货物流公司编号
51   - */
52   - private Long logisticsId;
53   - /**
54   - * 发货物流单号
55   - */
56   - private String logisticsNo;
57   -
58   - // TODO 同城配送
59   -
60   -}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderDeliveryMapper.java deleted
1   -package cn.iocoder.yudao.module.trade.dal.mysql.order;
2   -
3   -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
5   -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
6   -import org.apache.ibatis.annotations.Mapper;
7   -
8   -import java.util.List;
9   -
10   -// TODO @puhui999:应该去掉啦
11   -/**
12   - * 交易订单发货记录 Mapper
13   - *
14   - * @author HUIHUI
15   - */
16   -@Mapper
17   -public interface TradeOrderDeliveryMapper extends BaseMapperX<TradeOrderDeliveryDO> {
18   -
19   - default List<TradeOrderDeliveryDO> selsectListByOrderIdAndItemIds(Long orderId, List<Long> orderItemIds) {
20   - return selectList(new LambdaQueryWrapperX<TradeOrderDeliveryDO>()
21   - .eq(TradeOrderDeliveryDO::getOrderId, orderId).in(TradeOrderDeliveryDO::getOrderItemId, orderItemIds));
22   - }
23   -
24   -}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/message/TradeMessageService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.message;
  2 +
  3 +import cn.iocoder.yudao.module.trade.service.message.dto.TradeOrderMessageWhenDeliveryOrderReqDTO;
  4 +
  5 +/**
  6 + * Trade 消息 service 接口
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +public interface TradeMessageService {
  11 +
  12 + /**
  13 + * 订单发货时发送消息
  14 + *
  15 + * @param reqDTO 发送消息
  16 + */
  17 + void sendMessageWhenDeliveryOrder(TradeOrderMessageWhenDeliveryOrderReqDTO reqDTO);
  18 +
  19 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/message/TradeMessageServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.message;
  2 +
  3 +import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
  4 +import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
  5 +import cn.iocoder.yudao.module.trade.service.message.dto.TradeOrderMessageWhenDeliveryOrderReqDTO;
  6 +import org.springframework.stereotype.Service;
  7 +import org.springframework.validation.annotation.Validated;
  8 +
  9 +import javax.annotation.Resource;
  10 +import java.util.HashMap;
  11 +import java.util.Map;
  12 +
  13 +/**
  14 + * Trade 消息 service 实现类
  15 + *
  16 + * @author HUIHUI
  17 + */
  18 +@Service
  19 +@Validated
  20 +public class TradeMessageServiceImpl implements TradeMessageService {
  21 +
  22 + @Resource
  23 + private NotifyMessageSendApi notifyMessageSendApi;
  24 +
  25 + @Override
  26 + public void sendMessageWhenDeliveryOrder(TradeOrderMessageWhenDeliveryOrderReqDTO reqDTO) {
  27 + // 1、构造消息
  28 + Map<String, Object> msgMap = new HashMap<>();
  29 + msgMap.put("orderId", reqDTO.getOrderId());
  30 + msgMap.put("msg", reqDTO.getMessage());
  31 + // 2、发送站内信
  32 + notifyMessageSendApi.sendSingleMessageToMember(
  33 + new NotifySendSingleToUserReqDTO()
  34 + .setUserId(reqDTO.getUserId())
  35 + .setTemplateCode("order_delivery")
  36 + .setTemplateParams(msgMap));
  37 + }
  38 +
  39 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/message/dto/TradeOrderMessageWhenDeliveryOrderReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.message.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import javax.validation.constraints.NotEmpty;
  6 +import javax.validation.constraints.NotNull;
  7 +
  8 +/**
  9 + * 订单发货时 Req DTO
  10 + *
  11 + * @author HUIHUI
  12 + */
  13 +@Data
  14 +public class TradeOrderMessageWhenDeliveryOrderReqDTO {
  15 +
  16 + /**
  17 + * 订单编号
  18 + */
  19 + @NotNull(message = "订单编号不能为空")
  20 + private Long orderId;
  21 + /**
  22 + * 用户编号
  23 + */
  24 + @NotNull(message = "用户编号不能为空")
  25 + private Long userId;
  26 + /**
  27 + * 消息
  28 + */
  29 + @NotEmpty(message = "发送消息不能为空")
  30 + private String message;
  31 +
  32 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
... ... @@ -8,6 +8,7 @@ import cn.hutool.core.util.StrUtil;
8 8 import cn.iocoder.yudao.framework.common.core.KeyValue;
9 9 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
10 10 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  11 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
11 12 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
12 13 import cn.iocoder.yudao.module.member.api.address.AddressApi;
13 14 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
... ... @@ -21,12 +22,10 @@ import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
21 22 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
22 23 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
23 24 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
24   -import cn.iocoder.yudao.module.promotion.api.combination.CombinationApi;
  25 +import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
25 26 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
26 27 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
27 28 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
28   -import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
29   -import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
30 29 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
31 30 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
32 31 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
... ... @@ -38,9 +37,7 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
38 37 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
39 38 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
40 39 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
41   -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
42 40 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
43   -import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderDeliveryMapper;
44 41 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
45 42 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
46 43 import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
... ... @@ -49,6 +46,8 @@ import cn.iocoder.yudao.module.trade.enums.order.*;
49 46 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
50 47 import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
51 48 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
  49 +import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
  50 +import cn.iocoder.yudao.module.trade.service.message.dto.TradeOrderMessageWhenDeliveryOrderReqDTO;
52 51 import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
53 52 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
54 53 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
... ... @@ -102,15 +101,13 @@ public class TradeOrderServiceImpl implements TradeOrderService {
102 101 @Resource
103 102 private ProductCommentApi productCommentApi;
104 103 @Resource
105   - private NotifyMessageSendApi notifyMessageSendApi;
106   -
  104 + private TradeMessageService tradeMessageService;
107 105 @Resource
108 106 private TradeOrderProperties tradeOrderProperties;
109 107  
110 108 @Resource
111   - private CombinationApi combinationApi;
112   - @Resource
113   - private TradeOrderDeliveryMapper orderDeliveryMapper;
  109 + private CombinationRecordApi combinationRecordApi;
  110 +
114 111 // =================== Order ===================
115 112  
116 113 @Override
... ... @@ -177,7 +174,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
177 174 if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
178 175 MemberUserRespDTO user = memberUserApi.getUser(userId);
179 176 // TODO 拼团一次应该只能选择一种规格的商品
180   - combinationApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user)
  177 + combinationRecordApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user)
181 178 .setStatus(CombinationRecordStatusEnum.WAITING.getStatus()));
182 179 }
183 180 // TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除
... ... @@ -314,7 +311,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
314 311 // 1、拼团活动
315 312 if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
316 313 // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录
317   - combinationApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
  314 + combinationRecordApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
318 315 }
319 316 // TODO 芋艿:发送订单变化的消息
320 317  
... ... @@ -385,23 +382,26 @@ public class TradeOrderServiceImpl implements TradeOrderService {
385 382 // 校验并获得交易订单(可发货)
386 383 TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
387 384  
388   - TradeOrderDO tradeOrderDO = new TradeOrderDO();
389   - List<TradeOrderDeliveryDO> deliveryDOs = new ArrayList<>();
390 385 /* TODO
391 386 * fix: 首先需要店铺设置配送方式如: 自提 、配送、物流-配送、物流-配送-自提、商家配送
392 387 * 1.如果店铺有设置配送方式用户只填写收货地址的情况下店家后台自己选择配送方式
393 388 * 2.如果店铺只支持到店自提那么下单后默认发货不需要物流
394 389 * 3.如果店铺支持 物流-配送-自提 的情况下后台不需要选择配送方式按前端用户选择的配送方式发货即可
395 390 */
  391 + TradeOrderDO tradeOrderDO = new TradeOrderDO();
396 392 // 判断发货类型
397 393 // 快递发货
398 394 if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.EXPRESS.getMode())) {
399   - deliveryDOs = express(order, deliveryReqVO);
  395 + // 校验快递公司
  396 + DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
  397 + if (deliveryExpress == null) {
  398 + throw exception(EXPRESS_NOT_EXISTS);
  399 + }
400 400 tradeOrderDO.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
401 401 }
402 402 // 用户自提
403 403 if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.PICK_UP.getMode())) {
404   - deliveryDOs = pickUp(order, deliveryReqVO);
  404 + // TODO 校验自提门店是否存在
405 405 // 重置一下确保快递公司和快递单号为空
406 406 tradeOrderDO.setLogisticsId(null).setLogisticsNo("");
407 407 }
... ... @@ -409,6 +409,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
409 409 if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.NULL.getMode())) {
410 410 // TODO 情况一:正常走发货逻辑和用户自提有点像 不同点:不需要自提门店只需要用户确认收货
411 411 // TODO 情况二:用户下单付款后直接确认收货或等待用户确认收货
  412 + // 重置一下确保快递公司和快递单号为空
  413 + tradeOrderDO.setLogisticsId(null).setLogisticsNo("");
412 414 }
413 415  
414 416 // 更新 TradeOrderDO 状态为已发货,等待收货
... ... @@ -418,72 +420,16 @@ public class TradeOrderServiceImpl implements TradeOrderService {
418 420 if (updateCount == 0) {
419 421 throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
420 422 }
421   - // 发货成功记录发货表
422   - orderDeliveryMapper.insertBatch(deliveryDOs);
423 423 // TODO 芋艿:发送订单变化的消息
424 424  
425   - // TODO @puhui999:可以抽个 message 包,里面是 Order 所有的 message;类似工作流的
426 425 // 发送站内信
427   - // 1、构造消息
428   - Map<String, Object> msgMap = new HashMap<>();
429   - msgMap.put("orderId", deliveryReqVO.getId());
430   - msgMap.put("msg", TradeOrderStatusEnum.DELIVERED.getStatus());
431   - // 2、发送站内信
432   - notifyMessageSendApi.sendSingleMessageToMember(
433   - new NotifySendSingleToUserReqDTO()
434   - .setUserId(userId)
435   - .setTemplateCode("order_delivery")
436   - .setTemplateParams(msgMap));
  426 + tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqDTO().setOrderId(order.getId())
  427 + .setUserId(userId).setMessage(TradeOrderDeliveryStatusEnum.DELIVERED.getName()));
437 428  
438 429 // TODO 芋艿:OrderLog
439 430 // TODO 设计:lili:是不是发货后,才支持售后?
440 431 }
441 432  
442   - private List<TradeOrderDeliveryDO> express(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
443   - // 校验快递公司
444   - DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
445   - if (deliveryExpress == null) {
446   - throw exception(EXPRESS_NOT_EXISTS);
447   - }
448   - // 校验发货商品
449   - validateDeliveryOrderItem(order, deliveryReqVO);
450   - // 创建发货记录
451   - return TradeOrderConvert.INSTANCE.covert(order, deliveryReqVO);
452   - }
453   -
454   - private List<TradeOrderDeliveryDO> pickUp(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
455   - // TODO 校验自提门店是否存在
456   - // 重置一下确保快递公司和快递单号为空
457   - deliveryReqVO.setLogisticsId(null);
458   - deliveryReqVO.setLogisticsNo("");
459   - // 校验发货商品
460   - validateDeliveryOrderItem(order, deliveryReqVO);
461   - // 创建发货记录
462   - return TradeOrderConvert.INSTANCE.covert(order, deliveryReqVO);
463   - }
464   -
465   - private void validateDeliveryOrderItem(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
466   - // TODO 设计:like:是否要单独一个 delivery 发货单表??? fix: 多商品可分开单独发货,添加 trade_order_delivery 交易订单发货日志表关联发货所选订单项设置物流单号
467   - // TODO 设计:niu:要不要支持一个订单下,多个 order item 单独发货,类似有赞 fix
468   - // 校验发货商品
469   - if (CollUtil.isEmpty(deliveryReqVO.getOrderItemIds())) {
470   - throw exception(ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY);
471   - }
472   - // 校验发货商品是否存在
473   - List<TradeOrderItemDO> orderItemDOs = tradeOrderItemMapper.selectListByOrderId(order.getId());
474   - Set<Long> itemIds = convertSet(orderItemDOs, TradeOrderItemDO::getId);
475   - if (!itemIds.containsAll(deliveryReqVO.getOrderItemIds())) {
476   - throw exception(ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS);
477   - }
478   - // 校验所选订单项是否存在有已发货的
479   - List<TradeOrderDeliveryDO> deliveryDOList = orderDeliveryMapper.selsectListByOrderIdAndItemIds(order.getId(), deliveryReqVO.getOrderItemIds());
480   - if (CollUtil.isNotEmpty(deliveryDOList)) {
481   - HashSet<Long> hashSet = CollUtil.newHashSet(deliveryReqVO.getOrderItemIds());
482   - hashSet.retainAll(convertSet(deliveryDOList, TradeOrderDeliveryDO::getOrderItemId));
483   - throw exception(ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY);
484   - }
485   - }
486   -
487 433 /**
488 434 * 校验交易订单满足被发货的条件
489 435 *
... ... @@ -511,7 +457,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
511 457 if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
512 458 // 校验订单拼团是否成功
513 459 // TODO 用户 ID 使用当前登录用户的还是订单保存的?
514   - if (combinationApi.validateRecordStatusIsSuccess(order.getUserId(), order.getId())) {
  460 + if (combinationRecordApi.validateRecordStatusIsSuccess(order.getUserId(), order.getId())) {
515 461 throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
516 462 }
517 463 }
... ... @@ -532,7 +478,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
532 478 if (updateCount == 0) {
533 479 throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
534 480 }
535   -
536 481 // TODO 芋艿:OrderLog
537 482  
538 483 // TODO 芋艿:lili 发送订单变化的消息
... ... @@ -683,6 +628,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
683 628 }
684 629  
685 630 @Override
  631 + @Transactional(rollbackFor = Exception.class)
686 632 public Long createOrderItemComment(AppTradeOrderItemCommentCreateReqVO createReqVO) {
687 633 Long loginUserId = getLoginUserId();
688 634 // 先通过订单项 ID,查询订单项是否存在
... ... @@ -690,7 +636,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
690 636 if (orderItem == null) {
691 637 throw exception(ORDER_ITEM_NOT_FOUND);
692 638 }
693   - // 校验订单
  639 + // 校验订单相关状态
694 640 TradeOrderDO order = getOrderByIdAndUserId(orderItem.getOrderId(), loginUserId);
695 641 if (order == null) {
696 642 throw exception(ORDER_NOT_FOUND);
... ... @@ -701,11 +647,18 @@ public class TradeOrderServiceImpl implements TradeOrderService {
701 647 if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) {
702 648 throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
703 649 }
704   - // TODO @puhui999:是不是评论完,要更新 status、commentStatus;另外,是不是上面 order 可以不校验,直接只判断 orderItem 就够;
705   - // 对于 order 来说,就是评论完,把 order 更新完合理的 status 等字段。
706 650  
  651 + // 创建评价
707 652 ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem);
708   - return productCommentApi.createComment(productCommentCreateReqDTO);
  653 + Long comment = productCommentApi.createComment(productCommentCreateReqDTO);
  654 + // 更新订单项评价状态
  655 + tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
  656 + List<TradeOrderItemDO> orderItems = getOrderItemListByOrderId(CollUtil.newArrayList(order.getId()));
  657 + if (!CollectionUtils.isAny(orderItems, item -> ObjectUtil.equal(item.getCommentStatus(), Boolean.FALSE))) {
  658 + // 对于 order 来说,就是评论完,把 order 更新完合理的 status 等字段。
  659 + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE));
  660 + }
  661 + return comment;
709 662 }
710 663  
711 664 /**
... ...