Commit 84f6ec10bcc7e89cde9b4bc9145c8892fda4ad07
1 parent
16f5d0f5
trade:增加优惠劵使用、商品库存的扣减
Showing
8 changed files
with
88 additions
and
57 deletions
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuUpdateStockReqDTO.java
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 | ... | ... |