diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java index 5479b9adce..70e5a2ad4d 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java @@ -3,7 +3,8 @@ package cn.iocoder.yudao.module.product.api.spu.dto; import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; import lombok.Data; -// TODO @LeeYan9: ProductSpuRespDTO +import java.util.List; + /** * 商品 SPU 信息 Response DTO * @@ -76,6 +77,11 @@ public class ProductSpuRespDTO { */ private Long deliveryTemplateId; + /** + * 支持的配送方式 + */ + private List deliveryTypes; + // ========== 营销相关字段 ========= /** diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java index e92d75d624..cd8add9362 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java @@ -4,6 +4,7 @@ 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.spu.ProductSpuApi; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator; @@ -15,6 +16,8 @@ import org.springframework.validation.annotation.Validated; import jakarta.annotation.Resource; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; @@ -22,6 +25,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL; +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_TYPE_NOT_SUPPORTED; /** * 价格计算 Service 实现类 @@ -48,6 +52,9 @@ public class TradePriceServiceImpl implements TradePriceService { // 1.2 获得商品 SPU 数组 List spuList = checkSpuList(skuList); + // 1.3 校验配送方式是否匹配 + checkDeliveryType(calculateReqBO, spuList); + // 2.1 计算价格 TradePriceCalculateRespBO calculateRespBO = TradePriceCalculatorHelper .buildCalculateResp(calculateReqBO, spuList, skuList); @@ -85,4 +92,17 @@ public class TradePriceServiceImpl implements TradePriceService { return productSpuApi.validateSpuList(convertSet(skuList, ProductSkuRespDTO::getSpuId)); } + private void checkDeliveryType(TradePriceCalculateReqBO reqBO, List spuList) { + if (reqBO.getDeliveryType() == null) { + return; + } + Set supportedDeliveryTypes = spuList.stream() + .flatMap(spu -> spu.getDeliveryTypes().stream()) + .collect(Collectors.toSet()); + if (!supportedDeliveryTypes.contains(reqBO.getDeliveryType())) { + log.error("[checkDeliveryType][配送方式不匹配,请求 deliveryType({}),支持的 deliveryTypes({})]", + reqBO.getDeliveryType(), supportedDeliveryTypes); + throw exception(PRICE_CALCULATE_DELIVERY_TYPE_NOT_SUPPORTED); + } + } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java index 30558bdb67..d573a94565 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java @@ -223,4 +223,17 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { } } + private void checkDeliveryType(TradePriceCalculateReqBO param, List spuList) { + if (param.getDeliveryType() == null) { + return; + } + Set supportedDeliveryTypes = spuList.stream() + .flatMap(spu -> spu.getDeliveryTypes().stream()) + .collect(Collectors.toSet()); + if (!supportedDeliveryTypes.contains(param.getDeliveryType())) { + log.error("[checkDeliveryType][配送方式不匹配,请求 deliveryType({}),支持的 deliveryTypes({})]", + param.getDeliveryType(), supportedDeliveryTypes); + throw exception(PRICE_CALCULATE_DELIVERY_TYPE_NOT_SUPPORTED); + } + } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java index b3900e04b9..895d0e73d5 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java @@ -30,7 +30,6 @@ import static org.mockito.Mockito.when; * * @author 芋道源码 */ -@Disabled // TODO 芋艿:后续 fix 补充的单测 public class TradePriceServiceImplTest extends BaseMockitoUnitTest { @InjectMocks @@ -134,4 +133,34 @@ public class TradePriceServiceImplTest extends BaseMockitoUnitTest { assertEquals(skuList.get(2).getProperties(), calculateRespBO.getItems().get(2).getProperties()); } + @Test + public void testCalculatePrice_DeliveryTypeMismatch() { + // 准备参数 + TradePriceCalculateReqBO calculateReqBO = new TradePriceCalculateReqBO() + .setUserId(10L) + .setCouponId(20L).setAddressId(30L) + .setDeliveryType(DeliveryTypeEnum.EXPRESS.getType()) // 设置配送方式 + .setItems(Arrays.asList( + new TradePriceCalculateReqBO.Item().setSkuId(100L).setCount(1).setSelected(true), + new TradePriceCalculateReqBO.Item().setSkuId(200L).setCount(3).setSelected(true) + )); + // mock 方法 + List skuList = Arrays.asList( + new ProductSkuRespDTO().setId(100L).setStock(500).setPrice(1000).setPicUrl("https://t.cn/1.png").setSpuId(1001L) + .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(1L).setPropertyName("颜色") + .setValueId(2L).setValueName("红色"))), + new ProductSkuRespDTO().setId(200L).setStock(400).setPrice(2000).setPicUrl("https://t.cn/2.png").setSpuId(1001L) + .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(1L).setPropertyName("颜色") + .setValueId(3L).setValueName("黄色"))) + ); + when(productSkuApi.getSkuList(Mockito.eq(asSet(100L, 200L)))).thenReturn(skuList); + when(productSpuApi.getSpuList(Mockito.eq(asSet(1001L)))) + .thenReturn(singletonList(new ProductSpuRespDTO().setId(1001L).setName("小菜").setCategoryId(666L) + .setStatus(ProductSpuStatusEnum.ENABLE.getStatus()) + .setDeliveryTypes(singletonList(DeliveryTypeEnum.PICK_UP.getType())))); // 设置不支持的配送方式 + + // 调用并断言抛出异常 + assertThrows(ServiceException.class, () -> tradePriceService.calculatePrice(calculateReqBO)); + } + }