Commit 6d48bd1ed8651f4214b622052c5afd5269bff076
1 parent
544597d6
promotion:完善限时折扣的添加逻辑
Showing
16 changed files
with
308 additions
and
44 deletions
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/ProductSkuController.java
| 1 | package cn.iocoder.yudao.module.product.controller.admin.sku; | 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 | import io.swagger.annotations.Api; | 12 | import io.swagger.annotations.Api; |
| 13 | +import io.swagger.annotations.ApiOperation; | ||
| 4 | import org.springframework.validation.annotation.Validated; | 14 | import org.springframework.validation.annotation.Validated; |
| 15 | +import org.springframework.web.bind.annotation.GetMapping; | ||
| 5 | import org.springframework.web.bind.annotation.RequestMapping; | 16 | import org.springframework.web.bind.annotation.RequestMapping; |
| 6 | import org.springframework.web.bind.annotation.RestController; | 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 | @Api(tags = "管理后台 - 商品 sku") | 27 | @Api(tags = "管理后台 - 商品 sku") |
| 9 | @RestController | 28 | @RestController |
| 10 | @RequestMapping("/product/sku") | 29 | @RequestMapping("/product/sku") |
| 11 | @Validated | 30 | @Validated |
| 12 | public class ProductSkuController { | 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,7 +9,7 @@ import lombok.ToString; | ||
| 9 | import java.util.Date; | 9 | import java.util.Date; |
| 10 | import java.util.List; | 10 | import java.util.List; |
| 11 | 11 | ||
| 12 | -@ApiModel("管理后台 - 商品sku Response VO") | 12 | +@ApiModel("管理后台 - 商品 SKU Response VO") |
| 13 | @Data | 13 | @Data |
| 14 | @EqualsAndHashCode(callSuper = true) | 14 | @EqualsAndHashCode(callSuper = true) |
| 15 | @ToString(callSuper = true) | 15 | @ToString(callSuper = true) |
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,6 +2,7 @@ package cn.iocoder.yudao.module.product.convert.sku; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; | 3 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 4 | import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; | 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 | import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; | 6 | import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; |
| 6 | import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuDetailRespVO; | 7 | import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuDetailRespVO; |
| 7 | import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; | 8 | import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; |
| @@ -11,7 +12,7 @@ import org.mapstruct.factory.Mappers; | @@ -11,7 +12,7 @@ import org.mapstruct.factory.Mappers; | ||
| 11 | import java.util.List; | 12 | import java.util.List; |
| 12 | 13 | ||
| 13 | /** | 14 | /** |
| 14 | - * 商品sku Convert | 15 | + * 商品 SKU Convert |
| 15 | * | 16 | * |
| 16 | * @author 芋道源码 | 17 | * @author 芋道源码 |
| 17 | */ | 18 | */ |
| @@ -36,4 +37,6 @@ public interface ProductSkuConvert { | @@ -36,4 +37,6 @@ public interface ProductSkuConvert { | ||
| 36 | 37 | ||
| 37 | List<ProductSkuRespDTO> convertList04(List<ProductSkuDO> list); | 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/dal/dataobject/sku/ProductSkuDO.java
| @@ -97,6 +97,7 @@ public class ProductSkuDO extends BaseDO { | @@ -97,6 +97,7 @@ public class ProductSkuDO extends BaseDO { | ||
| 97 | * 商品属性 | 97 | * 商品属性 |
| 98 | */ | 98 | */ |
| 99 | @Data | 99 | @Data |
| 100 | + @NoArgsConstructor | ||
| 100 | @AllArgsConstructor | 101 | @AllArgsConstructor |
| 101 | public static class Property { | 102 | public static class Property { |
| 102 | 103 | ||
| @@ -130,19 +131,5 @@ public class ProductSkuDO extends BaseDO { | @@ -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,22 +14,29 @@ import java.util.List; | ||
| 14 | public interface ProductSkuService { | 14 | public interface ProductSkuService { |
| 15 | 15 | ||
| 16 | /** | 16 | /** |
| 17 | - * 删除商品sku | 17 | + * 删除商品 SKU |
| 18 | * | 18 | * |
| 19 | * @param id 编号 | 19 | * @param id 编号 |
| 20 | */ | 20 | */ |
| 21 | void deleteSku(Long id); | 21 | void deleteSku(Long id); |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | - * 获得商品sku | 24 | + * 获得商品 SKU 信息 |
| 25 | * | 25 | * |
| 26 | * @param id 编号 | 26 | * @param id 编号 |
| 27 | - * @return 商品sku | 27 | + * @return 商品 SKU 信息 |
| 28 | */ | 28 | */ |
| 29 | ProductSkuDO getSku(Long id); | 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 | * @param ids 编号 | 41 | * @param ids 编号 |
| 35 | * @return 商品sku列表 | 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,6 +62,11 @@ public class ProductSkuServiceImpl implements ProductSkuService { | ||
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | @Override | 64 | @Override |
| 65 | + public List<ProductSkuDO> getSkuList() { | ||
| 66 | + return productSkuMapper.selectList(); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @Override | ||
| 65 | public List<ProductSkuDO> getSkuList(Collection<Long> ids) { | 70 | public List<ProductSkuDO> getSkuList(Collection<Long> ids) { |
| 66 | return productSkuMapper.selectBatchIds(ids); | 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,6 +9,9 @@ import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; | ||
| 9 | import javax.validation.Valid; | 9 | import javax.validation.Valid; |
| 10 | import java.util.Collection; | 10 | import java.util.Collection; |
| 11 | import java.util.List; | 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 | * 商品 SPU Service 接口 | 17 | * 商品 SPU Service 接口 |
| @@ -64,6 +67,16 @@ public interface ProductSpuService { | @@ -64,6 +67,16 @@ public interface ProductSpuService { | ||
| 64 | List<ProductSpuDO> getSpuList(Collection<Long> ids); | 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 | + /** | ||
| 67 | * 获得所有商品 SPU 列表 | 80 | * 获得所有商品 SPU 列表 |
| 68 | * | 81 | * |
| 69 | * @return 商品 SPU 列表 | 82 | * @return 商品 SPU 列表 |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/vo/template/CouponTemplateBaseVO.java
| @@ -130,11 +130,11 @@ public class CouponTemplateBaseVO { | @@ -130,11 +130,11 @@ public class CouponTemplateBaseVO { | ||
| 130 | || fixedEndTerm != null; | 130 | || fixedEndTerm != null; |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | - @AssertTrue(message = "折扣百分比需要大于等于 1") | 133 | + @AssertTrue(message = "折扣百分比需要大于等于 1,小于等于 99") |
| 134 | @JsonIgnore | 134 | @JsonIgnore |
| 135 | public boolean isDiscountPercentValid() { | 135 | public boolean isDiscountPercentValid() { |
| 136 | return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType()) | 136 | return ObjectUtil.notEqual(discountType, PromotionDiscountTypeEnum.PERCENT.getType()) |
| 137 | - || (discountPercent != null && discountPercent >= 1); | 137 | + || (discountPercent != null && discountPercent >= 1 && discountPercent<= 99); |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | @AssertTrue(message = "优惠金额不能为空") | 140 | @AssertTrue(message = "优惠金额不能为空") |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityBaseVO.java
| 1 | package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo; | 1 | package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo; |
| 2 | 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; | ||
| 3 | import io.swagger.annotations.ApiModel; | 7 | import io.swagger.annotations.ApiModel; |
| 4 | import io.swagger.annotations.ApiModelProperty; | 8 | import io.swagger.annotations.ApiModelProperty; |
| 5 | import lombok.Data; | 9 | import lombok.Data; |
| 6 | import org.springframework.format.annotation.DateTimeFormat; | 10 | import org.springframework.format.annotation.DateTimeFormat; |
| 7 | 11 | ||
| 12 | +import javax.validation.constraints.AssertTrue; | ||
| 8 | import javax.validation.constraints.Min; | 13 | import javax.validation.constraints.Min; |
| 9 | import javax.validation.constraints.NotNull; | 14 | import javax.validation.constraints.NotNull; |
| 10 | import java.util.Date; | 15 | import java.util.Date; |
| @@ -47,10 +52,31 @@ public class DiscountActivityBaseVO { | @@ -47,10 +52,31 @@ public class DiscountActivityBaseVO { | ||
| 47 | @NotNull(message = "商品 SKU 编号不能为空") | 52 | @NotNull(message = "商品 SKU 编号不能为空") |
| 48 | private Long skuId; | 53 | private Long skuId; |
| 49 | 54 | ||
| 50 | - @ApiModelProperty(value = "折扣价格,单位:分", required = true, example = "1000") | ||
| 51 | - @NotNull(message = "折扣价格不能为空") | ||
| 52 | - @Min(value = 1, message = "折扣价格必须大于 0") | 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") | ||
| 53 | private Integer discountPrice; | 65 | private Integer discountPrice; |
| 54 | 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 | + | ||
| 55 | } | 81 | } |
| 56 | } | 82 | } |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityCreateReqVO.java
| @@ -5,7 +5,8 @@ import lombok.Data; | @@ -5,7 +5,8 @@ import lombok.Data; | ||
| 5 | import lombok.EqualsAndHashCode; | 5 | import lombok.EqualsAndHashCode; |
| 6 | import lombok.ToString; | 6 | import lombok.ToString; |
| 7 | 7 | ||
| 8 | -import javax.validation.constraints.NotNull; | 8 | +import javax.validation.Valid; |
| 9 | +import javax.validation.constraints.NotEmpty; | ||
| 9 | import java.util.List; | 10 | import java.util.List; |
| 10 | 11 | ||
| 11 | @ApiModel("管理后台 - 限时折扣活动创建 Request VO") | 12 | @ApiModel("管理后台 - 限时折扣活动创建 Request VO") |
| @@ -17,7 +18,8 @@ public class DiscountActivityCreateReqVO extends DiscountActivityBaseVO { | @@ -17,7 +18,8 @@ public class DiscountActivityCreateReqVO extends DiscountActivityBaseVO { | ||
| 17 | /** | 18 | /** |
| 18 | * 商品列表 | 19 | * 商品列表 |
| 19 | */ | 20 | */ |
| 20 | - @NotNull(message = "商品列表不能为空") | 21 | + @NotEmpty(message = "商品列表不能为空") |
| 22 | + @Valid | ||
| 21 | private List<Product> products; | 23 | private List<Product> products; |
| 22 | 24 | ||
| 23 | } | 25 | } |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityUpdateReqVO.java
| @@ -6,6 +6,8 @@ import lombok.Data; | @@ -6,6 +6,8 @@ import lombok.Data; | ||
| 6 | import lombok.EqualsAndHashCode; | 6 | import lombok.EqualsAndHashCode; |
| 7 | import lombok.ToString; | 7 | import lombok.ToString; |
| 8 | 8 | ||
| 9 | +import javax.validation.Valid; | ||
| 10 | +import javax.validation.constraints.NotEmpty; | ||
| 9 | import javax.validation.constraints.NotNull; | 11 | import javax.validation.constraints.NotNull; |
| 10 | import java.util.List; | 12 | import java.util.List; |
| 11 | 13 | ||
| @@ -22,7 +24,8 @@ public class DiscountActivityUpdateReqVO extends DiscountActivityBaseVO { | @@ -22,7 +24,8 @@ public class DiscountActivityUpdateReqVO extends DiscountActivityBaseVO { | ||
| 22 | /** | 24 | /** |
| 23 | * 商品列表 | 25 | * 商品列表 |
| 24 | */ | 26 | */ |
| 25 | - @NotNull(message = "商品列表不能为空") | 27 | + @NotEmpty(message = "商品列表不能为空") |
| 28 | + @Valid | ||
| 26 | private List<DiscountActivityCreateReqVO.Product> products; | 29 | private List<DiscountActivityCreateReqVO.Product> products; |
| 27 | 30 | ||
| 28 | } | 31 | } |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/discount/DiscountProductDO.java
| 1 | package cn.iocoder.yudao.module.promotion.dal.dataobject.discount; | 1 | package cn.iocoder.yudao.module.promotion.dal.dataobject.discount; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; | ||
| 4 | import com.baomidou.mybatisplus.annotation.KeySequence; | 5 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 5 | import com.baomidou.mybatisplus.annotation.TableId; | 6 | import com.baomidou.mybatisplus.annotation.TableId; |
| 6 | import com.baomidou.mybatisplus.annotation.TableName; | 7 | import com.baomidou.mybatisplus.annotation.TableName; |
| @@ -41,8 +42,23 @@ public class DiscountProductDO extends BaseDO { | @@ -41,8 +42,23 @@ public class DiscountProductDO extends BaseDO { | ||
| 41 | * 关联 ProductSkuDO 的 id 编号 | 42 | * 关联 ProductSkuDO 的 id 编号 |
| 42 | */ | 43 | */ |
| 43 | private Long skuId; | 44 | private Long skuId; |
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 折扣类型 | ||
| 48 | + * | ||
| 49 | + * 枚举 {@link PromotionDiscountTypeEnum} | ||
| 50 | + */ | ||
| 51 | + private Integer discountType; | ||
| 44 | /** | 52 | /** |
| 45 | - * 优惠价格,单位:分 | 53 | + * 折扣百分比 |
| 54 | + * | ||
| 55 | + * 例如,80% 为 80 | ||
| 56 | + */ | ||
| 57 | + private Integer discountPercent; | ||
| 58 | + /** | ||
| 59 | + * 优惠金额,单位:分 | ||
| 60 | + * | ||
| 61 | + * 当 {@link #discountType} 为 {@link PromotionDiscountTypeEnum#PRICE} 生效 | ||
| 46 | */ | 62 | */ |
| 47 | private Integer discountPrice; | 63 | private Integer discountPrice; |
| 48 | 64 |
yudao-ui-admin/src/api/mall/product/sku.js
0 → 100644
yudao-ui-admin/src/api/mall/product/spu.js
| 1 | import request from '@/utils/request' | 1 | import request from '@/utils/request' |
| 2 | 2 | ||
| 3 | -// 创建商品spu | 3 | +// 创建商品 SPU |
| 4 | export function createSpu(data) { | 4 | export function createSpu(data) { |
| 5 | return request({ | 5 | return request({ |
| 6 | url: '/product/spu/create', | 6 | url: '/product/spu/create', |
| @@ -9,7 +9,7 @@ export function createSpu(data) { | @@ -9,7 +9,7 @@ export function createSpu(data) { | ||
| 9 | }) | 9 | }) |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | -// 更新商品spu | 12 | +// 更新商品 SPU |
| 13 | export function updateSpu(data) { | 13 | export function updateSpu(data) { |
| 14 | return request({ | 14 | return request({ |
| 15 | url: '/product/spu/update', | 15 | url: '/product/spu/update', |
| @@ -18,7 +18,7 @@ export function updateSpu(data) { | @@ -18,7 +18,7 @@ export function updateSpu(data) { | ||
| 18 | }) | 18 | }) |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | -// 删除商品spu | 21 | +// 删除商品 SPU |
| 22 | export function deleteSpu(id) { | 22 | export function deleteSpu(id) { |
| 23 | return request({ | 23 | return request({ |
| 24 | url: '/product/spu/delete?id=' + id, | 24 | url: '/product/spu/delete?id=' + id, |
| @@ -34,7 +34,7 @@ export function getSpu(id) { | @@ -34,7 +34,7 @@ export function getSpu(id) { | ||
| 34 | }) | 34 | }) |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | -// 获得商品spu详情 | 37 | +// 获得商品 SPU 详情 |
| 38 | export function getSpuDetail(id) { | 38 | export function getSpuDetail(id) { |
| 39 | return request({ | 39 | return request({ |
| 40 | url: '/product/spu/get/detail?id=' + id, | 40 | url: '/product/spu/get/detail?id=' + id, |
| @@ -42,7 +42,7 @@ export function getSpuDetail(id) { | @@ -42,7 +42,7 @@ export function getSpuDetail(id) { | ||
| 42 | }) | 42 | }) |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | -// 获得商品spu分页 | 45 | +// 获得商品 SPU 分页 |
| 46 | export function getSpuPage(query) { | 46 | export function getSpuPage(query) { |
| 47 | return request({ | 47 | return request({ |
| 48 | url: '/product/spu/page', | 48 | url: '/product/spu/page', |
yudao-ui-admin/src/views/mall/promotion/discountActivity/index.vue
| @@ -70,7 +70,7 @@ | @@ -70,7 +70,7 @@ | ||
| 70 | @pagination="getList"/> | 70 | @pagination="getList"/> |
| 71 | 71 | ||
| 72 | <!-- 对话框(添加 / 修改) --> | 72 | <!-- 对话框(添加 / 修改) --> |
| 73 | - <el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body> | 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"> | 74 | <el-form ref="form" :model="form" :rules="rules" label-width="80px"> |
| 75 | <el-form-item label="活动名称" prop="name"> | 75 | <el-form-item label="活动名称" prop="name"> |
| 76 | <el-input v-model="form.name" placeholder="请输入活动名称" /> | 76 | <el-input v-model="form.name" placeholder="请输入活动名称" /> |
| @@ -80,7 +80,54 @@ | @@ -80,7 +80,54 @@ | ||
| 80 | </el-form-item> | 80 | </el-form-item> |
| 81 | <el-form-item label="活动时间" prop="startAndEndTime"> | 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']" | 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: 480px" /> | 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: 400px" @change="changeFormSku"> | ||
| 88 | + <el-option v-for="item in productSkus" :key="item.id" :label="item.name" :value="item.id"> | ||
| 89 | + <span style="float: left">{{ item.spuName }} {{ 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 }} {{ 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"> | ||
| 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" /> 元 | ||
| 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> | ||
| 84 | </el-form-item> | 131 | </el-form-item> |
| 85 | </el-form> | 132 | </el-form> |
| 86 | <div slot="footer" class="dialog-footer"> | 133 | <div slot="footer" class="dialog-footer"> |
| @@ -93,8 +140,11 @@ | @@ -93,8 +140,11 @@ | ||
| 93 | 140 | ||
| 94 | <script> | 141 | <script> |
| 95 | import { createDiscountActivity, updateDiscountActivity, deleteDiscountActivity, getDiscountActivity, getDiscountActivityPage } from "@/api/mall/promotion/discountActivity"; | 142 | import { createDiscountActivity, updateDiscountActivity, deleteDiscountActivity, getDiscountActivity, getDiscountActivityPage } from "@/api/mall/promotion/discountActivity"; |
| 96 | -import {PromotionActivityStatusEnum, PromotionProductScopeEnum} from "@/utils/constants"; | ||
| 97 | -import {closeRewardActivity} from "@/api/mall/promotion/rewardActivity"; | 143 | +import { |
| 144 | + PromotionActivityStatusEnum, PromotionDiscountTypeEnum, | ||
| 145 | + PromotionProductScopeEnum | ||
| 146 | +} from "@/utils/constants"; | ||
| 147 | +import { getSkuOptionList } from "@/api/mall/product/sku"; | ||
| 98 | 148 | ||
| 99 | export default { | 149 | export default { |
| 100 | name: "DiscountActivity", | 150 | name: "DiscountActivity", |
| @@ -115,7 +165,7 @@ export default { | @@ -115,7 +165,7 @@ export default { | ||
| 115 | // 弹出层名称 | 165 | // 弹出层名称 |
| 116 | title: "", | 166 | title: "", |
| 117 | // 是否显示弹出层 | 167 | // 是否显示弹出层 |
| 118 | - open: false, | 168 | + open: true, |
| 119 | // 查询参数 | 169 | // 查询参数 |
| 120 | queryParams: { | 170 | queryParams: { |
| 121 | pageNo: 1, | 171 | pageNo: 1, |
| @@ -125,17 +175,38 @@ export default { | @@ -125,17 +175,38 @@ export default { | ||
| 125 | createTime: [], | 175 | createTime: [], |
| 126 | }, | 176 | }, |
| 127 | // 表单参数 | 177 | // 表单参数 |
| 128 | - form: {}, | 178 | + form: { |
| 179 | + skuIds: [], // 选中的 SKU | ||
| 180 | + products: [], // 商品信息 | ||
| 181 | + // products: [{ | ||
| 182 | + // id: 2, | ||
| 183 | + // name: '白色', | ||
| 184 | + // price: 500, | ||
| 185 | + // stock: 20, | ||
| 186 | + // spuId: 1, | ||
| 187 | + // spuName: 'iPhone 14 Pro', | ||
| 188 | + // discountType: 1, | ||
| 189 | + // }, { | ||
| 190 | + // id: 10, | ||
| 191 | + // name: '蓝色', | ||
| 192 | + // price: 1000, | ||
| 193 | + // stock: 100, | ||
| 194 | + // spuId: 20, | ||
| 195 | + // spuName: 'iPhone 14 Pro', | ||
| 196 | + // discountType: 2, | ||
| 197 | + // }] | ||
| 198 | + }, | ||
| 129 | // 表单校验 | 199 | // 表单校验 |
| 130 | rules: { | 200 | rules: { |
| 131 | name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }], | 201 | name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }], |
| 132 | startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }], | 202 | startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }], |
| 133 | }, | 203 | }, |
| 134 | - // 商品列表 | ||
| 135 | - productSpus: [], // TODO 芋艿:需要重新写下 | 204 | + // 商品 SKU 列表 |
| 205 | + productSkus: [], | ||
| 136 | // 如下的变量,主要为了 v-if 判断可以使用到 | 206 | // 如下的变量,主要为了 v-if 判断可以使用到 |
| 137 | PromotionProductScopeEnum: PromotionProductScopeEnum, | 207 | PromotionProductScopeEnum: PromotionProductScopeEnum, |
| 138 | PromotionActivityStatusEnum: PromotionActivityStatusEnum, | 208 | PromotionActivityStatusEnum: PromotionActivityStatusEnum, |
| 209 | + PromotionDiscountTypeEnum: PromotionDiscountTypeEnum, | ||
| 139 | }; | 210 | }; |
| 140 | }, | 211 | }, |
| 141 | created() { | 212 | created() { |
| @@ -151,6 +222,10 @@ export default { | @@ -151,6 +222,10 @@ export default { | ||
| 151 | this.total = response.data.total; | 222 | this.total = response.data.total; |
| 152 | this.loading = false; | 223 | this.loading = false; |
| 153 | }); | 224 | }); |
| 225 | + // 获得 SKU 商品列表 | ||
| 226 | + getSkuOptionList().then(response => { | ||
| 227 | + this.productSkus = response.data; | ||
| 228 | + }); | ||
| 154 | }, | 229 | }, |
| 155 | /** 取消按钮 */ | 230 | /** 取消按钮 */ |
| 156 | cancel() { | 231 | cancel() { |
| @@ -166,6 +241,8 @@ export default { | @@ -166,6 +241,8 @@ export default { | ||
| 166 | startTime: undefined, | 241 | startTime: undefined, |
| 167 | endTime: undefined, | 242 | endTime: undefined, |
| 168 | remark: undefined, | 243 | remark: undefined, |
| 244 | + skuIds: [], | ||
| 245 | + products: [], | ||
| 169 | }; | 246 | }; |
| 170 | this.resetForm("form"); | 247 | this.resetForm("form"); |
| 171 | }, | 248 | }, |
| @@ -240,7 +317,49 @@ export default { | @@ -240,7 +317,49 @@ export default { | ||
| 240 | this.getList(); | 317 | this.getList(); |
| 241 | this.$modal.msgSuccess("关闭成功"); | 318 | this.$modal.msgSuccess("关闭成功"); |
| 242 | }).catch(() => {}); | 319 | }).catch(() => {}); |
| 243 | - } | 320 | + }, |
| 321 | + /** 当 Form 的 SKU 发生变化时 */ | ||
| 322 | + changeFormSku(skuIds) { | ||
| 323 | + // 处理【新增】 | ||
| 324 | + skuIds.forEach(skuId => { | ||
| 325 | + // 获得对应的 SKU 信息 | ||
| 326 | + const sku = this.productSkus.find(item => item.id === skuId); | ||
| 327 | + if (!sku) { | ||
| 328 | + // debugger | ||
| 329 | + return; | ||
| 330 | + } | ||
| 331 | + // 判断已存在,直接跳过 | ||
| 332 | + const product = this.form.products.find(item => item.skuId === skuId); | ||
| 333 | + if (product) { | ||
| 334 | + // debugger | ||
| 335 | + return; | ||
| 336 | + } | ||
| 337 | + this.form.products.push({ | ||
| 338 | + skuId: sku.id, | ||
| 339 | + name: sku.name, | ||
| 340 | + price: sku.price, | ||
| 341 | + stock: sku.stock, | ||
| 342 | + spuId: sku.spuId, | ||
| 343 | + spuName: sku.spuName, | ||
| 344 | + discountType: PromotionDiscountTypeEnum.PRICE.type, | ||
| 345 | + }); | ||
| 346 | + }); | ||
| 347 | + // 处理【移除】 | ||
| 348 | + this.form.products.map((product, index) => { | ||
| 349 | + if (!skuIds.includes(product.skuId)) { | ||
| 350 | + this.form.products.splice(index, 1); | ||
| 351 | + } | ||
| 352 | + }); | ||
| 353 | + }, | ||
| 354 | + /** 移除 Form 的 SKU */ | ||
| 355 | + removeFormSku(skuId) { | ||
| 356 | + this.form.skuIds.map((id, index) => { | ||
| 357 | + if (skuId === id) { | ||
| 358 | + this.form.skuIds.splice(index, 1); | ||
| 359 | + } | ||
| 360 | + }); | ||
| 361 | + this.changeFormSku(this.form.skuIds); | ||
| 362 | + }, | ||
| 244 | } | 363 | } |
| 245 | -}; | 364 | +} |
| 246 | </script> | 365 | </script> |