Commit fb45f2253339f4e844801fc7bbcf3774c1e24c60

Authored by LAPTOP-CNV4CMCJ\cheng
2 parents ea5e6b2c 05614cc0

Merge branch 'feature/1.8.0-uniapp' of https://gitee.com/zhijiantianya/ruoyi-vue…

…-pro into feature/1.8.0-uniapp
Showing 110 changed files with 4078 additions and 1007 deletions
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java
1 1 package cn.iocoder.yudao.module.product.controller.admin.sku;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuOptionRespVO;
  7 +import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
  8 +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
  9 +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
  10 +import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
  11 +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
3 12 import io.swagger.annotations.Api;
  13 +import io.swagger.annotations.ApiOperation;
4 14 import org.springframework.validation.annotation.Validated;
  15 +import org.springframework.web.bind.annotation.GetMapping;
5 16 import org.springframework.web.bind.annotation.RequestMapping;
6 17 import org.springframework.web.bind.annotation.RestController;
7 18  
  19 +import javax.annotation.Resource;
  20 +import java.util.Collections;
  21 +import java.util.List;
  22 +import java.util.Map;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  26 +
8 27 @Api(tags = "管理后台 - 商品 sku")
9 28 @RestController
10 29 @RequestMapping("/product/sku")
11 30 @Validated
12 31 public class ProductSkuController {
13 32  
  33 + @Resource
  34 + private ProductSkuService productSkuService;
  35 + @Resource
  36 + private ProductSpuService productSpuService;
  37 +
  38 + @GetMapping("/get-option-list")
  39 + @ApiOperation("获得商品 SKU 选项的列表")
  40 +// @PreAuthorize("@ss.hasPermission('product:sku:query')")
  41 + public CommonResult<List<ProductSkuOptionRespVO>> getSkuOptionList() {
  42 + // 获得 SKU 列表
  43 + List<ProductSkuDO> skus = productSkuService.getSkuList();
  44 + if (CollUtil.isEmpty(skus)) {
  45 + return success(Collections.emptyList());
  46 + }
  47 +
  48 + // 获得对应的 SPU 映射
  49 + Map<Long, ProductSpuDO> spuMap = productSpuService.getSpuMap(convertSet(skus, ProductSkuDO::getSpuId));
  50 + // 转换为返回结果
  51 + List<ProductSkuOptionRespVO> skuVOs = ProductSkuConvert.INSTANCE.convertList05(skus);
  52 + skuVOs.forEach(sku -> MapUtils.findAndThen(spuMap, sku.getSpuId(),
  53 + spu -> sku.setSpuId(spu.getId()).setSpuName(spu.getName())));
  54 + return success(skuVOs);
  55 + }
  56 +
14 57 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuOptionRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.product.controller.admin.sku.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +@ApiModel(value = "管理后台 - 商品 SKU 选项 Response VO", description = "用于前端 SELECT 选项")
  8 +@Data
  9 +public class ProductSkuOptionRespVO {
  10 +
  11 + @ApiModelProperty(value = "主键", required = true, example = "1024")
  12 + private Long id;
  13 +
  14 + @ApiModelProperty(value = "商品 SKU 名字", example = "红色")
  15 + private String name;
  16 +
  17 + @ApiModelProperty(value = "销售价格", required = true, example = "100", notes = "单位:分")
  18 + private String price;
  19 +
  20 + @ApiModelProperty(value = "库存", required = true, example = "100")
  21 + private Integer stock;
  22 +
  23 + // ========== 商品 SPU 信息 ==========
  24 +
  25 + @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
  26 + private Long spuId;
  27 +
  28 + @ApiModelProperty(value = "商品 SPU 名字", required = true, example = "iPhone 11")
  29 + private String spuName;
  30 +
  31 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuRespVO.java
... ... @@ -9,7 +9,7 @@ import lombok.ToString;
9 9 import java.util.Date;
10 10 import java.util.List;
11 11  
12   -@ApiModel("管理后台 - 商品sku Response VO")
  12 +@ApiModel("管理后台 - 商品 SKU Response VO")
13 13 @Data
14 14 @EqualsAndHashCode(callSuper = true)
15 15 @ToString(callSuper = true)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java
... ... @@ -53,6 +53,7 @@ public class ProductSpuController {
53 53 return success(true);
54 54 }
55 55  
  56 + // TODO 芋艿:修改接口
56 57 @GetMapping("/get/detail")
57 58 @ApiOperation("获得商品 SPU")
58 59 @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
... ... @@ -79,6 +80,14 @@ public class ProductSpuController {
79 80 return success(ProductSpuConvert.INSTANCE.convertList(list));
80 81 }
81 82  
  83 + @GetMapping("/get-simple-list")
  84 + @ApiOperation("获得商品 SPU 精简列表")
  85 + @PreAuthorize("@ss.hasPermission('product:spu:query')")
  86 + public CommonResult<List<ProductSpuSimpleRespVO>> getSpuSimpleList() {
  87 + List<ProductSpuDO> list = spuService.getSpuList();
  88 + return success(ProductSpuConvert.INSTANCE.convertList02(list));
  89 + }
  90 +
82 91 @GetMapping("/page")
83 92 @ApiOperation("获得商品 SPU 分页")
84 93 @PreAuthorize("@ss.hasPermission('product:spu:query')")
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuSimpleRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.product.controller.admin.spu.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +@ApiModel("管理后台 - 商品 SPU 精简 Response VO")
  10 +@Data
  11 +@EqualsAndHashCode(callSuper = true)
  12 +@ToString(callSuper = true)
  13 +public class ProductSpuSimpleRespVO extends ProductSpuBaseVO {
  14 +
  15 + @ApiModelProperty(value = "主键", required = true, example = "1")
  16 + private Long id;
  17 +
  18 + @ApiModelProperty(value = "商品名称", required = true, example = "芋道")
  19 + private String name;
  20 +
  21 + @ApiModelProperty(value = " 最小价格,单位使用:分", required = true, example = "1024")
  22 + private Integer minPrice;
  23 +
  24 + @ApiModelProperty(value = "最大价格,单位使用:分", required = true, example = "1024")
  25 + private Integer maxPrice;
  26 +
  27 +}
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.product.convert.sku;
2 2  
3 3 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
4 4 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
  5 +import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuOptionRespVO;
5 6 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
6 7 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuDetailRespVO;
7 8 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
... ... @@ -11,7 +12,7 @@ import org.mapstruct.factory.Mappers;
11 12 import java.util.List;
12 13  
13 14 /**
14   - * 商品sku Convert
  15 + * 商品 SKU Convert
15 16 *
16 17 * @author 芋道源码
17 18 */
... ... @@ -36,4 +37,6 @@ public interface ProductSkuConvert {
36 37  
37 38 List<ProductSkuRespDTO> convertList04(List<ProductSkuDO> list);
38 39  
  40 + List<ProductSkuOptionRespVO> convertList05(List<ProductSkuDO> skus);
  41 +
39 42 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
... ... @@ -36,5 +36,7 @@ public interface ProductSpuConvert {
36 36 AppSpuPageRespVO convertAppResp(ProductSpuDO list);
37 37  
38 38 List<SpuInfoRespDTO> convertList2(List<ProductSpuDO> list);
39   -
  39 +
  40 + List<ProductSpuSimpleRespVO> convertList02(List<ProductSpuDO> list);
  41 +
40 42 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java
... ... @@ -97,6 +97,7 @@ public class ProductSkuDO extends BaseDO {
97 97 * 商品属性
98 98 */
99 99 @Data
  100 + @NoArgsConstructor
100 101 @AllArgsConstructor
101 102 public static class Property {
102 103  
... ... @@ -130,19 +131,5 @@ public class ProductSkuDO extends BaseDO {
130 131  
131 132 }
132 133  
133   - // TODO ========== 待定字段:yv =========
134   - // TODO brokerage:一级返佣
135   - // TODO brokerage_two:二级返佣
136   - // TODO pink_price:拼团价
137   - // TODO pink_stock:拼团库存
138   - // TODO seckill_price:秒杀价
139   - // TODO seckill_stock:秒杀库存
140   - // TODO integral:需要积分
141   -
142   - // TODO ========== 待定字段:cf =========
143   - // TODO type 活动显示排序 0=默认 1=秒 2=砍价 3=拼团
144   - // TODO quota 活动限购数量
145   - // TODO quota_show 活动限购数量显示
146   -
147 134 }
148 135  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuService.java
... ... @@ -14,22 +14,29 @@ import java.util.List;
14 14 public interface ProductSkuService {
15 15  
16 16 /**
17   - * 删除商品sku
  17 + * 删除商品 SKU
18 18 *
19 19 * @param id 编号
20 20 */
21 21 void deleteSku(Long id);
22 22  
23 23 /**
24   - * 获得商品sku
  24 + * 获得商品 SKU 信息
25 25 *
26 26 * @param id 编号
27   - * @return 商品sku
  27 + * @return 商品 SKU 信息
28 28 */
29 29 ProductSkuDO getSku(Long id);
30 30  
31 31 /**
32   - * 获得商品sku列表
  32 + * 获得商品 SKU 列表
  33 + *
  34 + * @return 商品sku列表
  35 + */
  36 + List<ProductSkuDO> getSkuList();
  37 +
  38 + /**
  39 + * 获得商品 SKU 列表
33 40 *
34 41 * @param ids 编号
35 42 * @return 商品sku列表
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
... ... @@ -62,6 +62,11 @@ public class ProductSkuServiceImpl implements ProductSkuService {
62 62 }
63 63  
64 64 @Override
  65 + public List<ProductSkuDO> getSkuList() {
  66 + return productSkuMapper.selectList();
  67 + }
  68 +
  69 + @Override
65 70 public List<ProductSkuDO> getSkuList(Collection<Long> ids) {
66 71 return productSkuMapper.selectBatchIds(ids);
67 72 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
... ... @@ -9,6 +9,9 @@ import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
9 9 import javax.validation.Valid;
10 10 import java.util.Collection;
11 11 import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
12 15  
13 16 /**
14 17 * 商品 SPU Service 接口
... ... @@ -64,6 +67,23 @@ public interface ProductSpuService {
64 67 List<ProductSpuDO> getSpuList(Collection<Long> ids);
65 68  
66 69 /**
  70 + * 获得商品 SPU 映射
  71 + *
  72 + * @param ids 编号数组
  73 + * @return 商品 SPU 映射
  74 + */
  75 + default Map<Long, ProductSpuDO> getSpuMap(Collection<Long> ids) {
  76 + return convertMap(getSpuList(ids), ProductSpuDO::getId);
  77 + }
  78 +
  79 + /**
  80 + * 获得所有商品 SPU 列表
  81 + *
  82 + * @return 商品 SPU 列表
  83 + */
  84 + List<ProductSpuDO> getSpuList();
  85 +
  86 + /**
67 87 * 获得商品 SPU 分页
68 88 *
69 89 * @param pageReqVO 分页查询
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
... ... @@ -177,6 +177,11 @@ public class ProductSpuServiceImpl implements ProductSpuService {
177 177 }
178 178  
179 179 @Override
  180 + public List<ProductSpuDO> getSpuList() {
  181 + return productSpuMapper.selectList();
  182 + }
  183 +
  184 + @Override
180 185 public PageResult<ProductSpuRespVO> getSpuPage(ProductSpuPageReqVO pageReqVO) {
181 186 // 库存告警的 SPU 编号的集合
182 187 Set<Long> alarmStockSpuIds = null;
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateRespDTO.java
... ... @@ -48,10 +48,18 @@ public class PriceCalculateRespDTO {
48 48 * 商品原价(总),单位:分
49 49 *
50 50 * 基于 {@link OrderItem#getOriginalPrice()} 求和
  51 + *
51 52 * 对应 taobao 的 trade.total_fee 字段
52 53 */
53 54 private Integer originalPrice;
54 55 /**
  56 + * 订单原价(总),单位:分
  57 + *
  58 + * 基于 {@link OrderItem#getPayPrice()} 求和
  59 + * 和 {@link #originalPrice} 的差异:去除商品级优惠
  60 + */
  61 + private Integer orderPrice;
  62 + /**
55 63 * 订单优惠(总),单位:分
56 64 *
57 65 * 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。
... ... @@ -82,6 +90,7 @@ public class PriceCalculateRespDTO {
82 90 * - {@link #couponPrice}
83 91 * - {@link #pointPrice}
84 92 * + {@link #deliveryPrice}
  93 + * - {@link #discountPrice}
85 94 */
86 95 private Integer payPrice;
87 96 /**
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
... ... @@ -3,14 +3,19 @@ package cn.iocoder.yudao.module.promotion.enums;
3 3 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 4  
5 5 /**
6   - * market 错误码枚举类
7   - * <p>
  6 + * promotion 错误码枚举类
  7 + *
8 8 * market 系统,使用 1-003-000-000 段
9 9 */
10 10 public interface ErrorCodeConstants {
11 11  
12   - // ========== 促销活动相关 1003001000 ============ TODO 芋艿:看看是不是要删除掉
13   - ErrorCode ACTIVITY_NOT_EXISTS = new ErrorCode(1003001000, "促销活动不存在");
  12 + // ========== 促销活动相关 1003001000 ============
  13 + ErrorCode DISCOUNT_ACTIVITY_NOT_EXISTS = new ErrorCode(1003001000, "限时折扣活动不存在");
  14 + ErrorCode DISCOUNT_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1003006001, "存在商品参加了其它限时折扣活动");
  15 + ErrorCode DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1003006002, "限时折扣活动已关闭,不能修改");
  16 + ErrorCode DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1003006003, "限时折扣活动未关闭,不能删除");
  17 + ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1003006004, "限时折扣活动已关闭,不能重复关闭");
  18 + ErrorCode DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1003006004, "限时折扣活动已结束,不能关闭");
14 19  
15 20 // ========== Banner 相关 1003002000 ============
16 21 ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1003002000, "Banner 不存在");
... ... @@ -23,4 +28,19 @@ public interface ErrorCodeConstants {
23 28 ErrorCode COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1003004000, "优惠劵模板不存在");
24 29 ErrorCode COUPON_TEMPLATE_TOTAL_COUNT_TOO_SMALL = new ErrorCode(1003004001, "发放数量不能小于已领取数量({})");
25 30  
  31 + // ========== 优惠劵模板 1003005000 ==========
  32 + ErrorCode COUPON_NOT_EXISTS = new ErrorCode(1003005000, "优惠劵不存在");
  33 + ErrorCode COUPON_DELETE_FAIL_USED = new ErrorCode(1003005001, "回收优惠劵失败,优惠劵已被使用");
  34 +
  35 + // ========== 满减送活动 1003006000 ==========
  36 + ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1003006000, "满减送活动不存在");
  37 + ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1003006001, "存在商品参加了其它满减送活动");
  38 + ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1003006002, "满减送活动已关闭,不能修改");
  39 + ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1003006003, "满减送活动未关闭,不能删除");
  40 + ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1003006004, "满减送活动已关闭,不能重复关闭");
  41 + ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1003006004, "满减送活动已结束,不能关闭");
  42 +
  43 + // ========== Price 相关 1003007000 ============
  44 + ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1003007000, "支付价格计算异常,原因:价格小于等于 0");
  45 +
26 46 }
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.enums.coupon;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
  6 +
  7 +import java.util.Arrays;
  8 +
  9 +/**
  10 + * 优惠劵状态枚举
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@AllArgsConstructor
  15 +@Getter
  16 +public enum CouponStatusEnum implements IntArrayValuable {
  17 +
  18 + UNUSED(1, "未使用"),
  19 + USED(2, "已使用"),
  20 + EXPIRE(3, "已过期"),
  21 + ;
  22 +
  23 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CouponStatusEnum::getStatus).toArray();
  24 +
  25 + /**
  26 + * 值
  27 + */
  28 + private final Integer status;
  29 + /**
  30 + * 名字
  31 + */
  32 + private final String name;
  33 +
  34 + @Override
  35 + public int[] array() {
  36 + return ARRAYS;
  37 + }
  38 +
  39 +}
... ...
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java
1 1 package cn.iocoder.yudao.module.promotion.enums.coupon;
2 2  
3 3 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Getter;
4 6  
5 7 import java.util.Arrays;
6 8  
... ... @@ -9,6 +11,8 @@ import java.util.Arrays;
9 11 *
10 12 * @author 芋道源码
11 13 */
  14 +@AllArgsConstructor
  15 +@Getter
12 16 public enum CouponTakeTypeEnum implements IntArrayValuable {
13 17  
14 18 BY_USER(1, "直接领取"), // 用户可在首页、每日领劵直接领取
... ... @@ -26,19 +30,6 @@ public enum CouponTakeTypeEnum implements IntArrayValuable {
26 30 */
27 31 private final String name;
28 32  
29   - CouponTakeTypeEnum(Integer value, String name) {
30   - this.value = value;
31   - this.name = name;
32   - }
33   -
34   - public Integer getValue() {
35   - return value;
36   - }
37   -
38   - public String getName() {
39   - return name;
40   - }
41   -
42 33 @Override
43 34 public int[] array() {
44 35 return ARRAYS;
... ...
yudao-module-mall/yudao-module-promotion-biz/pom.xml
... ... @@ -29,6 +29,11 @@
29 29 <artifactId>yudao-module-product-api</artifactId>
30 30 <version>${revision}</version>
31 31 </dependency>
  32 + <dependency>
  33 + <groupId>cn.iocoder.boot</groupId>
  34 + <artifactId>yudao-module-member-api</artifactId>
  35 + <version>${revision}</version>
  36 + </dependency>
32 37  
33 38 <!-- 业务组件 -->
34 39 <dependency>
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
  8 +import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
  9 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
  10 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
  11 +import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
  12 +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  13 +import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
  14 +import io.swagger.annotations.Api;
  15 +import io.swagger.annotations.ApiImplicitParam;
  16 +import io.swagger.annotations.ApiOperation;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.validation.annotation.Validated;
  19 +import org.springframework.web.bind.annotation.*;
  20 +
  21 +import javax.annotation.Resource;
  22 +import javax.validation.Valid;
  23 +import java.util.Map;
  24 +import java.util.Set;
  25 +
  26 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  28 +
  29 +@Api(tags = "管理后台 - 优惠劵")
  30 +@RestController
  31 +@RequestMapping("/promotion/coupon")
  32 +@Validated
  33 +public class CouponController {
  34 +
  35 + @Resource
  36 + private CouponService couponService;
  37 + @Resource
  38 + private MemberUserApi memberUserApi;
  39 +
  40 +// @GetMapping("/get")
  41 +// @ApiOperation("获得优惠劵")
  42 +// @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  43 +// @PreAuthorize("@ss.hasPermission('promotion:coupon:query')")
  44 +// public CommonResult<CouponRespVO> getCoupon(@RequestParam("id") Long id) {
  45 +// CouponDO coupon = couponService.getCoupon(id);
  46 +// return success(CouponConvert.INSTANCE.convert(coupon));
  47 +// }
  48 +
  49 + @DeleteMapping("/delete")
  50 + @ApiOperation("回收优惠劵")
  51 + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
  52 + @PreAuthorize("@ss.hasPermission('promotion:coupon:delete')")
  53 + public CommonResult<Boolean> deleteCoupon(@RequestParam("id") Long id) {
  54 + couponService.deleteCoupon(id);
  55 + return success(true);
  56 + }
  57 +
  58 + @GetMapping("/page")
  59 + @ApiOperation("获得优惠劵分页")
  60 + @PreAuthorize("@ss.hasPermission('promotion:coupon:query')")
  61 + public CommonResult<PageResult<CouponPageItemRespVO>> getCouponPage(@Valid CouponPageReqVO pageVO) {
  62 + PageResult<CouponDO> pageResult = couponService.getCouponPage(pageVO);
  63 + PageResult<CouponPageItemRespVO> pageResulVO = CouponConvert.INSTANCE.convertPage(pageResult);
  64 + if (CollUtil.isEmpty(pageResulVO.getList())) {
  65 + return success(pageResulVO);
  66 + }
  67 + // 读取用户信息,进行拼接
  68 + Set<Long> userIds = convertSet(pageResult.getList(), CouponDO::getUserId);
  69 + Map<Long, UserRespDTO> userMap = memberUserApi.getUserMap(userIds);
  70 + pageResulVO.getList().forEach(itemRespVO -> MapUtils.findAndThen(userMap, itemRespVO.getUserId(),
  71 + userRespDTO -> itemRespVO.setNickname(userRespDTO.getNickname())));
  72 + return success(pageResulVO);
  73 + }
  74 +
  75 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponTemplateController.java
... ... @@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.coupon;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateCreateReqVO;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplatePageReqVO;
7   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateRespVO;
8   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateUpdateReqVO;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.*;
9 6 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;
10 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
11 8 import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
... ... @@ -45,6 +42,14 @@ public class CouponTemplateController {
45 42 return success(true);
46 43 }
47 44  
  45 + @PutMapping("/update-status")
  46 + @ApiOperation("更新优惠劵模板状态")
  47 + @PreAuthorize("@ss.hasPermission('promotion:coupon-template:update')")
  48 + public CommonResult<Boolean> updateCouponTemplateStatus(@Valid @RequestBody CouponTemplateUpdateStatusReqVO reqVO) {
  49 + couponTemplateService.updateCouponTemplateStatus(reqVO.getId(), reqVO.getStatus());
  50 + return success(true);
  51 + }
  52 +
48 53 @DeleteMapping("/delete")
49 54 @ApiOperation("删除优惠劵模板")
50 55 @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponBaseVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  6 +import com.fasterxml.jackson.annotation.JsonFormat;
  7 +import io.swagger.annotations.ApiModelProperty;
  8 +import lombok.Data;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import javax.validation.constraints.Min;
  12 +import javax.validation.constraints.NotNull;
  13 +import java.util.Date;
  14 +import java.util.List;
  15 +
  16 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  17 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
  18 +
  19 +/**
  20 +* 优惠劵 Base VO,提供给添加、修改、详细的子 VO 使用
  21 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  22 +*/
  23 +@Data
  24 +public class CouponBaseVO {
  25 +
  26 + // ========== 基本信息 BEGIN ==========
  27 + @ApiModelProperty(value = "优惠劵模板编号", required = true, example = "1024")
  28 + @NotNull(message = "优惠劵模板编号不能为空")
  29 + private Integer templateId;
  30 +
  31 + @ApiModelProperty(value = "优惠劵名", required = true, example = "春节送送送")
  32 + @NotNull(message = "优惠劵名不能为空")
  33 + private String name;
  34 +
  35 + @ApiModelProperty(value = "优惠码状态", required = true, example = "1", notes = "参见 CouponStatusEnum 枚举")
  36 + private Integer status;
  37 +
  38 + // ========== 基本信息 END ==========
  39 +
  40 + // ========== 领取情况 BEGIN ==========
  41 + @ApiModelProperty(value = "用户编号", required = true, example = "1")
  42 + @NotNull(message = "用户编号不能为空")
  43 + private Long userId;
  44 +
  45 + @ApiModelProperty(value = "领取方式", required = true, example = "1", notes = "参见 CouponTakeTypeEnum 枚举类")
  46 + @NotNull(message = "领取方式不能为空")
  47 + private Integer takeType;
  48 + // ========== 领取情况 END ==========
  49 +
  50 + // ========== 使用规则 BEGIN ==========
  51 + @ApiModelProperty(value = "是否设置满多少金额可用", required = true, example = "100", notes = "单位:分;0 - 不限制")
  52 + @NotNull(message = "是否设置满多少金额可用不能为空")
  53 + private Integer usePrice;
  54 +
  55 + @ApiModelProperty(value = "固定日期 - 生效开始时间")
  56 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  57 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  58 + private Date validStartTime;
  59 +
  60 + @ApiModelProperty(value = "固定日期 - 生效结束时间")
  61 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  62 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  63 + private Date validEndTime;
  64 +
  65 + @ApiModelProperty(value = "商品范围", required = true, example = "1", notes = "参见 PromotionProductScopeEnum 枚举类")
  66 + @NotNull(message = "商品范围不能为空")
  67 + @InEnum(PromotionProductScopeEnum.class)
  68 + private Integer productScope;
  69 +
  70 + @ApiModelProperty(value = "商品 SPU 编号的数组", example = "1,3")
  71 + private List<Long> productSpuIds;
  72 + // ========== 使用规则 END ==========
  73 +
  74 + // ========== 使用效果 BEGIN ==========
  75 + @ApiModelProperty(value = "优惠类型", required = true, example = "1", notes = "参见 PromotionDiscountTypeEnum 枚举")
  76 + @NotNull(message = "优惠类型不能为空")
  77 + @InEnum(PromotionDiscountTypeEnum.class)
  78 + private Integer discountType;
  79 +
  80 + @ApiModelProperty(value = "折扣百分比", example = "80", notes = "例如说,80% 为 80")
  81 + private Integer discountPercent;
  82 +
  83 + @ApiModelProperty(value = "优惠金额", example = "10", notes = "单位:分")
  84 + @Min(value = 0, message = "优惠金额需要大于等于 0")
  85 + private Integer discountPrice;
  86 +
  87 + @ApiModelProperty(value = "折扣上限", example = "100", notes = "单位:分,仅在 discountType 为 PERCENT 使用")
  88 + private Integer discountLimitPrice;
  89 + // ========== 使用效果 END ==========
  90 +
  91 + // ========== 使用情况 BEGIN ==========
  92 +
  93 + @ApiModelProperty(value = "使用订单号", example = "4096")
  94 + private Long useOrderId;
  95 +
  96 + @ApiModelProperty(value = "使用时间")
  97 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  98 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  99 + private Date useTime;
  100 +
  101 + // ========== 使用情况 END ==========
  102 +
  103 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageItemRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +@ApiModel("管理后台 - 优惠劵分页的每一项 Response VO")
  10 +@Data
  11 +@EqualsAndHashCode(callSuper = true)
  12 +@ToString(callSuper = true)
  13 +public class CouponPageItemRespVO extends CouponRespVO {
  14 +
  15 + @ApiModelProperty(value = "用户昵称", example = "老芋艿")
  16 + private String nickname;
  17 +
  18 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponPageReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import java.util.Date;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  14 +
  15 +@ApiModel("管理后台 - 优惠劵分页 Request VO")
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +public class CouponPageReqVO extends PageParam {
  20 +
  21 + @ApiModelProperty(value = "优惠劵模板编号", example = "2048")
  22 + private Long templateId;
  23 +
  24 + @ApiModelProperty(value = "优惠码状态", example = "1", notes = "参见 CouponStatusEnum 枚举")
  25 + private Integer status;
  26 +
  27 + @ApiModelProperty(value = "创建时间")
  28 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  29 + private Date[] createTime;
  30 +
  31 + @ApiModelProperty(value = "用户昵称", example = "芋艿", notes = "模糊匹配")
  32 + private String nickname;
  33 +
  34 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/coupon/CouponRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.util.Date;
  10 +
  11 +@ApiModel("管理后台 - 优惠劵 Response VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class CouponRespVO extends CouponBaseVO {
  16 +
  17 + @ApiModelProperty(value = "优惠劵编号", required = true, example = "1024")
  18 + private Long id;
  19 +
  20 + @ApiModelProperty(value = "创建时间", required = true)
  21 + private Date createTime;
  22 +
  23 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/CouponTemplateBaseVO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java
1   -package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo;
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ObjectUtil;
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.validation.InEnum;
6 6 import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
7 7 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
8 8 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
  9 +import com.fasterxml.jackson.annotation.JsonFormat;
9 10 import com.fasterxml.jackson.annotation.JsonIgnore;
10 11 import io.swagger.annotations.ApiModelProperty;
11 12 import lombok.Data;
... ... @@ -19,6 +20,7 @@ import java.util.List;
19 20 import java.util.Objects;
20 21  
21 22 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  23 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
22 24  
23 25 /**
24 26 * 优惠劵模板 Base VO,提供给添加、修改、详细的子 VO 使用
... ... @@ -62,10 +64,12 @@ public class CouponTemplateBaseVO {
62 64  
63 65 @ApiModelProperty(value = "固定日期 - 生效开始时间")
64 66 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  67 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
65 68 private Date validStartTime;
66 69  
67 70 @ApiModelProperty(value = "固定日期 - 生效结束时间")
68 71 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  72 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
69 73 private Date validEndTime;
70 74  
71 75 @ApiModelProperty(value = "领取日期 - 开始天数")
... ... @@ -82,15 +86,14 @@ public class CouponTemplateBaseVO {
82 86 private Integer discountType;
83 87  
84 88 @ApiModelProperty(value = "折扣百分比", example = "80", notes = "例如说,80% 为 80")
85   - @Min(value = 1)
86 89 private Integer discountPercent;
87 90  
88 91 @ApiModelProperty(value = "优惠金额", example = "10", notes = "单位:分")
89   - @Min(value = 1)
  92 + @Min(value = 0, message = "优惠金额需要大于等于 0")
90 93 private Integer discountPrice;
91 94  
92 95 @ApiModelProperty(value = "折扣上限", example = "100", notes = "单位:分,仅在 discountType 为 PERCENT 使用")
93   - private Integer discountPriceLimit;
  96 + private Integer discountLimitPrice;
94 97  
95 98 @AssertTrue(message = "商品 SPU 编号的数组不能为空")
96 99 @JsonIgnore
... ... @@ -127,11 +130,11 @@ public class CouponTemplateBaseVO {
127 130 || fixedEndTerm != null;
128 131 }
129 132  
130   - @AssertTrue(message = "折扣百分比不能为空")
  133 + @AssertTrue(message = "折扣百分比需要大于等于 1,小于等于 99")
131 134 @JsonIgnore
132 135 public boolean isDiscountPercentValid() {
133 136 return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())
134   - || discountPercent != null;
  137 + || (discountPercent != null && discountPercent >= 1 && discountPercent<= 99);
135 138 }
136 139  
137 140 @AssertTrue(message = "优惠金额不能为空")
... ... @@ -143,9 +146,9 @@ public class CouponTemplateBaseVO {
143 146  
144 147 @AssertTrue(message = "折扣上限不能为空")
145 148 @JsonIgnore
146   - public boolean isDiscountPriceLimit() {
  149 + public boolean isDiscountLimitPriceValid() {
147 150 return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())
148   - || discountPriceLimit != null;
  151 + || discountLimitPrice != null;
149 152 }
150 153  
151 154 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/CouponTemplateCreateReqVO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateCreateReqVO.java
1   -package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo;
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
2 2  
3 3 import io.swagger.annotations.ApiModel;
4 4 import lombok.Data;
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/CouponTemplatePageReqVO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplatePageReqVO.java
1   -package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo;
  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 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/CouponTemplateRespVO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateRespVO.java
1   -package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo;
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/CouponTemplateUpdateReqVO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateReqVO.java
1   -package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo;
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
2 2  
3 3 import io.swagger.annotations.ApiModel;
4 4 import io.swagger.annotations.ApiModelProperty;
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateUpdateStatusReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import io.swagger.annotations.ApiModel;
  6 +import io.swagger.annotations.ApiModelProperty;
  7 +import lombok.Data;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@ApiModel("管理后台 - 优惠劵模板更新状态 Request VO")
  12 +@Data
  13 +public class CouponTemplateUpdateStatusReqVO {
  14 +
  15 + @ApiModelProperty(value = "优惠劵模板编号", required = true, example = "1024")
  16 + @NotNull(message = "优惠劵模板编号不能为空")
  17 + private Long id;
  18 +
  19 + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "见 CommonStatusEnum 枚举")
  20 + @NotNull(message = "状态不能为空")
  21 + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
  22 + private Integer status;
  23 +
  24 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/DiscountActivityController.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.discount;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.*;
  6 +import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
  8 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
  9 +import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
  10 +import io.swagger.annotations.Api;
  11 +import io.swagger.annotations.ApiImplicitParam;
  12 +import io.swagger.annotations.ApiOperation;
  13 +import org.springframework.security.access.prepost.PreAuthorize;
  14 +import org.springframework.validation.annotation.Validated;
  15 +import org.springframework.web.bind.annotation.*;
  16 +
  17 +import javax.annotation.Resource;
  18 +import javax.validation.Valid;
  19 +import java.util.List;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +@Api(tags = "管理后台 - 限时折扣活动")
  24 +@RestController
  25 +@RequestMapping("/promotion/discount-activity")
  26 +@Validated
  27 +public class DiscountActivityController {
  28 +
  29 + @Resource
  30 + private DiscountActivityService discountActivityService;
  31 +
  32 + @PostMapping("/create")
  33 + @ApiOperation("创建限时折扣活动")
  34 + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:create')")
  35 + public CommonResult<Long> createDiscountActivity(@Valid @RequestBody DiscountActivityCreateReqVO createReqVO) {
  36 + return success(discountActivityService.createDiscountActivity(createReqVO));
  37 + }
  38 +
  39 + @PutMapping("/update")
  40 + @ApiOperation("更新限时折扣活动")
  41 + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:update')")
  42 + public CommonResult<Boolean> updateDiscountActivity(@Valid @RequestBody DiscountActivityUpdateReqVO updateReqVO) {
  43 + discountActivityService.updateDiscountActivity(updateReqVO);
  44 + return success(true);
  45 + }
  46 +
  47 + @PutMapping("/close")
  48 + @ApiOperation("关闭限时折扣活动")
  49 + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
  50 + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:close')")
  51 + public CommonResult<Boolean> closeRewardActivity(@RequestParam("id") Long id) {
  52 + discountActivityService.closeRewardActivity(id);
  53 + return success(true);
  54 + }
  55 +
  56 + @DeleteMapping("/delete")
  57 + @ApiOperation("删除限时折扣活动")
  58 + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
  59 + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:delete')")
  60 + public CommonResult<Boolean> deleteDiscountActivity(@RequestParam("id") Long id) {
  61 + discountActivityService.deleteDiscountActivity(id);
  62 + return success(true);
  63 + }
  64 +
  65 + @GetMapping("/get")
  66 + @ApiOperation("获得限时折扣活动")
  67 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  68 + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:query')")
  69 + public CommonResult<DiscountActivityDetailRespVO> getDiscountActivity(@RequestParam("id") Long id) {
  70 + DiscountActivityDO discountActivity = discountActivityService.getDiscountActivity(id);
  71 + if (discountActivity == null) {
  72 + return success(null);
  73 + }
  74 + // 拼接结果
  75 + List<DiscountProductDO> discountProducts = discountActivityService.getDiscountProductsByActivityId(id);
  76 + return success(DiscountActivityConvert.INSTANCE.convert(discountActivity, discountProducts));
  77 + }
  78 +
  79 + @GetMapping("/page")
  80 + @ApiOperation("获得限时折扣活动分页")
  81 + @PreAuthorize("@ss.hasPermission('promotion:discount-activity:query')")
  82 + public CommonResult<PageResult<DiscountActivityRespVO>> getDiscountActivityPage(@Valid DiscountActivityPageReqVO pageVO) {
  83 + PageResult<DiscountActivityDO> pageResult = discountActivityService.getDiscountActivityPage(pageVO);
  84 + return success(DiscountActivityConvert.INSTANCE.convertPage(pageResult));
  85 + }
  86 +
  87 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/package-info.java deleted
1   -/**
2   - * TODO 占位
3   - */
4   -package cn.iocoder.yudao.module.promotion.controller.admin.discount;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityBaseVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;
  2 +
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
  6 +import com.fasterxml.jackson.annotation.JsonIgnore;
  7 +import io.swagger.annotations.ApiModel;
  8 +import io.swagger.annotations.ApiModelProperty;
  9 +import lombok.Data;
  10 +import org.springframework.format.annotation.DateTimeFormat;
  11 +
  12 +import javax.validation.constraints.AssertTrue;
  13 +import javax.validation.constraints.Min;
  14 +import javax.validation.constraints.NotNull;
  15 +import java.util.Date;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  18 +
  19 +/**
  20 +* 限时折扣活动 Base VO,提供给添加、修改、详细的子 VO 使用
  21 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  22 +*/
  23 +@Data
  24 +public class DiscountActivityBaseVO {
  25 +
  26 + @ApiModelProperty(value = "活动标题", required = true, example = "一个标题")
  27 + @NotNull(message = "活动标题不能为空")
  28 + private String name;
  29 +
  30 + @ApiModelProperty(value = "开始时间", required = true)
  31 + @NotNull(message = "开始时间不能为空")
  32 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  33 + private Date startTime;
  34 +
  35 + @ApiModelProperty(value = "结束时间", required = true)
  36 + @NotNull(message = "结束时间不能为空")
  37 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  38 + private Date endTime;
  39 +
  40 + @ApiModelProperty(value = "备注", example = "我是备注")
  41 + private String remark;
  42 +
  43 + @ApiModel("商品")
  44 + @Data
  45 + public static class Product {
  46 +
  47 + @ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
  48 + @NotNull(message = "商品 SPU 编号不能为空")
  49 + private Long spuId;
  50 +
  51 + @ApiModelProperty(value = "商品 SKU 编号", required = true, example = "1")
  52 + @NotNull(message = "商品 SKU 编号不能为空")
  53 + private Long skuId;
  54 +
  55 + @ApiModelProperty(value = "优惠类型", required = true, example = "1", notes = "参见 PromotionDiscountTypeEnum 枚举")
  56 + @NotNull(message = "优惠类型不能为空")
  57 + @InEnum(PromotionDiscountTypeEnum.class)
  58 + private Integer discountType;
  59 +
  60 + @ApiModelProperty(value = "折扣百分比", example = "80", notes = "例如说,80% 为 80")
  61 + private Integer discountPercent;
  62 +
  63 + @ApiModelProperty(value = "优惠金额", example = "10", notes = "单位:分")
  64 + @Min(value = 0, message = "优惠金额需要大于等于 0")
  65 + private Integer discountPrice;
  66 +
  67 + @AssertTrue(message = "折扣百分比需要大于等于 1,小于等于 99")
  68 + @JsonIgnore
  69 + public boolean isDiscountPercentValid() {
  70 + return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType())
  71 + || (discountPercent != null && discountPercent >= 1 && discountPercent<= 99);
  72 + }
  73 +
  74 + @AssertTrue(message = "优惠金额不能为空")
  75 + @JsonIgnore
  76 + public boolean isDiscountPriceValid() {
  77 + return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PRICE.getType())
  78 + || discountPrice != null;
  79 + }
  80 +
  81 + }
  82 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityCreateReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +import javax.validation.Valid;
  9 +import javax.validation.constraints.NotEmpty;
  10 +import java.util.List;
  11 +
  12 +@ApiModel("管理后台 - 限时折扣活动创建 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class DiscountActivityCreateReqVO extends DiscountActivityBaseVO {
  17 +
  18 + /**
  19 + * 商品列表
  20 + */
  21 + @NotEmpty(message = "商品列表不能为空")
  22 + @Valid
  23 + private List<Product> products;
  24 +
  25 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityDetailRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +import java.util.List;
  9 +
  10 +@ApiModel("管理后台 - 限时折扣活动的详细 Response VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +@ToString(callSuper = true)
  14 +public class DiscountActivityDetailRespVO extends DiscountActivityRespVO {
  15 +
  16 + /**
  17 + * 商品列表
  18 + */
  19 + private List<Product> products;
  20 +
  21 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityPageReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.annotations.*;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +
  9 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  10 +
  11 +@ApiModel("管理后台 - 限时折扣活动分页 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class DiscountActivityPageReqVO extends PageParam {
  16 +
  17 + @ApiModelProperty(value = "活动标题", example = "一个标题")
  18 + private String name;
  19 +
  20 + @ApiModelProperty(value = "活动状态", example = "1")
  21 + private Integer status;
  22 +
  23 + @ApiModelProperty(value = "创建时间")
  24 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  25 + private Date[] createTime;
  26 +
  27 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +import java.util.Date;
  11 +
  12 +@ApiModel("管理后台 - 限时折扣活动 Response VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class DiscountActivityRespVO extends DiscountActivityBaseVO {
  17 +
  18 + @ApiModelProperty(value = "活动编号", required = true, example = "1024")
  19 + private Long id;
  20 +
  21 + @ApiModelProperty(value = "活动状态", required = true, example = "1")
  22 + @NotNull(message = "活动状态不能为空")
  23 + private Integer status;
  24 +
  25 + @ApiModelProperty(value = "创建时间", required = true)
  26 + private Date createTime;
  27 +
  28 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityUpdateReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.Valid;
  10 +import javax.validation.constraints.NotEmpty;
  11 +import javax.validation.constraints.NotNull;
  12 +import java.util.List;
  13 +
  14 +@ApiModel("管理后台 - 限时折扣活动更新 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class DiscountActivityUpdateReqVO extends DiscountActivityBaseVO {
  19 +
  20 + @ApiModelProperty(value = "活动编号", required = true, example = "1024")
  21 + @NotNull(message = "活动编号不能为空")
  22 + private Long id;
  23 +
  24 + /**
  25 + * 商品列表
  26 + */
  27 + @NotEmpty(message = "商品列表不能为空")
  28 + @Valid
  29 + private List<DiscountActivityCreateReqVO.Product> products;
  30 +
  31 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/RewardActivityController.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.reward;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityRespVO;
  8 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
  9 +import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
  10 +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  11 +import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
  12 +import io.swagger.annotations.Api;
  13 +import io.swagger.annotations.ApiImplicitParam;
  14 +import io.swagger.annotations.ApiOperation;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.validation.annotation.Validated;
  17 +import org.springframework.web.bind.annotation.*;
  18 +
  19 +import javax.annotation.Resource;
  20 +import javax.validation.Valid;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +
  24 +@Api(tags = "管理后台 - 满减送活动")
  25 +@RestController
  26 +@RequestMapping("/promotion/reward-activity")
  27 +@Validated
  28 +public class RewardActivityController {
  29 +
  30 + @Resource
  31 + private RewardActivityService rewardActivityService;
  32 +
  33 + @PostMapping("/create")
  34 + @ApiOperation("创建满减送活动")
  35 + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:create')")
  36 + public CommonResult<Long> createRewardActivity(@Valid @RequestBody RewardActivityCreateReqVO createReqVO) {
  37 + return success(rewardActivityService.createRewardActivity(createReqVO));
  38 + }
  39 +
  40 + @PutMapping("/update")
  41 + @ApiOperation("更新满减送活动")
  42 + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:update')")
  43 + public CommonResult<Boolean> updateRewardActivity(@Valid @RequestBody RewardActivityUpdateReqVO updateReqVO) {
  44 + rewardActivityService.updateRewardActivity(updateReqVO);
  45 + return success(true);
  46 + }
  47 +
  48 + @PutMapping("/close")
  49 + @ApiOperation("关闭满减送活动")
  50 + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
  51 + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:close')")
  52 + public CommonResult<Boolean> closeRewardActivity(@RequestParam("id") Long id) {
  53 + rewardActivityService.closeRewardActivity(id);
  54 + return success(true);
  55 + }
  56 +
  57 + @DeleteMapping("/delete")
  58 + @ApiOperation("删除满减送活动")
  59 + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
  60 + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:delete')")
  61 + public CommonResult<Boolean> deleteRewardActivity(@RequestParam("id") Long id) {
  62 + rewardActivityService.deleteRewardActivity(id);
  63 + return success(true);
  64 + }
  65 +
  66 + @GetMapping("/get")
  67 + @ApiOperation("获得满减送活动")
  68 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  69 + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:query')")
  70 + public CommonResult<RewardActivityRespVO> getRewardActivity(@RequestParam("id") Long id) {
  71 + RewardActivityDO rewardActivity = rewardActivityService.getRewardActivity(id);
  72 + return success(RewardActivityConvert.INSTANCE.convert(rewardActivity));
  73 + }
  74 +
  75 + @GetMapping("/page")
  76 + @ApiOperation("获得满减送活动分页")
  77 + @PreAuthorize("@ss.hasPermission('promotion:reward-activity:query')")
  78 + public CommonResult<PageResult<RewardActivityRespVO>> getRewardActivityPage(@Valid RewardActivityPageReqVO pageVO) {
  79 + PageResult<RewardActivityDO> pageResult = rewardActivityService.getRewardActivityPage(pageVO);
  80 + return success(RewardActivityConvert.INSTANCE.convertPage(pageResult));
  81 + }
  82 +
  83 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityBaseVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
  6 +import com.fasterxml.jackson.annotation.JsonIgnore;
  7 +import io.swagger.annotations.ApiModel;
  8 +import io.swagger.annotations.ApiModelProperty;
  9 +import lombok.Data;
  10 +import org.springframework.format.annotation.DateTimeFormat;
  11 +
  12 +import javax.validation.Valid;
  13 +import javax.validation.constraints.AssertTrue;
  14 +import javax.validation.constraints.Future;
  15 +import javax.validation.constraints.Min;
  16 +import javax.validation.constraints.NotNull;
  17 +import java.util.Date;
  18 +import java.util.List;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  21 +
  22 +/**
  23 +* 满减送活动 Base VO,提供给添加、修改、详细的子 VO 使用
  24 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  25 +*/
  26 +@Data
  27 +public class RewardActivityBaseVO {
  28 +
  29 + @ApiModelProperty(value = "活动标题", required = true, example = "满啦满啦")
  30 + @NotNull(message = "活动标题不能为空")
  31 + private String name;
  32 +
  33 + @ApiModelProperty(value = "开始时间", required = true)
  34 + @NotNull(message = "开始时间不能为空")
  35 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  36 + private Date startTime;
  37 +
  38 + @ApiModelProperty(value = "结束时间", required = true)
  39 + @NotNull(message = "结束时间不能为空")
  40 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  41 + @Future(message = "结束时间必须大于当前时间")
  42 + private Date endTime;
  43 +
  44 + @ApiModelProperty(value = "备注", example = "biubiubiu")
  45 + private String remark;
  46 +
  47 + @ApiModelProperty(value = "条件类型", required = true, example = "1")
  48 + @NotNull(message = "条件类型不能为空")
  49 + @InEnum(value = PromotionConditionTypeEnum.class, message = "条件类型必须是 {value}")
  50 + private Integer conditionType;
  51 +
  52 + @ApiModelProperty(value = "商品范围", required = true, example = "1")
  53 + @NotNull(message = "商品范围不能为空")
  54 + @InEnum(value = PromotionConditionTypeEnum.class, message = "商品范围必须是 {value}")
  55 + private Integer productScope;
  56 +
  57 + @ApiModelProperty(value = "商品 SPU 编号的数组", example = "1,2,3")
  58 + private List<Long> productSpuIds;
  59 +
  60 + /**
  61 + * 优惠规则的数组
  62 + */
  63 + @Valid // 校验下子对象
  64 + private List<Rule> rules;
  65 +
  66 + @ApiModel("优惠规则")
  67 + @Data
  68 + public static class Rule {
  69 +
  70 + @ApiModelProperty(value = "优惠门槛", required = true, example = "100", notes = "1. 满 N 元,单位:分; 2. 满 N 件")
  71 + @Min(value = 1L, message = "优惠门槛必须大于等于 1")
  72 + private Integer limit;
  73 +
  74 + @ApiModelProperty(value = "优惠价格", required = true, example = "100", notes = "单位:分")
  75 + @Min(value = 1L, message = "优惠价格必须大于等于 1")
  76 + private Integer discountPrice;
  77 +
  78 + @ApiModelProperty(value = "是否包邮", required = true, example = "true")
  79 + private Boolean freeDelivery;
  80 +
  81 + @ApiModelProperty(value = "赠送的积分", required = true, example = "100")
  82 + @Min(value = 1L, message = "赠送的积分必须大于等于 1")
  83 + private Integer point;
  84 +
  85 + @ApiModelProperty(value = "赠送的优惠劵编号的数组", example = "1,2,3")
  86 + private List<Long> couponIds;
  87 +
  88 + @ApiModelProperty(value = "赠送的优惠卷数量的数组", example = "1,2,3")
  89 + private List<Integer> couponCounts;
  90 +
  91 + @AssertTrue(message = "优惠劵和数量必须一一对应")
  92 + @JsonIgnore
  93 + public boolean isCouponCountsValid() {
  94 + return CollUtil.size(couponCounts) == CollUtil.size(couponCounts);
  95 + }
  96 +
  97 + }
  98 +
  99 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityCreateReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;
  2 +
  3 +import lombok.*;
  4 +import io.swagger.annotations.*;
  5 +
  6 +@ApiModel("管理后台 - 满减送活动创建 Request VO")
  7 +@Data
  8 +@EqualsAndHashCode(callSuper = true)
  9 +@ToString(callSuper = true)
  10 +public class RewardActivityCreateReqVO extends RewardActivityBaseVO {
  11 +
  12 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityPageReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;
  2 +
  3 +import lombok.*;
  4 +import io.swagger.annotations.*;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +
  7 +@ApiModel("管理后台 - 满减送活动分页 Request VO")
  8 +@Data
  9 +@EqualsAndHashCode(callSuper = true)
  10 +@ToString(callSuper = true)
  11 +public class RewardActivityPageReqVO extends PageParam {
  12 +
  13 + @ApiModelProperty(value = "活动标题", example = "满啦满啦")
  14 + private String name;
  15 +
  16 + @ApiModelProperty(value = "活动状态", example = "1")
  17 + private Integer status;
  18 +
  19 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.annotations.*;
  6 +
  7 +@ApiModel("管理后台 - 满减送活动 Response VO")
  8 +@Data
  9 +@EqualsAndHashCode(callSuper = true)
  10 +@ToString(callSuper = true)
  11 +public class RewardActivityRespVO extends RewardActivityBaseVO {
  12 +
  13 + @ApiModelProperty(value = "活动编号", required = true, example = "1024")
  14 + private Integer id;
  15 +
  16 + @ApiModelProperty(value = "活动状态", required = true, example = "1")
  17 + private Integer status;
  18 +
  19 + @ApiModelProperty(value = "创建时间", required = true)
  20 + private Date createTime;
  21 +
  22 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/vo/RewardActivityUpdateReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.reward.vo;
  2 +
  3 +import lombok.*;
  4 +import io.swagger.annotations.*;
  5 +import javax.validation.constraints.*;
  6 +
  7 +@ApiModel("管理后台 - 满减送活动更新 Request VO")
  8 +@Data
  9 +@EqualsAndHashCode(callSuper = true)
  10 +@ToString(callSuper = true)
  11 +public class RewardActivityUpdateReqVO extends RewardActivityBaseVO {
  12 +
  13 + @ApiModelProperty(value = "活动编号", required = true, example = "1024")
  14 + @NotNull(message = "活动编号不能为空")
  15 + private Long id;
  16 +
  17 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponConvert.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.convert.coupon;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
  5 +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  6 +import org.mapstruct.Mapper;
  7 +import org.mapstruct.factory.Mappers;
  8 +
  9 +/**
  10 + * 优惠劵 Convert
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Mapper
  15 +public interface CouponConvert {
  16 +
  17 + CouponConvert INSTANCE = Mappers.getMapper(CouponConvert.class);
  18 +
  19 + PageResult<CouponPageItemRespVO> convertPage(PageResult<CouponDO> page);
  20 +
  21 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponTemplateConvert.java
1 1 package cn.iocoder.yudao.module.promotion.convert.coupon;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateCreateReqVO;
5   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateRespVO;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateUpdateReqVO;
  4 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;
7 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
8 8 import org.mapstruct.Mapper;
9 9 import org.mapstruct.factory.Mappers;
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.convert.discount;
  2 +
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.*;
  8 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
  9 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
  10 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
  11 +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO;
  12 +import org.mapstruct.Mapper;
  13 +import org.mapstruct.factory.Mappers;
  14 +
  15 +import java.util.List;
  16 +import java.util.Map;
  17 +
  18 +/**
  19 + * 限时折扣活动 Convert
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +@Mapper
  24 +public interface DiscountActivityConvert {
  25 +
  26 + DiscountActivityConvert INSTANCE = Mappers.getMapper(DiscountActivityConvert.class);
  27 +
  28 + DiscountActivityDO convert(DiscountActivityCreateReqVO bean);
  29 +
  30 + DiscountActivityDO convert(DiscountActivityUpdateReqVO bean);
  31 +
  32 + DiscountActivityRespVO convert(DiscountActivityDO bean);
  33 +
  34 + List<DiscountActivityRespVO> convertList(List<DiscountActivityDO> list);
  35 +
  36 + PageResult<DiscountActivityRespVO> convertPage(PageResult<DiscountActivityDO> page);
  37 +
  38 + DiscountProductDetailBO convert(DiscountProductDO product);
  39 +
  40 + default List<DiscountProductDetailBO> convertList(List<DiscountProductDO> products, Map<Long, DiscountActivityDO> activityMap) {
  41 + return CollectionUtils.convertList(products, product -> {
  42 + DiscountProductDetailBO detail = convert(product);
  43 + MapUtils.findAndThen(activityMap, product.getActivityId(), activity -> {
  44 + detail.setActivityName(activity.getName());
  45 + });
  46 + return detail;
  47 + });
  48 + }
  49 +
  50 + DiscountProductDO convert(DiscountActivityBaseVO.Product bean);
  51 +
  52 + DiscountActivityDetailRespVO convert(DiscountActivityDO activity, List<DiscountProductDO> products);
  53 +
  54 + // =========== 比较是否相等 ==========
  55 + /**
  56 + * 比较两个限时折扣商品是否相等
  57 + *
  58 + * @param productDO 数据库中的商品
  59 + * @param productVO 前端传入的商品
  60 + * @return 是否匹配
  61 + */
  62 + @SuppressWarnings("DuplicatedCode")
  63 + default boolean isEquals(DiscountProductDO productDO, DiscountActivityBaseVO.Product productVO) {
  64 + if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
  65 + || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
  66 + || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) {
  67 + return false;
  68 + }
  69 + if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
  70 + return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice());
  71 + }
  72 + if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) {
  73 + return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent());
  74 + }
  75 + return true;
  76 + }
  77 +
  78 + /**
  79 + * 比较两个限时折扣商品是否相等
  80 + * 注意,比较时忽略 id 编号
  81 + *
  82 + * @param productDO 商品 1
  83 + * @param productVO 商品 2
  84 + * @return 是否匹配
  85 + */
  86 + @SuppressWarnings("DuplicatedCode")
  87 + default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) {
  88 + if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
  89 + || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
  90 + || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) {
  91 + return false;
  92 + }
  93 + if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
  94 + return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice());
  95 + }
  96 + if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) {
  97 + return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent());
  98 + }
  99 + return true;
  100 + }
  101 +
  102 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/package-info.java deleted
1   -/**
2   - * TODO 占位
3   - */
4   -package cn.iocoder.yudao.module.promotion.convert.discount;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/price/PriceConvert.java
... ... @@ -36,7 +36,8 @@ public interface PriceConvert {
36 36 orderItem.setPayPrice(orderItem.getOriginalPrice()).setOrderDividePrice(orderItem.getOriginalPrice());
37 37 priceCalculate.getOrder().getItems().add(orderItem);
38 38 // 补充价格信息到 Order 中
39   - order.setOriginalPrice(order.getOriginalPrice() + orderItem.getOriginalPrice()).setPayPrice(order.getOriginalPrice());
  39 + order.setOriginalPrice(order.getOriginalPrice() + orderItem.getOriginalPrice())
  40 + .setOrderPrice(order.getOriginalPrice()).setPayPrice(order.getOriginalPrice());
40 41 });
41 42 return priceCalculate;
42 43 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/reward/RewardActivityConvert.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.convert.reward;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityRespVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  8 +import org.mapstruct.Mapper;
  9 +import org.mapstruct.factory.Mappers;
  10 +
  11 +/**
  12 + * 满减送活动 Convert
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Mapper
  17 +public interface RewardActivityConvert {
  18 +
  19 + RewardActivityConvert INSTANCE = Mappers.getMapper(RewardActivityConvert.class);
  20 +
  21 + RewardActivityDO convert(RewardActivityCreateReqVO bean);
  22 +
  23 + RewardActivityDO convert(RewardActivityUpdateReqVO bean);
  24 +
  25 + RewardActivityRespVO convert(RewardActivityDO bean);
  26 +
  27 + PageResult<RewardActivityRespVO> convertPage(PageResult<RewardActivityDO> page);
  28 +
  29 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponDO.java
1 1 package cn.iocoder.yudao.module.promotion.dal.dataobject.coupon;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
  5 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
4 6 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  7 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
  8 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
5 9 import com.baomidou.mybatisplus.annotation.KeySequence;
6 10 import com.baomidou.mybatisplus.annotation.TableField;
7 11 import com.baomidou.mybatisplus.annotation.TableName;
8   -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
9 12 import lombok.Data;
10 13 import lombok.EqualsAndHashCode;
11 14  
... ... @@ -14,9 +17,11 @@ import java.util.List;
14 17  
15 18 /**
16 19 * 优惠劵 DO
  20 + *
  21 + * @author 芋道源码
17 22 */
18 23 @TableName(value = "promotion_coupon", autoResultMap = true)
19   -@KeySequence("promotion_coupo_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  24 +@KeySequence("promotion_coupon_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
20 25 @Data
21 26 @EqualsAndHashCode(callSuper = true)
22 27 public class CouponDO extends BaseDO {
... ... @@ -35,21 +40,13 @@ public class CouponDO extends BaseDO {
35 40 /**
36 41 * 优惠劵名
37 42 *
38   - * 冗余自 {@link CouponTemplateDO} 的 title
39   - *
40   - * TODO 芋艿,暂时不考虑冗余的更新
  43 + * 冗余 {@link CouponTemplateDO#getName()}
41 44 */
42   - private String title;
43   -// /**
44   -// * 核销码
45   -// */
46   -// private String verifyCode;
  45 + private String name;
47 46 /**
48 47 * 优惠码状态
49 48 *
50   - * 1-未使用
51   - * 2-已使用
52   - * 3-已失效
  49 + * 枚举 {@link CouponStatusEnum}
53 50 */
54 51 private Integer status;
55 52  
... ... @@ -58,13 +55,14 @@ public class CouponDO extends BaseDO {
58 55 // ========== 领取情况 BEGIN ==========
59 56 /**
60 57 * 用户编号
  58 + *
  59 + * 关联 MemberUserDO 的 id 字段
61 60 */
62   - private Integer userId;
  61 + private Long userId;
63 62 /**
64 63 * 领取类型
65 64 *
66   - * 1 - 用户主动领取
67   - * 2 - 后台自动发放
  65 + * 枚举 {@link CouponTakeTypeEnum}
68 66 */
69 67 private Integer takeType;
70 68 // ========== 领取情况 END ==========
... ... @@ -72,8 +70,10 @@ public class CouponDO extends BaseDO {
72 70 // ========== 使用规则 BEGIN ==========
73 71 /**
74 72 * 是否设置满多少金额可用,单位:分
  73 + *
  74 + * 冗余 {@link CouponTemplateDO#getUsePrice()}
75 75 */
76   - private Integer priceAvailable;
  76 + private Integer usePrice;
77 77 /**
78 78 * 生效开始时间
79 79 */
... ... @@ -90,50 +90,49 @@ public class CouponDO extends BaseDO {
90 90 private Integer productScope;
91 91 /**
92 92 * 商品 SPU 编号的数组
  93 + *
  94 + * 冗余 {@link CouponTemplateDO#getProductSpuIds()}
93 95 */
94   - @TableField(typeHandler = JacksonTypeHandler.class)
95   - private List<Long> spuIds;
  96 + @TableField(typeHandler = LongListTypeHandler.class)
  97 + private List<Long> productSpuIds;
96 98 // ========== 使用规则 END ==========
97 99  
98 100 // ========== 使用效果 BEGIN ==========
99 101 /**
100   - * 优惠类型
  102 + * 折扣类型
101 103 *
102   - * 1-代金卷
103   - * 2-折扣卷
  104 + * 冗余 {@link CouponTemplateDO#getDiscountType()}
104 105 */
105   - private Integer preferentialType;
  106 + private Integer discountType;
106 107 /**
107   - * 折扣
  108 + * 折扣百分比
  109 + *
  110 + * 冗余 {@link CouponTemplateDO#getDiscountPercent()}
108 111 */
109   - private Integer percentOff;
  112 + private Integer discountPercent;
110 113 /**
111   - * 优惠金额,单位:分。
  114 + * 优惠金额,单位:分
  115 + *
  116 + * 冗余 {@link CouponTemplateDO#getDiscountPrice()}
112 117 */
113   - private Integer priceOff;
  118 + private Integer discountPrice;
114 119 /**
115   - * 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
  120 + * 折扣上限,仅在 {@link #discountType} 等于 {@link PromotionDiscountTypeEnum#PERCENT} 时生效
116 121 *
117   - * 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
  122 + * 冗余 {@link CouponTemplateDO#getDiscountLimitPrice()}
118 123 */
119   - private Integer discountPriceLimit;
  124 + private Integer discountLimitPrice;
120 125 // ========== 使用效果 END ==========
121 126  
122 127 // ========== 使用情况 BEGIN ==========
123   -// /**
124   -// * 使用订单号
125   -// */
126   -// private Integer usedOrderId; // TODO 芋艿,暂时不考虑这个字段
127   -// /**
128   -// * 订单中优惠面值,单位:分
129   -// */
130   -// private Integer usedPrice; // TODO 芋艿,暂时不考虑这个字段
  128 + /**
  129 + * 使用订单号
  130 + */
  131 + private Long useOrderId;
131 132 /**
132 133 * 使用时间
133 134 */
134   - private Date usedTime;
135   -
136   - // TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
  135 + private Date useTime;
137 136  
138 137 // ========== 使用情况 END ==========
139 138  
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java
... ... @@ -21,6 +21,8 @@ import java.util.List;
21 21 * 优惠劵模板 DO
22 22 *
23 23 * 当用户领取时,会生成 {@link CouponDO} 优惠劵
  24 + *
  25 + * @author 芋道源码
24 26 */
25 27 @TableName(value = "promotion_coupon_template", autoResultMap = true)
26 28 @KeySequence("promotion_coupon_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountProductDO.java
1 1 package cn.iocoder.yudao.module.promotion.dal.dataobject.discount;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
4 5 import com.baomidou.mybatisplus.annotation.KeySequence;
5 6 import com.baomidou.mybatisplus.annotation.TableId;
6 7 import com.baomidou.mybatisplus.annotation.TableName;
7 8 import lombok.Data;
8 9 import lombok.EqualsAndHashCode;
9 10  
10   -import java.util.Date;
11   -
12 11 /**
13 12 * 限时折扣商品 DO
14 13 *
... ... @@ -32,12 +31,6 @@ public class DiscountProductDO extends BaseDO {
32 31 */
33 32 private Long activityId;
34 33 /**
35   - * 限时折扣活动的名字
36   - *
37   - * 冗余 {@link DiscountActivityDO#getName()}
38   - */
39   - private String activityName;
40   - /**
41 34 * 商品 SPU 编号
42 35 *
43 36 * 关联 ProductSpuDO 的 id 编号
... ... @@ -49,23 +42,24 @@ public class DiscountProductDO extends BaseDO {
49 42 * 关联 ProductSkuDO 的 id 编号
50 43 */
51 44 private Long skuId;
  45 +
52 46 /**
53   - * 开始时间
54   - */
55   - private Date startTime;
56   - /**
57   - * 结束时间
  47 + * 折扣类型
  48 + *
  49 + * 枚举 {@link PromotionDiscountTypeEnum}
58 50 */
59   - private Date endTime;
  51 + private Integer discountType;
60 52 /**
61   - * 销售价格,单位:分
  53 + * 折扣百分比
62 54 *
63   - * 冗余 ProductSkuDO 的 price 字段
  55 + * 例如,80% 为 80
64 56 */
65   - private Integer originalPrice;
  57 + private Integer discountPercent;
66 58 /**
67   - * 优惠价格,单位:分
  59 + * 优惠金额,单位:分
  60 + *
  61 + * 当 {@link #discountType} 为 {@link PromotionDiscountTypeEnum#PRICE} 生效
68 62 */
69   - private Integer promotionPrice;
  63 + private Integer discountPrice;
70 64  
71 65 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/reward/RewardActivityDO.java
1 1 package cn.iocoder.yudao.module.promotion.dal.dataobject.reward;
2 2  
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
3 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
4 6 import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
5 7 import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
6 8 import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
... ... @@ -8,10 +10,11 @@ import com.baomidou.mybatisplus.annotation.KeySequence;
8 10 import com.baomidou.mybatisplus.annotation.TableField;
9 11 import com.baomidou.mybatisplus.annotation.TableId;
10 12 import com.baomidou.mybatisplus.annotation.TableName;
11   -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  13 +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
12 14 import lombok.Data;
13 15 import lombok.EqualsAndHashCode;
14 16  
  17 +import java.io.Serializable;
15 18 import java.util.Date;
16 19 import java.util.List;
17 20  
... ... @@ -68,19 +71,19 @@ public class RewardActivityDO extends BaseDO {
68 71 /**
69 72 * 商品 SPU 编号的数组
70 73 */
71   - @TableField(typeHandler = JacksonTypeHandler.class)
  74 + @TableField(typeHandler = LongListTypeHandler.class)
72 75 private List<Long> productSpuIds;
73 76 /**
74 77 * 优惠规则的数组
75 78 */
76   - @TableField(typeHandler = JacksonTypeHandler.class)
  79 + @TableField(typeHandler = RuleTypeHandler.class)
77 80 private List<Rule> rules;
78 81  
79 82 /**
80 83 * 优惠规则
81 84 */
82 85 @Data
83   - public static class Rule {
  86 + public static class Rule implements Serializable {
84 87  
85 88 /**
86 89 * 优惠门槛
... ... @@ -112,5 +115,19 @@ public class RewardActivityDO extends BaseDO {
112 115  
113 116 }
114 117  
  118 + // TODO @芋艿:可以找一些新的思路
  119 + public static class RuleTypeHandler extends AbstractJsonTypeHandler<List<Rule>> {
  120 +
  121 + @Override
  122 + protected List<Rule> parse(String json) {
  123 + return JsonUtils.parseArray(json, Rule.class);
  124 + }
  125 +
  126 + @Override
  127 + protected String toJson(List<Rule> obj) {
  128 + return JsonUtils.toJsonString(obj);
  129 + }
  130 +
  131 + }
115 132  
116 133 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.Collection;
  12 +
  13 +/**
  14 + * 优惠劵 Mapper
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Mapper
  19 +public interface CouponMapper extends BaseMapperX<CouponDO> {
  20 +
  21 + default PageResult<CouponDO> selectPage(CouponPageReqVO reqVO, Collection<Long> userIds) {
  22 + return selectPage(reqVO, new LambdaQueryWrapperX<CouponDO>()
  23 + .eqIfPresent(CouponDO::getTemplateId, reqVO.getTemplateId())
  24 + .eqIfPresent(CouponDO::getStatus, reqVO.getStatus())
  25 + .inIfPresent(CouponDO::getUserId, userIds)
  26 + .betweenIfPresent(CouponDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(CouponDO::getId));
  28 + }
  29 +
  30 + default int delete(Long id, Collection<Integer> whereStatuses) {
  31 + return update(null, new LambdaUpdateWrapper<CouponDO>()
  32 + .eq(CouponDO::getId, id).in(CouponDO::getStatus, whereStatuses)
  33 + .set(CouponDO::getDeleted, 1));
  34 + }
  35 +
  36 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
... ... @@ -3,9 +3,10 @@ package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
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.coupon.vo.CouponTemplatePageReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
7 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
8 8 import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Param;
9 10  
10 11 /**
11 12 * 优惠劵模板 Mapper
... ... @@ -24,4 +25,6 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; {
24 25 .orderByDesc(CouponTemplateDO::getId));
25 26 }
26 27  
  28 + void updateTakeCount(@Param("id") Long id, @Param("incrCount") Integer incrCount);
  29 +
27 30 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountActivityMapper.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.dal.mysql.discount;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +import java.util.Set;
  13 +
  14 +/**
  15 + * 限时折扣活动 Mapper
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +@Mapper
  20 +public interface DiscountActivityMapper extends BaseMapperX<DiscountActivityDO> {
  21 +
  22 + default PageResult<DiscountActivityDO> selectPage(DiscountActivityPageReqVO reqVO) {
  23 + return selectPage(reqVO, new LambdaQueryWrapperX<DiscountActivityDO>()
  24 + .likeIfPresent(DiscountActivityDO::getName, reqVO.getName())
  25 + .eqIfPresent(DiscountActivityDO::getStatus, reqVO.getStatus())
  26 + .betweenIfPresent(DiscountActivityDO::getCreateTime, reqVO.getCreateTime())
  27 + .orderByDesc(DiscountActivityDO::getId));
  28 + }
  29 +
  30 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.dal.mysql.discount;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +import java.util.Collection;
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * 限时折扣商城 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface DiscountProductMapper extends BaseMapperX<DiscountProductDO> {
  17 +
  18 + default List<DiscountProductDO> selectListBySkuId(Collection<Long> skuIds) {
  19 + return selectList(DiscountProductDO::getSkuId, skuIds);
  20 + }
  21 +
  22 + default List<DiscountProductDO> selectListByActivityId(Long activityId) {
  23 + return selectList(DiscountProductDO::getActivityId, activityId);
  24 + }
  25 +
  26 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/package-info.java deleted
1   -/**
2   - * TODO 占位
3   - */
4   -package cn.iocoder.yudao.module.promotion.dal.mysql.discount;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.dal.mysql.reward;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * 满减送活动 Mapper
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Mapper
  19 +public interface RewardActivityMapper extends BaseMapperX<RewardActivityDO> {
  20 +
  21 + default PageResult<RewardActivityDO> selectPage(RewardActivityPageReqVO reqVO) {
  22 + return selectPage(reqVO, new LambdaQueryWrapperX<RewardActivityDO>()
  23 + .likeIfPresent(RewardActivityDO::getName, reqVO.getName())
  24 + .eqIfPresent(RewardActivityDO::getStatus, reqVO.getStatus())
  25 + .orderByDesc(RewardActivityDO::getId));
  26 + }
  27 +
  28 + default List<RewardActivityDO> selectListByStatus(Collection<Integer> statuses) {
  29 + return selectList(RewardActivityDO::getStatus, statuses);
  30 + }
  31 +
  32 + default List<RewardActivityDO> selectListByProductScopeAndStatus(Integer productScope, Integer status) {
  33 + return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
  34 + .eq(RewardActivityDO::getProductScope, productScope)
  35 + .eq(RewardActivityDO::getStatus, status));
  36 + }
  37 +
  38 +}
... ...
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.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
3 5 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
4 6  
5 7 /**
... ... @@ -21,4 +23,19 @@ public interface CouponService {
21 23 */
22 24 CouponDO validCoupon(Long id, Long userId);
23 25  
  26 + /**
  27 + * 获得优惠劵分页
  28 + *
  29 + * @param pageReqVO 分页查询
  30 + * @return 优惠劵分页
  31 + */
  32 + PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO);
  33 +
  34 + /**
  35 + * 回收优惠劵
  36 + *
  37 + * @param id 优惠劵编号
  38 + */
  39 + void deleteCoupon(Long id);
  40 +
24 41 }
... ...
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.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  7 +import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
  8 +import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
  9 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
3 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
  11 +import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
  12 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
4 13 import org.springframework.stereotype.Service;
  14 +import org.springframework.transaction.annotation.Transactional;
5 15 import org.springframework.validation.annotation.Validated;
6 16  
  17 +import javax.annotation.Resource;
  18 +import java.util.Set;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  21 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_DELETE_FAIL_USED;
  22 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NOT_EXISTS;
  23 +import static java.util.Arrays.asList;
  24 +
7 25 /**
8 26 * 优惠劵 Service 实现类
9 27 *
... ... @@ -13,9 +31,56 @@ import org.springframework.validation.annotation.Validated;
13 31 @Validated
14 32 public class CouponServiceImpl implements CouponService {
15 33  
  34 + @Resource
  35 + private CouponTemplateService couponTemplateService;
  36 +
  37 + @Resource
  38 + private CouponMapper couponMapper;
  39 +
  40 + @Resource
  41 + private MemberUserApi memberUserApi;
  42 +
16 43 // TODO 芋艿:待实现
17 44 @Override
18 45 public CouponDO validCoupon(Long id, Long userId) {
19 46 return null;
20 47 }
  48 +
  49 + @Override
  50 + public PageResult<CouponDO> getCouponPage(CouponPageReqVO pageReqVO) {
  51 + // 获得用户编号
  52 + Set<Long> userIds = null;
  53 + if (StrUtil.isNotEmpty(pageReqVO.getNickname())) {
  54 + userIds = CollectionUtils.convertSet(memberUserApi.getUserListByNickname(pageReqVO.getNickname()),
  55 + UserRespDTO::getId);
  56 + if (CollUtil.isEmpty(userIds)) {
  57 + return PageResult.empty();
  58 + }
  59 + }
  60 + // 分页查询
  61 + return couponMapper.selectPage(pageReqVO, userIds);
  62 + }
  63 +
  64 + @Override
  65 + @Transactional
  66 + public void deleteCoupon(Long id) {
  67 + // 校验存在
  68 + validateCouponExists(id);
  69 +
  70 + // 更新优惠劵
  71 + int deleteCount = couponMapper.delete(id,
  72 + asList(CouponStatusEnum.UNUSED.getStatus(), CouponStatusEnum.EXPIRE.getStatus()));
  73 + if (deleteCount == 0) {
  74 + throw exception(COUPON_DELETE_FAIL_USED);
  75 + }
  76 + // 减少优惠劵模板的领取数量 -1
  77 + couponTemplateService.updateCouponTemplateTakeCount(id, -1);
  78 + }
  79 +
  80 + private void validateCouponExists(Long id) {
  81 + if (couponMapper.selectById(id) == null) {
  82 + throw exception(COUPON_NOT_EXISTS);
  83 + }
  84 + }
  85 +
21 86 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java
1 1 package cn.iocoder.yudao.module.promotion.service.coupon;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateCreateReqVO;
5   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplatePageReqVO;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateUpdateReqVO;
  4 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;
7 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
8 8  
9 9 import javax.validation.Valid;
... ... @@ -31,6 +31,14 @@ public interface CouponTemplateService {
31 31 void updateCouponTemplate(@Valid CouponTemplateUpdateReqVO updateReqVO);
32 32  
33 33 /**
  34 + * 更新优惠劵模板的状态
  35 + *
  36 + * @param id 编号
  37 + * @param status 状态
  38 + */
  39 + void updateCouponTemplateStatus(Long id, Integer status);
  40 +
  41 + /**
34 42 * 删除优惠劵模板
35 43 *
36 44 * @param id 编号
... ... @@ -53,4 +61,12 @@ public interface CouponTemplateService {
53 61 */
54 62 PageResult<CouponTemplateDO> getCouponTemplatePage(CouponTemplatePageReqVO pageReqVO);
55 63  
  64 + /**
  65 + * 更新优惠劵模板的领取数量
  66 + *
  67 + * @param id 优惠劵模板编号
  68 + * @param incrCount 增加数量
  69 + */
  70 + void updateCouponTemplateTakeCount(Long id, int incrCount);
  71 +
56 72 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java
1 1 package cn.iocoder.yudao.module.promotion.service.coupon;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateCreateReqVO;
5   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplatePageReqVO;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateUpdateReqVO;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;
7 8 import cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;
8 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
9 10 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper;
... ... @@ -30,7 +31,8 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
30 31 @Override
31 32 public Long createCouponTemplate(CouponTemplateCreateReqVO createReqVO) {
32 33 // 插入
33   - CouponTemplateDO couponTemplate = CouponTemplateConvert.INSTANCE.convert(createReqVO);
  34 + CouponTemplateDO couponTemplate = CouponTemplateConvert.INSTANCE.convert(createReqVO)
  35 + .setStatus(CommonStatusEnum.ENABLE.getStatus());
34 36 couponTemplateMapper.insert(couponTemplate);
35 37 // 返回
36 38 return couponTemplate.getId();
... ... @@ -51,6 +53,14 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
51 53 }
52 54  
53 55 @Override
  56 + public void updateCouponTemplateStatus(Long id, Integer status) {
  57 + // 校验存在
  58 + validateCouponTemplateExists(id);
  59 + // 更新
  60 + couponTemplateMapper.updateById(new CouponTemplateDO().setId(id).setStatus(status));
  61 + }
  62 +
  63 + @Override
54 64 public void deleteCouponTemplate(Long id) {
55 65 // 校验存在
56 66 validateCouponTemplateExists(id);
... ... @@ -76,4 +86,9 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
76 86 return couponTemplateMapper.selectPage(pageReqVO);
77 87 }
78 88  
  89 + @Override
  90 + public void updateCouponTemplateTakeCount(Long id, int incrCount) {
  91 + couponTemplateMapper.updateTakeCount(id, incrCount);
  92 + }
  93 +
79 94 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.service.discount;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
  8 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
  9 +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO;
  10 +
  11 +import javax.validation.Valid;
  12 +import java.util.Collection;
  13 +import java.util.List;
  14 +import java.util.Map;
  15 +
  16 +/**
  17 + * 限时折扣 Service 接口
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +public interface DiscountActivityService {
  22 +
  23 + /**
  24 + * 基于指定 SKU 编号数组,获得匹配的限时折扣商品
  25 + *
  26 + * 注意,匹配的条件,仅仅是日期符合,并且处于开启状态
  27 + *
  28 + * @param skuIds SKU 编号数组
  29 + * @return 匹配的限时折扣商品
  30 + */
  31 + Map<Long, DiscountProductDetailBO> getMatchDiscountProducts(Collection<Long> skuIds);
  32 +
  33 + /**
  34 + * 创建限时折扣活动
  35 + *
  36 + * @param createReqVO 创建信息
  37 + * @return 编号
  38 + */
  39 + Long createDiscountActivity(@Valid DiscountActivityCreateReqVO createReqVO);
  40 +
  41 + /**
  42 + * 更新限时折扣活动
  43 + *
  44 + * @param updateReqVO 更新信息
  45 + */
  46 + void updateDiscountActivity(@Valid DiscountActivityUpdateReqVO updateReqVO);
  47 +
  48 + /**
  49 + * 关闭限时折扣活动
  50 + *
  51 + * @param id 编号
  52 + */
  53 + void closeRewardActivity(Long id);
  54 +
  55 + /**
  56 + * 删除限时折扣活动
  57 + *
  58 + * @param id 编号
  59 + */
  60 + void deleteDiscountActivity(Long id);
  61 +
  62 + /**
  63 + * 获得限时折扣活动
  64 + *
  65 + * @param id 编号
  66 + * @return 限时折扣活动
  67 + */
  68 + DiscountActivityDO getDiscountActivity(Long id);
  69 +
  70 + /**
  71 + * 获得限时折扣活动分页
  72 + *
  73 + * @param pageReqVO 分页查询
  74 + * @return 限时折扣活动分页
  75 + */
  76 + PageResult<DiscountActivityDO> getDiscountActivityPage(DiscountActivityPageReqVO pageReqVO);
  77 +
  78 + /**
  79 + * 获得活动编号,对应对应的商品列表
  80 + *
  81 + * @param activityId 活动编号
  82 + * @return 活动的商品列表
  83 + */
  84 + List<DiscountProductDO> getDiscountProductsByActivityId(Long activityId);
  85 +
  86 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.service.discount;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.collection.CollectionUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
  8 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
  9 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
  10 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
  11 +import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert;
  12 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
  13 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
  14 +import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountActivityMapper;
  15 +import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper;
  16 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
  17 +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO;
  18 +import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
  19 +import org.springframework.stereotype.Service;
  20 +import org.springframework.validation.annotation.Validated;
  21 +
  22 +import javax.annotation.Resource;
  23 +import java.util.*;
  24 +
  25 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  27 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
  28 +import static java.util.Arrays.asList;
  29 +
  30 +/**
  31 + * 限时折扣 Service 实现类
  32 + *
  33 + * @author 芋道源码
  34 + */
  35 +@Service
  36 +@Validated
  37 +public class DiscountActivityServiceImpl implements DiscountActivityService {
  38 +
  39 + @Resource
  40 + private DiscountActivityMapper discountActivityMapper;
  41 + @Resource
  42 + private DiscountProductMapper discountProductMapper;
  43 +
  44 + @Override
  45 + public Map<Long, DiscountProductDetailBO> getMatchDiscountProducts(Collection<Long> skuIds) {
  46 + List<DiscountProductDetailBO> discountProducts = getRewardProductListBySkuIds(skuIds, singleton(PromotionActivityStatusEnum.RUN.getStatus()));
  47 + return convertMap(discountProducts, DiscountProductDetailBO::getSkuId);
  48 + }
  49 +
  50 + @Override
  51 + public Long createDiscountActivity(DiscountActivityCreateReqVO createReqVO) {
  52 + // 校验商品是否冲突
  53 + validateDiscountActivityProductConflicts(null, createReqVO.getProducts());
  54 +
  55 + // 插入活动
  56 + DiscountActivityDO discountActivity = DiscountActivityConvert.INSTANCE.convert(createReqVO)
  57 + .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getStartTime(), createReqVO.getEndTime()));
  58 + discountActivityMapper.insert(discountActivity);
  59 + // 插入商品
  60 + List<DiscountProductDO> discountProducts = convertList(createReqVO.getProducts(),
  61 + product -> DiscountActivityConvert.INSTANCE.convert(product).setActivityId(discountActivity.getId()));
  62 + discountProductMapper.insertBatch(discountProducts);
  63 + // 返回
  64 + return discountActivity.getId();
  65 + }
  66 +
  67 + @Override
  68 + public void updateDiscountActivity(DiscountActivityUpdateReqVO updateReqVO) {
  69 + // 校验存在
  70 + DiscountActivityDO discountActivity = validateDiscountActivityExists(updateReqVO.getId());
  71 + if (discountActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能修改噢
  72 + throw exception(DISCOUNT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);
  73 + }
  74 + // 校验商品是否冲突
  75 + validateDiscountActivityProductConflicts(updateReqVO.getId(), updateReqVO.getProducts());
  76 +
  77 + // 更新活动
  78 + DiscountActivityDO updateObj = DiscountActivityConvert.INSTANCE.convert(updateReqVO)
  79 + .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getStartTime(), updateReqVO.getEndTime()));
  80 + discountActivityMapper.updateById(updateObj);
  81 + // 更新商品
  82 + updateDiscountProduct(updateReqVO);
  83 + }
  84 +
  85 + private void updateDiscountProduct(DiscountActivityUpdateReqVO updateReqVO) {
  86 + List<DiscountProductDO> dbDiscountProducts = discountProductMapper.selectListByActivityId(updateReqVO.getId());
  87 + // 计算要删除的记录
  88 + List<Long> deleteIds = convertList(dbDiscountProducts, DiscountProductDO::getId,
  89 + discountProductDO -> updateReqVO.getProducts().stream()
  90 + .noneMatch(product -> DiscountActivityConvert.INSTANCE.isEquals(discountProductDO, product)));
  91 + if (CollUtil.isNotEmpty(deleteIds)) {
  92 + discountProductMapper.deleteBatchIds(deleteIds);
  93 + }
  94 + // 计算新增的记录
  95 + List<DiscountProductDO> newDiscountProducts = convertList(updateReqVO.getProducts(),
  96 + product -> DiscountActivityConvert.INSTANCE.convert(product).setActivityId(updateReqVO.getId()));
  97 + newDiscountProducts.removeIf(product -> dbDiscountProducts.stream().anyMatch(
  98 + dbProduct -> DiscountActivityConvert.INSTANCE.isEquals(dbProduct, product))); // 如果匹配到,说明是更新的
  99 + if (CollectionUtil.isNotEmpty(newDiscountProducts)) {
  100 + discountProductMapper.insertBatch(newDiscountProducts);
  101 + }
  102 + }
  103 +
  104 + /**
  105 + * 校验商品是否冲突
  106 + *
  107 + * @param id 编号
  108 + * @param products 商品列表
  109 + */
  110 + private void validateDiscountActivityProductConflicts(Long id, List<DiscountActivityBaseVO.Product> products) {
  111 + if (CollUtil.isEmpty(products)) {
  112 + return;
  113 + }
  114 + // 查询商品参加的活动
  115 + List<DiscountProductDetailBO> discountActivityProductList = getRewardProductListBySkuIds(
  116 + convertSet(products, DiscountActivityBaseVO.Product::getSkuId),
  117 + asList(PromotionActivityStatusEnum.WAIT.getStatus(), PromotionActivityStatusEnum.RUN.getStatus()));
  118 + if (id != null) { // 排除自己这个活动
  119 + discountActivityProductList.removeIf(product -> id.equals(product.getActivityId()));
  120 + }
  121 + // 如果非空,则说明冲突
  122 + if (CollUtil.isNotEmpty(discountActivityProductList)) {
  123 + throw exception(DISCOUNT_ACTIVITY_SPU_CONFLICTS);
  124 + }
  125 + }
  126 +
  127 + private List<DiscountProductDetailBO> getRewardProductListBySkuIds(Collection<Long> skuIds,
  128 + Collection<Integer> statuses) {
  129 + // 查询商品
  130 + List<DiscountProductDO> products = discountProductMapper.selectListBySkuId(skuIds);
  131 + if (CollUtil.isEmpty(products)) {
  132 + return new ArrayList<>(0);
  133 + }
  134 +
  135 + // 查询活动
  136 + List<DiscountActivityDO> activities = discountActivityMapper.selectBatchIds(skuIds);
  137 + activities.removeIf(activity -> !statuses.contains(activity.getStatus())); // 移除不满足 statuses 状态的
  138 + Map<Long, DiscountActivityDO> activityMap = CollectionUtils.convertMap(activities, DiscountActivityDO::getId);
  139 +
  140 + // 移除不满足活动的商品
  141 + products.removeIf(product -> !activityMap.containsKey(product.getActivityId()));
  142 + return DiscountActivityConvert.INSTANCE.convertList(products, activityMap);
  143 + }
  144 +
  145 + @Override
  146 + public void closeRewardActivity(Long id) {
  147 + // 校验存在
  148 + DiscountActivityDO dbDiscountActivity = validateDiscountActivityExists(id);
  149 + if (dbDiscountActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能关闭噢
  150 + throw exception(DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
  151 + }
  152 + if (dbDiscountActivity.getStatus().equals(PromotionActivityStatusEnum.END.getStatus())) { // 已关闭的活动,不能关闭噢
  153 + throw exception(DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_END);
  154 + }
  155 +
  156 + // 更新
  157 + DiscountActivityDO updateObj = new DiscountActivityDO().setId(id).setStatus(PromotionActivityStatusEnum.CLOSE.getStatus());
  158 + discountActivityMapper.updateById(updateObj);
  159 + }
  160 +
  161 + @Override
  162 + public void deleteDiscountActivity(Long id) {
  163 + // 校验存在
  164 + DiscountActivityDO discountActivity = validateDiscountActivityExists(id);
  165 + if (!discountActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 未关闭的活动,不能删除噢
  166 + throw exception(DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED);
  167 + }
  168 +
  169 + // 删除
  170 + discountActivityMapper.deleteById(id);
  171 + }
  172 +
  173 + private DiscountActivityDO validateDiscountActivityExists(Long id) {
  174 + DiscountActivityDO discountActivity = discountActivityMapper.selectById(id);
  175 + if (discountActivity == null) {
  176 + throw exception(DISCOUNT_ACTIVITY_NOT_EXISTS);
  177 + }
  178 + return discountActivity;
  179 + }
  180 +
  181 + @Override
  182 + public DiscountActivityDO getDiscountActivity(Long id) {
  183 + return discountActivityMapper.selectById(id);
  184 + }
  185 +
  186 + @Override
  187 + public PageResult<DiscountActivityDO> getDiscountActivityPage(DiscountActivityPageReqVO pageReqVO) {
  188 + return discountActivityMapper.selectPage(pageReqVO);
  189 + }
  190 +
  191 + @Override
  192 + public List<DiscountProductDO> getDiscountProductsByActivityId(Long activityId) {
  193 + return discountProductMapper.selectListByActivityId(activityId);
  194 + }
  195 +
  196 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountService.java deleted
1   -package cn.iocoder.yudao.module.promotion.service.discount;
2   -
3   -import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
4   -
5   -import java.util.Collection;
6   -import java.util.Map;
7   -
8   -/**
9   - * 限时折扣 Service 接口
10   - *
11   - * @author 芋道源码
12   - */
13   -public interface DiscountService {
14   -
15   - /**
16   - * 基于指定 SKU 编号数组,获得匹配的限时折扣商品
17   - *
18   - * 注意,匹配的条件,仅仅是日期符合,并且处于开启状态
19   - *
20   - * @param skuIds SKU 编号数组
21   - * @return 匹配的限时折扣商品
22   - */
23   - Map<Long, DiscountProductDO> getMatchDiscountProducts(Collection<Long> skuIds);
24   -
25   -}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.promotion.service.discount;
2   -
3   -import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
4   -import org.springframework.stereotype.Service;
5   -import org.springframework.validation.annotation.Validated;
6   -
7   -import java.util.Collection;
8   -import java.util.HashMap;
9   -import java.util.Map;
10   -
11   -/**
12   - * 限时折扣 Service 实现类
13   - *
14   - * @author 芋道源码
15   - */
16   -@Service
17   -@Validated
18   -public class DiscountServiceImpl implements DiscountService {
19   -
20   - // TODO 芋艿:待实现
21   - @Override
22   - public Map<Long, DiscountProductDO> getMatchDiscountProducts(Collection<Long> skuIds) {
23   - Map<Long, DiscountProductDO> products = new HashMap<>();
24   - products.put(1L, new DiscountProductDO().setPromotionPrice(100));
25   - products.put(2L, new DiscountProductDO().setPromotionPrice(50));
26   - return products;
27   - }
28   -
29   -}
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/UserInfoDTO.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/bo/DiscountProductDetailBO.java
1   -package cn.iocoder.yudao.module.member.api.user.dto;
  1 +package cn.iocoder.yudao.module.promotion.service.discount.bo;
2 2  
3   -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 3 import lombok.Data;
5 4  
6   -import java.util.Date;
7   -
8 5 /**
9   - * 用户信息 Response DTO
  6 + * 限时折扣活动商品 BO
10 7 *
11 8 * @author 芋道源码
12 9 */
13 10 @Data
14   -public class UserInfoDTO {
  11 +public class DiscountProductDetailBO {
  12 +
  13 + // ========== DiscountProductDO 字段 ==========
15 14  
16 15 /**
17   - * 用户ID
  16 + * 编号,主键自增
18 17 */
19 18 private Long id;
20 19 /**
21   - * 用户昵称
  20 + * 限时折扣活动的编号
22 21 */
23   - private String nickname;
  22 + private Long activityId;
24 23 /**
25   - * 用户头像
  24 + * 商品 SPU 编号
26 25 */
27   - private String avatar;
  26 + private Long spuId;
28 27 /**
29   - * 帐号状态
30   - *
31   - * 枚举 {@link CommonStatusEnum}
  28 + * 商品 SKU 编号
32 29 */
33   - private Integer status;
34   -
  30 + private Long skuId;
35 31 /**
36   - * 手机
  32 + * 折扣类型
37 33 */
38   - private String mobile;
  34 + private Integer discountType;
39 35 /**
40   - * 注册 IP
  36 + * 折扣百分比
41 37 */
42   - private String registerIp;
  38 + private Integer discountPercent;
43 39 /**
44   - * 最后登录IP
  40 + * 优惠金额,单位:分
45 41 */
46   - private String loginIp;
  42 + private Integer discountPrice;
  43 +
  44 + // ========== DiscountActivityDO 字段 ==========
47 45 /**
48   - * 最后登录时间
  46 + * 活动标题
49 47 */
50   - private Date loginDate;
  48 + private String activityName;
51 49  
52 50 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceImpl.java
... ... @@ -4,22 +4,20 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.util.StrUtil;
6 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  7 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  8 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
7 9 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
8 10 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
9 11 import cn.iocoder.yudao.module.promotion.convert.price.PriceConvert;
10 12 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
11   -import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
12 13 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
13   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
14   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionLevelEnum;
15   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
16   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
  14 +import cn.iocoder.yudao.module.promotion.enums.common.*;
17 15 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
18   -import cn.iocoder.yudao.module.promotion.service.discount.DiscountService;
19   -import cn.iocoder.yudao.module.promotion.service.reward.RewardService;
20   -import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
21   -import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  16 +import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
  17 +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO;
  18 +import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
22 19 import com.google.common.base.Suppliers;
  20 +import lombok.extern.slf4j.Slf4j;
23 21 import org.springframework.stereotype.Service;
24 22 import org.springframework.validation.annotation.Validated;
25 23  
... ... @@ -33,9 +31,8 @@ import java.util.function.Supplier;
33 31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 32 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
35 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
36   -import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_MIN_PRICE;
37   -import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_SPU;
38 34 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
  35 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
39 36 import static java.util.Collections.singletonList;
40 37  
41 38 /**
... ... @@ -54,12 +51,13 @@ import static java.util.Collections.singletonList;
54 51 */
55 52 @Service
56 53 @Validated
  54 +@Slf4j
57 55 public class PriceServiceImpl implements PriceService {
58 56  
59 57 @Resource
60   - private DiscountService discountService;
  58 + private DiscountActivityService discountService;
61 59 @Resource
62   - private RewardService rewardService;
  60 + private RewardActivityService rewardActivityService;
63 61 @Resource
64 62 private CouponService couponService;
65 63  
... ... @@ -79,7 +77,13 @@ public class PriceServiceImpl implements PriceService {
79 77 calculatePriceForOrderLevel(calculateReqDTO.getUserId(), priceCalculate);
80 78 // 计算优惠劵级别的价格
81 79 calculatePriceForCouponLevel(calculateReqDTO.getUserId(), calculateReqDTO.getCouponId(), priceCalculate);
82   - // 计算【优惠劵】促销 TODO 待实现
  80 +
  81 + // 如果最终支付金额小于等于 0,则抛出业务异常
  82 + if (priceCalculate.getOrder().getPayPrice() <= 0) {
  83 + log.error("[calculatePrice][价格计算不正确,请求 calculateReqDTO({}),结果 priceCalculate({})]",
  84 + calculateReqDTO, priceCalculate);
  85 + throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL);
  86 + }
83 87 return priceCalculate;
84 88 }
85 89  
... ... @@ -115,24 +119,20 @@ public class PriceServiceImpl implements PriceService {
115 119 private void calculatePriceForSkuLevel(Long userId, PriceCalculateRespDTO priceCalculate) {
116 120 // 获取 SKU 级别的所有优惠信息
117 121 Supplier<Double> memberDiscountPercentSupplier = getMemberDiscountPercentSupplier(userId);
118   - Map<Long, DiscountProductDO> discountProducts = discountService.getMatchDiscountProducts(
  122 + Map<Long, DiscountProductDetailBO> discountProducts = discountService.getMatchDiscountProducts(
119 123 convertSet(priceCalculate.getOrder().getItems(), PriceCalculateRespDTO.OrderItem::getSkuId));
120 124  
121 125 // 处理每个 SKU 的优惠
122 126 priceCalculate.getOrder().getItems().forEach(orderItem -> {
123 127 // 获取该 SKU 的优惠信息
124 128 Double memberDiscountPercent = memberDiscountPercentSupplier.get();
125   - DiscountProductDO discountProduct = discountProducts.get(orderItem.getSkuId());
126   - if (discountProduct != null // 假设优惠价格更贵,则认为没优惠
127   - && discountProduct.getPromotionPrice() >= orderItem.getOriginalUnitPrice()) {
128   - discountProduct = null;
129   - }
  129 + DiscountProductDetailBO discountProduct = discountProducts.get(orderItem.getSkuId());
130 130 if (memberDiscountPercent == null && discountProduct == null) {
131 131 return;
132 132 }
133 133 // 计算价格,判断选择哪个折扣
134 134 Integer memberPrice = memberDiscountPercent != null ? (int) (orderItem.getPayPrice() * memberDiscountPercent / 100) : null;
135   - Integer promotionPrice = discountProduct != null ? discountProduct.getPromotionPrice() * orderItem.getCount() : null;
  135 + Integer promotionPrice = discountProduct != null ? getDiscountProductPrice(discountProduct, orderItem) : null;
136 136 if (memberPrice == null) {
137 137 calculatePriceByDiscountActivity(priceCalculate, orderItem, discountProduct, promotionPrice);
138 138 } else if (promotionPrice == null) {
... ... @@ -145,6 +145,19 @@ public class PriceServiceImpl implements PriceService {
145 145 });
146 146 }
147 147  
  148 + private Integer getDiscountProductPrice(DiscountProductDetailBO discountProduct,
  149 + PriceCalculateRespDTO.OrderItem orderItem) {
  150 + Integer price = orderItem.getPayPrice();
  151 + if (PromotionDiscountTypeEnum.PRICE.getType().equals(discountProduct.getDiscountType())) { // 减价
  152 + price -= discountProduct.getDiscountPrice() * orderItem.getCount();
  153 + } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discountProduct.getDiscountType())) { // 打折
  154 + price = price * discountProduct.getDiscountPercent() / 100;
  155 + } else {
  156 + throw new IllegalArgumentException(String.format("优惠活动的商品(%s) 的优惠类型不正确", discountProduct));
  157 + }
  158 + return price;
  159 + }
  160 +
148 161 private void calculatePriceByMemberDiscount(PriceCalculateRespDTO priceCalculate, PriceCalculateRespDTO.OrderItem orderItem,
149 162 Integer memberPrice) {
150 163 // 记录优惠明细
... ... @@ -156,7 +169,7 @@ public class PriceServiceImpl implements PriceService {
156 169 }
157 170  
158 171 private void calculatePriceByDiscountActivity(PriceCalculateRespDTO priceCalculate, PriceCalculateRespDTO.OrderItem orderItem,
159   - DiscountProductDO discountProduct, Integer promotionPrice) {
  172 + DiscountProductDetailBO discountProduct, Integer promotionPrice) {
160 173 // 记录优惠明细
161 174 addPromotion(priceCalculate, orderItem, discountProduct.getActivityId(), discountProduct.getActivityName(),
162 175 PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(), PromotionLevelEnum.SKU.getLevel(), promotionPrice,
... ... @@ -191,7 +204,7 @@ public class PriceServiceImpl implements PriceService {
191 204 private void calculatePriceForOrderLevel(Long userId, PriceCalculateRespDTO priceCalculate) {
192 205 // 获取 SKU 级别的所有优惠信息
193 206 Set<Long> spuIds = convertSet(priceCalculate.getOrder().getItems(), PriceCalculateRespDTO.OrderItem::getSpuId);
194   - Map<RewardActivityDO, Set<Long>> rewardActivities = rewardService.getMatchRewardActivities(spuIds);
  207 + Map<RewardActivityDO, Set<Long>> rewardActivities = rewardActivityService.getMatchRewardActivities(spuIds);
195 208  
196 209 // 处理满减送活动
197 210 if (CollUtil.isNotEmpty(rewardActivities)) {
... ... @@ -216,7 +229,6 @@ public class PriceServiceImpl implements PriceService {
216 229 }
217 230  
218 231 // 分摊金额
219   - // TODO 芋艿:limit 不能超过最大价格
220 232 List<Integer> discountPartPrices = dividePrice(orderItems, rule.getDiscountPrice());
221 233 // 记录优惠明细
222 234 addPromotion(priceCalculate, orderItems, rewardActivity.getId(), rewardActivity.getName(),
... ... @@ -283,7 +295,7 @@ public class PriceServiceImpl implements PriceService {
283 295 // 计算是否满足优惠劵的使用金额
284 296 Integer originPrice = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum);
285 297 assert originPrice != null;
286   - if (originPrice < coupon.getPriceAvailable()) {
  298 + if (originPrice < coupon.getUsePrice()) {
287 299 throw exception(COUPON_NO_MATCH_MIN_PRICE);
288 300 }
289 301  
... ... @@ -291,10 +303,9 @@ public class PriceServiceImpl implements PriceService {
291 303 priceCalculate.getOrder().setCouponId(couponId);
292 304 Integer couponPrice = getCouponPrice(coupon, originPrice);
293 305 // 分摊金额
294   - // TODO 芋艿:limit 不能超过最大价格
295 306 List<Integer> couponPartPrices = dividePrice(orderItems, couponPrice);
296 307 // 记录优惠明细
297   - addPromotion(priceCalculate, orderItems, coupon.getId(), coupon.getTitle(),
  308 + addPromotion(priceCalculate, orderItems, coupon.getId(), coupon.getName(),
298 309 PromotionTypeEnum.COUPON.getType(), PromotionLevelEnum.COUPON.getLevel(), couponPartPrices,
299 310 true, StrUtil.format("优惠劵:省 {} 元", formatPrice(couponPrice)));
300 311 // 修改 SKU 的分摊
... ... @@ -309,19 +320,18 @@ public class PriceServiceImpl implements PriceService {
309 320 return priceCalculate.getOrder().getItems();
310 321 }
311 322 return CollectionUtils.filterList(priceCalculate.getOrder().getItems(),
312   - orderItem -> coupon.getSpuIds().contains(orderItem.getSpuId()));
  323 + orderItem -> coupon.getProductSpuIds().contains(orderItem.getSpuId()));
313 324 }
314 325  
315 326 private Integer getCouponPrice(CouponDO coupon, Integer originPrice) {
316   - // TODO 芋艿 getPreferentialType 的枚举判断
317   - if (coupon.getPreferentialType().equals(1)) { // 减价
318   - return coupon.getPriceOff();
319   - } else if (coupon.getPreferentialType().equals(2)) { // 打折
320   - Integer couponPrice = originPrice * coupon.getPercentOff() / 100;
321   - return coupon.getDiscountPriceLimit() == null ? couponPrice
322   - : Math.min(couponPrice, coupon.getDiscountPriceLimit()); // 优惠上限
  327 + if (PromotionDiscountTypeEnum.PRICE.getType().equals(coupon.getDiscountType())) { // 减价
  328 + return coupon.getDiscountPrice();
  329 + } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(coupon.getDiscountType())) { // 打折
  330 + int couponPrice = originPrice * coupon.getDiscountPercent() / 100;
  331 + return coupon.getDiscountLimitPrice() == null ? couponPrice
  332 + : Math.min(couponPrice, coupon.getDiscountLimitPrice()); // 优惠上限
323 333 }
324   - throw new IllegalArgumentException(String.format("优惠劵(%s) 的优惠类型不正确", coupon.toString()));
  334 + throw new IllegalArgumentException(String.format("优惠劵(%s) 的优惠类型不正确", coupon));
325 335 }
326 336  
327 337 // ========== 其它相对通用的方法 ==========
... ... @@ -410,13 +420,16 @@ public class PriceServiceImpl implements PriceService {
410 420 */
411 421 private void modifyOrderItemPayPrice(PriceCalculateRespDTO.OrderItem orderItem, Integer newPayPrice,
412 422 PriceCalculateRespDTO priceCalculate) {
  423 + // diffPayPrice 等于额外增加的商品级的优惠
413 424 int diffPayPrice = orderItem.getPayPrice() - newPayPrice;
414 425 // 设置 OrderItem 价格相关字段
415 426 orderItem.setDiscountPrice(orderItem.getDiscountPrice() + diffPayPrice);
416 427 orderItem.setPayPrice(newPayPrice);
417 428 orderItem.setOrderDividePrice(orderItem.getPayPrice() - orderItem.getOrderPartPrice());
418 429 // 设置 Order 相关相关字段
419   - priceCalculate.getOrder().setPayPrice(priceCalculate.getOrder().getPayPrice() - diffPayPrice);
  430 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
  431 + order.setPayPrice(order.getPayPrice() - diffPayPrice);
  432 + order.setOrderPrice(order.getOrderPrice() - diffPayPrice);
420 433 }
421 434  
422 435 /**
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.service.reward;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
  7 +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  8 +
  9 +import javax.validation.Valid;
  10 +import java.util.Map;
  11 +import java.util.Set;
  12 +
  13 +/**
  14 + * 满减送活动 Service 接口
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +public interface RewardActivityService {
  19 +
  20 + /**
  21 + * 创建满减送活动
  22 + *
  23 + * @param createReqVO 创建信息
  24 + * @return 编号
  25 + */
  26 + Long createRewardActivity(@Valid RewardActivityCreateReqVO createReqVO);
  27 +
  28 + /**
  29 + * 更新满减送活动
  30 + *
  31 + * @param updateReqVO 更新信息
  32 + */
  33 + void updateRewardActivity(@Valid RewardActivityUpdateReqVO updateReqVO);
  34 +
  35 + /**
  36 + * 关闭满减送活动
  37 + *
  38 + * @param id 活动编号
  39 + */
  40 + void closeRewardActivity(Long id);
  41 +
  42 + /**
  43 + * 删除满减送活动
  44 + *
  45 + * @param id 编号
  46 + */
  47 + void deleteRewardActivity(Long id);
  48 +
  49 + /**
  50 + * 获得满减送活动
  51 + *
  52 + * @param id 编号
  53 + * @return 满减送活动
  54 + */
  55 + RewardActivityDO getRewardActivity(Long id);
  56 +
  57 + /**
  58 + * 获得满减送活动分页
  59 + *
  60 + * @param pageReqVO 分页查询
  61 + * @return 满减送活动分页
  62 + */
  63 + PageResult<RewardActivityDO> getRewardActivityPage(RewardActivityPageReqVO pageReqVO);
  64 +
  65 + /**
  66 + * 基于指定的 SPU 编号数组,获得它们匹配的满减送活动
  67 + *
  68 + * @param spuIds SPU 编号数组
  69 + * @return 满减送活动,与对应的 SPU 编号的映射。即,value 就是 SPU 编号的集合
  70 + */
  71 + Map<RewardActivityDO, Set<Long>> getMatchRewardActivities(Set<Long> spuIds);
  72 +
  73 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.service.reward;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
  8 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
  9 +import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
  10 +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  11 +import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper;
  12 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
  13 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  14 +import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
  15 +import org.springframework.stereotype.Service;
  16 +import org.springframework.validation.annotation.Validated;
  17 +
  18 +import javax.annotation.Resource;
  19 +import java.util.Collection;
  20 +import java.util.List;
  21 +import java.util.Map;
  22 +import java.util.Set;
  23 +
  24 +import static cn.hutool.core.collection.CollUtil.intersectionDistinct;
  25 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  27 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
  28 +import static java.util.Arrays.asList;
  29 +import static java.util.Collections.singleton;
  30 +
  31 +/**
  32 + * 满减送活动 Service 实现类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Service
  37 +@Validated
  38 +public class RewardActivityServiceImpl implements RewardActivityService {
  39 +
  40 + @Resource
  41 + private RewardActivityMapper rewardActivityMapper;
  42 +
  43 + @Override
  44 + public Long createRewardActivity(RewardActivityCreateReqVO createReqVO) {
  45 + // 校验商品是否冲突
  46 + validateRewardActivitySpuConflicts(null, createReqVO.getProductSpuIds());
  47 +
  48 + // 插入
  49 + RewardActivityDO rewardActivity = RewardActivityConvert.INSTANCE.convert(createReqVO)
  50 + .setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getStartTime(), createReqVO.getEndTime()));
  51 + rewardActivityMapper.insert(rewardActivity);
  52 + // 返回
  53 + return rewardActivity.getId();
  54 + }
  55 +
  56 + @Override
  57 + public void updateRewardActivity(RewardActivityUpdateReqVO updateReqVO) {
  58 + // 校验存在
  59 + RewardActivityDO dbRewardActivity = validateRewardActivityExists(updateReqVO.getId());
  60 + if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能修改噢
  61 + throw exception(REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);
  62 + }
  63 + // 校验商品是否冲突
  64 + validateRewardActivitySpuConflicts(updateReqVO.getId(), updateReqVO.getProductSpuIds());
  65 +
  66 + // 更新
  67 + RewardActivityDO updateObj = RewardActivityConvert.INSTANCE.convert(updateReqVO)
  68 + .setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getStartTime(), updateReqVO.getEndTime()));
  69 + rewardActivityMapper.updateById(updateObj);
  70 + }
  71 +
  72 + @Override
  73 + public void closeRewardActivity(Long id) {
  74 + // 校验存在
  75 + RewardActivityDO dbRewardActivity = validateRewardActivityExists(id);
  76 + if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能关闭噢
  77 + throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
  78 + }
  79 + if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.END.getStatus())) { // 已关闭的活动,不能关闭噢
  80 + throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END);
  81 + }
  82 +
  83 + // 更新
  84 + RewardActivityDO updateObj = new RewardActivityDO().setId(id).setStatus(PromotionActivityStatusEnum.CLOSE.getStatus());
  85 + rewardActivityMapper.updateById(updateObj);
  86 + }
  87 +
  88 + @Override
  89 + public void deleteRewardActivity(Long id) {
  90 + // 校验存在
  91 + RewardActivityDO dbRewardActivity = validateRewardActivityExists(id);
  92 + if (!dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 未关闭的活动,不能删除噢
  93 + throw exception(REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED);
  94 + }
  95 +
  96 + // 删除
  97 + rewardActivityMapper.deleteById(id);
  98 + }
  99 +
  100 + private RewardActivityDO validateRewardActivityExists(Long id) {
  101 + RewardActivityDO activity = rewardActivityMapper.selectById(id);
  102 + if (activity == null) {
  103 + throw exception(REWARD_ACTIVITY_NOT_EXISTS);
  104 + }
  105 + return activity;
  106 + }
  107 +
  108 + /**
  109 + * 校验商品参加的活动是否冲突
  110 + *
  111 + * @param id 活动编号
  112 + * @param spuIds 商品 SPU 编号数组
  113 + */
  114 + private void validateRewardActivitySpuConflicts(Long id, Collection<Long> spuIds) {
  115 + if (CollUtil.isEmpty(spuIds)) {
  116 + return;
  117 + }
  118 + // 查询商品参加的活动
  119 + List<RewardActivityDO> rewardActivityList = getRewardActivityListBySpuIds(spuIds,
  120 + asList(PromotionActivityStatusEnum.WAIT.getStatus(), PromotionActivityStatusEnum.RUN.getStatus()));
  121 + if (id != null) { // 排除自己这个活动
  122 + rewardActivityList.removeIf(activity -> id.equals(activity.getId()));
  123 + }
  124 + // 如果非空,则说明冲突
  125 + if (CollUtil.isNotEmpty(rewardActivityList)) {
  126 + throw exception(REWARD_ACTIVITY_SPU_CONFLICTS);
  127 + }
  128 + }
  129 +
  130 + /**
  131 + * 获得商品参加的满减送活动的数组
  132 + *
  133 + * @param spuIds 商品 SPU 编号数组
  134 + * @param statuses 活动状态数组
  135 + * @return 商品参加的满减送活动的数组
  136 + */
  137 + private List<RewardActivityDO> getRewardActivityListBySpuIds(Collection<Long> spuIds,
  138 + Collection<Integer> statuses) {
  139 + List<RewardActivityDO> list = rewardActivityMapper.selectListByStatus(statuses);
  140 + return CollUtil.filter(list, activity -> CollUtil.containsAny(activity.getProductSpuIds(), spuIds));
  141 + }
  142 +
  143 + @Override
  144 + public RewardActivityDO getRewardActivity(Long id) {
  145 + return rewardActivityMapper.selectById(id);
  146 + }
  147 +
  148 + @Override
  149 + public PageResult<RewardActivityDO> getRewardActivityPage(RewardActivityPageReqVO pageReqVO) {
  150 + return rewardActivityMapper.selectPage(pageReqVO);
  151 + }
  152 +
  153 + @Override
  154 + public Map<RewardActivityDO, Set<Long>> getMatchRewardActivities(Set<Long> spuIds) {
  155 + // 如果有全局活动,则直接选择它
  156 + List<RewardActivityDO> allActivities = rewardActivityMapper.selectListByProductScopeAndStatus(
  157 + PromotionProductScopeEnum.ALL.getScope(), PromotionActivityStatusEnum.RUN.getStatus());
  158 + if (CollUtil.isNotEmpty(allActivities)) {
  159 + return MapUtil.builder(allActivities.get(0), spuIds).build();
  160 + }
  161 +
  162 + // 查询某个活动参加的活动
  163 + List<RewardActivityDO> productActivityList = getRewardActivityListBySpuIds(spuIds,
  164 + singleton(PromotionActivityStatusEnum.RUN.getStatus()));
  165 + return convertMap(productActivityList, activity -> activity,
  166 + rewardActivityDO -> intersectionDistinct(rewardActivityDO.getProductSpuIds(), spuIds)); // 求交集返回
  167 + }
  168 +
  169 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardService.java deleted
1   -package cn.iocoder.yudao.module.promotion.service.reward;
2   -
3   -import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
4   -
5   -import java.util.Map;
6   -import java.util.Set;
7   -
8   -/**
9   - * 满减送 Service 接口
10   - *
11   - * @author 芋道源码
12   - */
13   -public interface RewardService {
14   -
15   - /**
16   - * 基于指定的 SPU 编号数组,获得它们匹配的满减送活动
17   - *
18   - * @param spuIds SPU 编号数组
19   - * @return 满减送活动,与对应的 SPU 编号的映射。即,value 就是 SPU 编号的集合
20   - */
21   - Map<RewardActivityDO, Set<Long>> getMatchRewardActivities(Set<Long> spuIds);
22   -
23   -}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.promotion.service.reward;
2   -
3   -import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
4   -import org.springframework.stereotype.Service;
5   -import org.springframework.validation.annotation.Validated;
6   -
7   -import java.util.Collections;
8   -import java.util.Map;
9   -import java.util.Set;
10   -
11   -/**
12   - * 满减送 Service 实现类
13   - *
14   - * @author 芋道源码
15   - */
16   -@Service
17   -@Validated
18   -public class RewardServiceImpl implements RewardService {
19   -
20   - // TODO 芋艿:待实现
21   - @Override
22   - public Map<RewardActivityDO, Set<Long>> getMatchRewardActivities(Set<Long> spuIds) {
23   - return Collections.emptyMap();
24   - }
25   -
26   -}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/util/PromotionUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.util;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
  4 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
  5 +
  6 +import java.util.Date;
  7 +
  8 +/**
  9 + * 活动工具类
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +public class PromotionUtils {
  14 +
  15 + /**
  16 + * 根据时间,计算活动状态
  17 + *
  18 + * @param startTime 开始时间
  19 + * @param endTime 结束时间
  20 + * @return 活动状态
  21 + */
  22 + public static Integer calculateActivityStatus(Date startTime, Date endTime) {
  23 + if (DateUtils.beforeNow(endTime)) {
  24 + return PromotionActivityStatusEnum.END.getStatus();
  25 + }
  26 + if (DateUtils.afterNow(startTime)) {
  27 + return PromotionActivityStatusEnum.WAIT.getStatus();
  28 + }
  29 + return PromotionActivityStatusEnum.RUN.getStatus();
  30 + }
  31 +
  32 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/coupon/CouponTemplateMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper">
  4 +
  5 + <update id="updateTakeCount">
  6 + UPDATE promotion_coupon_template
  7 + SET take_count = take_count + #{incrCount}
  8 + WHERE id = #{id}
  9 + </update>
  10 +
  11 +</mapper>
... ...
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImplTest.java
1 1 package cn.iocoder.yudao.module.promotion.service.coupon;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
5   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateCreateReqVO;
6   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplatePageReqVO;
7   -import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.CouponTemplateUpdateReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
  8 +import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateUpdateReqVO;
8 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
9 10 import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper;
10   -import org.junit.jupiter.api.Disabled;
  11 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
  12 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  13 +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
11 14 import org.junit.jupiter.api.Test;
12 15 import org.springframework.context.annotation.Import;
13 16  
14 17 import javax.annotation.Resource;
15 18 import java.util.Date;
16 19  
  20 +import static cn.hutool.core.util.RandomUtil.randomEle;
  21 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
17 22 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
18 23 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
19 24 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
... ... @@ -39,7 +44,10 @@ public class CouponTemplateServiceImplTest extends BaseDbUnitTest {
39 44 @Test
40 45 public void testCreateCouponTemplate_success() {
41 46 // 准备参数
42   - CouponTemplateCreateReqVO reqVO = randomPojo(CouponTemplateCreateReqVO.class);
  47 + CouponTemplateCreateReqVO reqVO = randomPojo(CouponTemplateCreateReqVO.class,
  48 + o -> o.setProductScope(randomEle(PromotionProductScopeEnum.values()).getScope())
  49 + .setValidityType(randomEle(CouponTemplateValidityTypeEnum.values()).getType())
  50 + .setDiscountType(randomEle(PromotionDiscountTypeEnum.values()).getType()));
43 51  
44 52 // 调用
45 53 Long couponTemplateId = couponTemplateService.createCouponTemplate(reqVO);
... ... @@ -58,6 +66,10 @@ public class CouponTemplateServiceImplTest extends BaseDbUnitTest {
58 66 // 准备参数
59 67 CouponTemplateUpdateReqVO reqVO = randomPojo(CouponTemplateUpdateReqVO.class, o -> {
60 68 o.setId(dbCouponTemplate.getId()); // 设置更新的 ID
  69 + // 其它通用字段
  70 + o.setProductScope(randomEle(PromotionProductScopeEnum.values()).getScope())
  71 + .setValidityType(randomEle(CouponTemplateValidityTypeEnum.values()).getType())
  72 + .setDiscountType(randomEle(PromotionDiscountTypeEnum.values()).getType());
61 73 });
62 74  
63 75 // 调用
... ... @@ -100,30 +112,29 @@ public class CouponTemplateServiceImplTest extends BaseDbUnitTest {
100 112 }
101 113  
102 114 @Test
103   - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
104 115 public void testGetCouponTemplatePage() {
105 116 // mock 数据
106 117 CouponTemplateDO dbCouponTemplate = randomPojo(CouponTemplateDO.class, o -> { // 等会查询到
107   - o.setName(null);
108   - o.setStatus(null);
109   - o.setDiscountType(null);
110   - o.setCreateTime(null);
  118 + o.setName("芋艿");
  119 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  120 + o.setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType());
  121 + o.setCreateTime(buildTime(2022, 2, 2));
111 122 });
112 123 couponTemplateMapper.insert(dbCouponTemplate);
113 124 // 测试 name 不匹配
114   - couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setName(null)));
  125 + couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setName("土豆")));
115 126 // 测试 status 不匹配
116   - couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setStatus(null)));
  127 + couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
117 128 // 测试 type 不匹配
118   - couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setDiscountType(null)));
  129 + couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setDiscountType(PromotionDiscountTypeEnum.PRICE.getType())));
119 130 // 测试 createTime 不匹配
120   - couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setCreateTime(null)));
  131 + couponTemplateMapper.insert(cloneIgnoreId(dbCouponTemplate, o -> o.setCreateTime(buildTime(2022, 1, 1))));
121 132 // 准备参数
122 133 CouponTemplatePageReqVO reqVO = new CouponTemplatePageReqVO();
123   - reqVO.setName(null);
124   - reqVO.setStatus(null);
125   - reqVO.setDiscountType(null);
126   - reqVO.setCreateTime((new Date[]{}));
  134 + reqVO.setName("芋艿");
  135 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  136 + reqVO.setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType());
  137 + reqVO.setCreateTime((new Date[]{buildTime(2022, 2, 1), buildTime(2022, 2, 3)}));
127 138  
128 139 // 调用
129 140 PageResult<CouponTemplateDO> pageResult = couponTemplateService.getCouponTemplatePage(reqVO);
... ...
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImplTest.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.service.discount;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
  8 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
  9 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
  10 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
  11 +import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountActivityMapper;
  12 +import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper;
  13 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
  14 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
  15 +import org.junit.jupiter.api.Test;
  16 +import org.springframework.context.annotation.Import;
  17 +
  18 +import javax.annotation.Resource;
  19 +import java.time.Duration;
  20 +import java.util.Date;
  21 +import java.util.List;
  22 +
  23 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
  24 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
  25 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  26 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  27 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  28 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  29 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  30 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DISCOUNT_ACTIVITY_NOT_EXISTS;
  31 +import static java.util.Arrays.asList;
  32 +import static org.junit.jupiter.api.Assertions.*;
  33 +
  34 +/**
  35 +* {@link DiscountActivityServiceImpl} 的单元测试类
  36 +*
  37 +* @author 芋道源码
  38 +*/
  39 +@Import(DiscountActivityServiceImpl.class)
  40 +public class DiscountActivityServiceImplTest extends BaseDbUnitTest {
  41 +
  42 + @Resource
  43 + private DiscountActivityServiceImpl discountActivityService;
  44 +
  45 + @Resource
  46 + private DiscountActivityMapper discountActivityMapper;
  47 + @Resource
  48 + private DiscountProductMapper discountProductMapper;
  49 +
  50 + @Test
  51 + public void testCreateDiscountActivity_success() {
  52 + // 准备参数
  53 + DiscountActivityCreateReqVO reqVO = randomPojo(DiscountActivityCreateReqVO.class, o -> {
  54 + // 用于触发进行中的状态
  55 + o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2)));
  56 + // 设置商品
  57 + o.setProducts(asList(new DiscountActivityBaseVO.Product().setSpuId(1L).setSkuId(2L)
  58 + .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3),
  59 + new DiscountActivityBaseVO.Product().setSpuId(10L).setSkuId(20L)
  60 + .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30)));
  61 + });
  62 +
  63 + // 调用
  64 + Long discountActivityId = discountActivityService.createDiscountActivity(reqVO);
  65 + // 断言
  66 + assertNotNull(discountActivityId);
  67 + // 校验活动
  68 + DiscountActivityDO discountActivity = discountActivityMapper.selectById(discountActivityId);
  69 + assertPojoEquals(reqVO, discountActivity);
  70 + assertEquals(discountActivity.getStatus(), PromotionActivityStatusEnum.WAIT.getStatus());
  71 + // 校验商品
  72 + List<DiscountProductDO> discountProducts = discountProductMapper.selectList(DiscountProductDO::getActivityId, discountActivity.getId());
  73 + assertEquals(discountProducts.size(), reqVO.getProducts().size());
  74 + for (int i = 0; i < reqVO.getProducts().size(); i++) {
  75 + DiscountActivityBaseVO.Product product = reqVO.getProducts().get(i);
  76 + DiscountProductDO discountProduct = discountProducts.get(i);
  77 + assertEquals(discountProduct.getActivityId(), discountActivity.getId());
  78 + assertEquals(discountProduct.getSpuId(), product.getSpuId());
  79 + assertEquals(discountProduct.getSkuId(), product.getSkuId());
  80 + assertEquals(discountProduct.getDiscountType(), product.getDiscountType());
  81 + assertEquals(discountProduct.getDiscountPrice(), product.getDiscountPrice());
  82 + assertEquals(discountProduct.getDiscountPercent(), product.getDiscountPercent());
  83 + }
  84 + }
  85 +
  86 + @Test
  87 + public void testUpdateDiscountActivity_success() {
  88 + // mock 数据(商品)
  89 + DiscountActivityDO dbDiscountActivity = randomPojo(DiscountActivityDO.class);
  90 + discountActivityMapper.insert(dbDiscountActivity);// @Sql: 先插入出一条存在的数据
  91 + // mock 数据(活动)
  92 + DiscountProductDO dbDiscountProduct01 = randomPojo(DiscountProductDO.class, o -> o.setActivityId(dbDiscountActivity.getId())
  93 + .setSpuId(1L).setSkuId(2L).setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3).setDiscountPercent(null));
  94 + DiscountProductDO dbDiscountProduct02 = randomPojo(DiscountProductDO.class, o -> o.setActivityId(dbDiscountActivity.getId())
  95 + .setSpuId(10L).setSkuId(20L).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30).setDiscountPrice(null));
  96 + discountProductMapper.insert(dbDiscountProduct01);
  97 + discountProductMapper.insert(dbDiscountProduct02);
  98 + // 准备参数
  99 + DiscountActivityUpdateReqVO reqVO = randomPojo(DiscountActivityUpdateReqVO.class, o -> {
  100 + o.setId(dbDiscountActivity.getId()); // 设置更新的 ID
  101 + // 用于触发进行中的状态
  102 + o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2)));
  103 + // 设置商品
  104 + o.setProducts(asList(new DiscountActivityBaseVO.Product().setSpuId(1L).setSkuId(2L)
  105 + .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3).setDiscountPercent(null),
  106 + new DiscountActivityBaseVO.Product().setSpuId(100L).setSkuId(200L)
  107 + .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30).setDiscountPrice(null)));
  108 + });
  109 +
  110 + // 调用
  111 + discountActivityService.updateDiscountActivity(reqVO);
  112 + // 校验活动
  113 + DiscountActivityDO discountActivity = discountActivityMapper.selectById(reqVO.getId()); // 获取最新的
  114 + assertPojoEquals(reqVO, discountActivity);
  115 + assertEquals(discountActivity.getStatus(), PromotionActivityStatusEnum.WAIT.getStatus());
  116 + // 校验商品
  117 + List<DiscountProductDO> discountProducts = discountProductMapper.selectList(DiscountProductDO::getActivityId, discountActivity.getId());
  118 + assertEquals(discountProducts.size(), reqVO.getProducts().size());
  119 + for (int i = 0; i < reqVO.getProducts().size(); i++) {
  120 + DiscountActivityBaseVO.Product product = reqVO.getProducts().get(i);
  121 + DiscountProductDO discountProduct = discountProducts.get(i);
  122 + assertEquals(discountProduct.getActivityId(), discountActivity.getId());
  123 + assertEquals(discountProduct.getSpuId(), product.getSpuId());
  124 + assertEquals(discountProduct.getSkuId(), product.getSkuId());
  125 + assertEquals(discountProduct.getDiscountType(), product.getDiscountType());
  126 + assertEquals(discountProduct.getDiscountPrice(), product.getDiscountPrice());
  127 + assertEquals(discountProduct.getDiscountPercent(), product.getDiscountPercent());
  128 + }
  129 + }
  130 +
  131 + @Test
  132 + public void testCloseDiscountActivity() {
  133 + // mock 数据
  134 + DiscountActivityDO dbDiscountActivity = randomPojo(DiscountActivityDO.class,
  135 + o -> o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus()));
  136 + discountActivityMapper.insert(dbDiscountActivity);// @Sql: 先插入出一条存在的数据
  137 + // 准备参数
  138 + Long id = dbDiscountActivity.getId();
  139 +
  140 + // 调用
  141 + discountActivityService.closeRewardActivity(id);
  142 + // 校验状态
  143 + DiscountActivityDO discountActivity = discountActivityMapper.selectById(id);
  144 + assertEquals(discountActivity.getStatus(), PromotionActivityStatusEnum.CLOSE.getStatus());
  145 + }
  146 +
  147 + @Test
  148 + public void testUpdateDiscountActivity_notExists() {
  149 + // 准备参数
  150 + DiscountActivityUpdateReqVO reqVO = randomPojo(DiscountActivityUpdateReqVO.class);
  151 +
  152 + // 调用, 并断言异常
  153 + assertServiceException(() -> discountActivityService.updateDiscountActivity(reqVO), DISCOUNT_ACTIVITY_NOT_EXISTS);
  154 + }
  155 +
  156 + @Test
  157 + public void testDeleteDiscountActivity_success() {
  158 + // mock 数据
  159 + DiscountActivityDO dbDiscountActivity = randomPojo(DiscountActivityDO.class,
  160 + o -> o.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus()));
  161 + discountActivityMapper.insert(dbDiscountActivity);// @Sql: 先插入出一条存在的数据
  162 + // 准备参数
  163 + Long id = dbDiscountActivity.getId();
  164 +
  165 + // 调用
  166 + discountActivityService.deleteDiscountActivity(id);
  167 + // 校验数据不存在了
  168 + assertNull(discountActivityMapper.selectById(id));
  169 + }
  170 +
  171 + @Test
  172 + public void testDeleteDiscountActivity_notExists() {
  173 + // 准备参数
  174 + Long id = randomLongId();
  175 +
  176 + // 调用, 并断言异常
  177 + assertServiceException(() -> discountActivityService.deleteDiscountActivity(id), DISCOUNT_ACTIVITY_NOT_EXISTS);
  178 + }
  179 +
  180 + @Test
  181 + public void testGetDiscountActivityPage() {
  182 + // mock 数据
  183 + DiscountActivityDO dbDiscountActivity = randomPojo(DiscountActivityDO.class, o -> { // 等会查询到
  184 + o.setName("芋艿");
  185 + o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus());
  186 + o.setCreateTime(buildTime(2021, 1, 15));
  187 + });
  188 + discountActivityMapper.insert(dbDiscountActivity);
  189 + // 测试 name 不匹配
  190 + discountActivityMapper.insert(cloneIgnoreId(dbDiscountActivity, o -> o.setName("土豆")));
  191 + // 测试 status 不匹配
  192 + discountActivityMapper.insert(cloneIgnoreId(dbDiscountActivity, o -> o.setStatus(PromotionActivityStatusEnum.END.getStatus())));
  193 + // 测试 createTime 不匹配
  194 + discountActivityMapper.insert(cloneIgnoreId(dbDiscountActivity, o -> o.setCreateTime(buildTime(2021, 2, 10))));
  195 + // 准备参数
  196 + DiscountActivityPageReqVO reqVO = new DiscountActivityPageReqVO();
  197 + reqVO.setName("芋艿");
  198 + reqVO.setStatus(PromotionActivityStatusEnum.WAIT.getStatus());
  199 + reqVO.setCreateTime((new Date[]{buildTime(2021, 1, 1), buildTime(2021, 1, 31)}));
  200 +
  201 + // 调用
  202 + PageResult<DiscountActivityDO> pageResult = discountActivityService.getDiscountActivityPage(reqVO);
  203 + // 断言
  204 + assertEquals(1, pageResult.getTotal());
  205 + assertEquals(1, pageResult.getList().size());
  206 + assertPojoEquals(dbDiscountActivity, pageResult.getList().get(0));
  207 + }
  208 +
  209 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java
... ... @@ -2,20 +2,17 @@ package cn.iocoder.yudao.module.promotion.service.price;
2 2  
3 3 import cn.hutool.core.map.MapUtil;
4 4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  5 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  6 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
5 7 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
6 8 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
7 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
8   -import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
9 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
10   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
11   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionLevelEnum;
12   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
13   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
  11 +import cn.iocoder.yudao.module.promotion.enums.common.*;
14 12 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
15   -import cn.iocoder.yudao.module.promotion.service.discount.DiscountService;
16   -import cn.iocoder.yudao.module.promotion.service.reward.RewardService;
17   -import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
18   -import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  13 +import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
  14 +import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO;
  15 +import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
19 16 import org.junit.jupiter.api.Test;
20 17 import org.mockito.InjectMocks;
21 18 import org.mockito.Mock;
... ... @@ -44,9 +41,9 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
44 41 private PriceServiceImpl priceService;
45 42  
46 43 @Mock
47   - private DiscountService discountService;
  44 + private DiscountActivityService discountService;
48 45 @Mock
49   - private RewardService rewardService;
  46 + private RewardActivityService rewardActivityService;
50 47 @Mock
51 48 private CouponService couponService;
52 49 @Mock
... ... @@ -67,6 +64,7 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
67 64 // 断言 Order 部分
68 65 PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
69 66 assertEquals(order.getOriginalPrice(), 200);
  67 + assertEquals(order.getOrderPrice(), 180);
70 68 assertEquals(order.getDiscountPrice(), 0);
71 69 assertEquals(order.getPointPrice(), 0);
72 70 assertEquals(order.getDeliveryPrice(), 0);
... ... @@ -112,22 +110,28 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
112 110 ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50));
113 111 when(productSkuApi.getSkuList(eq(asSet(10L, 20L)))).thenReturn(asList(productSku01, productSku02));
114 112 // mock 方法(限时折扣 DiscountActivity 信息)
115   - DiscountProductDO discountProduct01 = randomPojo(DiscountProductDO.class, o -> o.setActivityId(1000L).setActivityName("活动 1000 号")
116   - .setSkuId(10L).setPromotionPrice(80));
117   - DiscountProductDO discountProduct02 = randomPojo(DiscountProductDO.class, o -> o.setActivityId(2000L).setActivityName("活动 2000 号")
118   - .setSkuId(20L).setPromotionPrice(40));
  113 + DiscountProductDetailBO discountProduct01 = randomPojo(DiscountProductDetailBO.class, o -> o.setActivityId(1000L)
  114 + .setActivityName("活动 1000 号").setSkuId(10L)
  115 + .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(40));
  116 + DiscountProductDetailBO discountProduct02 = randomPojo(DiscountProductDetailBO.class, o -> o.setActivityId(2000L)
  117 + .setActivityName("活动 2000 号").setSkuId(20L)
  118 + .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(60));
119 119 when(discountService.getMatchDiscountProducts(eq(asSet(10L, 20L)))).thenReturn(
120 120 MapUtil.builder(10L, discountProduct01).put(20L, discountProduct02).map());
121 121  
  122 + // 10L: 100 * 2 - 40 * 2 = 120
  123 + // 20L:50 * 3 - 50 * 3 * 0.4 = 90
  124 +
122 125 // 调用
123 126 PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO);
124 127 // 断言 Order 部分
125 128 PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
126 129 assertEquals(order.getOriginalPrice(), 350);
  130 + assertEquals(order.getOrderPrice(), 210);
127 131 assertEquals(order.getDiscountPrice(), 0);
128 132 assertEquals(order.getPointPrice(), 0);
129 133 assertEquals(order.getDeliveryPrice(), 0);
130   - assertEquals(order.getPayPrice(), 280);
  134 + assertEquals(order.getPayPrice(), 210);
131 135 assertNull(order.getCouponId());
132 136 // 断言 OrderItem 部分
133 137 assertEquals(order.getItems().size(), 2);
... ... @@ -136,19 +140,19 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
136 140 assertEquals(orderItem01.getCount(), 2);
137 141 assertEquals(orderItem01.getOriginalPrice(), 200);
138 142 assertEquals(orderItem01.getOriginalUnitPrice(), 100);
139   - assertEquals(orderItem01.getDiscountPrice(), 40);
140   - assertEquals(orderItem01.getPayPrice(), 160);
  143 + assertEquals(orderItem01.getDiscountPrice(), 80);
  144 + assertEquals(orderItem01.getPayPrice(), 120);
141 145 assertEquals(orderItem01.getOrderPartPrice(), 0);
142   - assertEquals(orderItem01.getOrderDividePrice(), 160);
  146 + assertEquals(orderItem01.getOrderDividePrice(), 120);
143 147 PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);
144 148 assertEquals(orderItem02.getSkuId(), 20L);
145 149 assertEquals(orderItem02.getCount(), 3);
146 150 assertEquals(orderItem02.getOriginalPrice(), 150);
147 151 assertEquals(orderItem02.getOriginalUnitPrice(), 50);
148   - assertEquals(orderItem02.getDiscountPrice(), 30);
149   - assertEquals(orderItem02.getPayPrice(), 120);
  152 + assertEquals(orderItem02.getDiscountPrice(), 60);
  153 + assertEquals(orderItem02.getPayPrice(), 90);
150 154 assertEquals(orderItem02.getOrderPartPrice(), 0);
151   - assertEquals(orderItem02.getOrderDividePrice(), 120);
  155 + assertEquals(orderItem02.getOrderDividePrice(), 90);
152 156 // 断言 Promotion 部分
153 157 assertEquals(priceCalculate.getPromotions().size(), 2);
154 158 PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);
... ... @@ -157,28 +161,28 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
157 161 assertEquals(promotion01.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType());
158 162 assertEquals(promotion01.getLevel(), PromotionLevelEnum.SKU.getLevel());
159 163 assertEquals(promotion01.getOriginalPrice(), 200);
160   - assertEquals(promotion01.getDiscountPrice(), 40);
  164 + assertEquals(promotion01.getDiscountPrice(), 80);
161 165 assertTrue(promotion01.getMeet());
162   - assertEquals(promotion01.getMeetTip(), "限时折扣:省 0.40 元");
  166 + assertEquals(promotion01.getMeetTip(), "限时折扣:省 0.80 元");
163 167 PriceCalculateRespDTO.PromotionItem promotionItem01 = promotion01.getItems().get(0);
164 168 assertEquals(promotion01.getItems().size(), 1);
165 169 assertEquals(promotionItem01.getSkuId(), 10L);
166 170 assertEquals(promotionItem01.getOriginalPrice(), 200);
167   - assertEquals(promotionItem01.getDiscountPrice(), 40);
  171 + assertEquals(promotionItem01.getDiscountPrice(), 80);
168 172 PriceCalculateRespDTO.Promotion promotion02 = priceCalculate.getPromotions().get(1);
169 173 assertEquals(promotion02.getId(), 2000L);
170 174 assertEquals(promotion02.getName(), "活动 2000 号");
171 175 assertEquals(promotion02.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType());
172 176 assertEquals(promotion02.getLevel(), PromotionLevelEnum.SKU.getLevel());
173 177 assertEquals(promotion02.getOriginalPrice(), 150);
174   - assertEquals(promotion02.getDiscountPrice(), 30);
  178 + assertEquals(promotion02.getDiscountPrice(), 60);
175 179 assertTrue(promotion02.getMeet());
176   - assertEquals(promotion02.getMeetTip(), "限时折扣:省 0.30 元");
  180 + assertEquals(promotion02.getMeetTip(), "限时折扣:省 0.60 元");
177 181 PriceCalculateRespDTO.PromotionItem promotionItem02 = promotion02.getItems().get(0);
178 182 assertEquals(promotion02.getItems().size(), 1);
179 183 assertEquals(promotionItem02.getSkuId(), 20L);
180 184 assertEquals(promotionItem02.getOriginalPrice(), 150);
181   - assertEquals(promotionItem02.getDiscountPrice(), 30);
  185 + assertEquals(promotionItem02.getDiscountPrice(), 60);
182 186 }
183 187  
184 188 /**
... ... @@ -208,13 +212,14 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
208 212 Map<RewardActivityDO, Set<Long>> matchRewardActivities = new LinkedHashMap<>();
209 213 matchRewardActivities.put(rewardActivity01, asSet(1L, 2L));
210 214 matchRewardActivities.put(rewardActivity02, asSet(3L));
211   - when(rewardService.getMatchRewardActivities(eq(asSet(1L, 2L, 3L)))).thenReturn(matchRewardActivities);
  215 + when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L, 3L)))).thenReturn(matchRewardActivities);
212 216  
213 217 // 调用
214 218 PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO);
215 219 // 断言 Order 部分
216 220 PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
217 221 assertEquals(order.getOriginalPrice(), 470);
  222 + assertEquals(order.getOrderPrice(), 470);
218 223 assertEquals(order.getDiscountPrice(), 130);
219 224 assertEquals(order.getPointPrice(), 0);
220 225 assertEquals(order.getDeliveryPrice(), 0);
... ... @@ -305,13 +310,14 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
305 310 .setRules(singletonList(new RewardActivityDO.Rule().setLimit(351).setDiscountPrice(70))));
306 311 Map<RewardActivityDO, Set<Long>> matchRewardActivities = new LinkedHashMap<>();
307 312 matchRewardActivities.put(rewardActivity01, asSet(1L, 2L));
308   - when(rewardService.getMatchRewardActivities(eq(asSet(1L, 2L)))).thenReturn(matchRewardActivities);
  313 + when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L)))).thenReturn(matchRewardActivities);
309 314  
310 315 // 调用
311 316 PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO);
312 317 // 断言 Order 部分
313 318 PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
314 319 assertEquals(order.getOriginalPrice(), 350);
  320 + assertEquals(order.getOrderPrice(), 350);
315 321 assertEquals(order.getDiscountPrice(), 0);
316 322 assertEquals(order.getPointPrice(), 0);
317 323 assertEquals(order.getDeliveryPrice(), 0);
... ... @@ -373,19 +379,18 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
373 379 ProductSkuRespDTO productSku03 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(30L).setPrice(30).setSpuId(3L));
374 380 when(productSkuApi.getSkuList(eq(asSet(10L, 20L, 30L)))).thenReturn(asList(productSku01, productSku02, productSku03));
375 381 // mock 方法(优惠劵 Coupon 信息)
376   - CouponDO coupon = randomPojo(CouponDO.class, o -> o.setId(1024L).setTitle("程序员节")
377   - .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setSpuIds(asList(1L, 2L))
378   - .setPriceAvailable(350).setPreferentialType(2).setPercentOff(50).setDiscountPriceLimit(70));
  382 + CouponDO coupon = randomPojo(CouponDO.class, o -> o.setId(1024L).setName("程序员节")
  383 + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L))
  384 + .setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType())
  385 + .setDiscountPercent(50).setDiscountLimitPrice(70));
379 386 when(couponService.validCoupon(eq(1024L), eq(calculateReqDTO.getUserId()))).thenReturn(coupon);
380 387  
381   - // 200 + 150; 350
382   - //
383   -
384 388 // 调用
385 389 PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO);
386 390 // 断言 Order 部分
387 391 PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
388 392 assertEquals(order.getOriginalPrice(), 470);
  393 + assertEquals(order.getOrderPrice(), 470);
389 394 assertEquals(order.getDiscountPrice(), 0);
390 395 assertEquals(order.getPointPrice(), 0);
391 396 assertEquals(order.getDeliveryPrice(), 0);
... ...
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImplTest.java 0 → 100755
  1 +package cn.iocoder.yudao.module.promotion.service.reward;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
  8 +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  9 +import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper;
  10 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
  11 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
  12 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  13 +import org.junit.jupiter.api.Test;
  14 +import org.springframework.context.annotation.Import;
  15 +
  16 +import javax.annotation.Resource;
  17 +import java.time.Duration;
  18 +import java.util.Map;
  19 +import java.util.Set;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.randomEle;
  22 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  23 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime;
  24 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  25 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  26 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  27 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  28 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  29 +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.REWARD_ACTIVITY_NOT_EXISTS;
  30 +import static java.util.Arrays.asList;
  31 +import static java.util.Collections.singletonList;
  32 +import static org.junit.jupiter.api.Assertions.*;
  33 +
  34 +/**
  35 +* {@link RewardActivityServiceImpl} 的单元测试类
  36 +*
  37 +* @author 芋道源码
  38 +*/
  39 +@Import(RewardActivityServiceImpl.class)
  40 +public class RewardActivityServiceImplTest extends BaseDbUnitTest {
  41 +
  42 + @Resource
  43 + private RewardActivityServiceImpl rewardActivityService;
  44 +
  45 + @Resource
  46 + private RewardActivityMapper rewardActivityMapper;
  47 +
  48 + @Test
  49 + public void testCreateRewardActivity_success() {
  50 + // 准备参数
  51 + RewardActivityCreateReqVO reqVO = randomPojo(RewardActivityCreateReqVO.class, o -> {
  52 + o.setConditionType(randomEle(PromotionConditionTypeEnum.values()).getType());
  53 + o.setProductScope(randomEle(PromotionProductScopeEnum.values()).getScope());
  54 + // 用于触发进行中的状态
  55 + o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2)));
  56 + });
  57 +
  58 + // 调用
  59 + Long rewardActivityId = rewardActivityService.createRewardActivity(reqVO);
  60 + // 断言
  61 + assertNotNull(rewardActivityId);
  62 + // 校验记录的属性是否正确
  63 + RewardActivityDO rewardActivity = rewardActivityMapper.selectById(rewardActivityId);
  64 + assertPojoEquals(reqVO, rewardActivity, "rules");
  65 + assertEquals(rewardActivity.getStatus(), PromotionActivityStatusEnum.WAIT.getStatus());
  66 + for (int i = 0; i < reqVO.getRules().size(); i++) {
  67 + assertPojoEquals(reqVO.getRules().get(i), rewardActivity.getRules().get(i));
  68 + }
  69 + }
  70 +
  71 + @Test
  72 + public void testUpdateRewardActivity_success() {
  73 + // mock 数据
  74 + RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus()));
  75 + rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据
  76 + // 准备参数
  77 + RewardActivityUpdateReqVO reqVO = randomPojo(RewardActivityUpdateReqVO.class, o -> {
  78 + o.setId(dbRewardActivity.getId()); // 设置更新的 ID
  79 + o.setConditionType(randomEle(PromotionConditionTypeEnum.values()).getType());
  80 + o.setProductScope(randomEle(PromotionProductScopeEnum.values()).getScope());
  81 + // 用于触发进行中的状态
  82 + o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2)));
  83 + });
  84 +
  85 + // 调用
  86 + rewardActivityService.updateRewardActivity(reqVO);
  87 + // 校验是否更新正确
  88 + RewardActivityDO rewardActivity = rewardActivityMapper.selectById(reqVO.getId()); // 获取最新的
  89 + assertPojoEquals(reqVO, rewardActivity, "rules");
  90 + assertEquals(rewardActivity.getStatus(), PromotionActivityStatusEnum.WAIT.getStatus());
  91 + for (int i = 0; i < reqVO.getRules().size(); i++) {
  92 + assertPojoEquals(reqVO.getRules().get(i), rewardActivity.getRules().get(i));
  93 + }
  94 + }
  95 +
  96 + @Test
  97 + public void testCloseRewardActivity() {
  98 + // mock 数据
  99 + RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus()));
  100 + rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据
  101 + // 准备参数
  102 + Long id = dbRewardActivity.getId();
  103 +
  104 + // 调用
  105 + rewardActivityService.closeRewardActivity(id);
  106 + // 校验状态
  107 + RewardActivityDO rewardActivity = rewardActivityMapper.selectById(id);
  108 + assertEquals(rewardActivity.getStatus(), PromotionActivityStatusEnum.CLOSE.getStatus());
  109 + }
  110 +
  111 + @Test
  112 + public void testUpdateRewardActivity_notExists() {
  113 + // 准备参数
  114 + RewardActivityUpdateReqVO reqVO = randomPojo(RewardActivityUpdateReqVO.class);
  115 +
  116 + // 调用, 并断言异常
  117 + assertServiceException(() -> rewardActivityService.updateRewardActivity(reqVO), REWARD_ACTIVITY_NOT_EXISTS);
  118 + }
  119 +
  120 + @Test
  121 + public void testDeleteRewardActivity_success() {
  122 + // mock 数据
  123 + RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus()));
  124 + rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据
  125 + // 准备参数
  126 + Long id = dbRewardActivity.getId();
  127 +
  128 + // 调用
  129 + rewardActivityService.deleteRewardActivity(id);
  130 + // 校验数据不存在了
  131 + assertNull(rewardActivityMapper.selectById(id));
  132 + }
  133 +
  134 + @Test
  135 + public void testDeleteRewardActivity_notExists() {
  136 + // 准备参数
  137 + Long id = randomLongId();
  138 +
  139 + // 调用, 并断言异常
  140 + assertServiceException(() -> rewardActivityService.deleteRewardActivity(id), REWARD_ACTIVITY_NOT_EXISTS);
  141 + }
  142 +
  143 + @Test
  144 + public void testGetRewardActivityPage() {
  145 + // mock 数据
  146 + RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> { // 等会查询到
  147 + o.setName("芋艿");
  148 + o.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus());
  149 + });
  150 + rewardActivityMapper.insert(dbRewardActivity);
  151 + // 测试 name 不匹配
  152 + rewardActivityMapper.insert(cloneIgnoreId(dbRewardActivity, o -> o.setName("土豆")));
  153 + // 测试 status 不匹配
  154 + rewardActivityMapper.insert(cloneIgnoreId(dbRewardActivity, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus())));
  155 + // 准备参数
  156 + RewardActivityPageReqVO reqVO = new RewardActivityPageReqVO();
  157 + reqVO.setName("芋艿");
  158 + reqVO.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus());
  159 +
  160 + // 调用
  161 + PageResult<RewardActivityDO> pageResult = rewardActivityService.getRewardActivityPage(reqVO);
  162 + // 断言
  163 + assertEquals(1, pageResult.getTotal());
  164 + assertEquals(1, pageResult.getList().size());
  165 + assertPojoEquals(dbRewardActivity, pageResult.getList().get(0), "rules");
  166 + }
  167 +
  168 + @Test
  169 + public void testGetRewardActivities_all() {
  170 + // mock 数据
  171 + RewardActivityDO allActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus())
  172 + .setProductScope(PromotionProductScopeEnum.ALL.getScope()));
  173 + rewardActivityMapper.insert(allActivity);
  174 + RewardActivityDO productActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus())
  175 + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L)));
  176 + rewardActivityMapper.insert(productActivity);
  177 + // 准备参数
  178 + Set<Long> spuIds = asSet(1L, 2L);
  179 +
  180 + // 调用
  181 + Map<RewardActivityDO, Set<Long>> matchRewardActivities = rewardActivityService.getMatchRewardActivities(spuIds);
  182 + // 断言
  183 + assertEquals(matchRewardActivities.size(), 1);
  184 + Map.Entry<RewardActivityDO, Set<Long>> next = matchRewardActivities.entrySet().iterator().next();
  185 + assertPojoEquals(next.getKey(), allActivity);
  186 + assertEquals(next.getValue(), spuIds);
  187 + }
  188 +
  189 + @Test
  190 + public void testGetRewardActivities_product() {
  191 + // mock 数据
  192 + RewardActivityDO productActivity01 = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus())
  193 + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L)));
  194 + rewardActivityMapper.insert(productActivity01);
  195 + RewardActivityDO productActivity02 = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.RUN.getStatus())
  196 + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(singletonList(3L)));
  197 + rewardActivityMapper.insert(productActivity02);
  198 + // 准备参数
  199 + Set<Long> spuIds = asSet(1L, 2L, 3L);
  200 +
  201 + // 调用
  202 + Map<RewardActivityDO, Set<Long>> matchRewardActivities = rewardActivityService.getMatchRewardActivities(spuIds);
  203 + // 断言
  204 + assertEquals(matchRewardActivities.size(), 2);
  205 + matchRewardActivities.forEach((activity, activitySpuIds) -> {
  206 + if (activity.getId().equals(productActivity01.getId())) {
  207 + assertPojoEquals(activity, productActivity01);
  208 + assertEquals(activitySpuIds, asSet(1L, 2L));
  209 + } else if (activity.getId().equals(productActivity02.getId())) {
  210 + assertPojoEquals(activity, productActivity02);
  211 + assertEquals(activitySpuIds, asSet(3L));
  212 + } else {
  213 + fail();
  214 + }
  215 + });
  216 + }
  217 +
  218 +}
... ...
yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/clean.sql
1 1 DELETE FROM "market_activity";
  2 +DELETE FROM "promotion_coupon_template";
  3 +DELETE FROM "promotion_coupon";
  4 +DELETE FROM "promotion_reward_activity";
  5 +DELETE FROM "promotion_discount_activity";
  6 +DELETE FROM "promotion_discount_product";
... ...
yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql
... ... @@ -16,4 +16,109 @@ CREATE TABLE IF NOT EXISTS &quot;market_activity&quot; (
16 16 "deleted" bit NOT NULL DEFAULT FALSE,
17 17 "tenant_id" bigint(20) NOT NULL,
18 18 PRIMARY KEY ("id")
19   - ) COMMENT '促销活动';
20 19 \ No newline at end of file
  20 + ) COMMENT '促销活动';
  21 +
  22 +CREATE TABLE IF NOT EXISTS "promotion_coupon_template" (
  23 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  24 + "name" varchar NOT NULL,
  25 + "status" int NOT NULL,
  26 + "total_count" int NOT NULL,
  27 + "take_limit_count" int NOT NULL,
  28 + "take_type" int NOT NULL,
  29 + "use_price" int NOT NULL,
  30 + "product_scope" int NOT NULL,
  31 + "product_spu_ids" varchar,
  32 + "validity_type" int NOT NULL,
  33 + "valid_start_time" datetime,
  34 + "valid_end_time" datetime,
  35 + "fixed_start_term" int,
  36 + "fixed_end_term" int,
  37 + "discount_type" int NOT NULL,
  38 + "discount_percent" int,
  39 + "discount_price" int,
  40 + "discount_limit_price" int,
  41 + "take_count" int NOT NULL DEFAULT 0,
  42 + "use_count" int NOT NULL DEFAULT 0,
  43 + "creator" varchar DEFAULT '',
  44 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  45 + "updater" varchar DEFAULT '',
  46 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  47 + "deleted" bit NOT NULL DEFAULT FALSE,
  48 + PRIMARY KEY ("id")
  49 +) COMMENT '优惠劵模板';
  50 +
  51 +CREATE TABLE IF NOT EXISTS "promotion_coupon" (
  52 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  53 + "template_id" bigint NOT NULL,
  54 + "name" varchar NOT NULL,
  55 + "status" int NOT NULL,
  56 + "user_id" bigint NOT NULL,
  57 + "take_type" int NOT NULL,
  58 + "useprice" int NOT NULL,
  59 + "valid_start_time" datetime NOT NULL,
  60 + "valid_end_time" datetime NOT NULL,
  61 + "product_scope" int NOT NULL,
  62 + "product_spu_ids" varchar,
  63 + "discount_type" int NOT NULL,
  64 + "discount_percent" int,
  65 + "discount_price" int,
  66 + "discount_limit_price" int,
  67 + "use_order_id" bigint,
  68 + "use_time" datetime,
  69 + "creator" varchar DEFAULT '',
  70 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  71 + "updater" varchar DEFAULT '',
  72 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  73 + "deleted" bit NOT NULL DEFAULT FALSE,
  74 + PRIMARY KEY ("id")
  75 +) COMMENT '优惠劵';
  76 +
  77 +CREATE TABLE IF NOT EXISTS "promotion_reward_activity" (
  78 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  79 + "name" varchar NOT NULL,
  80 + "status" int NOT NULL,
  81 + "start_time" datetime NOT NULL,
  82 + "end_time" datetime NOT NULL,
  83 + "remark" varchar,
  84 + "condition_type" int NOT NULL,
  85 + "product_scope" int NOT NULL,
  86 + "product_spu_ids" varchar,
  87 + "rules" varchar,
  88 + "creator" varchar DEFAULT '',
  89 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  90 + "updater" varchar DEFAULT '',
  91 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  92 + "deleted" bit NOT NULL DEFAULT FALSE,
  93 + PRIMARY KEY ("id")
  94 +) COMMENT '满减送活动';
  95 +
  96 +CREATE TABLE IF NOT EXISTS "promotion_discount_activity" (
  97 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  98 + "name" varchar NOT NULL,
  99 + "status" int NOT NULL,
  100 + "start_time" datetime NOT NULL,
  101 + "end_time" datetime NOT NULL,
  102 + "remark" varchar,
  103 + "creator" varchar DEFAULT '',
  104 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  105 + "updater" varchar DEFAULT '',
  106 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  107 + "deleted" bit NOT NULL DEFAULT FALSE,
  108 + PRIMARY KEY ("id")
  109 +) COMMENT '限时折扣活动';
  110 +
  111 +CREATE TABLE IF NOT EXISTS "promotion_discount_product" (
  112 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  113 + "activity_id" bigint NOT NULL,
  114 + "spu_id" bigint NOT NULL,
  115 + "sku_id" bigint NOT NULL,
  116 + "discount_type" int NOT NULL,
  117 + "discount_percent" int,
  118 + "discount_price" int,
  119 + "creator" varchar DEFAULT '',
  120 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  121 + "updater" varchar DEFAULT '',
  122 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  123 + "deleted" bit NOT NULL DEFAULT FALSE,
  124 + PRIMARY KEY ("id")
  125 +) COMMENT '限时折扣活动';
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
... ... @@ -43,13 +43,12 @@ public class AppTradeOrderController {
43 43 @PreAuthenticated
44 44 public CommonResult<Long> createTradeOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO,
45 45 HttpServletRequest servletRequest) {
46   - // 获取登录用户
  46 + // 获取登录用户、用户 IP 地址
47 47 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
48   - // 获取用户ip地址
49 48 String clientIp = ServletUtil.getClientIP(servletRequest);
50 49 // 创建交易订单,预支付记录
51   - Long result = tradeOrderService.createTradeOrder(loginUserId, clientIp, createReqVO);
52   - return CommonResult.success(result);
  50 + Long orderId = tradeOrderService.createTradeOrder(loginUserId, clientIp, createReqVO);
  51 + return CommonResult.success(orderId);
53 52 }
54 53  
55 54 @GetMapping("/get")
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java
... ... @@ -19,7 +19,7 @@ public class AppTradeOrderCreateReqVO {
19 19 @ApiModelProperty(name = "优惠劵编号", example = "1024")
20 20 private Long couponId;
21 21  
22   - @ApiModelProperty(name = "备注", example = "1024")
  22 + @ApiModelProperty(name = "备注", example = "这个是我的订单哟")
23 23 private String remark;
24 24  
25 25 @ApiModelProperty(name = "是否来自购物车", required = true, example = "true", notes = "true - 来自购物车;false - 立即购买")
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.order;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO.OrderItem;
5 6 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderCancelTypeEnum;
6 7 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum;
7 8 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
... ... @@ -74,7 +75,7 @@ public class TradeOrderDO extends BaseDO {
74 75 /**
75 76 * 购买的商品数量
76 77 */
77   - private Integer productCount; // total_num
  78 + private Integer productCount;
78 79 /**
79 80 * 订单完成时间
80 81 */
... ... @@ -116,14 +117,25 @@ public class TradeOrderDO extends BaseDO {
116 117 * 商品原价(总),单位:分
117 118 *
118 119 * 基于 {@link TradeOrderItemDO#getTotalOriginalPrice()} 求和
  120 + *
  121 + * 对应 taobao 的 trade.total_fee 字段
119 122 */
120 123 private Integer originalPrice;
121 124 /**
  125 + * 订单原价(总),单位:分
  126 + *
  127 + * 基于 {@link OrderItem#getPayPrice()} 求和
  128 + * 和 {@link #originalPrice} 的差异:去除商品级优惠
  129 + */
  130 + private Integer orderPrice;
  131 + /**
122 132 * 订单优惠(总),单位:分
123 133 *
124   - * 例如说:满减折扣;不包括优惠劵、商品优惠(TODO)
  134 + * 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。
  135 + *
  136 + * 对应 taobao 的 order.discount_fee 字段
125 137 */
126   - private Integer promotionPrice;
  138 + private Integer discountPrice;
127 139 /**
128 140 * 运费金额,单位:分
129 141 */
... ... @@ -137,12 +149,12 @@ public class TradeOrderDO extends BaseDO {
137 149 /**
138 150 * 应付金额(总),单位:分
139 151 *
140   - * = {@link #originalPrice}
141   - * + {@link #deliveryPrice}
142   - * + {@link #adjustPrice}
143   - * - {@link #promotionPrice}
  152 + * = {@link OrderItem#getPayPrice()} 求和
144 153 * - {@link #couponPrice}
145 154 * - {@link #pointPrice}
  155 + * + {@link #deliveryPrice}
  156 + * - {@link #discountPrice}
  157 + * + {@link #adjustPrice}
146 158 */
147 159 private Integer payPrice;
148 160 /**
... ... @@ -164,11 +176,11 @@ public class TradeOrderDO extends BaseDO {
164 176 *
165 177 * 关联 DeliveryTemplateDO 的 id 编号
166 178 */
167   - private Long deliveryTemplateId; // dvy_id
  179 + private Long deliveryTemplateId;
168 180 /**
169 181 * 物流公司单号
170 182 */
171   - private String expressNo; // dvy_flow_id
  183 + private String expressNo;
172 184 /**
173 185 * 发货状态
174 186 *
... ... @@ -227,10 +239,14 @@ public class TradeOrderDO extends BaseDO {
227 239 private Long couponId;
228 240 /**
229 241 * 优惠劵减免金额,单位:分
  242 + *
  243 + * 对应 taobao 的 trade.coupon_fee 字段
230 244 */
231 245 private Integer couponPrice;
232 246 /**
233 247 * 积分抵扣的金额,单位:分
  248 + *
  249 + * 对应 taobao 的 trade.point_fee 字段
234 250 */
235 251 private Integer pointPrice;
236 252  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java
... ... @@ -71,7 +71,7 @@ public class TradeOrderItemDO extends BaseDO {
71 71 */
72 72 private Integer count;
73 73 /**
74   - * 是否评论
  74 + * 是否评论 TODO
75 75 *
76 76 * false - 未评论
77 77 * true - 已评论
... ... @@ -79,61 +79,61 @@ public class TradeOrderItemDO extends BaseDO {
79 79 private Boolean commented;
80 80  
81 81 // ========== 价格 + 支付基本信息 ==========
  82 +
82 83 /**
83   - * 商品原价(单),单位:分
  84 + * 商品原价(总),单位:分
84 85 *
85   - * 对应 ProductSkuDO 的 price 字段
  86 + * = {@link #originalUnitPrice} * {@link #getCount()}
86 87 */
87   - // like - original_price;niu - costPrice
88 88 private Integer originalPrice;
89 89 /**
90   - * 商品原价(总),单位:分
  90 + * 商品原价(单),单位:分
91 91 *
92   - * = {@link #originalPrice} * {@link #count}
  92 + * 对应 ProductSkuDO 的 price 字段
  93 + * 对应 taobao 的 order.price 字段
93 94 */
94   - // like - total_price;niu - 暂无
95   - private Integer totalOriginalPrice;
  95 + private Integer originalUnitPrice;
96 96 /**
97   - * 商品级优惠(总),单位:分
  97 + * 商品优惠(总),单位:分
  98 + *
  99 + * 商品级优惠:对单个商品的,常见如:商品原价的 8 折;商品原价的减 50 元
98 100 *
99   - * 例如说“限时折扣”:商品原价的 8 折;商品原价的减 50 元
  101 + * 对应 taobao 的 order.discount_fee 字段
100 102 */
101   - // taobao - order.discount_fee(子订单商品优惠)
102   - private Integer totalPromotionPrice;
  103 + private Integer discountPrice;
103 104 /**
104   - * 最终购买金额(单),单位:分。
  105 + * 子订单实付金额,不算主订单分摊金额,单位:分
  106 + *
  107 + * = {@link #originalPrice}
  108 + * - {@link #discountPrice}
105 109 *
106   - * = {@link #totalPresentPrice} / {@link #count}
  110 + * 对应 taobao 的 order.payment 字段
107 111 */
108   - private Integer presentPrice;
  112 + private Integer payPrice;
  113 +
109 114 /**
110   - * 最终购买金额(总),单位:分。
  115 + * 子订单分摊金额(总),单位:分
  116 + * 需要分摊 {@link TradeOrderDO#getDiscountPrice()}、{@link TradeOrderDO#getCouponPrice()}、{@link TradeOrderDO#getPointPrice()}
111 117 *
112   - * = {@link #totalOriginalPrice}
113   - * - {@link #totalPromotionPrice}
  118 + * 对应 taobao 的 order.part_mjz_discount 字段
  119 + * 淘宝说明:子订单分摊优惠基础逻辑:一般正常优惠券和满减优惠按照子订单的金额进行分摊,特殊情况如果优惠券是指定商品使用的,只会分摊到对应商品子订单上不分摊。
114 120 */
115   - // like - total_pay_price;niu - goods_money; taobao - order.payment(子订单实付金额,不算主订单分摊金额) | order.total_fee(子订单应付金额,参考使用)
116   - private Integer totalPresentPrice;
117   - // TODO 芋艿:part_mjz_discount(子订单分摊金额);本质上,totalOriginalPrice - totalPayPrice
  121 + private Integer orderPartPrice;
118 122 /**
119   - * 应付金额(总),单位:分
  123 + * 分摊后子订单实付金额(总),单位:分
  124 + *
  125 + * = {@link #payPrice}
  126 + * - {@link #orderPartPrice}
  127 + *
  128 + * 对应 taobao 的 divide_order_fee 字段
120 129 */
121   - // taobao - divide_order_fee (分摊后子订单实付金额);
122   - private Integer totalPayPrice;
  130 + private Integer orderDividePrice;
123 131  
124 132 // ========== 营销基本信息 ==========
125   -// /**
126   -// * 积分抵扣的金额,单位:分
127   -// */
128   -// private Integer integralTotal; // like - integral_price;niu - point_money
129   -// /**
130   -// * 使用的积分
131   -// */
132   -// private Integer useIntegral; // niu - use_point
133 133  
134 134 // ========== 退款基本信息 ==========
135 135 /**
136   - * 退款状态
  136 + * 退款状态 TODO
137 137 *
138 138 * 枚举 {@link TradeOrderItemRefundStatusEnum}
139 139 */
... ... @@ -148,7 +148,7 @@ public class TradeOrderItemDO extends BaseDO {
148 148 // presentTotal = buyTotal - discountTotal = 24 - 11 = 13
149 149 // 最终 presentPrice = presentTotal / stock = 13 / 3 = 4.33
150 150 /**
151   - * 退款总金额,单位:分
  151 + * 退款总金额,单位:分 TODO
152 152 */
153 153 private Integer refundTotal;
154 154  
... ... @@ -173,42 +173,5 @@ public class TradeOrderItemDO extends BaseDO {
173 173  
174 174 }
175 175  
176   - // TODO 芋艿:basket_date 加入购物车时间;
177   - // TODO 芋艿:distribution_card_no 推广员使用的推销卡号
178   -
179   - // TODO 待确定:mf
180   - // TODO give_integral:赠送积分
181   - // TODO is_reply:是否评价,0-未评价,1-已评价
182   - // TODO is_sub:是否单独分佣,0-否,1-是
183   - // TODO vip_price:会员价
184   - // TODO product_type:商品类型:0-普通,1-秒杀,2-砍价,3-拼团,4-视频号
185   -
186   - // TODO 待确定:lf
187   - // TODO integral_price:积分抵扣的金额
188   - // TODO member_price:会员价格
189   - // TODO is_member:是否为会员折扣;0-不是;1-是
190   - // TODO member_discount:会员折扣(百分比)
191   -
192   - // TODO goods_info 商品信息
193   -
194   - // TODO integral_price:积分抵扣的金额
195   -
196   - // TODO 待确定:niu
197   - // TODO is_virtual '是否是虚拟商品'
198   - // TODO goods_class '商品种类(1.实物 2.虚拟3.卡券)'
199   - // TODO adjust_money ''调整金额''
200   -
201   - // TODO is_fenxiao 是否分销,
202   - // TODO adjust_money 是否分销,
203   -
204   - // TODO delivery_status '配送状态'
205   - // TODO delivery_no ''配送单号''
206   - // TODO gift_flag '赠品标识'
207   - // TODO gift_flag '赠品标识'
208   -
209   - // TODO refund_status '退款状态'
210   - // TODO refund_type '退款状态'
211   - // TODO 一堆退款字段
212   -
213 176 }
214 177  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderService.java
... ... @@ -3,18 +3,20 @@ package cn.iocoder.yudao.module.trade.service.order;
3 3 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
4 4  
5 5 /**
6   - * TODO @LeeYan9: 类注释
  6 + * 交易订单 Service 接口
  7 + *
7 8 * @author LeeYan9
8 9 * @since 2022-08-26
9 10 */
10 11 public interface TradeOrderService {
11 12  
12 13 /**
13   - * 创建交易订单 TODO @LeeYan9: 方法注释, 和参数要空一行
  14 + * 创建交易订单
  15 + *
14 16 * @param loginUserId 登录用户
15   - * @param clientIp 用户ip地址 // TODO @LeeYan9: 中英文之间, 空一行哈
  17 + * @param clientIp 用户 IP 地址
16 18 * @param createReqVO 创建交易订单请求模型
17   - * @return 交易订单创建结果
  19 + * @return 交易订单的编号
18 20 */
19 21 Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO);
20 22 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
... ... @@ -45,14 +45,17 @@ import java.util.Map;
45 45 import java.util.Objects;
46 46  
47 47 /**
48   - * TODO @LeeYan9: 注释
  48 + * 交易订单 Service 实现类
  49 + *
49 50 * @author LeeYan9
50 51 * @since 2022-08-26
51 52 */
52 53 @Service
53 54 public class TradeOrderServiceImpl implements TradeOrderService {
54 55  
55   - // TODO @LeeYan9: 相同类型的, 可以放在一起,不用空行; 例如说 Mapper 和 API 和 Properties
  56 + // TODO LeeYan9: 静态变量, 需要在最前面哈; 另外, 静态变量的注释最好写下;
  57 + private static final String BLANK_PLACEHOLDER = " ";
  58 + private static final String MULTIPLIER_PLACEHOLDER = "x";
56 59  
57 60 @Resource
58 61 private TradeOrderMapper tradeOrderMapper;
... ... @@ -71,15 +74,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
71 74 @Resource
72 75 private TradeOrderProperties tradeOrderProperties;
73 76  
74   - // TODO LeeYan9: 静态变量, 需要在最前面哈; 另外, 静态变量的注释最好写下;
75   - private static final String BLANK_PLACEHOLDER = " ";
76   - private static final String MULTIPLIER_PLACEHOLDER = "x";
77   -
78 77 @Override
79 78 @Transactional(rollbackFor = Exception.class)
80 79 public Long createTradeOrder(Long loginUserId, String clientIp, AppTradeOrderCreateReqVO createReqVO) {
81   -
82   - List<Item> items = createReqVO.getItems(); // TODO @LeeYan9: 方法第一行, 不用空哈;
  80 + List<Item> items = createReqVO.getItems();
83 81 // 商品SKU检查 sku可售状态,库存
84 82 List<ProductSkuRespDTO> skuInfos = productSkuApi.getSkuList(CollectionUtils.convertSet(items, Item::getSkuId));
85 83 Map<Long, ProductSkuRespDTO> skuInfoMap = CollectionUtils.convertMap(skuInfos, ProductSkuRespDTO::getId);
... ...
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApi.java
... ... @@ -2,6 +2,12 @@ package cn.iocoder.yudao.module.member.api.user;
2 2  
3 3 import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
4 4  
  5 +import java.util.Collection;
  6 +import java.util.List;
  7 +import java.util.Map;
  8 +
  9 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  10 +
5 11 /**
6 12 * 会员用户的 API 接口
7 13 *
... ... @@ -17,4 +23,30 @@ public interface MemberUserApi {
17 23 */
18 24 UserRespDTO getUser(Long id);
19 25  
  26 + /**
  27 + * 获得会员用户信息们
  28 + *
  29 + * @param ids 用户编号的数组
  30 + * @return 用户信息们
  31 + */
  32 + List<UserRespDTO> getUsers(Collection<Long> ids);
  33 +
  34 + /**
  35 + * 获得会员用户 Map
  36 + *
  37 + * @param ids 用户编号的数组
  38 + * @return 会员用户 Map
  39 + */
  40 + default Map<Long, UserRespDTO> getUserMap(Collection<Long> ids) {
  41 + return convertMap(getUsers(ids), UserRespDTO::getId);
  42 + }
  43 +
  44 + /**
  45 + * 基于用户昵称,模糊匹配用户列表
  46 + *
  47 + * @param nickname 用户昵称,模糊匹配
  48 + * @return 用户信息的列表
  49 + */
  50 + List<UserRespDTO> getUserListByNickname(String nickname);
  51 +
20 52 }
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/user/MemberUserApiImpl.java
... ... @@ -8,6 +8,8 @@ import org.springframework.stereotype.Service;
8 8 import org.springframework.validation.annotation.Validated;
9 9  
10 10 import javax.annotation.Resource;
  11 +import java.util.Collection;
  12 +import java.util.List;
11 13  
12 14 /**
13 15 * 会员用户的 API 实现类
... ... @@ -27,4 +29,14 @@ public class MemberUserApiImpl implements MemberUserApi {
27 29 return UserConvert.INSTANCE.convert2(user);
28 30 }
29 31  
  32 + @Override
  33 + public List<UserRespDTO> getUsers(Collection<Long> ids) {
  34 + return UserConvert.INSTANCE.convertList2(userService.getUserList(ids));
  35 + }
  36 +
  37 + @Override
  38 + public List<UserRespDTO> getUserListByNickname(String nickname) {
  39 + return UserConvert.INSTANCE.convertList2(userService.getUserListByNickname(nickname));
  40 + }
  41 +
30 42 }
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java deleted
1   -package cn.iocoder.yudao.module.member.controller.admin.user;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.member.api.user.dto.UserInfoDTO;
5   -import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
6   -import cn.iocoder.yudao.module.member.convert.user.UserConvert;
7   -import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
8   -import cn.iocoder.yudao.module.member.service.user.MemberUserService;
9   -import io.swagger.annotations.Api;
10   -import io.swagger.annotations.ApiOperation;
11   -import lombok.extern.slf4j.Slf4j;
12   -import org.springframework.web.bind.annotation.GetMapping;
13   -import org.springframework.web.bind.annotation.PathVariable;
14   -import org.springframework.web.bind.annotation.RequestMapping;
15   -import org.springframework.web.bind.annotation.RestController;
16   -
17   -import javax.annotation.Resource;
18   -
19   -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
20   -
21   -/**
22   - * @author Banging
23   - */
24   -@Slf4j
25   -@Api("用户管理")
26   -@RestController(value = "memberUserController")
27   -@RequestMapping("/user")
28   -public class UserController {
29   -
30   - @Resource
31   - private MemberUserService userService;
32   -
33   - @ApiOperation(value = "用户信息获取",notes = "用户基本信息的获取")
34   - @GetMapping("/{tel}")
35   - public CommonResult<UserInfoDTO> getUserInfo(@PathVariable String tel){
36   - MemberUserDO user = userService.getUserByMobile(tel);
37   - return CommonResult.success(UserConvert.INSTANCE.convertInfo(user));
38   - }
39   -}
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/user/UserConvert.java
1 1 package cn.iocoder.yudao.module.member.convert.user;
2 2  
3   -import cn.iocoder.yudao.module.member.api.user.dto.UserInfoDTO;
4 3 import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
5 4 import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO;
6 5 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
7 6 import org.mapstruct.Mapper;
8 7 import org.mapstruct.factory.Mappers;
9 8  
  9 +import java.util.List;
  10 +
10 11 @Mapper
11 12 public interface UserConvert {
12 13  
... ... @@ -15,5 +16,7 @@ public interface UserConvert {
15 16 AppUserInfoRespVO convert(MemberUserDO bean);
16 17  
17 18 UserRespDTO convert2(MemberUserDO bean);
18   - UserInfoDTO convertInfo(MemberUserDO bean);
  19 +
  20 + List<UserRespDTO> convertList2(List<MemberUserDO> list);
  21 +
19 22 }
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java
1 1 package cn.iocoder.yudao.module.member.dal.mysql.user;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
4 5 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
5 6 import org.apache.ibatis.annotations.Mapper;
6 7  
  8 +import java.util.List;
  9 +
7 10 /**
8 11 * 会员 User Mapper
9 12 *
... ... @@ -16,4 +19,9 @@ public interface MemberUserMapper extends BaseMapperX&lt;MemberUserDO&gt; {
16 19 return selectOne(MemberUserDO::getMobile, mobile);
17 20 }
18 21  
  22 + default List<MemberUserDO> selectListByNicknameLike(String nickname) {
  23 + return selectList(new LambdaQueryWrapperX<MemberUserDO>()
  24 + .likeIfPresent(MemberUserDO::getNickname, nickname));
  25 + }
  26 +
19 27 }
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java
... ... @@ -5,6 +5,8 @@ import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobile
5 5 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
6 6  
7 7 import java.io.InputStream;
  8 +import java.util.Collection;
  9 +import java.util.List;
8 10  
9 11 /**
10 12 * 会员用户 Service 接口
... ... @@ -22,6 +24,15 @@ public interface MemberUserService {
22 24 MemberUserDO getUserByMobile(String mobile);
23 25  
24 26 /**
  27 + * 基于用户昵称,模糊匹配用户列表
  28 + *
  29 + * @param nickname 用户昵称,模糊匹配
  30 + * @return 用户信息的列表
  31 + */
  32 + List<MemberUserDO> getUserListByNickname(String nickname);
  33 +
  34 +
  35 + /**
25 36 * 基于手机号创建用户。
26 37 * 如果用户已经存在,则直接进行返回
27 38 *
... ... @@ -48,6 +59,14 @@ public interface MemberUserService {
48 59 MemberUserDO getUser(Long id);
49 60  
50 61 /**
  62 + * 通过用户 ID 查询用户们
  63 + *
  64 + * @param ids 用户 ID
  65 + * @return 用户对象信息数组
  66 + */
  67 + List<MemberUserDO> getUserList(Collection<Long> ids);
  68 +
  69 + /**
51 70 * 修改用户昵称
52 71 * @param userId 用户id
53 72 * @param nickname 用户新昵称
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
... ... @@ -19,7 +19,9 @@ import org.springframework.transaction.annotation.Transactional;
19 19 import javax.annotation.Resource;
20 20 import javax.validation.Valid;
21 21 import java.io.InputStream;
  22 +import java.util.Collection;
22 23 import java.util.Date;
  24 +import java.util.List;
23 25  
24 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
25 27 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
... ... @@ -52,6 +54,11 @@ public class MemberUserServiceImpl implements MemberUserService {
52 54 }
53 55  
54 56 @Override
  57 + public List<MemberUserDO> getUserListByNickname(String nickname) {
  58 + return memberUserMapper.selectListByNicknameLike(nickname);
  59 + }
  60 +
  61 + @Override
55 62 public MemberUserDO createUserIfAbsent(String mobile, String registerIp) {
56 63 // 用户已经存在
57 64 MemberUserDO user = memberUserMapper.selectByMobile(mobile);
... ... @@ -87,6 +94,11 @@ public class MemberUserServiceImpl implements MemberUserService {
87 94 }
88 95  
89 96 @Override
  97 + public List<MemberUserDO> getUserList(Collection<Long> ids) {
  98 + return memberUserMapper.selectBatchIds(ids);
  99 + }
  100 +
  101 + @Override
90 102 public void updateUserNickname(Long userId, String nickname) {
91 103 MemberUserDO user = this.checkUserExists(userId);
92 104 // 仅当新昵称不等于旧昵称时进行修改
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java
1 1 package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
3 5 import io.swagger.annotations.ApiModel;
4 6 import io.swagger.annotations.ApiModelProperty;
5 7 import lombok.Data;
... ... @@ -16,7 +18,7 @@ public class RoleUpdateStatusReqVO {
16 18  
17 19 @ApiModelProperty(value = "状态", required = true, example = "1", notes = "见 CommonStatusEnum 枚举")
18 20 @NotNull(message = "状态不能为空")
19   -// @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
  21 + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
20 22 private Integer status;
21 23  
22 24 }
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -174,6 +174,7 @@ logging:
174 174 cn.iocoder.yudao.module.tool.dal.mysql: debug
175 175 cn.iocoder.yudao.module.member.dal.mysql: debug
176 176 cn.iocoder.yudao.module.trade.dal.mysql: debug
  177 + cn.iocoder.yudao.module.promotion.dal.mysql: debug
177 178  
178 179 --- #################### 微信公众号、小程序相关配置 ####################
179 180 wx:
... ...
yudao-ui-admin/src/api/mall/product/sku.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获得商品 SKU 选项的列表
  4 +export function getSkuOptionList() {
  5 + return request({
  6 + url: '/product/sku/get-option-list',
  7 + method: 'get',
  8 + })
  9 +}
... ...
yudao-ui-admin/src/api/mall/product/spu.js
1 1 import request from '@/utils/request'
2 2  
3   -// 创建商品spu
  3 +// 创建商品 SPU
4 4 export function createSpu(data) {
5 5 return request({
6 6 url: '/product/spu/create',
... ... @@ -9,7 +9,7 @@ export function createSpu(data) {
9 9 })
10 10 }
11 11  
12   -// 更新商品spu
  12 +// 更新商品 SPU
13 13 export function updateSpu(data) {
14 14 return request({
15 15 url: '/product/spu/update',
... ... @@ -18,7 +18,7 @@ export function updateSpu(data) {
18 18 })
19 19 }
20 20  
21   -// 删除商品spu
  21 +// 删除商品 SPU
22 22 export function deleteSpu(id) {
23 23 return request({
24 24 url: '/product/spu/delete?id=' + id,
... ... @@ -34,7 +34,7 @@ export function getSpu(id) {
34 34 })
35 35 }
36 36  
37   -// 获得商品spu详情
  37 +// 获得商品 SPU 详情
38 38 export function getSpuDetail(id) {
39 39 return request({
40 40 url: '/product/spu/get/detail?id=' + id,
... ... @@ -42,7 +42,7 @@ export function getSpuDetail(id) {
42 42 })
43 43 }
44 44  
45   -// 获得商品spu分页
  45 +// 获得商品 SPU 分页
46 46 export function getSpuPage(query) {
47 47 return request({
48 48 url: '/product/spu/page',
... ... @@ -50,3 +50,11 @@ export function getSpuPage(query) {
50 50 params: query
51 51 })
52 52 }
  53 +
  54 +// 获得商品 SPU 精简列表
  55 +export function getSpuSimpleList() {
  56 + return request({
  57 + url: '/product/spu/get-simple-list',
  58 + method: 'get',
  59 + })
  60 +}
... ...
yudao-ui-admin/src/api/mall/promotion/coupon.js 0 → 100755
  1 +import request from '@/utils/request'
  2 +
  3 +// 删除优惠劵
  4 +export function deleteCoupon(id) {
  5 + return request({
  6 + url: '/promotion/coupon/delete?id=' + id,
  7 + method: 'delete'
  8 + })
  9 +}
  10 +
  11 +// 获得优惠劵分页
  12 +export function getCouponPage(query) {
  13 + return request({
  14 + url: '/promotion/coupon/page',
  15 + method: 'get',
  16 + params: query
  17 + })
  18 +}
... ...
yudao-ui-admin/src/api/promotion/couponTemplate.js renamed to yudao-ui-admin/src/api/mall/promotion/couponTemplate.js
... ... @@ -18,6 +18,19 @@ export function updateCouponTemplate(data) {
18 18 })
19 19 }
20 20  
  21 +// 更新优惠劵模板的状态
  22 +export function updateCouponTemplateStatus(id, status) {
  23 + const data = {
  24 + id,
  25 + status
  26 + }
  27 + return request({
  28 + url: '/promotion/coupon-template/update-status',
  29 + method: 'put',
  30 + data: data
  31 + })
  32 +}
  33 +
21 34 // 删除优惠劵模板
22 35 export function deleteCouponTemplate(id) {
23 36 return request({
... ...
yudao-ui-admin/src/api/mall/CouponTemplete/CouponTemplete.js renamed to yudao-ui-admin/src/api/mall/promotion/discountActivity.js 100644 → 100755
1 1 import request from '@/utils/request'
2 2  
3   -// 创建优惠券模板
4   -export function create(data) {
  3 +// 创建限时折扣活动
  4 +export function createDiscountActivity(data) {
5 5 return request({
6   - url: '/coupon/template/create',
  6 + url: '/promotion/discount-activity/create',
7 7 method: 'post',
8 8 data: data
9 9 })
10 10 }
11 11  
12   -// 更新优惠券模板
13   -export function update(data) {
  12 +// 更新限时折扣活动
  13 +export function updateDiscountActivity(data) {
14 14 return request({
15   - url: '/coupon/template/update',
  15 + url: '/promotion/discount-activity/update',
16 16 method: 'put',
17 17 data: data
18 18 })
19 19 }
20 20  
21   -// 删除优惠券模板
22   -export function deleteCouponTemplete (id) {
  21 +// 关闭限时折扣活动
  22 +export function closeDiscountActivity(id) {
23 23 return request({
24   - url: '/coupon/template/delete?id=' + id,
25   - method: 'delete'
  24 + url: '/promotion/discount-activity/close?id=' + id,
  25 + method: 'put'
26 26 })
27 27 }
28 28  
29   -// 获得优惠券模板
30   -export function get(id) {
  29 +// 删除限时折扣活动
  30 +export function deleteDiscountActivity(id) {
31 31 return request({
32   - url: '/coupon/template/get?id=' + id,
33   - method: 'get'
  32 + url: '/promotion/discount-activity/delete?id=' + id,
  33 + method: 'delete'
34 34 })
35 35 }
36 36  
37   -// 获得优惠券模板分页
38   -export function getPage(query) {
  37 +// 获得限时折扣活动
  38 +export function getDiscountActivity(id) {
39 39 return request({
40   - url: '/coupon/template/page',
41   - method: 'get',
42   - params: query
  40 + url: '/promotion/discount-activity/get?id=' + id,
  41 + method: 'get'
43 42 })
44 43 }
45 44  
46   -// 导出优惠券模板 Excel
47   -export function exportExcel(query) {
  45 +// 获得限时折扣活动分页
  46 +export function getDiscountActivityPage(query) {
48 47 return request({
49   - url: '/coupon/template/export-excel',
  48 + url: '/promotion/discount-activity/page',
50 49 method: 'get',
51   - params: query,
52   - responseType: 'blob'
  50 + params: query
53 51 })
54 52 }
... ...
yudao-ui-admin/src/api/mall/promotion/rewardActivity.js 0 → 100755
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建满减送活动
  4 +export function createRewardActivity(data) {
  5 + return request({
  6 + url: '/promotion/reward-activity/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新满减送活动
  13 +export function updateRewardActivity(data) {
  14 + return request({
  15 + url: '/promotion/reward-activity/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 关闭满减送活动
  22 +export function closeRewardActivity(id) {
  23 + return request({
  24 + url: '/promotion/reward-activity/close?id=' + id,
  25 + method: 'put'
  26 + })
  27 +}
  28 +
  29 +// 删除满减送活动
  30 +export function deleteRewardActivity(id) {
  31 + return request({
  32 + url: '/promotion/reward-activity/delete?id=' + id,
  33 + method: 'delete'
  34 + })
  35 +}
  36 +
  37 +// 获得满减送活动
  38 +export function getRewardActivity(id) {
  39 + return request({
  40 + url: '/promotion/reward-activity/get?id=' + id,
  41 + method: 'get'
  42 + })
  43 +}
  44 +
  45 +// 获得满减送活动分页
  46 +export function getRewardActivityPage(query) {
  47 + return request({
  48 + url: '/promotion/reward-activity/page',
  49 + method: 'get',
  50 + params: query
  51 + })
  52 +}
... ...
yudao-ui-admin/src/utils/constants.js
... ... @@ -278,3 +278,39 @@ export const PromotionProductScopeEnum = {
278 278 name: '指定商品参与'
279 279 }
280 280 }
  281 +
  282 +/**
  283 + * 营销的条件类型枚举
  284 + */
  285 +export const PromotionConditionTypeEnum = {
  286 + PRICE: {
  287 + type: 10,
  288 + name: '满 N 元'
  289 + },
  290 + COUNT: {
  291 + type: 20,
  292 + name: '满 N 件'
  293 + }
  294 +}
  295 +
  296 +/**
  297 + * 促销活动的状态枚举
  298 + */
  299 +export const PromotionActivityStatusEnum = {
  300 + WAIT: {
  301 + type: 10,
  302 + name: '未开始'
  303 + },
  304 + RUN: {
  305 + type: 20,
  306 + name: '进行中'
  307 + },
  308 + END: {
  309 + type: 30,
  310 + name: '已结束'
  311 + },
  312 + CLOSE: {
  313 + type: 40,
  314 + name: '已关闭'
  315 + }
  316 +}
... ...
yudao-ui-admin/src/utils/dict.js
... ... @@ -63,7 +63,11 @@ export const DICT_TYPE = {
63 63 // ========== MALL - PROMOTION 模块 ==========
64 64 PROMOTION_DISCOUNT_TYPE: 'promotion_discount_type', // 优惠类型
65 65 PROMOTION_PRODUCT_SCOPE: 'promotion_product_scope', // 营销的商品范围
66   - COUPON_TEMPLATE_VALIDITY_TYPE: 'coupon_template_validity_type', // 优惠劵模板的有限期类型
  66 + PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE: 'promotion_coupon_template_validity_type', // 优惠劵模板的有限期类型
  67 + PROMOTION_COUPON_STATUS: 'promotion_coupon_status', // 优惠劵的状态
  68 + PROMOTION_COUPON_TAKE_TYPE: 'promotion_coupon_take_type', // 优惠劵的领取方式
  69 + PROMOTION_ACTIVITY_STATUS: 'promotion_activity_status', // 优惠活动的状态
  70 + PROMOTION_CONDITION_TYPE: 'promotion_condition_type', // 营销的条件类型枚举
67 71 }
68 72  
69 73 /**
... ...
yudao-ui-admin/src/views/mall/CouponTemplete/index.vue deleted
1   -<template>
2   - <div class="app-container">
3   -
4   - <!-- 搜索工作栏 -->
5   - <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
6   - <el-form-item label="优惠券类" prop="type">
7   - <el-select v-model="queryParams.type" placeholder="请选择优惠券类型 reward-满减 discount-折扣 random-随机" clearable size="small">
8   - <el-option label="请选择字典生成" value="" />
9   - </el-select>
10   - </el-form-item>
11   - <el-form-item label="优惠券名称" prop="name">
12   - <el-input v-model="queryParams.name" placeholder="请输入优惠券名称" clearable @keyup.enter.native="handleQuery"/>
13   - </el-form-item>
14   -
15   - <el-form-item label="状态" prop="status">
16   - <el-select v-model="queryParams.status" placeholder="请选择状态(1进行中2已结束-1已关闭)" clearable size="small">
17   - <el-option label="请选择字典生成" value="" />
18   - </el-select>
19   - </el-form-item>
20   - <el-form-item label="有效日期结束时间" prop="endTime">
21   - <el-date-picker v-model="queryParams.endTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
22   - range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
23   - </el-form-item>
24   -
25   - <el-form-item>
26   - <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
27   - <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
28   - </el-form-item>
29   - </el-form>
30   -
31   - <!-- 操作工具栏 -->
32   - <el-row :gutter="10" class="mb8">
33   - <el-col :span="1.5">
34   - <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
35   - v-hasPermi="['CouponTemplete::create']">新增</el-button>
36   - </el-col>
37   - <el-col :span="1.5">
38   - <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
39   - v-hasPermi="['CouponTemplete::export']">导出</el-button>
40   - </el-col>
41   - <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
42   - </el-row>
43   -
44   - <!-- 列表 -->
45   - <el-table v-loading="loading" :data="list">
46   -
47   - <!-- todo 优惠券类型-->
48   - <el-table-column label="优惠券类型" align="center" prop="type" />
49   - <el-table-column label="优惠券名称" align="center" prop="name" />
50   - <el-table-column label="优惠券图片" align="center" prop="image" />
51   - <el-table-column label="发放数量" align="center" prop="count" />
52   - <el-table-column label="已领取数量" align="center" prop="leadCount" />
53   - <el-table-column label="已使用数量" align="center" prop="usedCount" />
54   -<!-- todo 适用商品类型-->
55   -<!-- <el-table-column label="适用商品类型" align="center" prop="goodsType" />-->
56   - <!-- todo 使用门槛0-无门槛 1-有门槛-->
57   -
58   - <el-table-column label="使用门槛" align="center" prop="hasUseLimit" />
59   -<!-- <el-table-column label="满多少元使用 0代表无限制" align="center" prop="atLeast" />-->
60   - <!-- todo 发放面额 折扣-->
61   - <el-table-column label="优惠金额/折扣" align="center" prop="money" />
62   -<!-- <el-table-column label="1 =< 折扣 <= 9.9 当type为discount时需要添加" align="center" prop="discount" />-->
63   -<!-- <el-table-column label="最多折扣金额 当type为discount时可选择性添加" align="center" prop="discountLimit" />-->
64   -<!-- <el-table-column label="最低金额 当type为radom时需要添加" align="center" prop="minMoney" />-->
65   -<!-- <el-table-column label="最大金额 当type为radom时需要添加" align="center" prop="maxMoney" />-->
66   - <!-- todo 1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期 -->
67   - <el-table-column label="有效期限" align="center" prop="validityType" />
68   -<!-- <el-table-column label="使用开始日期 过期类型1时必填" align="center" prop="startUseTime" width="180">-->
69   -<!-- <template slot-scope="scope">-->
70   -<!-- <span>{{ parseTime(scope.row.startUseTime) }}</span>-->
71   -<!-- </template>-->
72   -<!-- </el-table-column>-->
73   -<!-- <el-table-column label="使用结束日期 过期类型1时必填" align="center" prop="endUseTime" width="180">-->
74   -<!-- <template slot-scope="scope">-->
75   -<!-- <span>{{ parseTime(scope.row.endUseTime) }}</span>-->
76   -<!-- </template>-->
77   -<!-- </el-table-column>-->
78   -<!-- <el-table-column label="有效日期结束时间" align="center" prop="endTime" width="180">-->
79   -<!-- <template slot-scope="scope">-->
80   -<!-- <span>{{ parseTime(scope.row.endTime) }}</span>-->
81   -<!-- </template>-->
82   -<!-- </el-table-column>-->
83   -<!-- <el-table-column label="当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效" align="center" prop="fixedTerm" />-->
84   -<!-- <el-table-column label="是否无限制0-否 1是" align="center" prop="whetherLimitless" />-->
85   - <el-table-column label="领取上限" align="center" prop="maxFetch" />
86   - <!-- todo 0-不限制 1- 优惠券仅原价购买商品时可用-->
87   - <el-table-column label="优惠叠加" align="center" prop="whetherForbidPreference" />
88   - <el-table-column label="是否显示" align="center" prop="whetherShow" />
89   - <el-table-column label="订单的优惠总金额" align="center" prop="discountOrderMoney" />
90   - <el-table-column label="用券总成交额" align="center" prop="orderMoney" />
91   - <el-table-column label="是否禁止发放" align="center" prop="whetherForbidden" />
92   - <el-table-column label="使用优惠券购买的商品数量" align="center" prop="orderGoodsNum" />
93   - <el-table-column label="状态" align="center" prop="status" />
94   -
95   - <el-table-column label="备注" align="center" prop="couponNameRemark" />
96   -
97   - <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
98   -
99   - <template #scope>
100   - <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
101   - v-hasPermi="['CouponTemplete::update']">修改</el-button>
102   - <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
103   - v-hasPermi="['CouponTemplete::delete']">删除</el-button>
104   - </template>
105   - </el-table-column>
106   - </el-table>
107   - <!-- 分页组件 -->
108   - <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
109   - @pagination="getList"/>
110   -
111   - <!-- 对话框(添加 / 修改) -->
112   - <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
113   - <el-form ref="form" :model="form" :rules="rules" label-width="150px">
114   - <el-form-item label="优惠券类型" prop="type">
115   - <el-select v-model="form.type" placeholder="请选择优惠券类型 reward-满减 discount-折扣 random-随机">
116   - <el-option label="请选择字典生成" value="" />
117   - </el-select>
118   - </el-form-item>
119   - <el-form-item label="优惠券名称" prop="name">
120   - <el-input v-model="form.name" placeholder="请输入优惠券名称" />
121   - </el-form-item>
122   - <el-form-item label="名称备注" prop="couponNameRemark">
123   - <el-input v-model="form.couponNameRemark" placeholder="请输入名称备注" />
124   - </el-form-item>
125   - <el-form-item label="发放数量" prop="count">
126   - <el-input v-model="form.count" placeholder="请输入发放数量" />
127   - </el-form-item>
128   - <el-form-item label="适用商品类型" prop="goodsType">
129   - <el-radio-group v-model="form.goodsType">
130   - <el-radio label="1">请选择字典生成</el-radio>
131   - </el-radio-group>
132   - </el-form-item>
133   - <!-- todo 请输入适用商品id-->
134   - <el-form-item label="适用商品id" prop="productIds">
135   - <el-input v-model="form.productIds" placeholder="请输入适用商品id" />
136   - </el-form-item>
137   - <el-form-item label="使用门槛" prop="hasUseLimit">
138   - <el-radio-group v-model="form.hasUseLimit">
139   - <el-radio label="1">请选择字典生成</el-radio>
140   - </el-radio-group>
141   - </el-form-item>
142   - <el-form-item label="满多少元使用" prop="atLeast">
143   - <el-input v-model="form.atLeast" placeholder="请输入满多少元使用 0代表无限制" />
144   - </el-form-item>
145   - <el-form-item label="发放面额" prop="money">
146   - <el-input v-model="form.money" placeholder="请输入发放面额 当type为reward时需要添加" />
147   - </el-form-item>
148   - <el-form-item label="1 =< 折扣 <= 9.9" prop="discount">
149   - <el-input v-model="form.discount" placeholder="请输入1 =< 折扣 <= 9.9 当type为discount时需要添加" />
150   - </el-form-item>
151   - <el-form-item label="最多折扣金额" prop="discountLimit">
152   - <el-input v-model="form.discountLimit" placeholder="请输入最多折扣金额 当type为discount时可选择性添加" />
153   - </el-form-item>
154   - <el-form-item label="最低金额" prop="minMoney">
155   - <el-input v-model="form.minMoney" placeholder="请输入最低金额 当type为radom时需要添加" />
156   - </el-form-item>
157   - <el-form-item label="最大金额" prop="maxMoney">
158   - <el-input v-model="form.maxMoney" placeholder="请输入最大金额 当type为radom时需要添加" />
159   - </el-form-item>
160   - <el-form-item label="过期类型" prop="validityType">
161   - <el-radio-group v-model="form.validityType">
162   - <el-radio label="1">请选择字典生成</el-radio>
163   - </el-radio-group>
164   - </el-form-item>
165   - <el-form-item label="使用开始日期 过期类型1时必填" prop="startUseTime">
166   - <el-date-picker clearable v-model="form.startUseTime" type="date" value-format="timestamp" placeholder="选择使用开始日期 过期类型1时必填" />
167   - </el-form-item>
168   - <el-form-item label="使用结束日期 过期类型1时必填" prop="endUseTime">
169   - <el-date-picker clearable v-model="form.endUseTime" type="date" value-format="timestamp" placeholder="选择使用结束日期 过期类型1时必填" />
170   - </el-form-item>
171   - <el-form-item label="当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效" prop="fixedTerm">
172   - <el-input v-model="form.fixedTerm" placeholder="请输入当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效" />
173   - </el-form-item>
174   - <el-form-item label="是否无限制0-否 1是" prop="whetherLimitless">
175   - <el-radio-group v-model="form.whetherLimitless">
176   - <el-radio label="1">请选择字典生成</el-radio>
177   - </el-radio-group>
178   - </el-form-item>
179   - <el-form-item label="每人最大领取个数" prop="maxFetch">
180   - <el-input v-model="form.maxFetch" placeholder="请输入每人最大领取个数" />
181   - </el-form-item>
182   - <el-form-item label="是否开启过期提醒0-不开启 1-开启" prop="whetherExpireNotice">
183   - <el-radio-group v-model="form.whetherExpireNotice">
184   - <el-radio label="1">请选择字典生成</el-radio>
185   - </el-radio-group>
186   - </el-form-item>
187   - <el-form-item label="过期前N天提醒" prop="expireNoticeFixedTerm">
188   - <el-input v-model="form.expireNoticeFixedTerm" placeholder="请输入过期前N天提醒" />
189   - </el-form-item>
190   - <el-form-item label="优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用" prop="whetherForbidPreference">
191   - <el-radio-group v-model="form.whetherForbidPreference">
192   - <el-radio label="1">请选择字典生成</el-radio>
193   - </el-radio-group>
194   - </el-form-item>
195   - <el-form-item label="是否显示" prop="whetherShow">
196   - <el-input v-model="form.whetherShow" placeholder="请输入是否显示" />
197   - </el-form-item>
198   - <el-form-item label="订单的优惠总金额" prop="discountOrderMoney">
199   - <el-input v-model="form.discountOrderMoney" placeholder="请输入订单的优惠总金额" />
200   - </el-form-item>
201   - <el-form-item label="用券总成交额" prop="orderMoney">
202   - <el-input v-model="form.orderMoney" placeholder="请输入用券总成交额" />
203   - </el-form-item>
204   - <el-form-item label="是否禁止发放0-否 1-是" prop="whetherForbidden">
205   - <el-radio-group v-model="form.whetherForbidden">
206   - <el-radio label="1">请选择字典生成</el-radio>
207   - </el-radio-group>
208   - </el-form-item>
209   - <el-form-item label="使用优惠券购买的商品数量" prop="orderGoodsNum">
210   - <el-input v-model="form.orderGoodsNum" placeholder="请输入使用优惠券购买的商品数量" />
211   - </el-form-item>
212   - <el-form-item label="状态" prop="status">
213   - <el-radio-group v-model="form.status">
214   - <el-radio label="1">请选择字典生成</el-radio>
215   - </el-radio-group>
216   - </el-form-item>
217   - <el-form-item label="有效日期结束时间" prop="endTime">
218   - <el-date-picker clearable v-model="form.endTime" type="date" value-format="timestamp" placeholder="选择有效日期结束时间" />
219   - </el-form-item>
220   - </el-form>
221   - <div slot="footer" class="dialog-footer">
222   - <el-button type="primary" @click="submitForm">确 定</el-button>
223   - <el-button @click="cancel">取 消</el-button>
224   - </div>
225   - </el-dialog>
226   - </div>
227   -</template>
228   -
229   -<script>
230   -import { create, update, deleteCouponTemplete, get, getPage, exportExcel } from "@/api/mall/CouponTemplete/CouponTemplete.js";
231   -import {getDictDatas} from "@/utils/dict";
232   -
233   -export default {
234   - name: "",
235   - components: {
236   - },
237   - data() {
238   - return {
239   - // 遮罩层
240   - loading: true,
241   - // 导出遮罩层
242   - exportLoading: false,
243   - // 显示搜索条件
244   - showSearch: true,
245   - // 总条数
246   - total: 0,
247   - // 优惠券模板列表
248   - list: [],
249   - // 弹出层标题
250   - title: "",
251   - // 是否显示弹出层
252   - open: false,
253   - // 查询参数
254   - queryParams: {
255   - pageNo: 1,
256   - pageSize: 10,
257   - type: null,
258   - name: null,
259   - couponNameRemark: null,
260   - image: null,
261   - count: null,
262   - leadCount: null,
263   - usedCount: null,
264   - goodsType: null,
265   - productIds: null,
266   - hasUseLimit: null,
267   - atLeast: null,
268   - money: null,
269   - discount: null,
270   - discountLimit: null,
271   - minMoney: null,
272   - maxMoney: null,
273   - validityType: null,
274   - startUseTime: [],
275   - endUseTime: [],
276   - fixedTerm: null,
277   - whetherLimitless: null,
278   - maxFetch: null,
279   - whetherExpireNotice: null,
280   - expireNoticeFixedTerm: null,
281   - whetherForbidPreference: null,
282   - whetherShow: null,
283   - discountOrderMoney: null,
284   - orderMoney: null,
285   - whetherForbidden: null,
286   - orderGoodsNum: null,
287   - status: null,
288   - endTime: [],
289   - createTime: [],
290   - },
291   - //数据字典
292   - datas:getDictDatas(),
293   -
294   - // 表单参数
295   - form: {},
296   - // 表单校验
297   - rules: {
298   - type: [{ required: true, message: "优惠券类型 reward-满减 discount-折扣 random-随机不能为空", trigger: "change" }],
299   - name: [{ required: true, message: "优惠券名称不能为空", trigger: "blur" }],
300   - count: [{ required: true, message: "发放数量不能为空", trigger: "blur" }],
301   - leadCount: [{ required: true, message: "已领取数量不能为空", trigger: "blur" }],
302   - usedCount: [{ required: true, message: "已使用数量不能为空", trigger: "blur" }],
303   - goodsType: [{ required: true, message: "适用商品类型1-全部商品可用;2-指定商品可用;3-指定商品不可用不能为空", trigger: "blur" }],
304   - hasUseLimit: [{ required: true, message: "使用门槛0-无门槛 1-有门槛不能为空", trigger: "blur" }],
305   - atLeast: [{ required: true, message: "满多少元使用 0代表无限制不能为空", trigger: "blur" }],
306   - money: [{ required: true, message: "发放面额 当type为reward时需要添加不能为空", trigger: "blur" }],
307   - discount: [{ required: true, message: "1 =< 折扣 <= 9.9 当type为discount时需要添加不能为空", trigger: "blur" }],
308   - discountLimit: [{ required: true, message: "最多折扣金额 当type为discount时可选择性添加不能为空", trigger: "blur" }],
309   - minMoney: [{ required: true, message: "最低金额 当type为radom时需要添加不能为空", trigger: "blur" }],
310   - maxMoney: [{ required: true, message: "最大金额 当type为radom时需要添加不能为空", trigger: "blur" }],
311   - validityType: [{ required: true, message: "过期类型1-时间范围过期 2-领取之日固定日期后过期 3-领取次日固定日期后过期不能为空", trigger: "blur" }],
312   - fixedTerm: [{ required: true, message: "当validity_type为2或者3时需要添加 领取之日起或者次日N天内有效不能为空", trigger: "blur" }],
313   - whetherLimitless: [{ required: true, message: "是否无限制0-否 1是不能为空", trigger: "blur" }],
314   - maxFetch: [{ required: true, message: "每人最大领取个数不能为空", trigger: "blur" }],
315   - whetherExpireNotice: [{ required: true, message: "是否开启过期提醒0-不开启 1-开启不能为空", trigger: "blur" }],
316   - expireNoticeFixedTerm: [{ required: true, message: "过期前N天提醒不能为空", trigger: "blur" }],
317   - whetherForbidPreference: [{ required: true, message: "优惠叠加 0-不限制 1- 优惠券仅原价购买商品时可用不能为空", trigger: "blur" }],
318   - whetherShow: [{ required: true, message: "是否显示不能为空", trigger: "blur" }],
319   - discountOrderMoney: [{ required: true, message: "订单的优惠总金额不能为空", trigger: "blur" }],
320   - orderMoney: [{ required: true, message: "用券总成交额不能为空", trigger: "blur" }],
321   - whetherForbidden: [{ required: true, message: "是否禁止发放0-否 1-是不能为空", trigger: "blur" }],
322   - orderGoodsNum: [{ required: true, message: "使用优惠券购买的商品数量不能为空", trigger: "blur" }],
323   - status: [{ required: true, message: "状态(1进行中2已结束-1已关闭)不能为空", trigger: "blur" }],
324   - }
325   - };
326   - },
327   - created() {
328   - this.getList();
329   - },
330   - methods: {
331   - /** 查询列表 */
332   - getList() {
333   - this.loading = true;
334   - // 执行查询
335   - getPage(this.queryParams).then(response => {
336   - this.list = response.data.list;
337   - this.total = response.data.total;
338   - this.loading = false;
339   - });
340   - },
341   - /** 取消按钮 */
342   - cancel() {
343   - this.open = false;
344   - this.reset();
345   - },
346   - /** 表单重置 */
347   - reset() {
348   - this.form = {
349   - id: undefined,
350   - type: undefined,
351   - name: undefined,
352   - couponNameRemark: undefined,
353   - image: undefined,
354   - count: undefined,
355   - leadCount: undefined,
356   - usedCount: undefined,
357   - goodsType: undefined,
358   - productIds: undefined,
359   - hasUseLimit: undefined,
360   - atLeast: undefined,
361   - money: undefined,
362   - discount: undefined,
363   - discountLimit: undefined,
364   - minMoney: undefined,
365   - maxMoney: undefined,
366   - validityType: undefined,
367   - startUseTime: undefined,
368   - endUseTime: undefined,
369   - fixedTerm: undefined,
370   - whetherLimitless: undefined,
371   - maxFetch: undefined,
372   - whetherExpireNotice: undefined,
373   - expireNoticeFixedTerm: undefined,
374   - whetherForbidPreference: undefined,
375   - whetherShow: undefined,
376   - discountOrderMoney: undefined,
377   - orderMoney: undefined,
378   - whetherForbidden: undefined,
379   - orderGoodsNum: undefined,
380   - status: undefined,
381   - endTime: undefined,
382   - };
383   - this.resetForm("form");
384   - },
385   - /** 搜索按钮操作 */
386   - handleQuery() {
387   - this.queryParams.pageNo = 1;
388   - this.getList();
389   - },
390   - /** 重置按钮操作 */
391   - resetQuery() {
392   - this.resetForm("queryForm");
393   - this.handleQuery();
394   - },
395   - /** 新增按钮操作 */
396   - handleAdd() {
397   - this.reset();
398   - this.open = true;
399   - this.title = "添加优惠券模板";
400   - },
401   - /** 修改按钮操作 */
402   - handleUpdate(row) {
403   - this.reset();
404   - const id = row.id;
405   - get(id).then(response => {
406   - this.form = response.data;
407   - this.open = true;
408   - this.title = "修改优惠券模板";
409   - });
410   - },
411   - /** 提交按钮 */
412   - submitForm() {
413   - this.$refs["form"].validate(valid => {
414   - if (!valid) {
415   - return;
416   - }
417   - // 修改的提交
418   - if (this.form.id != null) {
419   - update(this.form).then(response => {
420   - this.$modal.msgSuccess("修改成功");
421   - this.open = false;
422   - this.getList();
423   - });
424   - return;
425   - }
426   - // 添加的提交
427   - create(this.form).then(response => {
428   - this.$modal.msgSuccess("新增成功");
429   - this.open = false;
430   - this.getList();
431   - });
432   - });
433   - },
434   - /** 删除按钮操作 */
435   - handleDelete(row) {
436   - const id = row.id;
437   - this.$modal.confirm('是否确认删除优惠券模板编号为"' + id + '"的数据项?').then(function() {
438   - return deleteCouponTemplete(id);
439   - }).then(() => {
440   - this.getList();
441   - this.$modal.msgSuccess("删除成功");
442   - }).catch(() => {});
443   - },
444   - /** 导出按钮操作 */
445   - handleExport() {
446   - // 处理查询参数
447   - let params = {...this.queryParams};
448   - params.pageNo = undefined;
449   - params.pageSize = undefined;
450   - this.$modal.confirm('是否确认导出所有优惠券模板数据项?').then(() => {
451   - this.exportLoading = true;
452   - return exportExcel(params);
453   - }).then(response => {
454   - this.$download.excel(response, '优惠券模板.xls');
455   - this.exportLoading = false;
456   - }).catch(() => {});
457   - }
458   - }
459   -};
460   -</script>
yudao-ui-admin/src/views/mall/promotion/coupon/index.vue 0 → 100755
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 搜索工作栏 -->
  4 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  5 + <el-form-item label="会员昵称" prop="nickname">
  6 + <el-input v-model="queryParams.nickname" placeholder="请输入会员昵称" clearable @keyup.enter.native="handleQuery"/>
  7 + </el-form-item>
  8 + <el-form-item label="创建时间" prop="createTime">
  9 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  10 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  11 + </el-form-item>
  12 + <el-form-item>
  13 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  14 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  15 + </el-form-item>
  16 + </el-form>
  17 +
  18 + <!-- 操作工具栏 -->
  19 + <el-row :gutter="10" class="mb8">
  20 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  21 + </el-row>
  22 +
  23 + <!-- Tab 选项:真正的内容在 Lab -->
  24 + <el-tabs v-model="activeTab" type="card" @tab-click="tabClick" style="margin-top: -40px;">
  25 + <el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
  26 + </el-tabs>
  27 +
  28 + <!-- 列表 -->
  29 + <el-table v-loading="loading" :data="list">
  30 + <el-table-column label="会员信息" align="center" prop="nickname" /> <!-- TODO 芋艿:以后支持头像,支持跳转 -->
  31 + <el-table-column label="优惠劵" align="center" prop="name" />
  32 + <el-table-column label="优惠券类型" align="center" prop="discountType">
  33 + <template slot-scope="scope">
  34 + <dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
  35 + </template>
  36 + </el-table-column>
  37 + <el-table-column label="领取方式" align="center" prop="takeType">
  38 + <template slot-scope="scope">
  39 + <dict-tag :type="DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE" :value="scope.row.takeType" />
  40 + </template>
  41 + </el-table-column>
  42 + <el-table-column label="状态" align="center" prop="status">
  43 + <template slot-scope="scope">
  44 + <dict-tag :type="DICT_TYPE.PROMOTION_COUPON_STATUS" :value="scope.row.status" />
  45 + </template>
  46 + </el-table-column>
  47 + <el-table-column label="领取时间" align="center" prop="createTime" width="180">
  48 + <template slot-scope="scope">
  49 + <span>{{ parseTime(scope.row.createTime) }}</span>
  50 + </template>
  51 + </el-table-column>
  52 + <el-table-column label="使用时间" align="center" prop="useTime" width="180">
  53 + <template slot-scope="scope">
  54 + <span>{{ parseTime(scope.row.useTime) }}</span>
  55 + </template>
  56 + </el-table-column>
  57 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  58 + <template slot-scope="scope">
  59 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  60 + v-hasPermi="['promotion:coupon:delete']">回收</el-button>
  61 + </template>
  62 + </el-table-column>
  63 + </el-table>
  64 + <!-- 分页组件 -->
  65 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  66 + @pagination="getList"/>
  67 + </div>
  68 +</template>
  69 +
  70 +<script>
  71 +import { deleteCoupon, getCouponPage } from "@/api/mall/promotion/coupon";
  72 +import { DICT_TYPE, getDictDatas} from "@/utils/dict";
  73 +
  74 +export default {
  75 + name: "Coupon",
  76 + components: {
  77 + },
  78 + data() {
  79 + return {
  80 + // 遮罩层
  81 + loading: true,
  82 + // 导出遮罩层
  83 + exportLoading: false,
  84 + // 显示搜索条件
  85 + showSearch: true,
  86 + // 总条数
  87 + total: 0,
  88 + // 优惠劵列表
  89 + list: [],
  90 + // 弹出层标题
  91 + title: "",
  92 + // 是否显示弹出层
  93 + open: false,
  94 + // 查询参数
  95 + queryParams: {
  96 + pageNo: 1,
  97 + pageSize: 10,
  98 + createTime: [],
  99 + status: undefined,
  100 + },
  101 + // Tab 筛选
  102 + activeTab: 'all',
  103 + statusTabs: [{
  104 + label: '全部',
  105 + value: 'all'
  106 + }],
  107 + };
  108 + },
  109 + created() {
  110 + this.getList();
  111 + // 设置 statuses 过滤
  112 + for (const dict of getDictDatas(DICT_TYPE.PROMOTION_COUPON_STATUS)) {
  113 + this.statusTabs.push({
  114 + label: dict.label,
  115 + value: dict.value
  116 + })
  117 + }
  118 + },
  119 + methods: {
  120 + /** 查询列表 */
  121 + getList() {
  122 + this.loading = true;
  123 + // 执行查询
  124 + getCouponPage(this.queryParams).then(response => {
  125 + this.list = response.data.list;
  126 + this.total = response.data.total;
  127 + this.loading = false;
  128 + });
  129 + },
  130 + /** 取消按钮 */
  131 + cancel() {
  132 + this.open = false;
  133 + this.reset();
  134 + },
  135 + /** 搜索按钮操作 */
  136 + handleQuery() {
  137 + this.queryParams.pageNo = 1;
  138 + this.getList();
  139 + },
  140 + /** 重置按钮操作 */
  141 + resetQuery() {
  142 + this.resetForm("queryForm");
  143 + this.handleQuery();
  144 + },
  145 + /** 删除按钮操作 */
  146 + handleDelete(row) {
  147 + const id = row.id;
  148 + this.$modal.confirm('回收将会收回会员领取的待使用的优惠券,已使用的将无法回收,确定要回收所选优惠券吗?').then(function() {
  149 + return deleteCoupon(id);
  150 + }).then(() => {
  151 + this.getList();
  152 + this.$modal.msgSuccess("回收成功");
  153 + }).catch(() => {});
  154 + },
  155 + /** tab 切换 */
  156 + tabClick(tab) {
  157 + this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
  158 + this.getList();
  159 + }
  160 + }
  161 +};
  162 +</script>
... ...
yudao-ui-admin/src/views/promotion/couponTemplate/index.vue renamed to yudao-ui-admin/src/views/mall/promotion/couponTemplate/index.vue
... ... @@ -33,6 +33,9 @@
33 33 <el-col :span="1.5">
34 34 <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
35 35 v-hasPermi="['promotion:coupon-template:create']">新增</el-button>
  36 + <el-button type="info" plain icon="el-icon-s-operation" size="mini"
  37 + @click="() => this.$router.push('/promotion/coupon')"
  38 + v-hasPermi="['promotion:coupon:query']">会员优惠劵</el-button>
36 39 </el-col>
37 40 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
38 41 </el-row>
... ... @@ -52,7 +55,7 @@
52 55 <el-table-column label="有效期限" align="center" prop="validityType" width="180" :formatter="validityTypeFormat" />
53 56 <el-table-column label="状态" align="center" prop="status">
54 57 <template slot-scope="scope">
55   - <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
  58 + <el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)"/>
56 59 </template>
57 60 </el-table-column>
58 61 <el-table-column label="创建时间" align="center" prop="createTime" width="180">
... ... @@ -108,7 +111,7 @@
108 111 </el-radio-group>
109 112 </el-form-item>
110 113 <el-form-item v-if="form.takeType === 1" label="发放数量" prop="totalCount">
111   - <el-input-number v-model="form.usePrice" placeholder="发放数量,没有之后不能领取或发放,-1 为不限制"
  114 + <el-input-number v-model="form.totalCount" placeholder="发放数量,没有之后不能领取或发放,-1 为不限制"
112 115 style="width: 400px" :precision="0" :min="-1" /> 张
113 116 </el-form-item>
114 117 <el-form-item v-if="form.takeType === 1" label="每人限领个数" prop="takeLimitCount">
... ... @@ -117,13 +120,13 @@
117 120 </el-form-item>
118 121 <el-form-item label="有效期类型" prop="validityType">
119 122 <el-radio-group v-model="form.validityType">
120   - <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COUPON_TEMPLATE_VALIDITY_TYPE)"
  123 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE)"
121 124 :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
122 125 </el-radio-group>
123 126 </el-form-item>
124 127 <el-form-item v-if="form.validityType === CouponTemplateValidityTypeEnum.DATE.type" label="固定日期" prop="validTimes">
125   - <el-date-picker v-model="queryParams.validTimes" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
126   - range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  128 + <el-date-picker v-model="form.validTimes" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange"
  129 + :default-time="['00:00:00', '23:59:59']" />
127 130 </el-form-item>
128 131 <el-form-item v-if="form.validityType === CouponTemplateValidityTypeEnum.TERM.type" label="领取日期" prop="fixedStartTerm">
129 132 第 <el-input-number v-model="form.fixedStartTerm" placeholder="0 为今天生效"
... ... @@ -138,7 +141,13 @@
138 141 </el-radio-group>
139 142 </el-form-item>
140 143 <el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
141   - <el-input v-model="form.productSpuIds" placeholder="请输入商品 SPU 编号的数组" />
  144 + <el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
  145 + multiple filterable style="width: 400px">
  146 + <el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
  147 + <span style="float: left">{{ item.name }}</span>
  148 + <span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.minPrice / 100.0).toFixed(2) }}</span>
  149 + </el-option>
  150 + </el-select>
142 151 </el-form-item>
143 152 </el-form>
144 153 <div slot="footer" class="dialog-footer">
... ... @@ -150,8 +159,23 @@
150 159 </template>
151 160  
152 161 <script>
153   -import { createCouponTemplate, updateCouponTemplate, deleteCouponTemplate, getCouponTemplate, getCouponTemplatePage } from "@/api/promotion/couponTemplate";
154   -import { CommonStatusEnum, CouponTemplateValidityTypeEnum, PromotionDiscountTypeEnum, PromotionProductScopeEnum} from "@/utils/constants";
  162 +import {
  163 + createCouponTemplate,
  164 + updateCouponTemplate,
  165 + deleteCouponTemplate,
  166 + getCouponTemplate,
  167 + getCouponTemplatePage,
  168 + updateCouponTemplateStatus
  169 +} from "@/api/mall/promotion/couponTemplate";
  170 +import {
  171 + CommonStatusEnum,
  172 + CouponTemplateValidityTypeEnum,
  173 + PromotionDiscountTypeEnum,
  174 + PromotionProductScopeEnum
  175 +} from "@/utils/constants";
  176 +import { getSpuSimpleList } from "@/api/mall/product/spu";
  177 +import { parseTime } from "@/utils/ruoyi";
  178 +import {changeRoleStatus} from "@/api/system/role";
155 179  
156 180 export default {
157 181 name: "CouponTemplate",
... ... @@ -202,6 +226,8 @@ export default {
202 226 productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
203 227 productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
204 228 },
  229 + // 商品列表
  230 + productSpus: [],
205 231 // 如下的变量,主要为了 v-if 判断可以使用到
206 232 PromotionProductScopeEnum: PromotionProductScopeEnum,
207 233 CouponTemplateValidityTypeEnum: CouponTemplateValidityTypeEnum,
... ... @@ -221,6 +247,10 @@ export default {
221 247 this.total = response.data.total;
222 248 this.loading = false;
223 249 });
  250 + // 查询商品列表
  251 + getSpuSimpleList().then(response => {
  252 + this.productSpus = response.data
  253 + })
224 254 },
225 255 /** 取消按钮 */
226 256 cancel() {
... ... @@ -233,7 +263,7 @@ export default {
233 263 id: undefined,
234 264 name: undefined,
235 265 discountType: PromotionDiscountTypeEnum.PRICE.type,
236   - discountPrice: 0,
  266 + discountPrice: undefined,
237 267 discountPercent: undefined,
238 268 discountLimitPrice: undefined,
239 269 usePrice: undefined,
... ... @@ -274,9 +304,11 @@ export default {
274 304 getCouponTemplate(id).then(response => {
275 305 this.form = {
276 306 ...response.data,
277   - discountPrice: response.data.discountPrice ? response.data.discountPrice / 100.0 : undefined,
278   - discountPercent: response.data.discountPercent ? response.data.discountPercent / 10.0 : undefined,
279   - usePrice: response.data.usePrice ? response.data.usePrice / 100.0 : undefined,
  307 + discountPrice: response.data.discountPrice !== undefined ? response.data.discountPrice / 100.0 : undefined,
  308 + discountPercent: response.data.discountPercent !== undefined ? response.data.discountPercent / 10.0 : undefined,
  309 + discountLimitPrice: response.data.discountLimitPrice !== undefined ? response.data.discountLimitPrice / 100.0 : undefined,
  310 + usePrice: response.data.usePrice !== undefined ? response.data.usePrice / 100.0 : undefined,
  311 + validTimes: [response.data.validStartTime, response.data.validEndTime]
280 312 }
281 313 this.open = true;
282 314 this.title = "修改优惠劵";
... ... @@ -291,9 +323,12 @@ export default {
291 323 // 金额相关字段的缩放
292 324 let data = {
293 325 ...this.form,
294   - discountPrice: this.form.discountPrice ? this.form.discountPrice * 100 : undefined,
295   - discountPercent: this.form.discountPercent ? this.form.discountPercent * 10 : undefined,
296   - usePrice: this.form.usePrice ? this.form.usePrice * 100 : undefined,
  326 + discountPrice: this.form.discountPrice !== undefined ? this.form.discountPrice * 100 : undefined,
  327 + discountPercent: this.form.discountPercent !== undefined ? this.form.discountPercent * 10 : undefined,
  328 + discountLimitPrice: this.form.discountLimitPrice !== undefined ? this.form.discountLimitPrice * 100 : undefined,
  329 + usePrice: this.form.usePrice !== undefined ? this.form.usePrice * 100 : undefined,
  330 + validStartTime: this.form.validTimes && this.form.validTimes.length === 2 ? this.form.validTimes[0] : undefined,
  331 + validEndTime: this.form.validTimes && this.form.validTimes.length === 2 ? this.form.validTimes[1] : undefined,
297 332 }
298 333 // 修改的提交
299 334 if (this.form.id != null) {
... ... @@ -312,6 +347,20 @@ export default {
312 347 });
313 348 });
314 349 },
  350 + /** 优惠劵模板状态修改 */
  351 + handleStatusChange(row) {
  352 + // 此时,row 已经变成目标状态了,所以可以直接提交请求和提示
  353 + let text = row.status === CommonStatusEnum.ENABLE ? "启用" : "停用";
  354 + this.$modal.confirm('确认要"' + text + '""' + row.name + '"优惠劵吗?').then(function() {
  355 + return updateCouponTemplateStatus(row.id, row.status);
  356 + }).then(() => {
  357 + this.$modal.msgSuccess(text + "成功");
  358 + }).catch(function() {
  359 + // 异常时,需要将 row.status 状态重置回之前的
  360 + row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE
  361 + : CommonStatusEnum.ENABLE;
  362 + });
  363 + },
315 364 /** 删除按钮操作 */
316 365 handleDelete(row) {
317 366 const id = row.id;
... ...
yudao-ui-admin/src/views/mall/promotion/discountActivity/index.vue 0 → 100755
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="活动名称" prop="name">
  7 + <el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="活动状态" prop="status">
  10 + <el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
  11 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
  12 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  13 + </el-select>
  14 + </el-form-item>
  15 + <el-form-item label="创建时间" prop="createTime">
  16 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  17 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  18 + </el-form-item>
  19 + <el-form-item>
  20 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  21 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  22 + </el-form-item>
  23 + </el-form>
  24 +
  25 + <!-- 操作工具栏 -->
  26 + <el-row :gutter="10" class="mb8">
  27 + <el-col :span="1.5">
  28 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  29 + v-hasPermi="['promotion:discount-activity:create']">新增</el-button>
  30 + </el-col>
  31 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  32 + </el-row>
  33 +
  34 + <!-- 列表 -->
  35 + <el-table v-loading="loading" :data="list">
  36 + <el-table-column label="活动名称" align="center" prop="name" />
  37 + <el-table-column label="活动时间" align="center" prop="startTime" width="240">
  38 + <template slot-scope="scope">
  39 + <div>开始:{{ parseTime(scope.row.startTime) }}</div>
  40 + <div>结束:{{ parseTime(scope.row.endTime) }}</div>
  41 + </template>
  42 + </el-table-column>
  43 + <el-table-column label="状态" align="center" prop="status">
  44 + <template slot-scope="scope">
  45 + <dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
  46 + </template>
  47 + </el-table-column>
  48 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  49 + <template slot-scope="scope">
  50 + <span>{{ parseTime(scope.row.createTime) }}</span>
  51 + </template>
  52 + </el-table-column>
  53 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  54 + <template slot-scope="scope">
  55 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  56 + v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
  57 + v-hasPermi="['promotion:discount-activity:update']">修改</el-button>
  58 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
  59 + v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
  60 + scope.row.status !== PromotionActivityStatusEnum.END.type"
  61 + v-hasPermi="['promotion:discount-activity:close']">关闭</el-button>
  62 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  63 + v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
  64 + v-hasPermi="['promotion:discount-activity:delete']">删除</el-button>
  65 + </template>
  66 + </el-table-column>
  67 + </el-table>
  68 + <!-- 分页组件 -->
  69 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  70 + @pagination="getList"/>
  71 +
  72 + <!-- 对话框(添加 / 修改) -->
  73 + <el-dialog :title="title" :visible.sync="open" width="1000px" v-dialogDrag append-to-body>
  74 + <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  75 + <el-form-item label="活动名称" prop="name">
  76 + <el-input v-model="form.name" placeholder="请输入活动名称" />
  77 + </el-form-item>
  78 + <el-form-item label="备注" prop="remark">
  79 + <el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
  80 + </el-form-item>
  81 + <el-form-item label="活动时间" prop="startAndEndTime">
  82 + <el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
  83 + value-format="timestamp" placeholder="选择开始时间" style="width: 880px" />
  84 + </el-form-item>
  85 + <el-form-item label="商品选择">
  86 + <el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small"
  87 + multiple filterable style="width: 880px" @change="changeFormSku">
  88 + <el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name" :value="item.id">
  89 + <span style="float: left">{{ item.spuName }} &nbsp; {{ item.name}}</span>
  90 + <span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.price / 100.0).toFixed(2) }}</span>
  91 + </el-option>
  92 + </el-select>
  93 + <el-table v-loading="loading" :data="form.products">
  94 + <el-table-column label="商品名称" align="center" width="200">
  95 + <template slot-scope="scope">
  96 + {{ scope.row.spuName }} &nbsp; {{ scope.row.name}}
  97 + </template>
  98 + </el-table-column>
  99 + <el-table-column label="商品价格" align="center" prop="price">
  100 + <template slot-scope="scope">
  101 + ¥{{ (scope.row.price / 100.0).toFixed(2) }}
  102 + </template>
  103 + </el-table-column>
  104 + <el-table-column label="库存" align="center" prop="stock" />
  105 + <el-table-column label="优惠类型" align="center" property="discountType">
  106 + <template slot-scope="scope">
  107 + <el-select v-model="scope.row.discountType" placeholder="请选择优惠类型">
  108 + <el-option v-for="dict in getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
  109 + :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"/>
  110 + </el-select>
  111 + </template>
  112 + </el-table-column>
  113 + <el-table-column label="优惠" align="center" prop="startTime" width="250">
  114 + <template slot-scope="scope">
  115 + <el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PRICE.type" prop="discountPrice">
  116 + 减 <el-input-number v-model="scope.row.discountPrice" placeholder="请输入优惠金额"
  117 + style="width: 190px" :precision="2" :min="0" :max="scope.row.price / 100.0 - 0.01" /> 元
  118 + </el-form-item>
  119 + <el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PERCENT.type" prop="discountPercent">
  120 + 打 <el-input-number v-model="scope.row.discountPercent" placeholder="请输入优惠折扣"
  121 + style="width: 190px" :precision="1" :min="1" :max="9.9" /> 折
  122 + </el-form-item>
  123 + </template>
  124 + </el-table-column>
  125 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  126 + <template slot-scope="scope">
  127 + <el-button size="mini" type="text" icon="el-icon-delete" @click="removeFormSku(scope.row.skuId)">删除</el-button>
  128 + </template>
  129 + </el-table-column>
  130 + </el-table>
  131 + </el-form-item>
  132 + </el-form>
  133 + <div slot="footer" class="dialog-footer">
  134 + <el-button type="primary" @click="submitForm">确 定</el-button>
  135 + <el-button @click="cancel">取 消</el-button>
  136 + </div>
  137 + </el-dialog>
  138 + </div>
  139 +</template>
  140 +
  141 +<script>
  142 +import {
  143 + createDiscountActivity,
  144 + updateDiscountActivity,
  145 + deleteDiscountActivity,
  146 + getDiscountActivity,
  147 + getDiscountActivityPage,
  148 + closeDiscountActivity
  149 +} from "@/api/mall/promotion/discountActivity";
  150 +import {
  151 + PromotionActivityStatusEnum, PromotionDiscountTypeEnum,
  152 + PromotionProductScopeEnum
  153 +} from "@/utils/constants";
  154 +import { getSkuOptionList } from "@/api/mall/product/sku";
  155 +import { deepClone } from "@/utils";
  156 +
  157 +export default {
  158 + name: "DiscountActivity",
  159 + components: {
  160 + },
  161 + data() {
  162 + return {
  163 + // 遮罩层
  164 + loading: true,
  165 + // 导出遮罩层
  166 + exportLoading: false,
  167 + // 显示搜索条件
  168 + showSearch: true,
  169 + // 总条数
  170 + total: 0,
  171 + // 限时折扣活动列表
  172 + list: [],
  173 + // 弹出层名称
  174 + title: "",
  175 + // 是否显示弹出层
  176 + open: false,
  177 + // 查询参数
  178 + queryParams: {
  179 + pageNo: 1,
  180 + pageSize: 10,
  181 + name: null,
  182 + status: null,
  183 + createTime: [],
  184 + },
  185 + // 表单参数
  186 + form: {
  187 + skuIds: [], // 选中的 SKU
  188 + products: [], // 商品信息
  189 + },
  190 + // 表单校验
  191 + rules: {
  192 + name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
  193 + startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
  194 + skuIds: [{ required: true, message: "选择商品不能为空", trigger: "blur" }],
  195 + },
  196 + // 商品 SKU 列表
  197 + productSkus: [],
  198 + // 如下的变量,主要为了 v-if 判断可以使用到
  199 + PromotionProductScopeEnum: PromotionProductScopeEnum,
  200 + PromotionActivityStatusEnum: PromotionActivityStatusEnum,
  201 + PromotionDiscountTypeEnum: PromotionDiscountTypeEnum,
  202 + };
  203 + },
  204 + created() {
  205 + this.getList();
  206 + },
  207 + methods: {
  208 + /** 查询列表 */
  209 + getList() {
  210 + this.loading = true;
  211 + // 执行查询
  212 + getDiscountActivityPage(this.queryParams).then(response => {
  213 + this.list = response.data.list;
  214 + this.total = response.data.total;
  215 + this.loading = false;
  216 + });
  217 + // 获得 SKU 商品列表
  218 + getSkuOptionList().then(response => {
  219 + this.productSkus = response.data;
  220 + });
  221 + },
  222 + /** 取消按钮 */
  223 + cancel() {
  224 + this.open = false;
  225 + this.reset();
  226 + },
  227 + /** 表单重置 */
  228 + reset() {
  229 + this.form = {
  230 + id: undefined,
  231 + name: undefined,
  232 + startAndEndTime: undefined,
  233 + startTime: undefined,
  234 + endTime: undefined,
  235 + remark: undefined,
  236 + skuIds: [],
  237 + products: [],
  238 + };
  239 + this.resetForm("form");
  240 + },
  241 + /** 搜索按钮操作 */
  242 + handleQuery() {
  243 + this.queryParams.pageNo = 1;
  244 + this.getList();
  245 + },
  246 + /** 重置按钮操作 */
  247 + resetQuery() {
  248 + this.resetForm("queryForm");
  249 + this.handleQuery();
  250 + },
  251 + /** 新增按钮操作 */
  252 + handleAdd() {
  253 + this.reset();
  254 + this.open = true;
  255 + this.title = "添加限时折扣活动";
  256 + },
  257 + /** 修改按钮操作 */
  258 + handleUpdate(row) {
  259 + this.reset();
  260 + const id = row.id;
  261 + getDiscountActivity(id).then(response => {
  262 + this.form = response.data;
  263 + // 修改数据
  264 + this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
  265 + this.form.skuIds = response.data.products.map(item => item.skuId);
  266 + this.form.products.forEach(product => {
  267 + // 获得对应的 SKU 信息
  268 + const sku = this.productSkus.find(item => item.id === product.skuId);
  269 + if (!sku) {
  270 + return;
  271 + }
  272 + // 设置商品信息
  273 + product.name = sku.name;
  274 + product.spuName = sku.spuName;
  275 + product.price = sku.price;
  276 + product.stock = sku.stock;
  277 + product.discountPrice = product.discountPrice !== undefined ? product.discountPrice / 100.0 : undefined;
  278 + product.discountPercent = product.discountPercent !== undefined ? product.discountPercent / 10.0 : undefined;
  279 + });
  280 + // 打开弹窗
  281 + this.open = true;
  282 + this.title = "修改限时折扣活动";
  283 + });
  284 + },
  285 + /** 提交按钮 */
  286 + submitForm() {
  287 + this.$refs["form"].validate(valid => {
  288 + if (!valid) {
  289 + return;
  290 + }
  291 + // 处理数据
  292 + const data = deepClone(this.form); // 必须深拷贝,不然后面的 products 操作会有影响
  293 + data.startTime = this.form.startAndEndTime[0];
  294 + data.endTime = this.form.startAndEndTime[1];
  295 + data.products.forEach(product => {
  296 + product.discountPrice = product.discountPrice !== undefined ? product.discountPrice * 100 : undefined;
  297 + product.discountPercent = product.discountPercent !== undefined ? product.discountPercent * 10 : undefined;
  298 + });
  299 + if (!valid) {
  300 + return;
  301 + }
  302 +
  303 + // 修改的提交
  304 + if (this.form.id != null) {
  305 + updateDiscountActivity(data).then(response => {
  306 + this.$modal.msgSuccess("修改成功");
  307 + this.open = false;
  308 + this.getList();
  309 + });
  310 + return;
  311 + }
  312 + // 添加的提交
  313 + createDiscountActivity(data).then(response => {
  314 + this.$modal.msgSuccess("新增成功");
  315 + this.open = false;
  316 + this.getList();
  317 + });
  318 + });
  319 + },
  320 + /** 删除按钮操作 */
  321 + handleDelete(row) {
  322 + const id = row.id;
  323 + this.$modal.confirm('是否确认删除限时折扣活动编号为"' + id + '"的数据项?').then(function() {
  324 + return deleteDiscountActivity(id);
  325 + }).then(() => {
  326 + this.getList();
  327 + this.$modal.msgSuccess("删除成功");
  328 + }).catch(() => {});
  329 + },
  330 + /** 关闭按钮操作 */
  331 + handleClose(row) {
  332 + const id = row.id;
  333 + this.$modal.confirm('是否确认关闭限时折扣活动编号为"' + id + '"的数据项?').then(function() {
  334 + return closeDiscountActivity(id);
  335 + }).then(() => {
  336 + this.getList();
  337 + this.$modal.msgSuccess("关闭成功");
  338 + }).catch(() => {});
  339 + },
  340 + /** 当 Form 的 SKU 发生变化时 */
  341 + changeFormSku(skuIds) {
  342 + // 处理【新增】
  343 + skuIds.forEach(skuId => {
  344 + // 获得对应的 SKU 信息
  345 + const sku = this.productSkus.find(item => item.id === skuId);
  346 + if (!sku) {
  347 + return;
  348 + }
  349 + // 判断已存在,直接跳过
  350 + const product = this.form.products.find(item => item.skuId === skuId);
  351 + if (product) {
  352 + return;
  353 + }
  354 + this.form.products.push({
  355 + skuId: sku.id,
  356 + name: sku.name,
  357 + price: sku.price,
  358 + stock: sku.stock,
  359 + spuId: sku.spuId,
  360 + spuName: sku.spuName,
  361 + discountType: PromotionDiscountTypeEnum.PRICE.type,
  362 + });
  363 + });
  364 + // 处理【移除】
  365 + this.form.products.map((product, index) => {
  366 + if (!skuIds.includes(product.skuId)) {
  367 + this.form.products.splice(index, 1);
  368 + }
  369 + });
  370 + },
  371 + /** 移除 Form 的 SKU */
  372 + removeFormSku(skuId) {
  373 + this.form.skuIds.map((id, index) => {
  374 + if (skuId === id) {
  375 + this.form.skuIds.splice(index, 1);
  376 + }
  377 + });
  378 + this.changeFormSku(this.form.skuIds);
  379 + },
  380 + }
  381 +}
  382 +</script>
... ...
yudao-ui-admin/src/views/mall/promotion/rewardActivity/index.vue 0 → 100755
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  6 + <el-form-item label="活动名称" prop="name">
  7 + <el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
  8 + </el-form-item>
  9 + <el-form-item label="活动状态" prop="status">
  10 + <el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
  11 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
  12 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  13 + </el-select>
  14 + </el-form-item>
  15 + <el-form-item>
  16 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  17 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  18 + </el-form-item>
  19 + </el-form>
  20 +
  21 + <!-- 操作工具栏 -->
  22 + <el-row :gutter="10" class="mb8">
  23 + <el-col :span="1.5">
  24 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  25 + v-hasPermi="['promotion:reward-activity:create']">新增</el-button>
  26 + </el-col>
  27 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  28 + </el-row>
  29 +
  30 + <!-- 列表 -->
  31 + <el-table v-loading="loading" :data="list">
  32 + <el-table-column label="活动名称" align="center" prop="name" />
  33 + <el-table-column label="活动时间" align="center" prop="startTime" width="240">
  34 + <template slot-scope="scope">
  35 + <div>开始:{{ parseTime(scope.row.startTime) }}</div>
  36 + <div>结束:{{ parseTime(scope.row.endTime) }}</div>
  37 + </template>
  38 + </el-table-column>
  39 + <el-table-column label="状态" align="center" prop="status">
  40 + <template slot-scope="scope">
  41 + <dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
  42 + </template>
  43 + </el-table-column>
  44 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  45 + <template slot-scope="scope">
  46 + <span>{{ parseTime(scope.row.createTime) }}</span>
  47 + </template>
  48 + </el-table-column>
  49 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  50 + <template slot-scope="scope">
  51 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  52 + v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
  53 + v-hasPermi="['promotion:reward-activity:update']">修改</el-button>
  54 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
  55 + v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
  56 + scope.row.status !== PromotionActivityStatusEnum.END.type"
  57 + v-hasPermi="['promotion:reward-activity:close']">关闭</el-button>
  58 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  59 + v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
  60 + v-hasPermi="['promotion:reward-activity:delete']">删除</el-button>
  61 + </template>
  62 + </el-table-column>
  63 + </el-table>
  64 + <!-- 分页组件 -->
  65 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  66 + @pagination="getList"/>
  67 +
  68 + <!-- 对话框(添加 / 修改) -->
  69 + <el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
  70 + <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  71 + <el-form-item label="活动名称" prop="name">
  72 + <el-input v-model="form.name" placeholder="请输入活动名称" />
  73 + </el-form-item>
  74 + <el-form-item label="活动时间" prop="startAndEndTime">
  75 + <el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
  76 + value-format="timestamp" placeholder="选择开始时间" style="width: 480px" />
  77 + </el-form-item>
  78 + <el-form-item label="条件类型" prop="conditionType">
  79 + <el-radio-group v-model="form.conditionType">
  80 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_CONDITION_TYPE)"
  81 + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
  82 + </el-radio-group>
  83 + </el-form-item>
  84 + <el-form-item label="优惠设置" prop="conditionType">
  85 + <!-- TODO 芋艿:待实现! -->
  86 + </el-form-item>
  87 + <el-form-item label="活动商品" prop="productScope">
  88 + <el-radio-group v-model="form.productScope">
  89 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
  90 + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
  91 + </el-radio-group>
  92 + </el-form-item>
  93 + <el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
  94 + <el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
  95 + multiple filterable style="width: 400px">
  96 + <el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
  97 + <span style="float: left">{{ item.name }}</span>
  98 + <span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.minPrice / 100.0).toFixed(2) }}</span>
  99 + </el-option>
  100 + </el-select>
  101 + </el-form-item>
  102 + <el-form-item label="备注" prop="remark">
  103 + <el-input v-model="form.remark" placeholder="请输入备注" />
  104 + </el-form-item>
  105 + </el-form>
  106 + <div slot="footer" class="dialog-footer">
  107 + <el-button type="primary" @click="submitForm">确 定</el-button>
  108 + <el-button @click="cancel">取 消</el-button>
  109 + </div>
  110 + </el-dialog>
  111 + </div>
  112 +</template>
  113 +
  114 +<script>
  115 +import {
  116 + createRewardActivity,
  117 + updateRewardActivity,
  118 + deleteRewardActivity,
  119 + getRewardActivity,
  120 + getRewardActivityPage,
  121 + closeRewardActivity
  122 +} from "@/api/mall/promotion/rewardActivity";
  123 +import {
  124 + PromotionConditionTypeEnum,
  125 + PromotionProductScopeEnum,
  126 + PromotionActivityStatusEnum
  127 +} from "@/utils/constants";
  128 +import {getSpuSimpleList} from "@/api/mall/product/spu";
  129 +
  130 +export default {
  131 + name: "RewardActivity",
  132 + components: {
  133 + },
  134 + data() {
  135 + return {
  136 + // 遮罩层
  137 + loading: true,
  138 + // 导出遮罩层
  139 + exportLoading: false,
  140 + // 显示搜索条件
  141 + showSearch: true,
  142 + // 总条数
  143 + total: 0,
  144 + // 满减送活动列表
  145 + list: [],
  146 + // 弹出层标题
  147 + title: "",
  148 + // 是否显示弹出层
  149 + open: false,
  150 + // 查询参数
  151 + queryParams: {
  152 + pageNo: 1,
  153 + pageSize: 10,
  154 + name: null,
  155 + status: null,
  156 + },
  157 + // 表单参数
  158 + form: {},
  159 + // 表单校验
  160 + rules: {
  161 + name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
  162 + startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
  163 + conditionType: [{ required: true, message: "条件类型不能为空", trigger: "change" }],
  164 + productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
  165 + productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
  166 + },
  167 + // 商品列表
  168 + productSpus: [],
  169 + // 如下的变量,主要为了 v-if 判断可以使用到
  170 + PromotionProductScopeEnum: PromotionProductScopeEnum,
  171 + PromotionActivityStatusEnum: PromotionActivityStatusEnum,
  172 + };
  173 + },
  174 + created() {
  175 + this.getList();
  176 + // 查询商品列表
  177 + getSpuSimpleList().then(response => {
  178 + this.productSpus = response.data
  179 + })
  180 + },
  181 + methods: {
  182 + /** 查询列表 */
  183 + getList() {
  184 + this.loading = true;
  185 + // 执行查询
  186 + getRewardActivityPage(this.queryParams).then(response => {
  187 + this.list = response.data.list;
  188 + this.total = response.data.total;
  189 + this.loading = false;
  190 + });
  191 + },
  192 + /** 取消按钮 */
  193 + cancel() {
  194 + this.open = false;
  195 + this.reset();
  196 + },
  197 + /** 表单重置 */
  198 + reset() {
  199 + this.form = {
  200 + id: undefined,
  201 + name: undefined,
  202 + startAndEndTime: undefined,
  203 + startTime: undefined,
  204 + endTime: undefined,
  205 + conditionType: PromotionConditionTypeEnum.PRICE.type,
  206 + remark: undefined,
  207 + productScope: PromotionProductScopeEnum.ALL.scope,
  208 + productSpuIds: undefined,
  209 + rules: undefined,
  210 + };
  211 + this.resetForm("form");
  212 + },
  213 + /** 搜索按钮操作 */
  214 + handleQuery() {
  215 + this.queryParams.pageNo = 1;
  216 + this.getList();
  217 + },
  218 + /** 重置按钮操作 */
  219 + resetQuery() {
  220 + this.resetForm("queryForm");
  221 + this.handleQuery();
  222 + },
  223 + /** 新增按钮操作 */
  224 + handleAdd() {
  225 + this.reset();
  226 + this.open = true;
  227 + this.title = "添加满减送活动";
  228 + },
  229 + /** 修改按钮操作 */
  230 + handleUpdate(row) {
  231 + this.reset();
  232 + const id = row.id;
  233 + getRewardActivity(id).then(response => {
  234 + this.form = response.data;
  235 + this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
  236 + this.open = true;
  237 + this.title = "修改满减送活动";
  238 + });
  239 + },
  240 + /** 提交按钮 */
  241 + submitForm() {
  242 + this.$refs["form"].validate(valid => {
  243 + if (!valid) {
  244 + return;
  245 + }
  246 + this.form.startTime = this.form.startAndEndTime[0];
  247 + this.form.endTime = this.form.startAndEndTime[1];
  248 + // TODO 芋艿:临时实现
  249 + this.form.rules = [
  250 + {
  251 + limit: 1,
  252 + discountPrice: 10,
  253 + freeDelivery: true,
  254 + point: 10,
  255 + couponIds: [10, 20],
  256 + couponCounts: [1, 2]
  257 + }, {
  258 + limit: 2,
  259 + discountPrice: 20,
  260 + freeDelivery: false,
  261 + point: 20,
  262 + couponIds: [30, 40],
  263 + couponCounts: [3, 4]
  264 + }
  265 + ];
  266 + // 修改的提交
  267 + if (this.form.id != null) {
  268 + updateRewardActivity(this.form).then(response => {
  269 + this.$modal.msgSuccess("修改成功");
  270 + this.open = false;
  271 + this.getList();
  272 + });
  273 + return;
  274 + }
  275 + // 添加的提交
  276 + createRewardActivity(this.form).then(response => {
  277 + this.$modal.msgSuccess("新增成功");
  278 + this.open = false;
  279 + this.getList();
  280 + });
  281 + });
  282 + },
  283 + /** 删除按钮操作 */
  284 + handleDelete(row) {
  285 + const id = row.id;
  286 + this.$modal.confirm('是否确认删除满减送活动编号为"' + id + '"的数据项?').then(function() {
  287 + return deleteRewardActivity(id);
  288 + }).then(() => {
  289 + this.getList();
  290 + this.$modal.msgSuccess("删除成功");
  291 + }).catch(() => {});
  292 + },
  293 + /** 关闭按钮操作 */
  294 + handleClose(row) {
  295 + const id = row.id;
  296 + this.$modal.confirm('是否确认关闭满减送活动编号为"' + id + '"的数据项?').then(function() {
  297 + return closeRewardActivity(id);
  298 + }).then(() => {
  299 + this.getList();
  300 + this.$modal.msgSuccess("关闭成功");
  301 + }).catch(() => {});
  302 + }
  303 + }
  304 +};
  305 +</script>
... ...
yudao-ui-admin/src/views/system/role/index.vue
... ... @@ -50,7 +50,7 @@
50 50 <el-table-column label="显示顺序" prop="sort" width="100" />
51 51 <el-table-column label="状态" align="center" width="100">
52 52 <template slot-scope="scope">
53   - <el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
  53 + <el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)"/>
54 54 </template>
55 55 </el-table-column>
56 56 <el-table-column label="创建时间" align="center" prop="createTime" width="180">
... ... @@ -167,7 +167,6 @@
167 167 import {
168 168 addRole,
169 169 changeRoleStatus,
170   - dataScope,
171 170 delRole,
172 171 exportRole,
173 172 getRole,
... ...