Commit 6f740fab7cad2140b9bd15e65069dd8a8ba492b1

Authored by YunaiV
1 parent 20775791

【代码优化】商城:完善满减送的计算逻辑

yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/category/ProductCategoryServiceImpl.java
... ... @@ -113,14 +113,16 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
113 113 Map<Long, ProductCategoryDO> categoryMap = CollectionUtils.convertMap(list, ProductCategoryDO::getId);
114 114 // 校验
115 115 ids.forEach(id -> {
  116 + // 校验分类是否存在
116 117 ProductCategoryDO category = categoryMap.get(id);
117 118 if (category == null) {
118 119 throw exception(CATEGORY_NOT_EXISTS);
119 120 }
  121 + // 校验分类是否启用
120 122 if (!CommonStatusEnum.ENABLE.getStatus().equals(category.getStatus())) {
121 123 throw exception(CATEGORY_DISABLED, category.getName());
122 124 }
123   - // 校验层级
  125 + // 商品分类层级校验,必须使用第二级的商品分类
124 126 if (getCategoryLevel(id) < CATEGORY_LEVEL) {
125 127 throw exception(SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR);
126 128 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java
1 1 package cn.iocoder.yudao.module.promotion.convert.discount;
2 2  
3   -import cn.hutool.core.util.ObjectUtil;
4 3 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.product.api.spu.dto.ProductSpuRespDTO;
8   -import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.*;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  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.DiscountActivityRespVO;
  8 +import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
9 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
10 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
11   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
12 11 import org.mapstruct.Mapper;
13 12 import org.mapstruct.factory.Mappers;
14 13  
15 14 import java.util.List;
16   -import java.util.Map;
17 15  
18 16 /**
19 17 * 限时折扣活动 Convert
... ... @@ -32,105 +30,22 @@ public interface DiscountActivityConvert {
32 30 DiscountActivityRespVO convert(DiscountActivityDO bean);
33 31  
34 32 List<DiscountActivityRespVO> convertList(List<DiscountActivityDO> list);
  33 +
35 34 List<DiscountActivityBaseVO.Product> convertList2(List<DiscountProductDO> list);
  35 +
36 36 PageResult<DiscountActivityRespVO> convertPage(PageResult<DiscountActivityDO> page);
37 37  
38 38 default PageResult<DiscountActivityRespVO> convertPage(PageResult<DiscountActivityDO> page,
39   - List<DiscountProductDO> discountProductDOList,
40   - List<ProductSpuRespDTO> spuList) {
  39 + List<DiscountProductDO> discountProductDOList) {
41 40 PageResult<DiscountActivityRespVO> pageResult = convertPage(page);
42   -
43   - // 拼接商品 TODO @zhangshuai:类似空行的问题,也可以看看
44   - Map<Long, DiscountProductDO> discountActivityMap = CollectionUtils.convertMap(discountProductDOList, DiscountProductDO::getActivityId);
45   - Map<Long, ProductSpuRespDTO> spuMap = CollectionUtils.convertMap(spuList, ProductSpuRespDTO::getId);
46   - pageResult.getList().forEach(item -> {
47   - item.setProducts(convertList2(discountProductDOList));
48   - item.setSpuId(discountActivityMap.get(item.getId())==null?null: discountActivityMap.get(item.getId()).getSpuId());
49   - if (item.getSpuId() != null) {
50   - MapUtils.findAndThen(spuMap, item.getSpuId(),
51   - spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
52   - }
53   -
54   - });
  41 + pageResult.getList().forEach(item -> item.setProducts(convertList2(discountProductDOList)));
55 42 return pageResult;
56 43 }
57 44  
58 45 DiscountProductDO convert(DiscountActivityBaseVO.Product bean);
59 46  
60   - default DiscountActivityDetailRespVO convert(DiscountActivityDO activity, List<DiscountProductDO> products){
61   - if ( activity == null && products == null ) {
62   - return null;
63   - }
64   -
65   - DiscountActivityDetailRespVO discountActivityDetailRespVO = new DiscountActivityDetailRespVO();
66   -
67   - if ( activity != null ) {
68   - discountActivityDetailRespVO.setName( activity.getName() );
69   - discountActivityDetailRespVO.setStartTime( activity.getStartTime() );
70   - discountActivityDetailRespVO.setEndTime( activity.getEndTime() );
71   - discountActivityDetailRespVO.setRemark( activity.getRemark() );
72   - discountActivityDetailRespVO.setId( activity.getId() );
73   - discountActivityDetailRespVO.setStatus( activity.getStatus() );
74   - discountActivityDetailRespVO.setCreateTime( activity.getCreateTime() );
75   - }
76   - if (!products.isEmpty()) {
77   - discountActivityDetailRespVO.setSpuId(products.get(0).getSpuId());
78   - }
79   - discountActivityDetailRespVO.setProducts( convertList2( products ) );
80   -
81   - return discountActivityDetailRespVO;
82   - }
83   -
84   - // =========== 比较是否相等 ==========
85   - /**
86   - * 比较两个限时折扣商品是否相等
87   - *
88   - * @param productDO 数据库中的商品
89   - * @param productVO 前端传入的商品
90   - * @return 是否匹配
91   - */
92   - @SuppressWarnings("DuplicatedCode")
93   - default boolean isEquals(DiscountProductDO productDO, DiscountActivityBaseVO.Product productVO) {
94   - if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
95   - || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
96   - || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) {
97   - return false;
98   - }
99   - if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
100   - return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice());
101   - }
102   - if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) {
103   - return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent());
104   - }
105   - return true;
  47 + default DiscountActivityRespVO convert(DiscountActivityDO activity, List<DiscountProductDO> products) {
  48 + return BeanUtils.toBean(activity, DiscountActivityRespVO.class).setProducts(convertList2(products));
106 49 }
107 50  
108   - /**
109   - * 比较两个限时折扣商品是否相等
110   - * 注意,比较时忽略 id 编号
111   - *
112   - * @param productDO 商品 1
113   - * @param productVO 商品 2
114   - * @return 是否匹配
115   - */
116   - @SuppressWarnings("DuplicatedCode")
117   - default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) {
118   - if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
119   - || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
120   - || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())
121   - || ObjectUtil.notEqual(productDO.getActivityEndTime(), productVO.getActivityEndTime())
122   - || ObjectUtil.notEqual(productDO.getActivityStartTime(), productVO.getActivityStartTime())
123   - || ObjectUtil.notEqual(productDO.getActivityStatus(), productVO.getActivityStatus())) {
124   - return false;
125   - }
126   - if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
127   - return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice());
128   - }
129   - if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) {
130   - return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent());
131   - }
132   - return true;
133   - }
134   -
135   -
136   -}
  51 +}
137 52 \ No newline at end of file
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java
... ... @@ -3,13 +3,12 @@ package cn.iocoder.yudao.module.promotion.dal.mysql.discount;
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
5 5 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
6   -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  6 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
7 7 import org.apache.ibatis.annotations.Mapper;
8 8  
9 9 import java.time.LocalDateTime;
10 10 import java.util.Collection;
11 11 import java.util.List;
12   -import java.util.Map;
13 12  
14 13 /**
15 14 * 限时折扣商城 Mapper
... ... @@ -23,8 +22,23 @@ public interface DiscountProductMapper extends BaseMapperX&lt;DiscountProductDO&gt; {
23 22 return selectList(DiscountProductDO::getActivityId, activityId);
24 23 }
25 24  
26   - default List<DiscountProductDO> selectListBySkuIds(Collection<Long> skuIds) {
27   - return selectList(DiscountProductDO::getSkuId, skuIds);
  25 + default List<DiscountProductDO> selectListByActivityId(Collection<Long> activityIds) {
  26 + return selectList(DiscountProductDO::getActivityId, activityIds);
  27 + }
  28 +
  29 + default List<DiscountProductDO> selectListBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
  30 + return selectList(new LambdaQueryWrapperX<DiscountProductDO>()
  31 + .in(DiscountProductDO::getSpuId, spuIds)
  32 + .eq(DiscountProductDO::getActivityStatus, status));
  33 + }
  34 +
  35 + default void updateByActivityId(DiscountProductDO discountProductDO) {
  36 + update(discountProductDO, new LambdaUpdateWrapper<DiscountProductDO>()
  37 + .eq(DiscountProductDO::getActivityId, discountProductDO.getActivityId()));
  38 + }
  39 +
  40 + default void deleteByActivityId(Long activityId) {
  41 + delete(DiscountProductDO::getActivityId, activityId);
28 42 }
29 43  
30 44 default List<DiscountProductDO> selectListBySkuIdsAndStatusAndNow(Collection<Long> skuIds, Integer status) {
... ... @@ -36,19 +50,4 @@ public interface DiscountProductMapper extends BaseMapperX&lt;DiscountProductDO&gt; {
36 50 .gt(DiscountProductDO::getActivityEndTime, now));
37 51 }
38 52  
39   - /**
40   - * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
41   - *
42   - * @param spuIds spu 编号
43   - * @param status 状态
44   - * @return 包含 spuId 和 activityId 的 map 对象列表
45   - */
46   - default List<Map<String, Object>> selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
47   - return selectMaps(new QueryWrapper<DiscountProductDO>()
48   - .select("spu_id AS spuId, MAX(DISTINCT(activity_id)) AS activityId")
49   - .in("spu_id", spuIds)
50   - .eq("activity_status", status)
51   - .groupBy("spu_id"));
52   - }
53   -
54 53 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java
... ... @@ -89,12 +89,4 @@ public interface DiscountActivityService {
89 89 */
90 90 List<DiscountProductDO> getDiscountProductsByActivityId(Collection<Long> activityIds);
91 91  
92   - /**
93   - * 获取指定 SPU 编号最近参加的活动,每个 spuId 只返回一条记录
94   - *
95   - * @param spuIds SPU 编号数组
96   - * @return 折扣活动列表
97   - */
98   - List<DiscountActivityDO> getDiscountActivityListBySpuIds(Collection<Long> spuIds);
99   -
100 92 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java
1 1 package cn.iocoder.yudao.module.promotion.service.discount;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.collection.CollectionUtil;
5   -import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.core.util.ObjectUtil;
6 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  10 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
9 11 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
10 12 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
11 13 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
... ... @@ -20,15 +22,15 @@ import org.springframework.stereotype.Service;
20 22 import org.springframework.transaction.annotation.Transactional;
21 23 import org.springframework.validation.annotation.Validated;
22 24  
23   -import java.time.LocalDateTime;
24 25 import java.util.Collection;
25   -import java.util.Collections;
26 26 import java.util.List;
27 27 import java.util.Map;
28 28  
  29 +import static cn.hutool.core.collection.CollUtil.intersectionDistinct;
29 30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
30   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
31   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  33 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
32 34 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
33 35  
34 36 /**
... ... @@ -45,16 +47,16 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
45 47 @Resource
46 48 private DiscountProductMapper discountProductMapper;
47 49  
48   - @Override
49   - public List<DiscountProductDO> getMatchDiscountProductListBySkuIds(Collection<Long> skuIds) {
50   - return discountProductMapper.selectListBySkuIdsAndStatusAndNow(skuIds, CommonStatusEnum.ENABLE.getStatus());
51   - }
  50 + @Resource
  51 + private ProductSkuApi productSkuApi;
52 52  
53 53 @Override
54 54 @Transactional(rollbackFor = Exception.class)
55 55 public Long createDiscountActivity(DiscountActivityCreateReqVO createReqVO) {
56 56 // 校验商品是否冲突
57 57 validateDiscountActivityProductConflicts(null, createReqVO.getProducts());
  58 + // 校验商品是否存在
  59 + validateProductExists(createReqVO.getProducts());
58 60  
59 61 // 插入活动
60 62 DiscountActivityDO discountActivity = DiscountActivityConvert.INSTANCE.convert(createReqVO)
... ... @@ -66,6 +68,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
66 68 .setActivityName(discountActivity.getName()).setActivityStatus(discountActivity.getStatus())
67 69 .setActivityStartTime(createReqVO.getStartTime()).setActivityEndTime(createReqVO.getEndTime()));
68 70 discountProductMapper.insertBatch(discountProducts);
  71 + // 返回
69 72 return discountActivity.getId();
70 73 }
71 74  
... ... @@ -79,36 +82,40 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
79 82 }
80 83 // 校验商品是否冲突
81 84 validateDiscountActivityProductConflicts(updateReqVO.getId(), updateReqVO.getProducts());
  85 + // 校验商品是否存在
  86 + validateProductExists(updateReqVO.getProducts());
82 87  
83 88 // 更新活动
84 89 DiscountActivityDO updateObj = DiscountActivityConvert.INSTANCE.convert(updateReqVO);
85 90 discountActivityMapper.updateById(updateObj);
86 91 // 更新商品
87   - updateDiscountProduct(updateReqVO);
  92 + updateDiscountProduct(updateObj, updateReqVO.getProducts());
88 93 }
89 94  
90   - private void updateDiscountProduct(DiscountActivityUpdateReqVO updateReqVO) {
91   - // TODO @zhangshuai:这里的逻辑,可以优化下哈;参考 CombinationActivityServiceImpl 的 updateCombinationProduct,主要是 CollectionUtils.diffList 的使用哈;
92   - // 然后原先是使用 DiscountActivityConvert.INSTANCE.isEquals 对比,现在看看是不是简化就基于 skuId 对比就完事了;之前写的太精细,意义不大;
93   - List<DiscountProductDO> dbDiscountProducts = discountProductMapper.selectListByActivityId(updateReqVO.getId());
94   - // 计算要删除的记录
95   - List<Long> deleteIds = convertList(dbDiscountProducts, DiscountProductDO::getId,
96   - discountProductDO -> updateReqVO.getProducts().stream()
97   - .noneMatch(product -> DiscountActivityConvert.INSTANCE.isEquals(discountProductDO, product)));
98   - if (CollUtil.isNotEmpty(deleteIds)) {
99   - discountProductMapper.deleteByIds(deleteIds);
  95 + private void updateDiscountProduct(DiscountActivityDO activity, List<DiscountActivityCreateReqVO.Product> products) {
  96 + // 第一步,对比新老数据,获得添加、修改、删除的列表
  97 + List<DiscountProductDO> newList = BeanUtils.toBean(products, DiscountProductDO.class,
  98 + product -> product.setActivityId(activity.getId())
  99 + .setActivityName(activity.getName()).setActivityStatus(activity.getStatus())
  100 + .setActivityStartTime(activity.getStartTime()).setActivityEndTime(activity.getEndTime()));
  101 + List<DiscountProductDO> oldList = discountProductMapper.selectListByActivityId(activity.getId());
  102 + List<List<DiscountProductDO>> diffList = CollectionUtils.diffList(oldList, newList, (oldVal, newVal) -> {
  103 + boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());
  104 + if (same) {
  105 + newVal.setId(oldVal.getId());
  106 + }
  107 + return same;
  108 + });
  109 +
  110 + // 第二步,批量添加、修改、删除
  111 + if (CollUtil.isNotEmpty(diffList.get(0))) {
  112 + discountProductMapper.insertBatch(diffList.get(0));
100 113 }
101   - // 计算新增的记录
102   - List<DiscountProductDO> newDiscountProducts = convertList(updateReqVO.getProducts(),
103   - product -> DiscountActivityConvert.INSTANCE.convert(product)
104   - .setActivityId(updateReqVO.getId())
105   - .setActivityName(updateReqVO.getName())
106   - .setActivityStartTime(updateReqVO.getStartTime())
107   - .setActivityEndTime(updateReqVO.getEndTime()));
108   - newDiscountProducts.removeIf(product -> dbDiscountProducts.stream().anyMatch(
109   - dbProduct -> DiscountActivityConvert.INSTANCE.isEquals(dbProduct, product))); // 如果匹配到,说明是更新的
110   - if (CollectionUtil.isNotEmpty(newDiscountProducts)) {
111   - discountProductMapper.insertBatch(newDiscountProducts);
  114 + if (CollUtil.isNotEmpty(diffList.get(1))) {
  115 + discountProductMapper.updateBatch(diffList.get(1));
  116 + }
  117 + if (CollUtil.isNotEmpty(diffList.get(2))) {
  118 + discountProductMapper.deleteByIds(convertList(diffList.get(2), DiscountProductDO::getId));
112 119 }
113 120 }
114 121  
... ... @@ -119,20 +126,44 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
119 126 * @param products 商品列表
120 127 */
121 128 private void validateDiscountActivityProductConflicts(Long id, List<DiscountActivityBaseVO.Product> products) {
122   - if (CollUtil.isEmpty(products)) {
123   - return;
124   - }
125   - // 查询商品参加的活动
126   - List<DiscountProductDO> list = discountProductMapper.selectListByActivityId(id);
127   - List<DiscountProductDO> matchDiscountProductList = discountProductMapper.selectListBySkuIds(
128   - convertSet(list, DiscountProductDO::getSkuId));
129   - if (id != null) { // 排除自己这个活动
130   - matchDiscountProductList.removeIf(product -> id.equals(product.getActivityId()));
131   - }
132   - // 如果非空,则说明冲突
133   - if (CollUtil.isNotEmpty(matchDiscountProductList)) {
134   - throw exception(DISCOUNT_ACTIVITY_SPU_CONFLICTS);
  129 + // 1.1 查询所有开启的折扣活动
  130 + List<DiscountActivityDO> activityList = discountActivityMapper.selectList(DiscountActivityDO::getStatus,
  131 + CommonStatusEnum.ENABLE.getStatus());
  132 + if (id != null) { // 时排除自己
  133 + activityList.removeIf(item -> ObjectUtil.equal(item.getId(), id));
135 134 }
  135 + // 1.2 查询活动下的所有商品
  136 + List<DiscountProductDO> productList = discountProductMapper.selectListByActivityId(
  137 + convertList(activityList, DiscountActivityDO::getId));
  138 + Map<Long, List<DiscountProductDO>> productListMap = convertMultiMap(productList, DiscountProductDO::getActivityId);
  139 +
  140 + // 2. 校验商品是否冲突
  141 + activityList.forEach(item -> {
  142 + findAndThen(productListMap, item.getId(), discountProducts -> {
  143 + if (!intersectionDistinct(convertList(discountProducts, DiscountProductDO::getSpuId),
  144 + convertList(products, DiscountActivityBaseVO.Product::getSpuId)).isEmpty()) {
  145 + throw exception(DISCOUNT_ACTIVITY_SPU_CONFLICTS, item.getName());
  146 + }
  147 + });
  148 + });
  149 + }
  150 +
  151 + /**
  152 + * 校验活动商品是否都存在
  153 + *
  154 + * @param products 活动商品
  155 + */
  156 + private void validateProductExists(List<DiscountActivityBaseVO.Product> products) {
  157 + // 1.获得商品所有的 sku
  158 + List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(
  159 + convertList(products, DiscountActivityBaseVO.Product::getSpuId));
  160 + Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
  161 + // 2. 校验商品 sku 都存在
  162 + products.forEach(product -> {
  163 + if (!skuMap.containsKey(product.getSkuId())) {
  164 + throw exception(SKU_NOT_EXISTS);
  165 + }
  166 + });
136 167 }
137 168  
138 169 @Override
... ... @@ -143,9 +174,11 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
143 174 throw exception(DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
144 175 }
145 176  
146   - // 更新
147   - DiscountActivityDO updateObj = new DiscountActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus());
148   - discountActivityMapper.updateById(updateObj);
  177 + // 更新活动状态
  178 + discountActivityMapper.updateById(new DiscountActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus()));
  179 + // 更新活动商品状态
  180 + discountProductMapper.updateByActivityId(new DiscountProductDO().setActivityId(id).setActivityStatus(
  181 + CommonStatusEnum.DISABLE.getStatus()));
149 182 }
150 183  
151 184 @Override
... ... @@ -156,8 +189,10 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
156 189 throw exception(DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED);
157 190 }
158 191  
159   - // 删除
  192 + // 删除活动
160 193 discountActivityMapper.deleteById(id);
  194 + // 删除活动商品
  195 + discountProductMapper.deleteByActivityId(id);
161 196 }
162 197  
163 198 private DiscountActivityDO validateDiscountActivityExists(Long id) {
... ... @@ -185,21 +220,12 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
185 220  
186 221 @Override
187 222 public List<DiscountProductDO> getDiscountProductsByActivityId(Collection<Long> activityIds) {
188   - return discountProductMapper.selectList("activity_id", activityIds);
  223 + return discountProductMapper.selectList(DiscountProductDO::getActivityId, activityIds);
189 224 }
190 225  
191 226 @Override
192   - public List<DiscountActivityDO> getDiscountActivityListBySpuIds(Collection<Long> spuIds) {
193   - // 1. 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
194   - List<Map<String, Object>> spuIdAndActivityIdMaps = discountProductMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(
195   - spuIds, CommonStatusEnum.ENABLE.getStatus());
196   - if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
197   - return Collections.emptyList();
198   - }
199   -
200   - // 2. 查询活动详情
201   - return discountActivityMapper.selectListByIdsAndDateTimeLt(
202   - convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), LocalDateTime.now());
  227 + public List<DiscountProductDO> getMatchDiscountProductListBySkuIds(Collection<Long> skuIds) {
  228 + return discountProductMapper.selectListBySkuIdsAndStatusAndNow(skuIds, CommonStatusEnum.ENABLE.getStatus());
203 229 }
204 230  
205 231 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java
... ... @@ -137,8 +137,8 @@ public class RewardActivityServiceImpl implements RewardActivityService {
137 137 if (PromotionProductScopeEnum.isAll(item.getProductScope()) ||
138 138 PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {
139 139 throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(),
140   - PromotionProductScopeEnum.isAll(item.getProductScope()) ? "该活动商品范围为全部已覆盖包含本活动范围" :
141   - "本活动商品范围为全部已覆盖包含了该活动商品范围");
  140 + PromotionProductScopeEnum.isAll(item.getProductScope()) ?
  141 + "该活动商品范围为全部已覆盖包含本活动范围" : "本活动商品范围为全部已覆盖包含了该活动商品范围");
142 142 }
143 143 // 情况二:如果与该时间段内商品范围为类别的活动冲突
144 144 if (PromotionProductScopeEnum.isCategory(item.getProductScope())) {
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java
... ... @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
2 2  
3 3 import cn.hutool.core.util.ObjectUtil;
4 4 import cn.hutool.core.util.StrUtil;
5   -import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
6 5 import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
7 6 import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
8 7 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
... ... @@ -23,6 +22,7 @@ import java.util.Map;
23 22  
24 23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
25 24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  25 +import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.calculateRatePrice;
26 26 import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
27 27  
28 28 /**
... ... @@ -125,7 +125,7 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
125 125 if (PromotionDiscountTypeEnum.PRICE.getType().equals(discount.getDiscountType())) { // 减价
126 126 newPrice -= discount.getDiscountPrice() * orderItem.getCount();
127 127 } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discount.getDiscountType())) { // 打折
128   - newPrice = newPrice * discount.getDiscountPercent() / 100;
  128 + newPrice = calculateRatePrice(orderItem.getPayPrice(), discount.getDiscountPercent() / 100.0);
129 129 } else {
130 130 throw new IllegalArgumentException(String.format("优惠活动的商品(%s) 的优惠类型不正确", discount));
131 131 }
... ... @@ -144,7 +144,7 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
144 144 if (level == null || level.getDiscountPercent() == null) {
145 145 return 0;
146 146 }
147   - Integer newPrice = MoneyUtils.calculateRatePrice(orderItem.getPayPrice(), level.getDiscountPercent().doubleValue());
  147 + Integer newPrice = calculateRatePrice(orderItem.getPayPrice(), level.getDiscountPercent().doubleValue());
148 148 return orderItem.getPayPrice() - newPrice;
149 149 }
150 150  
... ...