Commit 8f86f65884df2984cfb43f3c6c2aab1fba52b367

Authored by YunaiV
2 parents 8321d14b 482a84a6

Merge branch 'feature/mall_product' of https://gitee.com/zhijiantianya/ruoyi-vue-pro

Showing 43 changed files with 530 additions and 74 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnum.java
... ... @@ -17,7 +17,7 @@ import java.lang.annotation.*;
17 17 @Retention(RetentionPolicy.RUNTIME)
18 18 @Documented
19 19 @Constraint(
20   - validatedBy = InEnumValidator.class
  20 + validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class}
21 21 )
22 22 public @interface InEnum {
23 23  
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/validation/InEnumCollectionValidator.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.common.validation;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  5 +
  6 +import javax.validation.ConstraintValidator;
  7 +import javax.validation.ConstraintValidatorContext;
  8 +import java.util.Arrays;
  9 +import java.util.Collection;
  10 +import java.util.Collections;
  11 +import java.util.List;
  12 +import java.util.stream.Collectors;
  13 +
  14 +public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> {
  15 +
  16 + private List<Integer> values;
  17 +
  18 + @Override
  19 + public void initialize(InEnum annotation) {
  20 + IntArrayValuable[] values = annotation.value().getEnumConstants();
  21 + if (values.length == 0) {
  22 + this.values = Collections.emptyList();
  23 + } else {
  24 + this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
  25 + }
  26 + }
  27 +
  28 + @Override
  29 + public boolean isValid(Collection<Integer> list, ConstraintValidatorContext context) {
  30 + // 校验通过
  31 + if (CollUtil.containsAll(values, list)) {
  32 + return true;
  33 + }
  34 + // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
  35 + context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
  36 + context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
  37 + .replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
  38 + return false;
  39 + }
  40 +
  41 +}
  42 +
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApi.java
... ... @@ -21,6 +21,13 @@ public interface CouponApi {
21 21 void useCoupon(@Valid CouponUseReqDTO useReqDTO);
22 22  
23 23 /**
  24 + * 退还已使用的优惠券
  25 + *
  26 + * @param id 优惠券编号
  27 + */
  28 + void returnUsedCoupon(Long id);
  29 +
  30 + /**
24 31 * 校验优惠劵
25 32 *
26 33 * @param validReqDTO 校验请求
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponRespDTO.java
... ... @@ -71,9 +71,9 @@ public class CouponRespDTO {
71 71 */
72 72 private Integer productScope;
73 73 /**
74   - * 商品 SPU 编号的数组
  74 + * 商品范围编号的数组
75 75 */
76   - private List<Long> productSpuIds;
  76 + private List<Long> productScopeValues;
77 77 // ========== 使用规则 END ==========
78 78  
79 79 // ========== 使用效果 BEGIN ==========
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
... ... @@ -27,12 +27,17 @@ public interface ErrorCodeConstants {
27 27 // ========== 优惠劵模板 1013004000 ==========
28 28 ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1013004000, "优惠劵模板不存在");
29 29 ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1013004001, "发放数量不能小于已领取数量({})");
  30 + ErrorCode COUPON_TEMPLATE_NOT_ENOUGH = new ErrorCode(1013004002, "当前剩余数量不够领取");
  31 + ErrorCode COUPON_TEMPLATE_USER_ALREADY_TAKE = new ErrorCode(1013004003, "用户已领取过此优惠券");
  32 + ErrorCode COUPON_TEMPLATE_EXPIRED = new ErrorCode(1013004004, "优惠券已过期");
  33 + ErrorCode COUPON_TEMPLATE_CANNOT_TAKE = new ErrorCode(1013004005, "领取方式不正确");
30 34  
31   - // ========== 优惠劵模板 1013005000 ==========
  35 + // ========== 优惠劵 1013005000 ==========
32 36 ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1013005000, "优惠券不存在");
33 37 ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1013005001, "回收优惠劵失败,优惠劵已被使用");
34 38 ErrorCode COUPON_STATUS_NOT_UNUSED = new ErrorCode(1013005002, "优惠劵不处于待使用状态");
35 39 ErrorCode COUPON_VALID_TIME_NOT_NOW = new ErrorCode(1013005003, "优惠券不在使用时间范围内");
  40 + ErrorCode COUPON_STATUS_NOT_USED = new ErrorCode(1013005004, "优惠劵不是已使用状态");
36 41  
37 42 // ========== 满减送活动 1013006000 ==========
38 43 ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1013006000, "满减送活动不存在");
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java
... ... @@ -15,8 +15,9 @@ import java.util.Arrays;
15 15 @AllArgsConstructor
16 16 public enum PromotionProductScopeEnum implements IntArrayValuable {
17 17  
18   - ALL(1, "全部商品参与"),
19   - SPU(2, "指定商品参与"),
  18 + ALL(1, "通用卷"), // 全部商品
  19 + SPU(2, "商品卷"), // 指定商品
  20 + CATEGORY(3, "品类卷"), // 指定商品
20 21 ;
21 22  
22 23 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionProductScopeEnum::getScope).toArray();
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java
... ... @@ -15,8 +15,9 @@ import java.util.Arrays;
15 15 @Getter
16 16 public enum CouponTakeTypeEnum implements IntArrayValuable {
17 17  
18   - BY_USER(1, "直接领取"), // 用户可在首页、每日领劵直接领取
19   - BY_ADMIN(2, "指定发放"), // 后台指定会员赠送优惠劵
  18 + USER(1, "直接领取"), // 用户可在首页、每日领劵直接领取
  19 + ADMIN(2, "指定发放"), // 后台指定会员赠送优惠劵
  20 + REGISTER(3, "新人券"), // 注册时自动领取
20 21 ;
21 22  
22 23 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponTakeTypeEnum::getValue).toArray();
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponApiImpl.java
... ... @@ -29,6 +29,11 @@ public class CouponApiImpl implements CouponApi {
29 29 }
30 30  
31 31 @Override
  32 + public void returnUsedCoupon(Long id) {
  33 + couponService.returnUsedCoupon(id);
  34 + }
  35 +
  36 + @Override
32 37 public CouponRespDTO validateCoupon(CouponValidReqDTO validReqDTO) {
33 38 CouponDO coupon = couponService.validCoupon(validReqDTO.getId(), validReqDTO.getUserId());
34 39 return CouponConvert.INSTANCE.convert(coupon);
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java
... ... @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
8 8 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
9 9 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
10 10 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
  11 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponSendReqVO;
11 12 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
12 13 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
13 14 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
... ... @@ -21,7 +22,6 @@ import org.springframework.web.bind.annotation.*;
21 22 import javax.annotation.Resource;
22 23 import javax.validation.Valid;
23 24 import java.util.Map;
24   -import java.util.Set;
25 25  
26 26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
27 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
... ... @@ -64,12 +64,20 @@ public class CouponController {
64 64 if (CollUtil.isEmpty(pageResulVO.getList())) {
65 65 return success(pageResulVO);
66 66 }
  67 +
67 68 // 读取用户信息,进行拼接
68   - Set<Long> userIds = convertSet(pageResult.getList(), CouponDO::getUserId);
69   - Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(userIds);
  69 + Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(convertSet(pageResult.getList(), CouponDO::getUserId));
70 70 pageResulVO.getList().forEach(itemRespVO -> MapUtils.findAndThen(userMap, itemRespVO.getUserId(),
71 71 userRespDTO -> itemRespVO.setNickname(userRespDTO.getNickname())));
72 72 return success(pageResulVO);
73 73 }
74 74  
  75 + @PostMapping("/send")
  76 + @Operation(summary = "发送优惠劵")
  77 + @PreAuthorize("@ss.hasPermission('promotion:coupon:send')")
  78 + public CommonResult<Boolean> sendCoupon(@Valid @RequestBody CouponSendReqVO reqVO) {
  79 + couponService.takeCouponByAdmin(reqVO.getTemplateId(), reqVO.getUserIds());
  80 + return success(true);
  81 + }
  82 +
75 83 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java
... ... @@ -75,5 +75,4 @@ public class CouponTemplateController {
75 75 PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(pageVO);
76 76 return success(CouponTemplateConvert.INSTANCE.convertPage(pageResult));
77 77 }
78   -
79 78 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java
... ... @@ -26,7 +26,7 @@ public class CouponBaseVO {
26 26 // ========== 基本信息 BEGIN ==========
27 27 @Schema(description = "优惠劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
28 28 @NotNull(message = "优惠劵模板编号不能为空")
29   - private Integer templateId;
  29 + private Long templateId;
30 30  
31 31 @Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
32 32 @NotNull(message = "优惠劵名不能为空")
... ... @@ -67,8 +67,8 @@ public class CouponBaseVO {
67 67 @InEnum(PromotionProductScopeEnum.class)
68 68 private Integer productScope;
69 69  
70   - @Schema(description = "商品 SPU 编号的数组", example = "1,3")
71   - private List<Long> productSpuIds;
  70 + @Schema(description = "商品范围编号的数组", example = "1,3")
  71 + private List<Long> productScopeValues;
72 72 // ========== 使用规则 END ==========
73 73  
74 74 // ========== 使用效果 BEGIN ==========
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponSendReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.ToString;
  6 +
  7 +import javax.validation.constraints.NotEmpty;
  8 +import javax.validation.constraints.NotNull;
  9 +import java.util.Set;
  10 +
  11 +@Schema(description = "管理后台 - 优惠劵发放 Request VO")
  12 +@Data
  13 +@ToString(callSuper = true)
  14 +public class CouponSendReqVO {
  15 +
  16 + @Schema(description = "优惠劵模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  17 + @NotNull(message = "优惠劵模板编号不能为空")
  18 + private Long templateId;
  19 +
  20 + @Schema(description = "用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
  21 + @NotEmpty(message = "用户编号列表不能为空")
  22 + private Set<Long> userIds;
  23 +
  24 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java
... ... @@ -54,8 +54,8 @@ public class CouponTemplateBaseVO {
54 54 @InEnum(PromotionProductScopeEnum.class)
55 55 private Integer productScope;
56 56  
57   - @Schema(description = "商品 SPU 编号的数组", example = "1,3")
58   - private List<Long> productSpuIds;
  57 + @Schema(description = "商品范围编号的数组", example = "[1, 3]")
  58 + private List<Long> productScopeValues;
59 59  
60 60 @Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
61 61 @NotNull(message = "生效日期类型不能为空")
... ... @@ -95,11 +95,11 @@ public class CouponTemplateBaseVO {
95 95 @Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用
96 96 private Integer discountLimitPrice;
97 97  
98   - @AssertTrue(message = "商品 SPU 编号的数组不能为空")
  98 + @AssertTrue(message = "商品范围编号的数组不能为空")
99 99 @JsonIgnore
100   - public boolean isProductSpuIdsValid() {
  100 + public boolean isProductScopeValuesValid() {
101 101 return Objects.equals(productScope, PromotionProductScopeEnum.ALL.getScope()) // 全部范围时,可以为空
102   - || CollUtil.isNotEmpty(productSpuIds);
  102 + || CollUtil.isNotEmpty(productScopeValues);
103 103 }
104 104  
105 105 @AssertTrue(message = "生效开始时间不能为空")
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java
1 1 package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
4 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
6 8 import lombok.EqualsAndHashCode;
... ... @@ -8,6 +10,7 @@ import lombok.ToString;
8 10 import org.springframework.format.annotation.DateTimeFormat;
9 11  
10 12 import java.time.LocalDateTime;
  13 +import java.util.List;
11 14  
12 15 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
13 16  
... ... @@ -30,4 +33,8 @@ public class CouponTemplatePageReqVO extends PageParam {
30 33 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
31 34 private LocalDateTime[] createTime;
32 35  
  36 + @Schema(description = "可以领取的类型", example = "[1,2, 3]")
  37 + @InEnum(value = CouponTakeTypeEnum.class, message = "可以领取的类型,必须是 {value}")
  38 + private List<Integer> canTakeTypes;
  39 +
33 40 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/vo/template/AppCouponTemplateRespVO.java
... ... @@ -28,8 +28,8 @@ public class AppCouponTemplateRespVO {
28 28 // @InEnum(PromotionProductScopeEnum.class)
29 29 // private Integer productScope;
30 30 //
31   -// @Schema(description = "商品 SPU 编号的数组", example = "1,3")
32   -// private List<Long> productSpuIds;
  31 +// @Schema(description = "商品范围编号的数组", example = "1,3")
  32 +// private List<Long> productScopeValues;
33 33  
34 34 @Schema(description = "生效日期类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
35 35 private Integer validityType;
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java
... ... @@ -4,9 +4,14 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
5 5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
6 6 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
  8 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
  9 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
7 10 import org.mapstruct.Mapper;
8 11 import org.mapstruct.factory.Mappers;
9 12  
  13 +import java.time.LocalDateTime;
  14 +
10 15 /**
11 16 * 优惠劵 Convert
12 17 *
... ... @@ -21,4 +26,27 @@ public interface CouponConvert {
21 26  
22 27 CouponRespDTO convert(CouponDO bean);
23 28  
  29 + default CouponDO convert(CouponTemplateDO template, Long userId) {
  30 + CouponDO couponDO = new CouponDO()
  31 + .setTemplateId(template.getId())
  32 + .setName(template.getName())
  33 + .setTakeType(template.getTakeType())
  34 + .setUsePrice(template.getUsePrice())
  35 + .setProductScope(template.getProductScope())
  36 + .setProductScopeValues(template.getProductScopeValues())
  37 + .setDiscountType(template.getDiscountType())
  38 + .setDiscountPercent(template.getDiscountPercent())
  39 + .setDiscountPrice(template.getDiscountPrice())
  40 + .setDiscountLimitPrice(template.getDiscountLimitPrice())
  41 + .setStatus(CouponStatusEnum.UNUSED.getStatus())
  42 + .setUserId(userId);
  43 + if (CouponTemplateValidityTypeEnum.DATE.getType().equals(template.getValidityType())) {
  44 + couponDO.setValidStartTime(template.getValidStartTime());
  45 + couponDO.setValidEndTime(template.getValidEndTime());
  46 + } else if (CouponTemplateValidityTypeEnum.TERM.getType().equals(template.getValidityType())) {
  47 + couponDO.setValidStartTime(LocalDateTime.now().plusDays(template.getFixedStartTerm()));
  48 + couponDO.setValidEndTime(LocalDateTime.now().plusDays(template.getFixedEndTerm()));
  49 + }
  50 + return couponDO;
  51 + }
24 52 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java
... ... @@ -36,7 +36,7 @@ public class CouponDO extends BaseDO {
36 36 *
37 37 * 关联 {@link CouponTemplateDO#getId()}
38 38 */
39   - private Integer templateId;
  39 + private Long templateId;
40 40 /**
41 41 * 优惠劵名
42 42 *
... ... @@ -89,12 +89,12 @@ public class CouponDO extends BaseDO {
89 89 */
90 90 private Integer productScope;
91 91 /**
92   - * 商品 SPU 编号的数组
  92 + * 商品范围编号的数组
93 93 *
94   - * 冗余 {@link CouponTemplateDO#getProductSpuIds()}
  94 + * 冗余 {@link CouponTemplateDO#getProductScopeValues()}
95 95 */
96 96 @TableField(typeHandler = LongListTypeHandler.class)
97   - private List<Long> productSpuIds;
  97 + private List<Long> productScopeValues;
98 98 // ========== 使用规则 END ==========
99 99  
100 100 // ========== 使用效果 BEGIN ==========
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java
... ... @@ -85,10 +85,10 @@ public class CouponTemplateDO extends BaseDO {
85 85 */
86 86 private Integer productScope;
87 87 /**
88   - * 商品 SPU 编号的数组
  88 + * 商品范围编号的数组
89 89 */
90 90 @TableField(typeHandler = LongListTypeHandler.class)
91   - private List<Long> productSpuIds;
  91 + private List<Long> productScopeValues;
92 92 /**
93 93 * 生效日期类型
94 94 *
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
... ... @@ -55,4 +55,11 @@ public interface CouponMapper extends BaseMapperX&lt;CouponDO&gt; {
55 55 .eq(CouponDO::getStatus, status));
56 56 }
57 57  
  58 + default List<CouponDO> selectListByTemplateIdAndUserId(Long templateId, Collection<Long> userIds) {
  59 + return selectList(new LambdaQueryWrapperX<CouponDO>()
  60 + .eq(CouponDO::getTemplateId, templateId)
  61 + .in(CouponDO::getUserId, userIds)
  62 + );
  63 + }
  64 +
58 65 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
1 1 package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
7 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
  10 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 11 import org.apache.ibatis.annotations.Mapper;
9 12 import org.apache.ibatis.annotations.Param;
10 13  
  14 +import java.time.LocalDateTime;
  15 +import java.util.function.Consumer;
  16 +
11 17 /**
12 18 * 优惠劵模板 Mapper
13 19 *
... ... @@ -17,11 +23,23 @@ import org.apache.ibatis.annotations.Param;
17 23 public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> {
18 24  
19 25 default PageResult<CouponTemplateDO> selectPage(CouponTemplatePageReqVO reqVO) {
  26 + // 构建可领取的查询条件, 好啰嗦 ( ╯-_-)╯┴—┴
  27 + Consumer<LambdaQueryWrapper<CouponTemplateDO>> canTakeConsumer = null;
  28 + if (CollUtil.isNotEmpty(reqVO.getCanTakeTypes())) {
  29 + canTakeConsumer = w ->
  30 + w.eq(CouponTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) // 1. 状态为可用的
  31 + .in(CouponTemplateDO::getTakeType, reqVO.getCanTakeTypes()) // 2. 领取方式一致
  32 + .and(ww -> ww.isNull(CouponTemplateDO::getValidEndTime) // 3. 未过期
  33 + .or().gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()))
  34 + .apply(" take_count < total_count "); // 4. 剩余数量大于 0
  35 + }
  36 + // 执行分页查询
20 37 return selectPage(reqVO, new LambdaQueryWrapperX<CouponTemplateDO>()
21 38 .likeIfPresent(CouponTemplateDO::getName, reqVO.getName())
22 39 .eqIfPresent(CouponTemplateDO::getStatus, reqVO.getStatus())
23 40 .eqIfPresent(CouponTemplateDO::getDiscountType, reqVO.getDiscountType())
24 41 .betweenIfPresent(CouponTemplateDO::getCreateTime, reqVO.getCreateTime())
  42 + .and(canTakeConsumer != null, canTakeConsumer)
25 43 .orderByDesc(CouponTemplateDO::getId));
26 44 }
27 45  
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
1 1 package cn.iocoder.yudao.module.promotion.service.coupon;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
5 6 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  7 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
6 8  
7 9 import java.util.List;
  10 +import java.util.Set;
8 11  
9 12 /**
10 13 * 优惠劵 Service 接口
... ... @@ -52,6 +55,13 @@ public interface CouponService {
52 55 void useCoupon(Long id, Long userId, Long orderId);
53 56  
54 57 /**
  58 + * 退还已使用的优惠券
  59 + *
  60 + * @param id 优惠券编号
  61 + */
  62 + void returnUsedCoupon(Long id);
  63 +
  64 + /**
55 65 * 回收优惠劵
56 66 *
57 67 * @param id 优惠劵编号
... ... @@ -75,4 +85,43 @@ public interface CouponService {
75 85 */
76 86 Long getUnusedCouponCount(Long userId);
77 87  
  88 + /**
  89 + * 领取优惠券
  90 + *
  91 + * @param templateId 优惠券模板编号
  92 + * @param userIds 用户编号列表
  93 + * @param takeType 领取方式
  94 + */
  95 + void takeCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType);
  96 +
  97 + /**
  98 + * 【管理员】给用户发送优惠券
  99 + *
  100 + * @param templateId 优惠券模板编号
  101 + * @param userIds 用户编号列表
  102 + */
  103 + default void takeCouponByAdmin(Long templateId, Set<Long> userIds) {
  104 + takeCoupon(templateId, userIds, CouponTakeTypeEnum.ADMIN);
  105 + }
  106 +
  107 + /**
  108 + * 【会员】领取优惠券
  109 + *
  110 + * @param templateId 优惠券模板编号
  111 + * @param userId 用户编号
  112 + */
  113 + default void takeCouponByUser(Long templateId, Long userId) {
  114 + takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.USER);
  115 + }
  116 +
  117 + /**
  118 + * 【系统】给用户发送新人券
  119 + *
  120 + * @param templateId 优惠券模板编号
  121 + * @param userId 用户编号列表
  122 + */
  123 + default void takeCouponByRegister(Long templateId, Long userId) {
  124 + takeCoupon(templateId, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER);
  125 + }
  126 +
78 127 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
1 1 package cn.iocoder.yudao.module.promotion.service.coupon;
2 2  
  3 +import cn.hutool.core.collection.CollStreamUtil;
3 4 import cn.hutool.core.collection.CollUtil;
  5 +import cn.hutool.core.map.MapUtil;
4 6 import cn.hutool.core.util.ObjectUtil;
5 7 import cn.hutool.core.util.StrUtil;
6 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
... ... @@ -9,9 +11,13 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
9 11 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
10 12 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
11 13 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
  14 +import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
12 15 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  16 +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
13 17 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
14 18 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
  19 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
  20 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
15 21 import org.springframework.stereotype.Service;
16 22 import org.springframework.transaction.annotation.Transactional;
17 23 import org.springframework.validation.annotation.Validated;
... ... @@ -19,9 +25,12 @@ import org.springframework.validation.annotation.Validated;
19 25 import javax.annotation.Resource;
20 26 import java.time.LocalDateTime;
21 27 import java.util.List;
  28 +import java.util.Map;
22 29 import java.util.Set;
  30 +import java.util.stream.Collectors;
23 31  
24 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
25 34 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
26 35 import static java.util.Arrays.asList;
27 36  
... ... @@ -84,6 +93,7 @@ public class CouponServiceImpl implements CouponService {
84 93 public void useCoupon(Long id, Long userId, Long orderId) {
85 94 // 校验优惠劵
86 95 validCoupon(id, userId);
  96 +
87 97 // 更新状态
88 98 int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
89 99 new CouponDO().setStatus(CouponStatusEnum.USED.getStatus())
... ... @@ -94,6 +104,31 @@ public class CouponServiceImpl implements CouponService {
94 104 }
95 105  
96 106 @Override
  107 + public void returnUsedCoupon(Long id) {
  108 + // 校验存在
  109 + CouponDO coupon = couponMapper.selectById(id);
  110 + if (coupon == null) {
  111 + throw exception(COUPON_NOT_EXISTS);
  112 + }
  113 + // 校验状态
  114 + if (ObjectUtil.notEqual(coupon.getTemplateId(), CouponStatusEnum.USED.getStatus())) {
  115 + throw exception(COUPON_STATUS_NOT_USED);
  116 + }
  117 +
  118 + // 退还
  119 + Integer status = LocalDateTimeUtils.beforeNow(coupon.getValidEndTime())
  120 + ? CouponStatusEnum.EXPIRE.getStatus() // 退还时可能已经过期了
  121 + : CouponStatusEnum.UNUSED.getStatus();
  122 + int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
  123 + new CouponDO().setStatus(status));
  124 + if (updateCount == 0) {
  125 + throw exception(COUPON_STATUS_NOT_USED);
  126 + }
  127 +
  128 + // TODO 增加优惠券变动记录?
  129 + }
  130 +
  131 + @Override
97 132 @Transactional
98 133 public void deleteCoupon(Long id) {
99 134 // 校验存在
... ... @@ -125,4 +160,72 @@ public class CouponServiceImpl implements CouponService {
125 160 return couponMapper.selectCountByUserIdAndStatus(userId, CouponStatusEnum.UNUSED.getStatus());
126 161 }
127 162  
  163 + @Override
  164 + public void takeCoupon(Long templateId, Set<Long> userIds, CouponTakeTypeEnum takeType) {
  165 + CouponTemplateDO template = couponTemplateService.getCouponTemplate(templateId);
  166 + // 1. 过滤掉达到领取限制的用户
  167 + removeTakeLimitUser(userIds, template);
  168 + // 2. 校验优惠劵是否可以领取
  169 + validateCouponTemplateCanTake(template, userIds, takeType);
  170 +
  171 + // 3. 批量保存优惠劵
  172 + couponMapper.insertBatch(convertList(userIds, userId -> CouponConvert.INSTANCE.convert(template, userId)));
  173 +
  174 + // 3. 增加优惠劵模板的领取数量
  175 + couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size());
  176 + }
  177 +
  178 + /**
  179 + * 校验优惠券是否可以领取
  180 + *
  181 + * @param couponTemplate 优惠券模板
  182 + * @param userIds 领取人列表
  183 + * @param takeType 领取方式
  184 + */
  185 + private void validateCouponTemplateCanTake(CouponTemplateDO couponTemplate, Set<Long> userIds, CouponTakeTypeEnum takeType) {
  186 + // 如果所有用户都领取过,则抛出异常
  187 + if (CollUtil.isEmpty(userIds)) {
  188 + throw exception(COUPON_TEMPLATE_USER_ALREADY_TAKE);
  189 + }
  190 +
  191 + // 校验模板
  192 + if (couponTemplate == null) {
  193 + throw exception(COUPON_TEMPLATE_NOT_EXISTS);
  194 + }
  195 + // 校验剩余数量
  196 + if (couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
  197 + throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
  198 + }
  199 + // 校验"固定日期"的有效期类型是否过期
  200 + if (CouponTemplateValidityTypeEnum.DATE.getType().equals(couponTemplate.getValidityType())) {
  201 + if (LocalDateTimeUtils.beforeNow(couponTemplate.getValidEndTime())) {
  202 + throw exception(COUPON_TEMPLATE_EXPIRED);
  203 + }
  204 + }
  205 + // 校验领取方式
  206 + if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getValue())) {
  207 + throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
  208 + }
  209 + }
  210 +
  211 + /**
  212 + * 过滤掉达到领取上线的用户
  213 + *
  214 + * @param userIds 用户编号数组
  215 + * @param couponTemplate 优惠劵模版
  216 + */
  217 + private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
  218 + if (couponTemplate.getTakeLimitCount() <= 0) {
  219 + return;
  220 + }
  221 + // 查询已领过券的用户
  222 + List<CouponDO> alreadyTakeCoupons = couponMapper.selectListByTemplateIdAndUserId(couponTemplate.getId(), userIds);
  223 + if (CollUtil.isEmpty(alreadyTakeCoupons)) {
  224 + return;
  225 + }
  226 + // 移除达到领取限制的用户
  227 + Map<Long, Integer> userTakeCountMap = CollStreamUtil.groupBy(alreadyTakeCoupons, CouponDO::getUserId, Collectors.summingInt(c -> 1));
  228 + userIds.removeIf(userId -> MapUtil.getInt(userTakeCountMap, userId, 0) >= couponTemplate.getTakeLimitCount());
  229 + }
  230 +
128 231 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java
... ... @@ -33,7 +33,7 @@ public interface CouponTemplateService {
33 33 /**
34 34 * 更新优惠劵模板的状态
35 35 *
36   - * @param id 编号
  36 + * @param id 编号
37 37 * @param status 状态
38 38 */
39 39 void updateCouponTemplateStatus(Long id, Integer status);
... ... @@ -64,7 +64,7 @@ public interface CouponTemplateService {
64 64 /**
65 65 * 更新优惠劵模板的领取数量
66 66 *
67   - * @param id 优惠劵模板编号
  67 + * @param id 优惠劵模板编号
68 68 * @param incrCount 增加数量
69 69 */
70 70 void updateCouponTemplateTakeCount(Long id, int incrCount);
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java
... ... @@ -14,7 +14,8 @@ import org.springframework.validation.annotation.Validated;
14 14 import javax.annotation.Resource;
15 15  
16 16 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
17   -import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
  17 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_TEMPLATE_NOT_EXISTS;
  18 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL;
18 19  
19 20 /**
20 21 * 优惠劵模板 Service 实现类
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
... ... @@ -33,6 +33,7 @@ public interface ErrorCodeConstants {
33 33 ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1011000022, "交易订单发货失败,拼团未成功");
34 34 ErrorCode ORDER_DELIVERY_FAIL_BARGAIN_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1011000023, "交易订单发货失败,砍价未成功");
35 35 ErrorCode ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS = new ErrorCode(1011000024, "交易订单发货失败,发货类型不是快递");
  36 + ErrorCode ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID = new ErrorCode(1011000025, "交易订单取消失败,订单不是【待支付】状态");
36 37  
37 38 // ========== After Sale 模块 1011000100 ==========
38 39 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.java
... ... @@ -27,6 +27,8 @@ import org.springframework.web.bind.annotation.*;
27 27 import javax.annotation.Resource;
28 28 import javax.annotation.security.PermitAll;
29 29 import javax.validation.Valid;
  30 +import java.time.LocalDateTime;
  31 +import java.util.ArrayList;
30 32 import java.util.List;
31 33 import java.util.Map;
32 34  
... ... @@ -82,6 +84,24 @@ public class TradeAfterSaleController {
82 84 MemberUserRespDTO user = memberUserApi.getUser(afterSale.getUserId());
83 85 // 获取售后日志
84 86 List<TradeAfterSaleLogRespDTO> logs = afterSaleLogService.getLog(afterSale.getId());
  87 + // TODO 方便测试看效果,review 后移除
  88 + if (logs == null) {
  89 + logs = new ArrayList<>();
  90 + }
  91 + for (int i = 1; i <= 6; i++) {
  92 + TradeAfterSaleLogRespDTO respVO = new TradeAfterSaleLogRespDTO();
  93 + respVO.setId((long) i);
  94 + respVO.setUserId((long) i);
  95 + respVO.setUserType(1);
  96 + respVO.setAfterSaleId(id);
  97 + respVO.setOrderId((long) i);
  98 + respVO.setOrderItemId((long) i);
  99 + respVO.setBeforeStatus((i - 1) * 10);
  100 + respVO.setAfterStatus(i * 10);
  101 + respVO.setContent("66+6");
  102 + respVO.setCreateTime(LocalDateTime.now());
  103 + logs.add(respVO);
  104 + }
85 105 return success(TradeAfterSaleConvert.INSTANCE.convert(afterSale, order, orderItems, user, logs));
86 106 }
87 107  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java
... ... @@ -25,7 +25,6 @@ import java.util.Map;
25 25  
26 26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
27 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
28   -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
29 28  
30 29 @Tag(name = "管理后台 - 交易订单")
31 30 @RestController
... ... @@ -70,7 +69,7 @@ public class TradeOrderController {
70 69 TradeOrderDO order = tradeOrderQueryService.getOrder(id);
71 70 // 查询订单项
72 71 List<TradeOrderItemDO> orderItems = tradeOrderQueryService.getOrderItemListByOrderId(id);
73   -
  72 + // orderLog
74 73 // 拼接数据
75 74 MemberUserRespDTO user = memberUserApi.getUser(order.getUserId());
76 75 return success(TradeOrderConvert.INSTANCE.convert(order, orderItems, user));
... ... @@ -82,7 +81,7 @@ public class TradeOrderController {
82 81 @PreAuthorize("@ss.hasPermission('trade:order:query')")
83 82 public CommonResult<List<?>> getOrderExpressTrackList(@RequestParam("id") Long id) {
84 83 return success(TradeOrderConvert.INSTANCE.convertList02(
85   - tradeOrderQueryService.getExpressTrackList(id, getLoginUserId())));
  84 + tradeOrderQueryService.getExpressTrackList(id)));
86 85 }
87 86  
88 87 @PutMapping("/delivery")
... ... @@ -117,11 +116,4 @@ public class TradeOrderController {
117 116 return success(true);
118 117 }
119 118  
120   - // TODO @puhui999 订单物流详情
121   - // TODO @puhui999 【前台】订单取消
122   - // TODO @puhui999 【后台】订单取消
123   - // TODO @puhui999 【前台】订单核销
124   - // TODO @puhui999 【前台】订单删除
125   - // TODO @puhui999 【后台】订单统计
126   -
127 119 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderDetailRespVO.java
... ... @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.Prod
5 5 import io.swagger.v3.oas.annotations.media.Schema;
6 6 import lombok.Data;
7 7  
  8 +import java.time.LocalDateTime;
8 9 import java.util.List;
9 10  
10 11 @Schema(description = "管理后台 - 交易订单的详情 Response VO")
... ... @@ -24,6 +25,27 @@ public class TradeOrderDetailRespVO extends TradeOrderBaseVO {
24 25 */
25 26 private MemberUserRespVO user;
26 27  
  28 + /**
  29 + * TODO 订单操作日志, 先模拟一波;返回 logs,简洁,然后复数哈
  30 + */
  31 + private List<OrderLog> orderLog;
  32 +
  33 + // TODO @puhui999:swagger 注解
  34 + @Data
  35 + public static class OrderLog {
  36 +
  37 + /**
  38 + * 内容
  39 + */
  40 + private String content;
  41 +
  42 + /**
  43 + * 创建时间
  44 + */
  45 + private LocalDateTime createTime;
  46 +
  47 + }
  48 +
27 49 @Schema(description = "管理后台 - 交易订单的详情的订单项目")
28 50 @Data
29 51 public static class Item extends TradeOrderItemBaseVO {
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/vo/TradeOrderPageReqVO.java
... ... @@ -61,5 +61,5 @@ public class TradeOrderPageReqVO extends PageParam {
61 61 @Schema(description = "订单来源", example = "10")
62 62 @InEnum(value = TerminalEnum.class, message = "订单来源 {value}")
63 63 private Integer terminal;
64   -
  64 +// TODO 添加配送方式筛选
65 65 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/delivery/AppDeliverPickUpStoreController.java
... ... @@ -24,6 +24,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
24 24 @Validated
25 25 public class AppDeliverPickUpStoreController {
26 26  
  27 + // TODO 待实现[门店自提]:如果 latitude、longitude 非空,计算经纬度,并排序。计算的库,可以使用 hutool 的 DistanceUtil 计算。
27 28 @GetMapping("/list")
28 29 @Operation(summary = "获得自提门店列表")
29 30 public CommonResult<List<AppDeliveryPickUpStoreRespVO>> getDeliveryPickUpStoreList(
... ... @@ -50,6 +51,7 @@ public class AppDeliverPickUpStoreController {
50 51 return success(list);
51 52 }
52 53  
  54 + // TODO 待实现[门店自提]:
53 55 @GetMapping("/get")
54 56 @Operation(summary = "获得自提门店")
55 57 @Parameter(name = "id", description = "门店编号")
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
... ... @@ -50,9 +50,6 @@ public class AppTradeOrderController {
50 50 private DeliveryExpressService deliveryExpressService;
51 51  
52 52 @Resource
53   - private ProductPropertyValueApi productPropertyValueApi;
54   -
55   - @Resource
56 53 private TradeOrderProperties tradeOrderProperties;
57 54  
58 55 @GetMapping("/settlement")
... ... @@ -78,6 +75,7 @@ public class AppTradeOrderController {
78 75 return success(true);
79 76 }
80 77  
  78 + // TODO @芋艿:如果拼团活动、秒杀活动、砍价活动时,是不是要额外在返回活动之类的信息;
81 79 @GetMapping("/get-detail")
82 80 @Operation(summary = "获得交易订单")
83 81 @Parameter(name = "id", description = "交易订单编号")
... ... @@ -93,6 +91,7 @@ public class AppTradeOrderController {
93 91 // 查询物流公司
94 92 DeliveryExpressDO express = order.getLogisticsId() != null && order.getLogisticsId() > 0 ?
95 93 deliveryExpressService.getDeliveryExpress(order.getLogisticsId()) : null;
  94 + // TODO @puhui999:如果门店自提,信息的拼接;
96 95 // 最终组合
97 96 return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express));
98 97 }
... ... @@ -141,7 +140,7 @@ public class AppTradeOrderController {
141 140 @PutMapping("/receive")
142 141 @Operation(summary = "确认交易订单收货")
143 142 @Parameter(name = "id", description = "交易订单编号")
144   - public CommonResult<Boolean> takeOrder(@RequestParam("id") Long id) {
  143 + public CommonResult<Boolean> receiveOrder(@RequestParam("id") Long id) {
145 144 tradeOrderUpdateService.receiveOrder(getLoginUserId(), id);
146 145 return success(true);
147 146 }
... ... @@ -150,7 +149,7 @@ public class AppTradeOrderController {
150 149 @Operation(summary = "取消交易订单")
151 150 @Parameter(name = "id", description = "交易订单编号")
152 151 public CommonResult<Boolean> cancelOrder(@RequestParam("id") Long id) {
153   - // TODO @芋艿:未实现,mock 用
  152 + tradeOrderUpdateService.cancelOrder(getLoginUserId(), id);
154 153 return success(true);
155 154 }
156 155  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/TradeAfterSaleConvert.java
... ... @@ -72,16 +72,18 @@ public interface TradeAfterSaleConvert {
72 72  
73 73 default TradeAfterSaleDetailRespVO convert(TradeAfterSaleDO afterSale, TradeOrderDO order, List<TradeOrderItemDO> orderItems,
74 74 MemberUserRespDTO user, List<TradeAfterSaleLogRespDTO> logs) {
75   - TradeAfterSaleDetailRespVO respVO = convert(afterSale, orderItems, convertList1(logs));
  75 + TradeAfterSaleDetailRespVO respVO = convert(afterSale, orderItems);
76 76 // 处理用户信息
77 77 respVO.setUser(convert(user));
78 78 // 处理订单信息
79 79 respVO.setOrder(convert(order));
  80 + // 处理售后日志
  81 + respVO.setAfterSaleLog(convertList1(logs));
80 82 return respVO;
81 83 }
82 84 List<TradeAfterSaleLogRespVO> convertList1(List<TradeAfterSaleLogRespDTO> list);
83 85 @Mapping(target = "id", source = "afterSale.id")
84   - TradeAfterSaleDetailRespVO convert(TradeAfterSaleDO afterSale, List<TradeOrderItemDO> orderItems, List<TradeAfterSaleLogRespVO> logs);
  86 + TradeAfterSaleDetailRespVO convert(TradeAfterSaleDO afterSale, List<TradeOrderItemDO> orderItems);
85 87 TradeOrderBaseVO convert(TradeOrderDO order);
86 88  
87 89 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
1 1 package cn.iocoder.yudao.module.trade.convert.order;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4 3 import cn.hutool.core.util.StrUtil;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
... ... @@ -37,8 +36,10 @@ import org.mapstruct.Mapping;
37 36 import org.mapstruct.Mappings;
38 37 import org.mapstruct.factory.Mappers;
39 38  
40   -import java.util.*;
41   -import java.util.stream.Collectors;
  39 +import java.time.LocalDateTime;
  40 +import java.util.ArrayList;
  41 +import java.util.List;
  42 +import java.util.Map;
42 43  
43 44 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
44 45 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
... ... @@ -86,7 +87,14 @@ public interface TradeOrderConvert {
86 87 default ProductSkuUpdateStockReqDTO convert(List<TradeOrderItemDO> list) {
87 88 return new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(list));
88 89 }
  90 +
  91 + default ProductSkuUpdateStockReqDTO convertNegative(List<TradeOrderItemDO> list) {
  92 + List<ProductSkuUpdateStockReqDTO.Item> items = TradeOrderConvert.INSTANCE.convertList(list);
  93 + items.forEach(item -> item.setIncrCount(-item.getIncrCount()));
  94 + return new ProductSkuUpdateStockReqDTO(items);
  95 + }
89 96 List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
  97 +
90 98 @Mappings({
91 99 @Mapping(source = "skuId", target = "id"),
92 100 @Mapping(source = "count", target = "incrCount"),
... ... @@ -137,6 +145,15 @@ public interface TradeOrderConvert {
137 145 orderVO.setReceiverAreaName(AreaUtils.format(order.getReceiverAreaId()));
138 146 // 处理用户信息
139 147 orderVO.setUser(convert(user));
  148 + // TODO puhui999:模拟订单操作日志
  149 + ArrayList<TradeOrderDetailRespVO.OrderLog> orderLogs = new ArrayList<>();
  150 + for (int i = 0; i < 6; i++) {
  151 + TradeOrderDetailRespVO.OrderLog orderLog = new TradeOrderDetailRespVO.OrderLog();
  152 + orderLog.setContent("订单操作" + i);
  153 + orderLog.setCreateTime(LocalDateTime.now());
  154 + orderLogs.add(orderLog);
  155 + }
  156 + orderVO.setOrderLog(orderLogs);
140 157 return orderVO;
141 158 }
142 159  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java
... ... @@ -143,6 +143,8 @@ public class TradeOrderItemDO extends BaseDO {
143 143 * 对应 taobao 的 trade.point_fee 字段
144 144 */
145 145 private Integer pointPrice;
  146 + // TODO @芋艿:如果商品 vip 折扣时,到底是新增一个 vipPrice 记录优惠记录,还是 vipDiscountPrice,记录 vip 的优惠;还是直接使用 vipPrice;
  147 + // 目前 crmeb 的选择,单独一个 vipPrice 记录优惠价格;感觉不一定合理,可以在看看有赞的;
146 148  
147 149 // ========== 售后基本信息 ==========
148 150  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java
... ... @@ -98,6 +98,8 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
98 98 return afterSale;
99 99 }
100 100  
  101 + // TODO 芋艿:拼团失败,要不要发起售后的方式退款?还是走取消逻辑?
  102 +
101 103 @Override
102 104 @Transactional(rollbackFor = Exception.class)
103 105 public Long createAfterSale(Long userId, AppTradeAfterSaleCreateReqVO createReqVO) {
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java
... ... @@ -66,7 +66,7 @@ public interface TradeOrderQueryService {
66 66 Long getOrderCount(Long userId, Integer status, Boolean commonStatus);
67 67  
68 68 /**
69   - * 获得订单的物流轨迹
  69 + * 【前台】获得订单的物流轨迹
70 70 *
71 71 * @param id 订单编号
72 72 * @param userId 用户编号
... ... @@ -74,6 +74,14 @@ public interface TradeOrderQueryService {
74 74 */
75 75 List<ExpressTrackRespDTO> getExpressTrackList(Long id, Long userId);
76 76  
  77 + /**
  78 + * 【后台】获得订单的物流轨迹
  79 + *
  80 + * @param id 订单编号
  81 + * @return 物流轨迹数组
  82 + */
  83 + List<ExpressTrackRespDTO> getExpressTrackList(Long id);
  84 +
77 85 // =================== Order Item ===================
78 86  
79 87 /**
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java
... ... @@ -106,6 +106,27 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
106 106 throw exception(ORDER_NOT_FOUND);
107 107 }
108 108  
  109 + return getExpressTrackList(order);
  110 + }
  111 +
  112 + @Override
  113 + public List<ExpressTrackRespDTO> getExpressTrackList(Long id) {
  114 + // 查询订单
  115 + TradeOrderDO order = tradeOrderMapper.selectById(id);
  116 + if (order == null) {
  117 + throw exception(ORDER_NOT_FOUND);
  118 + }
  119 +
  120 + return getExpressTrackList(order);
  121 + }
  122 +
  123 + /**
  124 + * 获得订单的物流轨迹
  125 + *
  126 + * @param order 订单
  127 + * @return 物流轨迹
  128 + */
  129 + private List<ExpressTrackRespDTO> getExpressTrackList(TradeOrderDO order) {
109 130 // 查询物流公司
110 131 if (order.getLogisticsId() == null) {
111 132 return Collections.emptyList();
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
... ... @@ -117,4 +117,11 @@ public interface TradeOrderUpdateService {
117 117 */
118 118 Long createOrderItemComment(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO);
119 119  
  120 + /**
  121 + * 【会员】取消订单
  122 + *
  123 + * @param userId 用户ID
  124 + * @param id 订单编号
  125 + */
  126 + void cancelOrder(Long userId, Long id);
120 127 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
... ... @@ -96,6 +96,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
96 96 private TradePriceService tradePriceService;
97 97 @Resource
98 98 private DeliveryExpressService deliveryExpressService;
  99 + @Resource
  100 + private TradeMessageService tradeMessageService;
99 101  
100 102 @Resource
101 103 private ProductSkuApi productSkuApi;
... ... @@ -105,26 +107,21 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
105 107 private AddressApi addressApi;
106 108 @Resource
107 109 private CouponApi couponApi;
108   -
  110 + @Resource
  111 + private CombinationRecordApi combinationRecordApi;
  112 + @Resource
  113 + private BargainRecordApi bargainRecordApi;
109 114 @Resource
110 115 private MemberUserApi memberUserApi;
111 116 @Resource
112 117 private MemberLevelApi memberLevelApi;
113 118 @Resource
114 119 private MemberPointApi memberPointApi;
115   -
116 120 @Resource
117 121 private ProductCommentApi productCommentApi;
118   - @Resource
119   - private TradeMessageService tradeMessageService;
120   - @Resource
121   - private TradeOrderProperties tradeOrderProperties;
122   -
123   - @Resource
124   - private CombinationRecordApi combinationRecordApi;
125 122  
126 123 @Resource
127   - private BargainRecordApi bargainRecordApi;
  124 + private TradeOrderProperties tradeOrderProperties;
128 125  
129 126 // =================== Order ===================
130 127  
... ... @@ -179,15 +176,17 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
179 176 @Override
180 177 @Transactional(rollbackFor = Exception.class)
181 178 public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) {
182   - // 2. 价格计算
  179 + // 1. 价格计算
183 180 TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
184   - // 3.1 插入 TradeOrderDO 订单
  181 +
  182 + // 2.1 插入 TradeOrderDO 订单
185 183 TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO);
186   - // 3.2 插入 TradeOrderItemDO 订单项
  184 + // 2.2 插入 TradeOrderItemDO 订单项
187 185 List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO);
188   - // 订单创建完后的逻辑
  186 +
  187 + // 3. 订单创建完后的逻辑
189 188 afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO);
190   - // 3.3 校验订单类型
  189 + // 3.1 拼团的特殊逻辑
191 190 // TODO @puhui999:这个逻辑,先抽个小方法;未来要通过设计模式,把这些拼团之类的逻辑,抽象出去
192 191 // 拼团
193 192 if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
... ... @@ -205,15 +204,19 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
205 204  
206 205 combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, orderItemDO, createReqVO, user));
207 206 }
  207 + // 3.2 秒杀的特殊逻辑
208 208 // TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除
209 209 if (Objects.equals(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) {
210 210  
211 211 }
  212 + // 3.3 砍价的特殊逻辑
212 213  
213 214 // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
214 215 return order;
215 216 }
216 217  
  218 + // TODO @puhui999:订单超时,自动取消;
  219 +
217 220 /**
218 221 * 校验收件地址是否存在
219 222 *
... ... @@ -239,7 +242,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
239 242 }
240 243 TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
241 244 order.setType(validateActivity(createReqVO));
242   - order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;
  245 + order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @puhui999: 参考支付订单,的 no 生成哈;
243 246 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
244 247 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
245 248 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
... ... @@ -251,6 +254,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
251 254 // 退款信息
252 255 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
253 256 tradeOrderMapper.insert(order);
  257 + // TODO @puhui999:如果是门店订单,则需要生成核销码;
254 258 return order;
255 259 }
256 260  
... ... @@ -291,7 +295,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
291 295 TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems,
292 296 TradePriceCalculateRespBO calculateRespBO) {
293 297 // 下单时扣减商品库存
294   - productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
  298 + // TODO @puhui999:扣库存,需要前置;
  299 + // 1)如果是秒杀商品:额外扣减秒杀的库存;
  300 + // 2)如果是拼团活动:额外扣减拼团的库存;
  301 + // 3)如果是砍价活动:额外扣减砍价的库存;
  302 + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems));
295 303  
296 304 // 删除购物车商品
297 305 Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
... ... @@ -299,9 +307,10 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
299 307 cartService.deleteCart(userId, cartIds);
300 308 }
301 309  
302   - // 扣减积分 TODO 芋艿:待实现
  310 + // 扣减积分 TODO 芋艿:待实现,需要前置;
  311 + // 这个是不是应该放到支付成功之后?如果支付后的话,可能积分可以重复使用哈。资源类,都要预扣
303 312  
304   - // 有使用优惠券时更新
  313 + // 有使用优惠券时更新 TODO 芋艿:需要前置;
305 314 if (createReqVO.getCouponId() != null) {
306 315 couponApi.useCoupon(new CouponUseReqDTO().setId(createReqVO.getCouponId()).setUserId(userId)
307 316 .setOrderId(tradeOrderDO.getId()));
... ... @@ -510,6 +519,10 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
510 519 // TODO 芋艿:lili 发送订单变化的消息
511 520  
512 521 // TODO 芋艿:lili 发送商品被购买完成的数据
  522 +
  523 + // TODO 芋艿:销售佣金的记录;
  524 +
  525 + // TODO 芋艿:获得积分;
513 526 }
514 527  
515 528 @Override
... ... @@ -529,11 +542,13 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
529 542 if (order.getPayStatus()) {
530 543 throw exception(ORDER_UPDATE_PRICE_FAIL_PAID);
531 544 }
  545 + // TODO @puhui999:如果改价,需要校验下是否真的变化;
532 546  
533 547 // 更新
534 548 // TODO @puhui999:TradeOrderItemDO 需要做 adjustPrice 的分摊;另外,支付订单那的价格,需要 update 下;
535 549 TradeOrderDO update = TradeOrderConvert.INSTANCE.convert(reqVO);
536 550 update.setPayPrice(update.getPayPrice() + update.getAdjustPrice());
  551 + // TODO @芋艿:改价时,赠送的积分,要不要做改动???
537 552 tradeOrderMapper.updateById(update);
538 553 }
539 554  
... ... @@ -651,10 +666,46 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
651 666 List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
652 667 if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) {
653 668 tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE));
  669 + // TODO 待实现:已完成评价,要不要写一条订单日志?目前 crmeb 会写,有赞可以研究下
654 670 }
655 671 return comment;
656 672 }
657 673  
  674 + @Override
  675 + public void cancelOrder(Long userId, Long id) {
  676 + // 校验存在
  677 + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
  678 + if (order == null) {
  679 + throw exception(ORDER_NOT_FOUND);
  680 + }
  681 + // 校验状态
  682 + if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
  683 + throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
  684 + }
  685 +
  686 + // 1.更新 TradeOrderDO 状态为已取消
  687 + int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
  688 + new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
  689 + .setCancelTime(LocalDateTime.now())
  690 + .setCancelType(TradeOrderCancelTypeEnum.MEMBER_CANCEL.getType()));
  691 + if (updateCount == 0) {
  692 + throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
  693 + }
  694 +
  695 + // 2.回滚库存
  696 + List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
  697 + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
  698 +
  699 + // 3.回滚优惠券
  700 + couponApi.returnUsedCoupon(order.getCouponId());
  701 +
  702 + // 4.回滚积分:积分是支付成功后才增加的吧? 回复:每个项目不同,目前看下来,确认收货貌似更合适,我再看看其它项目的业务选择;
  703 +
  704 + // TODO 芋艿:OrderLog
  705 +
  706 + // TODO 芋艿:lili 发送订单变化的消息
  707 + }
  708 +
658 709 /**
659 710 * 判断指定订单的所有订单项,是不是都售后成功
660 711 *
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java
... ... @@ -41,6 +41,7 @@ public class TradePriceServiceImpl implements TradePriceService {
41 41 @Resource
42 42 private List<TradePriceCalculator> priceCalculators;
43 43  
  44 + // TODO @疯狂:需要搞个 TradePriceCalculator,计算赠送积分;
44 45 @Override
45 46 public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) {
46 47 // 1.1 获得商品 SKU 数组
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java
... ... @@ -102,7 +102,10 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
102 102 Predicate<TradePriceCalculateRespBO.OrderItem> matchPredicate = TradePriceCalculateRespBO.OrderItem::getSelected;
103 103 if (PromotionProductScopeEnum.SPU.getScope().equals(coupon.getProductScope())) {
104 104 matchPredicate = matchPredicate // 额外加如下条件
105   - .and(orderItem -> coupon.getProductSpuIds().contains(orderItem.getSpuId()));
  105 + .and(orderItem -> coupon.getProductScopeValues().contains(orderItem.getSpuId()));
  106 + } else if (PromotionProductScopeEnum.CATEGORY.getScope().equals(coupon.getProductScope())) {
  107 + matchPredicate = matchPredicate // 额外加如下条件
  108 + .and(orderItem -> coupon.getProductScopeValues().contains(orderItem.getCategoryId()));
106 109 }
107 110 return filterList(result.getItems(), matchPredicate);
108 111 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
... ... @@ -42,6 +42,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
42 42  
43 43 @Override
44 44 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
  45 + // TODO @芋艿:如果门店自提,需要校验是否开启;
45 46 // 1.1 判断配送方式
46 47 if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) {
47 48 return;
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java
... ... @@ -65,7 +65,7 @@ public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest {
65 65  
66 66 // mock 方法(优惠劵 Coupon 信息)
67 67 CouponRespDTO coupon = randomPojo(CouponRespDTO.class, o -> o.setId(1024L).setName("程序员节")
68   - .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L))
  68 + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
69 69 .setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType())
70 70 .setDiscountPercent(50).setDiscountLimitPrice(70));
71 71 when(couponApi.validateCoupon(eq(new CouponValidReqDTO().setId(1024L).setUserId(233L)))).thenReturn(coupon);
... ...