Commit 15798cea108390075f788ec491ecc3a1f5988d76

Authored by YunaiV
1 parent 67d36547

mall:价格计算的 API 简单实现

Showing 39 changed files with 1017 additions and 375 deletions
yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateRespDTO.java
... ... @@ -20,14 +20,9 @@ public class PriceCalculateRespDTO {
20 20 private Order order;
21 21  
22 22 /**
23   - * 商品 SKU 数组
24   - */
25   - private List<Item> items;
26   -
27   - /**
28 23 * 营销活动数组
29 24 *
30   - * 只对应 {@link #items} 商品匹配的活动
  25 + * 只对应 {@link Order#items} 商品匹配的活动
31 26 */
32 27 private List<Promotion> promotions;
33 28  
... ... @@ -40,13 +35,13 @@ public class PriceCalculateRespDTO {
40 35 /**
41 36 * 商品原价(总),单位:分
42 37 *
43   - * 基于 {@link Item#getTotalOriginalPrice()} 求和
  38 + * 基于 {@link OrderItem#getTotalOriginalPrice()} 求和
44 39 */
45 40 private Integer skuOriginalPrice;
46 41 /**
47 42 * 商品优惠(总),单位:分
48 43 *
49   - * 基于 {@link Item#getTotalPromotionPrice()} 求和
  44 + * 基于 {@link OrderItem#getTotalPromotionPrice()} 求和
50 45 */
51 46 private Integer skuPromotionPrice;
52 47 /**
... ... @@ -69,6 +64,10 @@ public class PriceCalculateRespDTO {
69 64 */
70 65 // * - {@link #couponPrice} // TODO 芋艿:靠营销表记录
71 66 private Integer payPrice;
  67 + /**
  68 + * 商品 SKU 数组
  69 + */
  70 + private List<OrderItem> items;
72 71  
73 72 // ========== 营销基本信息 ==========
74 73 /**
... ... @@ -85,10 +84,15 @@ public class PriceCalculateRespDTO {
85 84 }
86 85  
87 86 /**
88   - * 商品 SKU
  87 + * 订单商品 SKU
89 88 */
90 89 @Data
91   - public static class Item extends PriceCalculateReqDTO.Item {
  90 + public static class OrderItem extends PriceCalculateReqDTO.Item {
  91 +
  92 + /**
  93 + * 购买数量
  94 + */
  95 + private Integer count;
92 96  
93 97 /**
94 98 * 商品原价(单),单位:分
... ... @@ -141,6 +145,10 @@ public class PriceCalculateRespDTO {
141 145 */
142 146 private Long id;
143 147 /**
  148 + * 营销名字
  149 + */
  150 + private String name;
  151 + /**
144 152 * 营销类型
145 153 *
146 154 * 枚举 {@link PromotionTypeEnum}
... ... @@ -153,10 +161,6 @@ public class PriceCalculateRespDTO {
153 161 */
154 162 private Integer level;
155 163 /**
156   - * 匹配的商品 SKU 数组
157   - */
158   - private List<Item> items;
159   - /**
160 164 * 计算时的原价(总),单位:分
161 165 */
162 166 private Integer totalOriginalPrice;
... ... @@ -165,6 +169,13 @@ public class PriceCalculateRespDTO {
165 169 */
166 170 private Integer totalPromotionPrice;
167 171 /**
  172 + * 匹配的商品 SKU 数组
  173 + */
  174 + private List<PromotionItem> items;
  175 +
  176 + // ========== 匹配情况 ==========
  177 +
  178 + /**
168 179 * 是否满足优惠条件
169 180 */
170 181 private Boolean meet;
... ... @@ -176,26 +187,26 @@ public class PriceCalculateRespDTO {
176 187 */
177 188 private String meetTip;
178 189  
179   - /**
180   - * 匹配的商品 SKU
181   - */
182   - @Data
183   - public static class Item {
  190 + }
184 191  
185   - /**
186   - * 商品 SKU 编号
187   - */
188   - private Long skuId;
189   - /**
190   - * 计算时的原价(总),单位:分
191   - */
192   - private Integer totalOriginalPrice;
193   - /**
194   - * 计算时的优惠(总),单位:分
195   - */
196   - private Integer totalPromotionPrice;
  192 + /**
  193 + * 营销匹配的商品 SKU
  194 + */
  195 + @Data
  196 + public static class PromotionItem {
197 197  
198   - }
  198 + /**
  199 + * 商品 SKU 编号
  200 + */
  201 + private Long skuId;
  202 + /**
  203 + * 计算时的原价(总),单位:分
  204 + */
  205 + private Integer totalOriginalPrice;
  206 + /**
  207 + * 计算时的优惠(总),单位:分
  208 + */
  209 + private Integer totalPromotionPrice;
199 210  
200 211 }
201 212  
... ...
yudao-module-mall/yudao-module-market-biz/pom.xml
... ... @@ -24,6 +24,11 @@
24 24 <artifactId>yudao-module-market-api</artifactId>
25 25 <version>${revision}</version>
26 26 </dependency>
  27 + <dependency>
  28 + <groupId>cn.iocoder.boot</groupId>
  29 + <artifactId>yudao-module-product-api</artifactId>
  30 + <version>${revision}</version>
  31 + </dependency>
27 32  
28 33 <!-- 业务组件 -->
29 34 <dependency>
... ...
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/api/discount/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.market.api.discount;
... ...
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceApiImpl.java renamed to yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/api/price/PriceApiImpl.java
1   -package cn.iocoder.yudao.module.market.service.price;
  1 +package cn.iocoder.yudao.module.market.api.price;
2 2  
3   -import cn.iocoder.yudao.module.market.api.price.PriceApi;
4 3 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
5 4 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
  5 +import cn.iocoder.yudao.module.market.service.price.PriceService;
6 6 import org.springframework.stereotype.Service;
7 7  
  8 +import javax.annotation.Resource;
  9 +
8 10 /**
9 11 * 价格 API 实现类
10 12 *
11   - * TODO 完善注释
12   - *
13   - * @author TODO
  13 + * @author 芋道源码
14 14 */
15 15 @Service
16 16 public class PriceApiImpl implements PriceApi {
17 17  
  18 + @Resource
  19 + private PriceService priceService;
  20 +
18 21 @Override
19 22 public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) {
20   - // TODO fixme:实现逻辑
21   - return new PriceCalculateRespDTO();
  23 + return priceService.calculatePrice(calculateReqDTO);
22 24 }
23 25  
24 26 }
... ...
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/price/PriceConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.market.convert.price;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  4 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
  5 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
  6 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  7 +import org.mapstruct.Mapper;
  8 +import org.mapstruct.factory.Mappers;
  9 +
  10 +import java.util.ArrayList;
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +@Mapper
  15 +public interface PriceConvert {
  16 +
  17 + PriceConvert INSTANCE = Mappers.getMapper(PriceConvert.class);
  18 +
  19 + default PriceCalculateRespDTO convert(PriceCalculateReqDTO calculateReqDTO, List<ProductSkuRespDTO> skuList) {
  20 + // 创建 PriceCalculateRespDTO 对象
  21 + PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();
  22 + priceCalculate.setOrder(new PriceCalculateRespDTO.Order().setSkuOriginalPrice(0).setSkuPromotionPrice(0)
  23 + .setOrderPromotionPrice(0).setDeliveryPrice(0).setPayPrice(0).setItems(new ArrayList<>())
  24 + .setCouponId(calculateReqDTO.getCouponId()));
  25 + priceCalculate.setPromotions(new ArrayList<>());
  26 + // 创建它的 OrderItem 属性
  27 + Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(),
  28 + PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount);
  29 + skuList.forEach(sku -> {
  30 + Integer count = skuIdCountMap.get(sku.getId());
  31 + PriceCalculateRespDTO.OrderItem orderItem = new PriceCalculateRespDTO.OrderItem().setCount(count)
  32 + .setOriginalPrice(sku.getPrice()).setTotalOriginalPrice(sku.getPrice() * count).setTotalPromotionPrice(0);
  33 + orderItem.setTotalPresentPrice(orderItem.getTotalPresentPrice()).setPresentPrice(orderItem.getOriginalPrice())
  34 + .setTotalPayPrice(orderItem.getTotalPayPrice());
  35 + priceCalculate.getOrder().getItems().add(orderItem);
  36 + });
  37 + return priceCalculate;
  38 + }
  39 +
  40 +}
... ...
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.market.service.price;
  2 +
  3 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
  4 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
  5 +
  6 +/**
  7 + * 价格计算 Service 接口
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public interface PriceService {
  12 +
  13 + /**
  14 + * 计算商品的价格
  15 + *
  16 + * @param calculateReqDTO 价格请求
  17 + * @return 价格相应
  18 + */
  19 + PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO);
  20 +
  21 +}
... ...
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.market.service.price;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  4 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
  5 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
  6 +import cn.iocoder.yudao.module.market.convert.price.PriceConvert;
  7 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  8 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  9 +import org.springframework.stereotype.Service;
  10 +import org.springframework.validation.annotation.Validated;
  11 +
  12 +import javax.annotation.Resource;
  13 +import java.util.List;
  14 +import java.util.Map;
  15 +
  16 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  17 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
  18 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
  19 +
  20 +/**
  21 + * 价格计算 Service 实现类
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@Service
  26 +@Validated
  27 +public class PriceServiceImpl implements PriceService {
  28 +
  29 + @Resource
  30 + private ProductSkuApi productSkuApi;
  31 +
  32 + @Override
  33 + public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) {
  34 + // 获得商品 SKU 数组
  35 + List<ProductSkuRespDTO> skuList = checkProductSkus(calculateReqDTO);
  36 + // 初始化 PriceCalculateRespDTO 对象
  37 + PriceCalculateRespDTO priceCalculate = PriceConvert.INSTANCE.convert(calculateReqDTO, skuList);
  38 +
  39 + // 计算【限时折扣】促销 TODO 待实现
  40 + // 计算【满减送】促销 TODO 待实现
  41 + // 计算【优惠劵】促销 TODO 待实现
  42 + return priceCalculate;
  43 + }
  44 +
  45 + private List<ProductSkuRespDTO> checkProductSkus(PriceCalculateReqDTO calculateReqDTO) {
  46 + // 获得商品 SKU 数组
  47 + Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(),
  48 + PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount);
  49 + List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(skuIdCountMap.keySet());
  50 +
  51 + // 校验商品 SKU
  52 + skus.forEach(sku -> {
  53 + Integer count = skuIdCountMap.get(sku.getId());
  54 + if (count == null) {
  55 + throw exception(SKU_NOT_EXISTS);
  56 + }
  57 + if (count > sku.getStock()) {
  58 + throw exception(SKU_STOCK_NOT_ENOUGH);
  59 + }
  60 + });
  61 + return skus;
  62 + }
  63 +
  64 +}
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java
1 1 package cn.iocoder.yudao.module.product.api.sku;
2 2  
3 3 import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
4   -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
  4 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
5 5  
6 6 import java.util.Collection;
7 7 import java.util.List;
8 8  
9 9 /**
  10 + * 商品 SKU API 接口
  11 + *
10 12 * @author LeeYan9
11 13 * @since 2022-08-26
12 14 */
13 15 public interface ProductSkuApi {
14 16  
  17 + /**
  18 + * 查询 SKU 信息
  19 + *
  20 + * @param id SKU 编号
  21 + * @return SKU 信息
  22 + */
  23 + ProductSkuRespDTO getSku(Long id);
15 24  
16 25 /**
17   - * 根据skuId列表 查询sku信息
  26 + * 批量查询 SKU 数组
18 27 *
19   - * @param skuIds sku ID列表
20   - * @return sku信息列表
  28 + * @param ids SKU 编号列表
  29 + * @return SKU 数组
21 30 */
22   - List<SkuInfoRespDTO> getSkusByIds(Collection<Long> skuIds);
  31 + List<ProductSkuRespDTO> getSkuList(Collection<Long> ids);
23 32  
24 33 /**
25 34 * 批量扣减sku库存
... ... @@ -27,4 +36,5 @@ public interface ProductSkuApi {
27 36 * @param batchReqDTO sku库存信息列表
28 37 */
29 38 void decrementStockBatch(SkuDecrementStockBatchReqDTO batchReqDTO);
  39 +
30 40 }
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/SkuInfoRespDTO.java renamed to yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
... ... @@ -6,11 +6,13 @@ import lombok.Data;
6 6 import java.util.List;
7 7  
8 8 /**
  9 + * 商品 SKU 信息 Response DTO
  10 + *
9 11 * @author LeeYan9
10 12 * @since 2022-08-26
11 13 */
12 14 @Data
13   -public class SkuInfoRespDTO {
  15 +public class ProductSkuRespDTO {
14 16  
15 17 /**
16 18 * 商品 SKU 编号,自增
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApi.java
1 1 package cn.iocoder.yudao.module.product.api.spu;
2 2  
3   -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
4 3 import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO;
5 4  
6 5 import java.util.Collection;
7 6 import java.util.List;
8 7  
9 8 /**
  9 + * 商品 SPU API 接口
  10 + *
10 11 * @author LeeYan9
11 12 * @since 2022-08-26
12 13 */
13 14 public interface ProductSpuApi {
14 15  
15   -
16 16 /**
17   - * 根据spuId列表 查询spu信息
  17 + * 批量查询 SPU 数组
18 18 *
19   - * @param spuIds spu ID列表
20   - * @return spu信息列表
  19 + * @param ids SPU 编号列表
  20 + * @return SPU 数组
21 21 */
22   - List<SpuInfoRespDTO> getSpusByIds(Collection<Long> spuIds);
  22 + List<SpuInfoRespDTO> getSpuList(Collection<Long> ids);
  23 +
23 24 }
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/SpuInfoRespDTO.java
1 1 package cn.iocoder.yudao.module.product.api.spu.dto;
2 2  
3   -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
  3 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
4 4 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuSpecTypeEnum;
5 5 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
6 6 import lombok.Data;
... ... @@ -80,25 +80,25 @@ public class SpuInfoRespDTO {
80 80 /**
81 81 * 最小价格,单位使用:分
82 82 * <p>
83   - * 基于其对应的 {@link SkuInfoRespDTO#getPrice()} 最小值
  83 + * 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最小值
84 84 */
85 85 private Integer minPrice;
86 86 /**
87 87 * 最大价格,单位使用:分
88 88 * <p>
89   - * 基于其对应的 {@link SkuInfoRespDTO#getPrice()} 最大值
  89 + * 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最大值
90 90 */
91 91 private Integer maxPrice;
92 92 /**
93 93 * 市场价,单位使用:分
94 94 * <p>
95   - * 基于其对应的 {@link SkuInfoRespDTO#getMarketPrice()} 最大值
  95 + * 基于其对应的 {@link ProductSkuRespDTO#getMarketPrice()} 最大值
96 96 */
97 97 private Integer marketPrice;
98 98 /**
99 99 * 总库存
100 100 * <p>
101   - * 基于其对应的 {@link SkuInfoRespDTO#getStock()} 求和
  101 + * 基于其对应的 {@link ProductSkuRespDTO#getStock()} 求和
102 102 */
103 103 private Integer totalStock;
104 104 /**
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java
... ... @@ -36,5 +36,6 @@ public interface ErrorCodeConstants {
36 36 ErrorCode SKU_PROPERTIES_DUPLICATED = new ErrorCode(1008006001, "商品 SKU 的属性组合存在重复");
37 37 ErrorCode SPU_ATTR_NUMBERS_MUST_BE_EQUALS = new ErrorCode(1008006002, "一个 SPU 下的每个 SKU,其规格数必须一致");
38 38 ErrorCode SPU_SKU_NOT_DUPLICATE = new ErrorCode(1008006003, "一个 SPU 下的每个 SKU,必须不重复");
  39 + ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足");
39 40  
40 41 }
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java
1 1 package cn.iocoder.yudao.module.product.api.sku;
2 2  
3 3 import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
4   -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
  4 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  5 +import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
  6 +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
  7 +import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
5 8 import org.springframework.stereotype.Service;
6 9  
  10 +import javax.annotation.Resource;
7 11 import java.util.Collection;
8 12 import java.util.List;
9 13  
10 14 /**
11   - * todo 注释
  15 + * 商品 SKU API 实现类
  16 + *
  17 + * @author 芋道源码
  18 + * @since 2022-08-26
12 19 */
13 20 @Service
14 21 public class ProductSkuApiImpl implements ProductSkuApi {
15 22  
  23 + @Resource
  24 + private ProductSkuService productSkuService;
  25 +
  26 + @Override
  27 + public ProductSkuRespDTO getSku(Long id) {
  28 + ProductSkuDO skuDO = productSkuService.getSku(id);
  29 + return ProductSkuConvert.INSTANCE.convert02(skuDO);
  30 + }
  31 +
16 32 @Override
17   - public List<SkuInfoRespDTO> getSkusByIds(Collection<Long> skuIds) {
18   - return null;
  33 + public List<ProductSkuRespDTO> getSkuList(Collection<Long> ids) {
  34 + List<ProductSkuDO> list = productSkuService.getSkuList(ids);
  35 + return ProductSkuConvert.INSTANCE.convertList02(list);
19 36 }
20 37  
21 38 @Override
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/spu/ProductSpuApiImpl.java
... ... @@ -7,13 +7,16 @@ import java.util.Collection;
7 7 import java.util.List;
8 8  
9 9 /**
10   - * todo 注释
  10 + * 商品 SPU API 实现类
  11 + *
  12 + * @author LeeYan9
  13 + * @since 2022-08-26
11 14 */
12 15 @Service
13 16 public class ProductSpuApiImpl implements ProductSpuApi {
14 17  
15 18 @Override
16   - public List<SpuInfoRespDTO> getSpusByIds(Collection<Long> spuIds) {
  19 + public List<SpuInfoRespDTO> getSpuList(Collection<Long> spuIds) {
17 20 return null;
18 21 }
19 22  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/sku/vo/ProductSkuBaseVO.java
... ... @@ -33,7 +33,7 @@ public class ProductSkuBaseVO {
33 33 @ApiModelProperty(value = "条形码", example = "haha")
34 34 private String barCode;
35 35  
36   - @ApiModelProperty(value = "图片地址")
  36 + @ApiModelProperty(value = "图片地址", required = true, example = "https://www.iocoder.cn/xx.png")
37 37 @NotNull(message = "图片地址不能为空")
38 38 private String picUrl;
39 39  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java
1 1 package cn.iocoder.yudao.module.product.convert.sku;
2 2  
  3 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
3 4 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
4 5 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO;
5 6 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
6 7 import org.mapstruct.Mapper;
7   -import org.mapstruct.Mapping;
8 8 import org.mapstruct.factory.Mappers;
9 9  
10 10 import java.util.List;
... ... @@ -21,11 +21,14 @@ public interface ProductSkuConvert {
21 21  
22 22 ProductSkuDO convert(ProductSkuCreateOrUpdateReqVO bean);
23 23  
24   - @Mapping(source = "properties", target = "properties")
25 24 ProductSkuRespVO convert(ProductSkuDO bean);
26 25  
27 26 List<ProductSkuRespVO> convertList(List<ProductSkuDO> list);
28 27  
29 28 List<ProductSkuDO> convertSkuDOList(List<ProductSkuCreateOrUpdateReqVO> list);
30 29  
  30 + ProductSkuRespDTO convert02(ProductSkuDO bean);
  31 +
  32 + List<ProductSkuRespDTO> convertList02(List<ProductSkuDO> list);
  33 +
31 34 }
... ...
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/enums/ErrorCodeConstants.java renamed to yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
1   -package cn.iocoder.yudao.module.trade.enums.enums;
  1 +package cn.iocoder.yudao.module.trade.enums;
2 2  
3 3 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 4  
5 5 /**
6   - * Trade-Order 错误码 Core 枚举类
7   - * <p>
8   - * Trade-Order 系统,使用 1-011-000-000 段
  6 + * 交易 错误码枚举类
  7 + * 交易系统,使用 1-011-000-000 段
9 8 *
10 9 * @author LeeYan9
11 10 * @since 2022-08-26
12 11 */
13 12 public interface ErrorCodeConstants {
14 13  
15   - /**
16   - * ========== Order 模块 1-011-000-000 ==========
17   - */
  14 + // ========== Order 模块 1-011-000-000 ==========
18 15 ErrorCode ORDER_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品不存在");
19 16 ErrorCode ORDER_SPU_NOT_SALE = new ErrorCode(1011000002, "商品不可售卖");
20 17 ErrorCode ORDER_SKU_NOT_SALE = new ErrorCode(1011000003, "商品Sku不可售卖");
21 18 ErrorCode ORDER_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品库存不足");
22 19  
  20 + // ========== Cart 模块 1-011-001-000 ==========
  21 + ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1001001000, "购物车项不存在");
  22 +
23 23 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/package-info.java 0 → 100644
  1 +/**
  2 + * 基础包,放一些通用的 VO 类
  3 + */
  4 +package cn.iocoder.yudao.module.trade.controller.app.base;
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/property/AppProductPropertyValueDetailRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.base.property;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +@ApiModel("用户 App - 规格 + 规格值 Response VO")
  8 +@Data
  9 +public class AppProductPropertyValueDetailRespVO {
  10 +
  11 + @ApiModelProperty(value = "属性的编号", required = true, example = "1")
  12 + private Long propertyId;
  13 +
  14 + @ApiModelProperty(value = "属性的名称", required = true, example = "颜色")
  15 + private String propertyName;
  16 +
  17 + @ApiModelProperty(value = "属性值的编号", required = true, example = "1024")
  18 + private Long valueId;
  19 +
  20 + @ApiModelProperty(value = "属性值的名称", required = true, example = "红色")
  21 + private String valueName;
  22 +
  23 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/sku/AppProductSkuBaseRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.base.sku;
  2 +
  3 +import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * 商品 SKU 基础 Response VO
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@Data
  15 +public class AppProductSkuBaseRespVO {
  16 +
  17 + @ApiModelProperty(value = "主键", required = true, example = "1024")
  18 + private Long id;
  19 +
  20 + @ApiModelProperty(value = "商品 SKU 名字", required = true, example = "芋道")
  21 + private String name;
  22 +
  23 + @ApiModelProperty(value = "图片地址", example = "https://www.iocoder.cn/xx.png")
  24 + private String picUrl;
  25 +
  26 + @ApiModelProperty(value = "库存", required = true, example = "1")
  27 + private Integer stock;
  28 +
  29 + /**
  30 + * 规格数组
  31 + */
  32 + private List<AppProductPropertyValueDetailRespVO> properties;
  33 +
  34 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.base.spu;
  2 +
  3 +import io.swagger.annotations.ApiModelProperty;
  4 +import lombok.Data;
  5 +
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * 商品 SPU 基础 Response VO
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Data
  14 +public class AppProductSpuBaseRespVO {
  15 +
  16 + @ApiModelProperty(value = "主键", required = true, example = "1024")
  17 + private Long id;
  18 +
  19 + @ApiModelProperty(value = "商品 SPU 名字", required = true, example = "芋道")
  20 + private String name;
  21 +
  22 + @ApiModelProperty(value = "商品主图地址", example = "https://www.iocoder.cn/xx.png")
  23 + private List<String> picUrls;
  24 +
  25 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/CartController.java deleted
1   -package cn.iocoder.yudao.module.trade.controller.app.cart;
2   -
3   -import io.swagger.annotations.Api;
4   -import org.springframework.validation.annotation.Validated;
5   -import org.springframework.web.bind.annotation.RequestMapping;
6   -import org.springframework.web.bind.annotation.RestController;
7   -
8   -@Api(tags = "购物车 API")
9   -@RestController
10   -@RequestMapping("/cart")
11   -@Validated
12   -public class CartController {
13   -
14   -// @Autowired
15   -// private CartManager cartManager;
16   -//
17   -// @PostMapping("add")
18   -// @ApiOperation("添加商品到购物车")
19   -// @ApiImplicitParams({
20   -// @ApiImplicitParam(name = "skuId", value = "商品 SKU 编号", required = true, example = "1"),
21   -// @ApiImplicitParam(name = "quantity", value = "增加数量", required = true, example = "1024")
22   -// })
23   -// @RequiresAuthenticate
24   -// public CommonResult<Boolean> addCartItem(@RequestParam("skuId") Integer skuId,
25   -// @RequestParam("quantity") Integer quantity) {
26   -// cartManager.addCartItem(UserSecurityContextHolder.getUserId(), skuId, quantity);
27   -// return success(true);
28   -// }
29   -//
30   -// @GetMapping("sum-quantity")
31   -// @ApiOperation("查询用户在购物车中的商品数量")
32   -// @RequiresAuthenticate
33   -// public CommonResult<Integer> sumCartItemQuantity() {
34   -// return success(cartManager.sumCartItemQuantity(UserSecurityContextHolder.getUserId()));
35   -// }
36   -//
37   -// @GetMapping("/get-detail")
38   -// @ApiOperation("查询用户的购物车的商品列表")
39   -// @RequiresAuthenticate
40   -// public CommonResult<CartDetailVO> getCartDetail() {
41   -// return success(cartManager.getCartDetail(UserSecurityContextHolder.getUserId()));
42   -// }
43   -//
44   -// @PostMapping("update-quantity")
45   -// @ApiOperation("更新购物车商品数量")
46   -// @ApiImplicitParams({
47   -// @ApiImplicitParam(name = "skuId", value = "商品 SKU 编号", required = true, example = "1"),
48   -// @ApiImplicitParam(name = "quantity", value = "增加数量", required = true, example = "1024")
49   -// })
50   -// @RequiresAuthenticate
51   -// public CommonResult<Boolean> updateCartItemQuantity(@RequestParam("skuId") Integer skuId,
52   -// @RequestParam("quantity") Integer quantity) {
53   -// cartManager.updateCartItemQuantity(UserSecurityContextHolder.getUserId(), skuId, quantity);
54   -// return success(true);
55   -// }
56   -//
57   -// @PostMapping("update-selected")
58   -// @ApiOperation("更新购物车商品是否选中")
59   -// @ApiImplicitParams({
60   -// @ApiImplicitParam(name = "skuIds", value = "商品 SKU 编号数组", required = true, example = "1,3"),
61   -// @ApiImplicitParam(name = "selected", value = "是否选中", required = true, example = "true")
62   -// })
63   -// @RequiresAuthenticate
64   -// public CommonResult<Boolean> updateCartItemSelected(@RequestParam("skuIds") Set<Integer> skuIds,
65   -// @RequestParam("selected") Boolean selected) {
66   -// cartManager.updateCartItemSelected(UserSecurityContextHolder.getUserId(), skuIds, selected);
67   -// // 获得目前购物车明细
68   -// return success(true);
69   -// }
70   -
71   -}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.http 0 → 100644
  1 +### 请求 /trade/cart/add-count 接口 => 成功
  2 +POST {{appApi}}/trade/cart/add-count
  3 +tenant-id: {{appTenentId}}
  4 +Authorization: Bearer {{appToken}}
  5 +Content-Type: application/json
  6 +
  7 +{
  8 + "skuId": 1,
  9 + "count": 1
  10 +}
  11 +
  12 +### 请求 /trade/cart/update-count 接口 => 成功
  13 +PUT {{appApi}}/trade/cart/update-count
  14 +tenant-id: {{appTenentId}}
  15 +Authorization: Bearer {{appToken}}
  16 +Content-Type: application/json
  17 +
  18 +{
  19 + "skuId": 1,
  20 + "count": 5
  21 +}
  22 +
  23 +### 请求 /trade/cart/update-selected 接口 => 成功
  24 +PUT {{appApi}}/trade/cart/update-selected
  25 +tenant-id: {{appTenentId}}
  26 +Authorization: Bearer {{appToken}}
  27 +Content-Type: application/json
  28 +
  29 +{
  30 + "skuIds": [1],
  31 + "selected": false
  32 +}
  33 +
  34 +### 请求 /trade/cart/delete 接口 => 成功
  35 +DELETE {{appApi}}/trade/cart/delete?skuIds=1
  36 +tenant-id: {{appTenentId}}
  37 +Authorization: Bearer {{appToken}}
  38 +
  39 +### 请求 /trade/cart/get-count 接口 => 成功
  40 +GET {{appApi}}/trade/cart/get-count
  41 +tenant-id: {{appTenentId}}
  42 +Authorization: Bearer {{appToken}}
  43 +
  44 +### 请求 /trade/cart/get-detail 接口 => 成功
  45 +GET {{appApi}}/trade/cart/get-detail
  46 +tenant-id: {{appTenentId}}
  47 +Authorization: Bearer {{appToken}}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.cart;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
  5 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
  6 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
  7 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
  8 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
  9 +import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
  10 +import io.swagger.annotations.Api;
  11 +import io.swagger.annotations.ApiImplicitParam;
  12 +import io.swagger.annotations.ApiOperation;
  13 +import lombok.RequiredArgsConstructor;
  14 +import lombok.extern.slf4j.Slf4j;
  15 +import org.springframework.validation.annotation.Validated;
  16 +import org.springframework.web.bind.annotation.*;
  17 +
  18 +import javax.annotation.Resource;
  19 +import javax.validation.Valid;
  20 +import java.util.List;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  24 +
  25 +@Api(tags = "用户 App - 购物车")
  26 +@RestController
  27 +@RequestMapping("/trade/cart")
  28 +@RequiredArgsConstructor
  29 +@Validated
  30 +@Slf4j
  31 +public class TradeCartController {
  32 +
  33 + @Resource
  34 + private TradeCartService cartService;
  35 +
  36 + @PostMapping("/add-count")
  37 + @ApiOperation("添加商品到购物车")
  38 + @PreAuthenticated
  39 + public CommonResult<Boolean> addCartItemCount(@Valid @RequestBody AppTradeCartItemAddCountReqVO addCountReqVO) {
  40 + cartService.addCartItemCount(getLoginUserId(), addCountReqVO);
  41 + return success(true);
  42 + }
  43 +
  44 + @PutMapping("update-count")
  45 + @ApiOperation("更新购物车商品数量")
  46 + @PreAuthenticated
  47 + public CommonResult<Boolean> updateCartItemQuantity(@Valid @RequestBody AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
  48 + cartService.updateCartItemCount(getLoginUserId(), updateCountReqVO);
  49 + return success(true);
  50 + }
  51 +
  52 + @PutMapping("update-selected")
  53 + @ApiOperation("更新购物车商品是否选中")
  54 + @PreAuthenticated
  55 + public CommonResult<Boolean> updateCartItemSelected(@Valid @RequestBody AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
  56 + cartService.updateCartItemSelected(getLoginUserId(), updateSelectedReqVO);
  57 + // 获得目前购物车明细
  58 + return success(true);
  59 + }
  60 +
  61 + @DeleteMapping("/delete")
  62 + @ApiOperation("删除购物车商品")
  63 + @ApiImplicitParam(name = "skuId", value = "商品 SKU 编号的数组", required = true, example = "1024,2048", dataTypeClass = List.class)
  64 + @PreAuthenticated
  65 + public CommonResult<Boolean> deleteCartItem(@RequestParam("skuIds") List<Long> skuIds) {
  66 + cartService.deleteCartItems(getLoginUserId(), skuIds);
  67 + return success(true);
  68 + }
  69 +
  70 + @GetMapping("get-count")
  71 + @ApiOperation("查询用户在购物车中的商品数量")
  72 + @PreAuthenticated
  73 + public CommonResult<Integer> getCartCount() {
  74 + return success(cartService.getCartCount(getLoginUserId()));
  75 + }
  76 +
  77 + @GetMapping("/get-detail")
  78 + @ApiOperation("查询用户的购物车的详情")
  79 + @PreAuthenticated
  80 + public CommonResult<AppTradeCartDetailRespVO> getCartDetail() {
  81 + return success(cartService.getCartDetail(getLoginUserId()));
  82 + }
  83 +
  84 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartDetailRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
  2 +
  3 +import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +
  8 +import java.util.List;
  9 +
  10 +@ApiModel(value = "用户 App - 用户的购物车明细 Response VO")
  11 +@Data
  12 +public class AppTradeCartDetailRespVO {
  13 +
  14 + /**
  15 + * 商品分组数组
  16 + */
  17 + private List<ItemGroup> itemGroups;
  18 +
  19 + /**
  20 + * 费用
  21 + */
  22 + private Order order;
  23 +
  24 + @ApiModel(value = "商品分组", description = "多个商品,参加同一个活动,从而形成分组")
  25 + @Data
  26 + public static class ItemGroup {
  27 +
  28 + /**
  29 + * 商品数组
  30 + */
  31 + private List<Sku> items;
  32 + /**
  33 + * 营销活动,订单级别
  34 + */
  35 + private Promotion promotion;
  36 +
  37 + }
  38 +
  39 + @ApiModel(value = "商品 SKU")
  40 + @Data
  41 + public static class Sku extends AppProductSkuBaseRespVO {
  42 +
  43 + /**
  44 + * SPU 信息
  45 + */
  46 + private AppProductSkuBaseRespVO spu;
  47 +
  48 + // ========== 购物车相关的字段 ==========
  49 +
  50 + @ApiModelProperty(value = "商品数量", required = true, example = "1")
  51 + private Integer count;
  52 + @ApiModelProperty(value = "是否选中", required = true, example = "true")
  53 + private Boolean selected;
  54 +
  55 + // ========== 价格相关的字段,对应 PriceCalculateRespDTO.OrderItem 的属性 ==========
  56 +
  57 + // TODO 芋艿:后续可以去除一些无用的字段
  58 +
  59 + @ApiModelProperty(value = "商品原价(单)", required = true, example = "100")
  60 + private Integer originalPrice;
  61 + @ApiModelProperty(value = "商品原价(总)", required = true, example = "200")
  62 + private Integer totalOriginalPrice;
  63 + @ApiModelProperty(value = "商品级优惠(总)", required = true, example = "300")
  64 + private Integer totalPromotionPrice;
  65 + @ApiModelProperty(value = "最终购买金额(总)", required = true, example = "400")
  66 + private Integer totalPresentPrice;
  67 + @ApiModelProperty(value = "最终购买金额(单)", required = true, example = "500")
  68 + private Integer presentPrice;
  69 + @ApiModelProperty(value = "应付金额(总)", required = true, example = "600")
  70 + private Integer totalPayPrice;
  71 +
  72 + // ========== 营销相关的字段 ==========
  73 + /**
  74 + * 营销活动,商品级别
  75 + */
  76 + private Promotion promotion;
  77 +
  78 + }
  79 +
  80 + @ApiModel(value = "订单", description = "对应 PriceCalculateRespDTO.Order 类,用于费用(合计)")
  81 + @Data
  82 + public static class Order {
  83 +
  84 + // TODO 芋艿:后续可以去除一些无用的字段
  85 +
  86 + @ApiModelProperty(value = "商品原价(总)", required = true, example = "100")
  87 + private Integer skuOriginalPrice;
  88 + @ApiModelProperty(value = "商品优惠(总)", required = true, example = "200")
  89 + private Integer skuPromotionPrice;
  90 + @ApiModelProperty(value = "订单优惠(总)", required = true, example = "300")
  91 + private Integer orderPromotionPrice;
  92 + @ApiModelProperty(value = "运费金额", required = true, example = "400")
  93 + private Integer deliveryPrice;
  94 + @ApiModelProperty(value = "应付金额(总)", required = true, example = "500")
  95 + private Integer payPrice;
  96 +
  97 + }
  98 +
  99 + @ApiModel(value = "营销活动", description = "对应 PriceCalculateRespDTO.Promotion 类的属性")
  100 + @Data
  101 + public static class Promotion {
  102 +
  103 + @ApiModelProperty(value = "营销编号", required = true, example = "1024", notes = "营销活动的编号、优惠劵的编号")
  104 + private Long id;
  105 + @ApiModelProperty(value = "营销名字", required = true, example = "xx 活动")
  106 + private String name;
  107 + @ApiModelProperty(value = "营销类型", required = true, example = "1", notes = "参见 PromotionTypeEnum 枚举类")
  108 + private Integer type;
  109 +
  110 + // ========== 匹配情况 ==========
  111 + @ApiModelProperty(value = "是否满足优惠条件", required = true, example = "true")
  112 + private Boolean meet;
  113 + @ApiModelProperty(value = "满足条件的提示", required = true, example = "圣诞价:省 150.00 元")
  114 + private String meetTip;
  115 +
  116 + }
  117 +
  118 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemAddCountReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.Min;
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +@ApiModel(value = "用户 App - 购物车添加购物项 Request VO")
  11 +@Data
  12 +public class AppTradeCartItemAddCountReqVO {
  13 +
  14 + @ApiModelProperty(value = "商品 SKU 编号", required = true,example = "1024")
  15 + @NotNull(message = "商品 SKU 编号不能为空")
  16 + private Long skuId;
  17 +
  18 + @ApiModelProperty(value = "商品数量", required = true, example = "1", notes = "注意,这是新增数量")
  19 + @NotNull(message = "数量不能为空")
  20 + @Min(message = "数量必须大于 0", value = 1L)
  21 + private Integer count;
  22 +
  23 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateCountReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.Min;
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +@ApiModel(value = "用户 App - 购物车更新数量 Request VO")
  11 +@Data
  12 +public class AppTradeCartItemUpdateCountReqVO {
  13 +
  14 + @ApiModelProperty(value = "商品 SKU 编号", required = true, example = "1024")
  15 + @NotNull(message = "商品 SKU 编号不能为空")
  16 + private Long skuId;
  17 +
  18 + @ApiModelProperty(value = "商品数量", required = true, example = "1")
  19 + @NotNull(message = "数量不能为空")
  20 + @Min(message = "数量必须大于 0", value = 1L)
  21 + private Integer count;
  22 +
  23 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateSelectedReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +import java.util.Collection;
  9 +
  10 +@ApiModel(value = "用户 App - 购物车更新是否选中 Request VO")
  11 +@Data
  12 +public class AppTradeCartItemUpdateSelectedReqVO {
  13 +
  14 + @ApiModelProperty(value = "商品 SKU 编号列表", required = true, example = "1024,2048")
  15 + @NotNull(message = "商品 SKU 编号列表不能为空")
  16 + private Collection<Long> skuIds;
  17 +
  18 + @ApiModelProperty(value = "是否选中", required = true, example = "true")
  19 + @NotNull(message = "是否选中不能为空")
  20 + private Boolean selected;
  21 +
  22 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/CartDetailVO.java deleted
1   -package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
2   -
3   -import io.swagger.annotations.ApiModel;
4   -import lombok.Data;
5   -import lombok.experimental.Accessors;
6   -
7   -import java.util.List;
8   -
9   -@ApiModel(value = "用户的购物车明细 Response VO") // TODO 芋艿:swagger 文档完善
10   -@Data
11   -@Accessors(chain = true)
12   -public class CartDetailVO {
13   -
14   - /**
15   - * 商品分组数组
16   - */
17   - private List<ItemGroup> itemGroups;
18   - /**
19   - * 费用
20   - */
21   - private Fee fee;
22   -
23   - /**
24   - * 商品分组
25   - *
26   - * 多个商品,参加同一个活动,从而形成分组。
27   - */
28   - @Data
29   - @Accessors(chain = true)
30   - public static class ItemGroup {
31   -
32   -// /**
33   -// * 优惠活动
34   -// */
35   -// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
36   - /**
37   - * 促销减少的金额
38   - *
39   - * 1. 若未参与促销活动,或不满足促销条件,返回 null
40   - * 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。
41   - */
42   - private Integer activityDiscountTotal;
43   - /**
44   - * 商品数组
45   - */
46   - private List<Sku> items;
47   -
48   - }
49   -
50   - @Data
51   - @Accessors(chain = true)
52   - public static class Sku {
53   -
54   - // SKU 自带信息
55   - /**
56   - * sku 编号
57   - */
58   - private Integer id;
59   - /**
60   - * SPU 信息
61   - */
62   - private Spu spu;
63   - /**
64   - * 图片地址
65   - */
66   - private String picURL;
67   -// /**
68   -// * 规格值数组
69   -// */
70   -// private List<ProductAttrKeyValueRespVO> attrs; // TODO 后面改下
71   - /**
72   - * 价格,单位:分
73   - */
74   - private Integer price;
75   - /**
76   - * 库存数量
77   - */
78   - private Integer quantity;
79   -
80   - // 非 SKU 自带信息
81   -
82   - /**
83   - * 购买数量
84   - */
85   - private Integer buyQuantity;
86   - /**
87   - * 是否选中
88   - */
89   - private Boolean selected;
90   -// /**
91   -// * 优惠活动
92   -// */
93   -// private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
94   - /**
95   - * 原始单价,单位:分。
96   - */
97   - private Integer originPrice;
98   - /**
99   - * 购买单价,单位:分
100   - */
101   - private Integer buyPrice;
102   - /**
103   - * 最终价格,单位:分。
104   - */
105   - private Integer presentPrice;
106   - /**
107   - * 购买总金额,单位:分
108   - *
109   - * 用途类似 {@link #presentTotal}
110   - */
111   - private Integer buyTotal;
112   - /**
113   - * 优惠总金额,单位:分。
114   - */
115   - private Integer discountTotal;
116   - /**
117   - * 最终总金额,单位:分。
118   - *
119   - * 注意,presentPrice * quantity 不一定等于 presentTotal 。
120   - * 因为,存在无法整除的情况。
121   - * 举个例子,presentPrice = 8.33 ,quantity = 3 的情况,presentTotal 有可能是 24.99 ,也可能是 25 。
122   - * 所以,需要存储一个该字段。
123   - */
124   - private Integer presentTotal;
125   -
126   - }
127   -
128   - @Data
129   - @Accessors(chain = true)
130   - public static class Spu {
131   -
132   - /**
133   - * SPU 编号
134   - */
135   - private Integer id;
136   -
137   - // ========== 基本信息 =========
138   - /**
139   - * SPU 名字
140   - */
141   - private String name;
142   - /**
143   - * 分类编号
144   - */
145   - private Integer cid;
146   - /**
147   - * 商品主图地址
148   - *
149   - * 数组,以逗号分隔
150   - *
151   - * 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张
152   - */
153   - private List<String> picUrls;
154   -
155   - }
156   -
157   - /**
158   - * 费用(合计)
159   - */
160   - @Data
161   - @Accessors(chain = true)
162   - public static class Fee {
163   -
164   - /**
165   - * 购买总价
166   - */
167   - private Integer buyTotal;
168   - /**
169   - * 优惠总价
170   - *
171   - * 注意,满多少元包邮,不算在优惠中。
172   - */
173   - private Integer discountTotal;
174   - /**
175   - * 邮费
176   - */
177   - private Integer postageTotal;
178   - /**
179   - * 最终价格
180   - *
181   - * 计算公式 = 总价 - 优惠总价 + 邮费
182   - */
183   - private Integer presentTotal;
184   -
185   - public Fee() {
186   - }
187   -
188   - public Fee(Integer buyTotal, Integer discountTotal, Integer postageTotal, Integer presentTotal) {
189   - this.buyTotal = buyTotal;
190   - this.discountTotal = discountTotal;
191   - this.postageTotal = postageTotal;
192   - this.presentTotal = presentTotal;
193   - }
194   -
195   - }
196   -
197   - /**
198   - * 邮费信息 TODO 芋艿,未完成
199   - */
200   - @Data
201   - @Accessors(chain = true)
202   - public static class Postage {
203   -
204   - /**
205   - * 需要满足多少钱,可以包邮。单位:分
206   - */
207   - private Integer threshold;
208   -
209   - }
210   -
211   -}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
... ... @@ -60,8 +60,8 @@ public class AppTradeOrderController {
60 60  
61 61 @GetMapping("/get")
62 62 @ApiOperation("获得交易订单")
63   - @ApiImplicitParam(name = "tradeOrderId", value = "交易订单编号", required = true)
64   - public CommonResult<TradeOrderRespVO> getTradeOrder(@RequestParam("tradeOrderId") Integer tradeOrderId) {
  63 + @ApiImplicitParam(name = "id", value = "交易订单编号", required = true, dataTypeClass = Long.class)
  64 + public CommonResult<TradeOrderRespVO> getTradeOrder(@RequestParam("id") Integer id) {
65 65 // return success(tradeOrderService.getTradeOrder(tradeOrderId));
66 66 return null;
67 67 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.convert.cart;
  2 +
  3 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
  4 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
  5 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
  6 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
  7 +import org.mapstruct.Mapper;
  8 +import org.mapstruct.Mapping;
  9 +import org.mapstruct.Mappings;
  10 +import org.mapstruct.factory.Mappers;
  11 +
  12 +import java.util.Collections;
  13 +import java.util.List;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  16 +
  17 +@Mapper
  18 +public interface TradeCartConvert {
  19 +
  20 + TradeCartConvert INSTANCE = Mappers.getMapper(TradeCartConvert.class);
  21 +
  22 + default AppTradeCartDetailRespVO buildEmptyAppTradeCartDetailRespVO() {
  23 + return new AppTradeCartDetailRespVO().setItemGroups(Collections.emptyList())
  24 + .setOrder(new AppTradeCartDetailRespVO.Order().setSkuOriginalPrice(0).setSkuPromotionPrice(0)
  25 + .setOrderPromotionPrice(0).setDeliveryPrice(0).setPayPrice(0));
  26 + }
  27 +
  28 + default PriceCalculateReqDTO convert(Long userId, List<TradeCartItemDO> cartItems) {
  29 + return new PriceCalculateReqDTO().setUserId(userId)
  30 + .setItems(convertList(cartItems, cartItem -> new PriceCalculateReqDTO.Item().setSkuId(cartItem.getSkuId())
  31 + .setCount(cartItem.getSelected() ? cartItem.getCount() : 0)));
  32 + }
  33 +
  34 + // ========== AppTradeCartDetailRespVO 相关 ==========
  35 +
  36 + AppTradeCartDetailRespVO.Promotion convert(PriceCalculateRespDTO.Promotion bean);
  37 +
  38 + @Mappings({
  39 + @Mapping(source = "cartItem.count", target = "count")
  40 + })
  41 + AppTradeCartDetailRespVO.Sku convert(PriceCalculateRespDTO.OrderItem orderItem, TradeCartItemDO cartItem);
  42 +
  43 + AppTradeCartDetailRespVO.Order convert(PriceCalculateRespDTO.Order bean);
  44 +
  45 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderItemConvert.java
... ... @@ -29,7 +29,7 @@ public interface TradeOrderItemConvert {
29 29 @Mapping(source = "tradeOrder.userId", target = "userId"),
30 30 @Mapping(source = "tradeOrder.orderId", target = "orderId")
31 31 })
32   - default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrder, List<PriceCalculateRespDTO.Item> items) {
  32 + default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrder, List<PriceCalculateRespDTO.OrderItem> items) {
33 33 // TODO @Com: Mapstruct 生成会报错
34 34 throw new UnsupportedOperationException("无法实现");
35 35 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java
... ... @@ -7,6 +7,7 @@ import org.mapstruct.factory.Mappers;
7 7  
8 8 import java.util.List;
9 9  
  10 +// TODO @LeeYan9:挪到 OrderConvert 那
10 11 /**
11 12 * @author LeeYan9
12 13 * @since 2022-08-26
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/CartItemDO.java renamed to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/TradeCartItemDO.java
... ... @@ -6,16 +6,16 @@ import lombok.Data;
6 6 import lombok.EqualsAndHashCode;
7 7 import lombok.experimental.Accessors;
8 8  
9   -import java.util.Date;
10   -
11 9 /**
12 10 * 购物车的商品信息 DO
  11 + *
  12 + * @author 芋道源码
13 13 */
14 14 @TableName("trade_cart_item")
15 15 @Data
16 16 @EqualsAndHashCode(callSuper = true)
17 17 @Accessors(chain = true)
18   -public class CartItemDO extends BaseDO {
  18 +public class TradeCartItemDO extends BaseDO {
19 19  
20 20 // ========= 基础字段 BEGIN =========
21 21  
... ... @@ -27,10 +27,6 @@ public class CartItemDO extends BaseDO {
27 27 * 是否选中
28 28 */
29 29 private Boolean selected;
30   - /**
31   - * 购物时间
32   - */
33   - private Date buyTime; // basket_date
34 30  
35 31 // ========= 基础字段 END =========
36 32  
... ... @@ -62,7 +58,7 @@ public class CartItemDO extends BaseDO {
62 58 /**
63 59 * 商品购买数量
64 60 */
65   - private Integer stock;
  61 + private Integer count;
66 62  
67 63 // ========= 商品信息 END =========
68 64  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartItemMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.dal.mysql.cart;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
  8 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  9 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +
  12 +import java.util.Collection;
  13 +import java.util.List;
  14 +import java.util.Map;
  15 +
  16 +@Mapper
  17 +public interface TradeCartItemMapper extends BaseMapperX<TradeCartItemDO> {
  18 +
  19 + default TradeCartItemDO selectByUserIdAndSkuId(Long userId, Long skuId) {
  20 + return selectOne(TradeCartItemDO::getUserId, userId,
  21 + TradeCartItemDO::getSkuId, skuId);
  22 + }
  23 +
  24 + default List<TradeCartItemDO> selectListByUserIdAndSkuIds(Long userId, Collection<Long> skuIds) {
  25 + return selectList(new LambdaQueryWrapper<TradeCartItemDO>().eq(TradeCartItemDO::getUserId, userId)
  26 + .in(TradeCartItemDO::getSkuId, skuIds));
  27 + }
  28 +
  29 + default void updateByIds(Collection<Long> ids, TradeCartItemDO updateObject) {
  30 + update(updateObject, new LambdaQueryWrapper<TradeCartItemDO>().in(TradeCartItemDO::getId, ids));
  31 + }
  32 +
  33 + default Integer selectSumByUserId(Long userId) {
  34 + // SQL sum 查询
  35 + List<Map<String, Object>> result = selectMaps(new QueryWrapper<TradeCartItemDO>()
  36 + .select("SUM(count) AS sumCount")
  37 + .eq("user_id", userId));
  38 + // 获得数量
  39 + return CollUtil.isNotEmpty(result) ? MapUtil.getInt(result.get(0), "sumCount") : 0;
  40 + }
  41 +
  42 + default List<TradeCartItemDO> selectListByUserId(Long userId, Boolean selected) {
  43 + return selectList(new LambdaQueryWrapperX<TradeCartItemDO>().eq(TradeCartItemDO::getUserId, userId)
  44 + .eqIfPresent(TradeCartItemDO::getSelected, selected));
  45 + }
  46 +
  47 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.cart;
  2 +
  3 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
  4 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
  5 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
  6 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
  7 +
  8 +import javax.validation.Valid;
  9 +import java.util.Collection;
  10 +
  11 +/**
  12 + * 购物车 Service 接口
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public interface TradeCartService {
  17 +
  18 + /**
  19 + * 添加商品到购物车
  20 + *
  21 + * @param userId 用户编号
  22 + * @param addCountReqVO 添加信息
  23 + */
  24 + void addCartItemCount(Long userId, @Valid AppTradeCartItemAddCountReqVO addCountReqVO);
  25 +
  26 + /**
  27 + * 更新购物车商品数量
  28 + *
  29 + * @param userId 用户编号
  30 + * @param updateCountReqVO 更新信息
  31 + */
  32 + void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO);
  33 +
  34 + /**
  35 + * 更新购物车商品是否选中
  36 + *
  37 + * @param userId 用户编号
  38 + * @param updateSelectedReqVO 更新信息
  39 + */
  40 + void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO);
  41 +
  42 + /**
  43 + * 删除购物车商品
  44 + *
  45 + * @param userId 用户编号
  46 + * @param skuIds SKU 编号的数组
  47 + */
  48 + void deleteCartItems(Long userId, Collection<Long> skuIds);
  49 +
  50 + /**
  51 + * 查询用户在购物车中的商品数量
  52 + *
  53 + * @param userId 用户编号
  54 + * @return 商品数量
  55 + */
  56 + Integer getCartCount(Long userId);
  57 +
  58 + /**
  59 + * 查询用户的购物车详情
  60 + *
  61 + * @param userId 用户编号
  62 + * @return 购物车详情
  63 + */
  64 + AppTradeCartDetailRespVO getCartDetail(Long userId);
  65 +
  66 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.cart;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  7 +import cn.iocoder.yudao.module.market.api.price.PriceApi;
  8 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
  9 +import cn.iocoder.yudao.module.market.enums.common.PromotionLevelEnum;
  10 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  11 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  12 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
  13 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
  14 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
  15 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
  16 +import cn.iocoder.yudao.module.trade.convert.cart.TradeCartConvert;
  17 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
  18 +import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartItemMapper;
  19 +import org.springframework.stereotype.Service;
  20 +import org.springframework.validation.annotation.Validated;
  21 +
  22 +import javax.annotation.Resource;
  23 +import java.util.ArrayList;
  24 +import java.util.Collection;
  25 +import java.util.List;
  26 +import java.util.Map;
  27 +
  28 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  30 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
  31 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
  32 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_NOT_FOUND;
  33 +
  34 +/**
  35 + * 购物车 Service 实现类
  36 + *
  37 + * @author 芋道源码
  38 + */
  39 +@Service
  40 +@Validated
  41 +public class TradeCartServiceImpl implements TradeCartService {
  42 +
  43 + @Resource
  44 + private TradeCartItemMapper cartItemMapper;
  45 +
  46 + @Resource
  47 + private ProductSkuApi productSkuApi;
  48 + @Resource
  49 + private PriceApi priceApi;
  50 +
  51 + @Override
  52 + public void addCartItemCount(Long userId, AppTradeCartItemAddCountReqVO addCountReqVO) {
  53 + Long skuId = addCountReqVO.getSkuId();
  54 + Integer count = addCountReqVO.getCount();
  55 + // 查询 CartItemDO
  56 + TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, addCountReqVO.getSkuId());
  57 +
  58 + // 存在,则进行数量更新
  59 + if (tradeItem != null) {
  60 + checkProductSku(skuId, tradeItem.getCount() + count);
  61 + cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId())
  62 + .setSelected(true).setCount(tradeItem.getCount() + count));
  63 + return;
  64 + }
  65 +
  66 + // 不存在,则进行插入
  67 + ProductSkuRespDTO sku = checkProductSku(skuId, count);
  68 + cartItemMapper.insert(new TradeCartItemDO().setUserId(userId).setSpuId(sku.getSpuId()).setSkuId(sku.getId())
  69 + .setSelected(true).setCount(count));
  70 + }
  71 +
  72 + @Override
  73 + public void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
  74 + // 校验 TradeCartItemDO 存在
  75 + TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, updateCountReqVO.getSkuId());
  76 + if (tradeItem == null) {
  77 + throw exception(CARD_ITEM_NOT_FOUND);
  78 + }
  79 + // 校验商品 SKU
  80 + checkProductSku(updateCountReqVO.getSkuId(), updateCountReqVO.getCount());
  81 +
  82 + // 更新数量
  83 + cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId()).setCount(updateCountReqVO.getCount()));
  84 + }
  85 +
  86 + @Override
  87 + public void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
  88 + // 查询 CartItemDO 列表
  89 + List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, updateSelectedReqVO.getSkuIds());
  90 + if (CollUtil.isEmpty(cartItems)) {
  91 + return;
  92 + }
  93 +
  94 + // 更新选中
  95 + cartItemMapper.updateByIds(CollectionUtils.convertList(cartItems, TradeCartItemDO::getId),
  96 + new TradeCartItemDO().setSelected(updateSelectedReqVO.getSelected()));
  97 + }
  98 +
  99 + /**
  100 + * 购物车删除商品
  101 + *
  102 + * @param userId 用户编号
  103 + * @param skuIds 商品 SKU 编号的数组
  104 + */
  105 + @Override
  106 + public void deleteCartItems(Long userId, Collection<Long> skuIds) {
  107 + // 查询 CartItemDO 列表
  108 + List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, skuIds);
  109 + if (CollUtil.isEmpty(cartItems)) {
  110 + return;
  111 + }
  112 +
  113 + // 批量标记删除
  114 + cartItemMapper.deleteBatchIds(CollectionUtils.convertSet(cartItems, TradeCartItemDO::getId));
  115 + }
  116 +
  117 + @Override
  118 + public Integer getCartCount(Long userId) {
  119 + return cartItemMapper.selectSumByUserId(userId);
  120 + }
  121 +
  122 + @Override
  123 + public AppTradeCartDetailRespVO getCartDetail(Long userId) {
  124 + // 获得购物车的商品
  125 + List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserId(userId, null);
  126 + // 如果未空,则返回空结果
  127 + if (CollUtil.isEmpty(cartItems)) {
  128 + return TradeCartConvert.INSTANCE.buildEmptyAppTradeCartDetailRespVO();
  129 + }
  130 +
  131 + // 调用价格服务,计算价格
  132 + PriceCalculateRespDTO priceCalculate = priceApi.calculatePrice(TradeCartConvert.INSTANCE.convert(userId, cartItems));
  133 +
  134 + // 转换返回
  135 + Map<Long, TradeCartItemDO> cartItemMap = convertMap(cartItems, TradeCartItemDO::getSkuId);
  136 + Map<Long, PriceCalculateRespDTO.OrderItem> orderItemMap = convertMap(priceCalculate.getOrder().getItems(),
  137 + PriceCalculateRespDTO.OrderItem::getSkuId);
  138 + List<AppTradeCartDetailRespVO.ItemGroup> itemGroups = new ArrayList<>(cartItems.size());
  139 + // ① 场景一,营销活动,订单级别 TODO 芋艿:待测试
  140 + priceCalculate.getPromotions().stream().filter(promotion -> PromotionLevelEnum.ORDER.getLevel().equals(promotion.getLevel()))
  141 + .forEach(promotion -> {
  142 + AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>())
  143 + .setPromotion(TradeCartConvert.INSTANCE.convert(promotion));
  144 + itemGroups.add(itemGroup);
  145 + promotion.getItems().forEach(promotionItem -> {
  146 + PriceCalculateRespDTO.OrderItem orderItem = orderItemMap.remove(promotionItem.getSkuId());
  147 + Assert.notNull(orderItem, "商品 SKU({}) 对应的订单项不能为空", promotionItem.getSkuId());
  148 + TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
  149 + itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
  150 + });
  151 + });
  152 + // ② 场景二,营销活动,商品级别
  153 + orderItemMap.values().forEach(orderItem -> {
  154 + AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>(1)).setPromotion(null);
  155 + itemGroups.add(itemGroup);
  156 + TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
  157 + itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
  158 + });
  159 + return new AppTradeCartDetailRespVO().setItemGroups(itemGroups)
  160 + .setOrder(TradeCartConvert.INSTANCE.convert(priceCalculate.getOrder()));
  161 + }
  162 +
  163 + /**
  164 + * 校验商品 SKU 是否合法
  165 + * 1. 是否存在
  166 + * 2. 是否下架
  167 + * 3. 库存不足
  168 + *
  169 + * @param skuId 商品 SKU 编号
  170 + * @param count 商品数量
  171 + * @return 商品 SKU
  172 + */
  173 + private ProductSkuRespDTO checkProductSku(Long skuId, Integer count) {
  174 + ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
  175 + if (sku == null || CommonStatusEnum.DISABLE.getStatus().equals(sku.getStatus())) {
  176 + throw exception(SKU_NOT_EXISTS);
  177 + }
  178 + if (count > sku.getStock()) {
  179 + throw exception(SKU_STOCK_NOT_ENOUGH);
  180 + }
  181 + return sku;
  182 + }
  183 +
  184 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
... ... @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
11 11 import cn.iocoder.yudao.module.pay.api.order.PayOrderDataCreateReqDTO;
12 12 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
13 13 import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
14   -import cn.iocoder.yudao.module.product.api.sku.dto.SkuInfoRespDTO;
  14 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
15 15 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
16 16 import cn.iocoder.yudao.module.product.api.spu.dto.SpuInfoRespDTO;
17 17 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
... ... @@ -26,7 +26,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
26 26 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
27 27 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
28 28 import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper;
29   -import cn.iocoder.yudao.module.trade.enums.enums.ErrorCodeConstants;
  29 +import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
30 30 import lombok.RequiredArgsConstructor;
31 31 import org.springframework.stereotype.Service;
32 32 import org.springframework.transaction.annotation.Transactional;
... ... @@ -62,12 +62,12 @@ public class TradeOrderServiceImpl implements TradeOrderService {
62 62  
63 63 List<Item> items = createReqVO.getItems();
64 64 // 商品SKU检查 sku可售状态,库存
65   - List<SkuInfoRespDTO> skuInfos = productSkuApi.getSkusByIds(CollectionUtils.convertSet(items, Item::getSkuId));
66   - Map<Long, SkuInfoRespDTO> skuInfoMap = CollectionUtils.convertMap(skuInfos, SkuInfoRespDTO::getId);
  65 + List<ProductSkuRespDTO> skuInfos = productSkuApi.getSkuList(CollectionUtils.convertSet(items, Item::getSkuId));
  66 + Map<Long, ProductSkuRespDTO> skuInfoMap = CollectionUtils.convertMap(skuInfos, ProductSkuRespDTO::getId);
67 67 checkSaleableAndStockFromSpu(skuInfoMap, items);
68 68  
69 69 // 商品SPU检查 sku可售状态,库存
70   - List<SpuInfoRespDTO> spuInfos = productSpuApi.getSpusByIds(CollectionUtils.convertSet(skuInfos, SkuInfoRespDTO::getSpuId));
  70 + List<SpuInfoRespDTO> spuInfos = productSpuApi.getSpuList(CollectionUtils.convertSet(skuInfos, ProductSkuRespDTO::getSpuId));
71 71 checkSaleableFromSpu(spuInfos);
72 72  
73 73 // 价格计算
... ... @@ -79,7 +79,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
79 79 tradeOrderMapper.insert(tradeOrderDO);
80 80  
81 81 // 订单项信息记录
82   - List<TradeOrderItemDO> tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(tradeOrderDO, priceResp.getItems());
  82 + List<TradeOrderItemDO> tradeOrderItems = TradeOrderItemConvert.INSTANCE.convertList(tradeOrderDO, priceResp.getOrder().getItems());
83 83 //-填充订单项-SKU信息
84 84 fillItemsInfoFromSku(tradeOrderItems, skuInfoMap);
85 85 tradeOrderItemMapper.insertBatch(tradeOrderItems);
... ... @@ -94,10 +94,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
94 94 }
95 95  
96 96 private void fillItemsInfoFromSku(List<TradeOrderItemDO> tradeOrderItems,
97   - Map<Long, SkuInfoRespDTO> spuInfos) {
  97 + Map<Long, ProductSkuRespDTO> spuInfos) {
98 98 for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) {
99 99 // 填充SKU信息
100   - SkuInfoRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId());
  100 + ProductSkuRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId());
101 101 tradeOrderItem.setSpuId(skuInfoRespDTO.getSpuId());
102 102 tradeOrderItem.setPicUrl(skuInfoRespDTO.getPicUrl());
103 103 tradeOrderItem.setName(skuInfoRespDTO.getName());
... ... @@ -116,14 +116,14 @@ public class TradeOrderServiceImpl implements TradeOrderService {
116 116 }
117 117 }
118 118  
119   - private void checkSaleableAndStockFromSpu(Map<Long, SkuInfoRespDTO> skuInfoMap,
  119 + private void checkSaleableAndStockFromSpu(Map<Long, ProductSkuRespDTO> skuInfoMap,
120 120 List<Item> items) {
121 121 // sku 不存在
122 122 if (items.size() != skuInfoMap.size()) {
123 123 throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_FOUND);
124 124 }
125 125 for (Item item : items) {
126   - SkuInfoRespDTO skuInfoDTO = skuInfoMap.get(item.getSkuId());
  126 + ProductSkuRespDTO skuInfoDTO = skuInfoMap.get(item.getSkuId());
127 127 // sku禁用
128 128 if (!Objects.equals(CommonStatusEnum.ENABLE.getStatus(), skuInfoDTO.getStatus())) {
129 129 throw ServiceExceptionUtil.exception(ErrorCodeConstants.ORDER_SKU_NOT_SALE);
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -173,6 +173,7 @@ logging:
173 173 cn.iocoder.yudao.module.system.dal.mysql: debug
174 174 cn.iocoder.yudao.module.tool.dal.mysql: debug
175 175 cn.iocoder.yudao.module.member.dal.mysql: debug
  176 + cn.iocoder.yudao.module.trade.dal.mysql: debug
176 177  
177 178 --- #################### 微信公众号、小程序相关配置 ####################
178 179 wx:
... ...