Commit 84f6ec10bcc7e89cde9b4bc9145c8892fda4ad07

Authored by YunaiV
1 parent 16f5d0f5

trade:增加优惠劵使用、商品库存的扣减

yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuUpdateStockReqDTO.java
... ... @@ -40,7 +40,7 @@ public class ProductSkuUpdateStockReqDTO {
40 40 * 负数:扣减库存
41 41 */
42 42 @NotNull(message = "库存变化数量不能为空")
43   - private Integer incCount;
  43 + private Integer incrCount;
44 44  
45 45 }
46 46  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/sku/ProductSkuConvert.java
... ... @@ -60,7 +60,7 @@ public interface ProductSkuConvert {
60 60 if (spuId == null) {
61 61 return;
62 62 }
63   - Integer stock = spuIdAndStockMap.getOrDefault(spuId, 0) + item.getIncCount();
  63 + Integer stock = spuIdAndStockMap.getOrDefault(spuId, 0) + item.getIncrCount();
64 64 spuIdAndStockMap.put(spuId, stock);
65 65 });
66 66 return spuIdAndStockMap;
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceImpl.java
... ... @@ -203,10 +203,10 @@ public class ProductSkuServiceImpl implements ProductSkuService {
203 203 public void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) {
204 204 // 更新 SKU 库存
205 205 updateStockReqDTO.getItems().forEach(item -> {
206   - if (item.getIncCount() > 0) {
207   - productSkuMapper.updateStockIncr(item.getId(), item.getIncCount());
208   - } else if (item.getIncCount() < 0) {
209   - int updateStockIncr = productSkuMapper.updateStockDecr(item.getId(), item.getIncCount());
  206 + if (item.getIncrCount() > 0) {
  207 + productSkuMapper.updateStockIncr(item.getId(), item.getIncrCount());
  208 + } else if (item.getIncrCount() < 0) {
  209 + int updateStockIncr = productSkuMapper.updateStockDecr(item.getId(), item.getIncrCount());
210 210 if (updateStockIncr == 0) {
211 211 throw exception(SKU_STOCK_NOT_ENOUGH);
212 212 }
... ...
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceTest.java
... ... @@ -46,7 +46,7 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
46 46 public void testUpdateSkuStock_incrSuccess() {
47 47 // 准备参数
48 48 ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO()
49   - .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncCount(10)));
  49 + .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(10)));
50 50 // mock 数据
51 51 productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20)));
52 52  
... ... @@ -66,7 +66,7 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
66 66 public void testUpdateSkuStock_decrSuccess() {
67 67 // 准备参数
68 68 ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO()
69   - .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncCount(-10)));
  69 + .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(-10)));
70 70 // mock 数据
71 71 productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20)));
72 72  
... ... @@ -86,7 +86,7 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
86 86 public void testUpdateSkuStock_decrFail() {
87 87 // 准备参数
88 88 ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO()
89   - .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncCount(-30)));
  89 + .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(-30)));
90 90 // mock 数据
91 91 productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20)));
92 92  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.convert.order;
3 3 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 4 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
5 5 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  6 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
6 7 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
7 8 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
8 9 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
... ... @@ -17,7 +18,7 @@ import org.mapstruct.factory.Mappers;
17 18 import java.util.List;
18 19 import java.util.Map;
19 20  
20   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
21 22  
22 23 @Mapper
23 24 public interface TradeOrderConvert {
... ... @@ -59,4 +60,11 @@ public interface TradeOrderConvert {
59 60 @Mapping(source = "userId" , target = "userId")
60 61 PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId);
61 62  
  63 + @Mappings({
  64 + @Mapping(source = "skuId", target = "id"),
  65 + @Mapping(source = "count", target = "incrCount"),
  66 + })
  67 + ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean);
  68 + List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
  69 +
62 70 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/sku/ProductSkuConvert.java deleted
1   -package cn.iocoder.yudao.module.trade.convert.sku;
2   -
3   -import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
4   -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
5   -import org.mapstruct.Mapper;
6   -import org.mapstruct.factory.Mappers;
7   -
8   -import java.util.List;
9   -
10   -// TODO @LeeYan9:挪到 OrderConvert 那
11   -/**
12   - * @author LeeYan9
13   - * @since 2022-08-26
14   - */
15   -@Mapper
16   -public interface ProductSkuConvert {
17   -
18   - ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class);
19   -
20   - List<SkuDecrementStockBatchReqDTO.Item> convert(List<TradeOrderItemDO> list);
21   -
22   -}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
... ... @@ -14,9 +14,12 @@ import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
14 14 import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO;
15 15 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
16 16 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  17 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
17 18 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
18 19 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
19 20 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
  21 +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
  22 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
20 23 import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
21 24 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
22 25 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
... ... @@ -74,6 +77,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
74 77 private PayOrderApi payOrderApi;
75 78 @Resource
76 79 private AddressApi addressApi;
  80 + @Resource
  81 + private CouponApi couponApi;
77 82  
78 83 @Resource
79 84 private TradeOrderProperties tradeOrderProperties;
... ... @@ -94,26 +99,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
94 99 // 插入 TradeOrderDO 订单
95 100 TradeOrderDO tradeOrderDO = createTradeOrder(userId, userIp, createReqVO, priceResp.getOrder(), address);
96 101 // 插入 TradeOrderItemDO 订单项
97   - createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus);
98   -
99   - // 下单时扣减商品库存 TODO
100   -// List<SkuDecrementStockBatchReqDTO.Item> skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems);
101   -// productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems));
102   -
103   - // 删除购物车商品 TODO 芋艿:待实现
104   -
105   - // 扣减积分,抵扣金额 TODO 芋艿:待实现
106   -
107   - // 有使用优惠券时更新
108   -
109   - // 增加订单日志 TODO 芋艿:待实现
110   -
111   - // 构建预支付请求参数
112   - // TODO @LeeYan9: 需要更新到订单上
113   -// PayOrderInfoCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO);
114   -// fillPayOrderInfoFromItems(payOrderCreateReqDTO, tradeOrderItems);
115   - // 生成预支付
  102 + List<TradeOrderItemDO> tradeOrderItems = createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus);
116 103  
  104 + // 订单创建完后的逻辑
  105 + afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems);
117 106 // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
118 107 return tradeOrderDO.getId();
119 108 }
... ... @@ -242,10 +231,44 @@ public class TradeOrderServiceImpl implements TradeOrderService {
242 231 return tradeOrderDO;
243 232 }
244 233  
245   - private void createTradeOrderItems(TradeOrderDO tradeOrderDO,
246   - List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {
  234 + private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO,
  235 + List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {
247 236 List<TradeOrderItemDO> tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus);
248 237 tradeOrderItemMapper.insertBatch(tradeOrderItemDOs);
  238 + return tradeOrderItemDOs;
  239 + }
  240 +
  241 + /**
  242 + * 执行创建完创建完订单后的逻辑
  243 + *
  244 + * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
  245 + *
  246 + * @param userId 用户编号
  247 + * @param createReqVO 创建订单请求
  248 + * @param tradeOrderDO 交易订单
  249 + */
  250 + private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
  251 + TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs) {
  252 + // 下单时扣减商品库存
  253 + productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(tradeOrderItemDOs)));
  254 +
  255 + // 删除购物车商品 TODO 芋艿:待实现
  256 +
  257 + // 扣减积分,抵扣金额 TODO 芋艿:待实现
  258 +
  259 + // 有使用优惠券时更新
  260 + if (createReqVO.getCouponId() != null) {
  261 + couponApi.useCoupon(new CouponUseReqDTO().setId(createReqVO.getCouponId()).setUserId(userId)
  262 + .setOrderId(tradeOrderDO.getId()));
  263 + }
  264 +
  265 + // 构建预支付请求参数
  266 + // TODO @LeeYan9: 需要更新到订单上
  267 +// PayOrderInfoCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO);
  268 +// fillPayOrderInfoFromItems(payOrderCreateReqDTO, tradeOrderItems);
  269 + // 生成预支付
  270 +
  271 + // 增加订单日志 TODO 芋艿:待实现
249 272 }
250 273  
251 274 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java
... ... @@ -8,9 +8,11 @@ import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
8 8 import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
9 9 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
10 10 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  11 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
11 12 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
12 13 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
13 14 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
  15 +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
14 16 import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
15 17 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
16 18 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
... ... @@ -24,6 +26,7 @@ import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
24 26 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
25 27 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
26 28 import org.junit.jupiter.api.Test;
  29 +import org.mockito.ArgumentMatcher;
27 30 import org.springframework.boot.test.mock.mockito.MockBean;
28 31 import org.springframework.context.annotation.Import;
29 32  
... ... @@ -37,6 +40,7 @@ import static java.util.Collections.singletonList;
37 40 import static org.junit.jupiter.api.Assertions.*;
38 41 import static org.mockito.ArgumentMatchers.argThat;
39 42 import static org.mockito.ArgumentMatchers.eq;
  43 +import static org.mockito.Mockito.verify;
40 44 import static org.mockito.Mockito.when;
41 45  
42 46 /**
... ... @@ -66,10 +70,8 @@ class TradeOrderServiceTest extends BaseDbUnitTest {
66 70 private PayOrderApi payOrderApi;
67 71 @MockBean
68 72 private AddressApi addressApi;
69   -
70   - // 1, 3 个,50 块;打折 20;总和 = 60;42;
71   - // 2, 4 个,20 块;打折 10;总和 = 40;28;
72   - // 优惠劵,满 100 减 30
  73 + @MockBean
  74 + private CouponApi couponApi;
73 75  
74 76 @Test
75 77 public void testCreateTradeOrder_success() {
... ... @@ -205,6 +207,26 @@ class TradeOrderServiceTest extends BaseDbUnitTest {
205 207 assertEquals(tradeOrderItemDO02.getOrderDividePrice(), 25);
206 208 assertEquals(tradeOrderItemDO02.getRefundStatus(), TradeOrderItemRefundStatusEnum.NONE.getStatus());
207 209 assertEquals(tradeOrderItemDO02.getRefundTotal(), 0);
  210 + // 校验调用
  211 + verify(productSkuApi).updateSkuStock(argThat(new ArgumentMatcher<ProductSkuUpdateStockReqDTO>() {
  212 +
  213 + @Override
  214 + public boolean matches(ProductSkuUpdateStockReqDTO updateStockReqDTO) {
  215 + assertEquals(updateStockReqDTO.getItems().size(), 2);
  216 + assertEquals(updateStockReqDTO.getItems().get(0).getId(), 1L);
  217 + assertEquals(updateStockReqDTO.getItems().get(0).getIncrCount(), 3);
  218 + assertEquals(updateStockReqDTO.getItems().get(1).getId(), 2L);
  219 + assertEquals(updateStockReqDTO.getItems().get(1).getIncrCount(), 4);
  220 + return true;
  221 + }
  222 +
  223 + }));
  224 + verify(couponApi).useCoupon(argThat(reqDTO -> {
  225 + assertEquals(reqDTO.getId(), reqVO.getCouponId());
  226 + assertEquals(reqDTO.getUserId(), userId);
  227 + assertEquals(reqDTO.getOrderId(), tradeOrderId);
  228 + return true;
  229 + }));
208 230 // //mock 支付订单信息
209 231 // when(payOrderApi.createPayOrder(any())).thenReturn(1L);
210 232  
... ...