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

This commit is contained in:
YunaiV 2022-11-10 00:32:35 +08:00
parent 16f5d0f5a4
commit 84f6ec10bc
8 changed files with 88 additions and 57 deletions

View File

@ -40,7 +40,7 @@ public class ProductSkuUpdateStockReqDTO {
* 负数扣减库存 * 负数扣减库存
*/ */
@NotNull(message = "库存变化数量不能为空") @NotNull(message = "库存变化数量不能为空")
private Integer incCount; private Integer incrCount;
} }

View File

@ -60,7 +60,7 @@ public interface ProductSkuConvert {
if (spuId == null) { if (spuId == null) {
return; return;
} }
Integer stock = spuIdAndStockMap.getOrDefault(spuId, 0) + item.getIncCount(); Integer stock = spuIdAndStockMap.getOrDefault(spuId, 0) + item.getIncrCount();
spuIdAndStockMap.put(spuId, stock); spuIdAndStockMap.put(spuId, stock);
}); });
return spuIdAndStockMap; return spuIdAndStockMap;

View File

@ -203,10 +203,10 @@ public class ProductSkuServiceImpl implements ProductSkuService {
public void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) { public void updateSkuStock(ProductSkuUpdateStockReqDTO updateStockReqDTO) {
// 更新 SKU 库存 // 更新 SKU 库存
updateStockReqDTO.getItems().forEach(item -> { updateStockReqDTO.getItems().forEach(item -> {
if (item.getIncCount() > 0) { if (item.getIncrCount() > 0) {
productSkuMapper.updateStockIncr(item.getId(), item.getIncCount()); productSkuMapper.updateStockIncr(item.getId(), item.getIncrCount());
} else if (item.getIncCount() < 0) { } else if (item.getIncrCount() < 0) {
int updateStockIncr = productSkuMapper.updateStockDecr(item.getId(), item.getIncCount()); int updateStockIncr = productSkuMapper.updateStockDecr(item.getId(), item.getIncrCount());
if (updateStockIncr == 0) { if (updateStockIncr == 0) {
throw exception(SKU_STOCK_NOT_ENOUGH); throw exception(SKU_STOCK_NOT_ENOUGH);
} }

View File

@ -46,7 +46,7 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
public void testUpdateSkuStock_incrSuccess() { public void testUpdateSkuStock_incrSuccess() {
// 准备参数 // 准备参数
ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO() ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO()
.setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncCount(10))); .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(10)));
// mock 数据 // mock 数据
productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20))); productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20)));
@ -66,7 +66,7 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
public void testUpdateSkuStock_decrSuccess() { public void testUpdateSkuStock_decrSuccess() {
// 准备参数 // 准备参数
ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO() ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO()
.setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncCount(-10))); .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(-10)));
// mock 数据 // mock 数据
productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20))); productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20)));
@ -86,7 +86,7 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
public void testUpdateSkuStock_decrFail() { public void testUpdateSkuStock_decrFail() {
// 准备参数 // 准备参数
ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO() ProductSkuUpdateStockReqDTO updateStockReqDTO = new ProductSkuUpdateStockReqDTO()
.setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncCount(-30))); .setItems(singletonList(new ProductSkuUpdateStockReqDTO.Item().setId(1L).setIncrCount(-30)));
// mock 数据 // mock 数据
productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20))); productSkuMapper.insert(randomPojo(ProductSkuDO.class, o -> o.setId(1L).setSpuId(10L).setStock(20)));

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.convert.order;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
@ -17,7 +18,7 @@ import org.mapstruct.factory.Mappers;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
@Mapper @Mapper
public interface TradeOrderConvert { public interface TradeOrderConvert {
@ -59,4 +60,11 @@ public interface TradeOrderConvert {
@Mapping(source = "userId" , target = "userId") @Mapping(source = "userId" , target = "userId")
PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId); PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId);
@Mappings({
@Mapping(source = "skuId", target = "id"),
@Mapping(source = "count", target = "incrCount"),
})
ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean);
List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
} }

View File

@ -1,22 +0,0 @@
package cn.iocoder.yudao.module.trade.convert.sku;
import cn.iocoder.yudao.module.product.api.sku.dto.SkuDecrementStockBatchReqDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
// TODO @LeeYan9挪到 OrderConvert
/**
* @author LeeYan9
* @since 2022-08-26
*/
@Mapper
public interface ProductSkuConvert {
ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class);
List<SkuDecrementStockBatchReqDTO.Item> convert(List<TradeOrderItemDO> list);
}

View File

@ -14,9 +14,12 @@ import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO; import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
import cn.iocoder.yudao.module.promotion.api.price.PriceApi; import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
@ -74,6 +77,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
private PayOrderApi payOrderApi; private PayOrderApi payOrderApi;
@Resource @Resource
private AddressApi addressApi; private AddressApi addressApi;
@Resource
private CouponApi couponApi;
@Resource @Resource
private TradeOrderProperties tradeOrderProperties; private TradeOrderProperties tradeOrderProperties;
@ -94,26 +99,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
// 插入 TradeOrderDO 订单 // 插入 TradeOrderDO 订单
TradeOrderDO tradeOrderDO = createTradeOrder(userId, userIp, createReqVO, priceResp.getOrder(), address); TradeOrderDO tradeOrderDO = createTradeOrder(userId, userIp, createReqVO, priceResp.getOrder(), address);
// 插入 TradeOrderItemDO 订单项 // 插入 TradeOrderItemDO 订单项
createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus); List<TradeOrderItemDO> tradeOrderItems = createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus);
// 下单时扣减商品库存 TODO
// List<SkuDecrementStockBatchReqDTO.Item> skuDecrementStockItems = ProductSkuConvert.INSTANCE.convert(tradeOrderItems);
// productSkuApi.decrementStockBatch(SkuDecrementStockBatchReqDTO.of(skuDecrementStockItems));
// 删除购物车商品 TODO 芋艿待实现
// 扣减积分抵扣金额 TODO 芋艿待实现
// 有使用优惠券时更新
// 增加订单日志 TODO 芋艿待实现
// 构建预支付请求参数
// TODO @LeeYan9: 需要更新到订单上
// PayOrderInfoCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO);
// fillPayOrderInfoFromItems(payOrderCreateReqDTO, tradeOrderItems);
// 生成预支付
// 订单创建完后的逻辑
afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems);
// TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
return tradeOrderDO.getId(); return tradeOrderDO.getId();
} }
@ -242,10 +231,44 @@ public class TradeOrderServiceImpl implements TradeOrderService {
return tradeOrderDO; return tradeOrderDO;
} }
private void createTradeOrderItems(TradeOrderDO tradeOrderDO, private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO,
List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) { List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {
List<TradeOrderItemDO> tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus); List<TradeOrderItemDO> tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus);
tradeOrderItemMapper.insertBatch(tradeOrderItemDOs); tradeOrderItemMapper.insertBatch(tradeOrderItemDOs);
return tradeOrderItemDOs;
}
/**
* 执行创建完创建完订单后的逻辑
*
* 例如说优惠劵的扣减积分的扣减支付单的创建等等
*
* @param userId 用户编号
* @param createReqVO 创建订单请求
* @param tradeOrderDO 交易订单
*/
private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs) {
// 下单时扣减商品库存
productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(tradeOrderItemDOs)));
// 删除购物车商品 TODO 芋艿待实现
// 扣减积分抵扣金额 TODO 芋艿待实现
// 有使用优惠券时更新
if (createReqVO.getCouponId() != null) {
couponApi.useCoupon(new CouponUseReqDTO().setId(createReqVO.getCouponId()).setUserId(userId)
.setOrderId(tradeOrderDO.getId()));
}
// 构建预支付请求参数
// TODO @LeeYan9: 需要更新到订单上
// PayOrderInfoCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO);
// fillPayOrderInfoFromItems(payOrderCreateReqDTO, tradeOrderItems);
// 生成预支付
// 增加订单日志 TODO 芋艿待实现
} }
} }

View File

@ -8,9 +8,11 @@ import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.price.PriceApi; import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO; import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
@ -24,6 +26,7 @@ import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -37,6 +40,7 @@ import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@ -66,10 +70,8 @@ class TradeOrderServiceTest extends BaseDbUnitTest {
private PayOrderApi payOrderApi; private PayOrderApi payOrderApi;
@MockBean @MockBean
private AddressApi addressApi; private AddressApi addressApi;
@MockBean
// 1, 3 50 ;打折 20总和 = 6042; private CouponApi couponApi;
// 2, 4 20 打折 10总和 = 4028;
// 优惠劵 100 30
@Test @Test
public void testCreateTradeOrder_success() { public void testCreateTradeOrder_success() {
@ -205,6 +207,26 @@ class TradeOrderServiceTest extends BaseDbUnitTest {
assertEquals(tradeOrderItemDO02.getOrderDividePrice(), 25); assertEquals(tradeOrderItemDO02.getOrderDividePrice(), 25);
assertEquals(tradeOrderItemDO02.getRefundStatus(), TradeOrderItemRefundStatusEnum.NONE.getStatus()); assertEquals(tradeOrderItemDO02.getRefundStatus(), TradeOrderItemRefundStatusEnum.NONE.getStatus());
assertEquals(tradeOrderItemDO02.getRefundTotal(), 0); assertEquals(tradeOrderItemDO02.getRefundTotal(), 0);
// 校验调用
verify(productSkuApi).updateSkuStock(argThat(new ArgumentMatcher<ProductSkuUpdateStockReqDTO>() {
@Override
public boolean matches(ProductSkuUpdateStockReqDTO updateStockReqDTO) {
assertEquals(updateStockReqDTO.getItems().size(), 2);
assertEquals(updateStockReqDTO.getItems().get(0).getId(), 1L);
assertEquals(updateStockReqDTO.getItems().get(0).getIncrCount(), 3);
assertEquals(updateStockReqDTO.getItems().get(1).getId(), 2L);
assertEquals(updateStockReqDTO.getItems().get(1).getIncrCount(), 4);
return true;
}
}));
verify(couponApi).useCoupon(argThat(reqDTO -> {
assertEquals(reqDTO.getId(), reqVO.getCouponId());
assertEquals(reqDTO.getUserId(), userId);
assertEquals(reqDTO.getOrderId(), tradeOrderId);
return true;
}));
// //mock 支付订单信息 // //mock 支付订单信息
// when(payOrderApi.createPayOrder(any())).thenReturn(1L); // when(payOrderApi.createPayOrder(any())).thenReturn(1L);