Commit 5949727c8daba66c38f8b78f21ca9efb0dcf448e
1 parent
35569c5e
trade:全局配送价格配置
Showing
3 changed files
with
74 additions
and
5 deletions
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/config/TradeConfigDO.java
| @@ -35,6 +35,17 @@ public class TradeConfigDO extends BaseDO { | @@ -35,6 +35,17 @@ public class TradeConfigDO extends BaseDO { | ||
| 35 | @TableId | 35 | @TableId |
| 36 | private Long id; | 36 | private Long id; |
| 37 | 37 | ||
| 38 | + // ========== 配送相关 ========== | ||
| 39 | + // TODO 芋艿:未配置 | ||
| 40 | + /** | ||
| 41 | + * 是否启用全场包邮 | ||
| 42 | + */ | ||
| 43 | + private Boolean deliveryExpressFreeEnabled; | ||
| 44 | + /** | ||
| 45 | + * 全场包邮的最小金额,单位:分 | ||
| 46 | + */ | ||
| 47 | + private Integer deliveryExpressFreePrice; | ||
| 48 | + | ||
| 38 | // ========== 分销相关 ========== | 49 | // ========== 分销相关 ========== |
| 39 | 50 | ||
| 40 | /** | 51 | /** |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
| @@ -5,9 +5,11 @@ import cn.hutool.core.lang.Assert; | @@ -5,9 +5,11 @@ import cn.hutool.core.lang.Assert; | ||
| 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 6 | import cn.iocoder.yudao.module.member.api.address.AddressApi; | 6 | import cn.iocoder.yudao.module.member.api.address.AddressApi; |
| 7 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; | 7 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; |
| 8 | +import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO; | ||
| 8 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; | 9 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; |
| 9 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; | 10 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; |
| 10 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; | 11 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 12 | +import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; | ||
| 11 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; | 13 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; |
| 12 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService; | 14 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService; |
| 13 | import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; | 15 | import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; |
| @@ -44,6 +46,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | @@ -44,6 +46,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | ||
| 44 | private DeliveryPickUpStoreService deliveryPickUpStoreService; | 46 | private DeliveryPickUpStoreService deliveryPickUpStoreService; |
| 45 | @Resource | 47 | @Resource |
| 46 | private DeliveryExpressTemplateService deliveryExpressTemplateService; | 48 | private DeliveryExpressTemplateService deliveryExpressTemplateService; |
| 49 | + @Resource | ||
| 50 | + private TradeConfigService tradeConfigService; | ||
| 47 | 51 | ||
| 48 | @Override | 52 | @Override |
| 49 | public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { | 53 | public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { |
| @@ -70,19 +74,25 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | @@ -70,19 +74,25 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | ||
| 70 | // ========= 快递发货 ========== | 74 | // ========= 快递发货 ========== |
| 71 | 75 | ||
| 72 | private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { | 76 | private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { |
| 73 | - // 1. 得到收件地址区域 | 77 | + // 0. 得到收件地址区域 |
| 74 | if (param.getAddressId() == null) { | 78 | if (param.getAddressId() == null) { |
| 75 | throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY); | 79 | throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY); |
| 76 | } | 80 | } |
| 77 | AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); | 81 | AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); |
| 78 | Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); | 82 | Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); |
| 79 | 83 | ||
| 80 | - // 2. 过滤出已选中的商品SKU | 84 | + // 情况一:全局包邮 |
| 85 | + if (isGlobalExpressFree(param, result)) { | ||
| 86 | + return; | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + // 情况二: | ||
| 90 | + // 2.1 过滤出已选中的商品SKU | ||
| 81 | List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); | 91 | List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); |
| 82 | Set<Long> deliveryTemplateIds = convertSet(selectedItem, OrderItem::getDeliveryTemplateId); | 92 | Set<Long> deliveryTemplateIds = convertSet(selectedItem, OrderItem::getDeliveryTemplateId); |
| 83 | Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap = | 93 | Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap = |
| 84 | deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(deliveryTemplateIds, address.getAreaId()); | 94 | deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(deliveryTemplateIds, address.getAreaId()); |
| 85 | - // 3. 计算配送费用 | 95 | + // 2.2 计算配送费用 |
| 86 | if (CollUtil.isEmpty(expressTemplateMap)) { | 96 | if (CollUtil.isEmpty(expressTemplateMap)) { |
| 87 | log.error("[calculate][找不到商品 templateIds {} areaId{} 对应的运费模板]", deliveryTemplateIds, address.getAreaId()); | 97 | log.error("[calculate][找不到商品 templateIds {} areaId{} 对应的运费模板]", deliveryTemplateIds, address.getAreaId()); |
| 88 | throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND); | 98 | throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND); |
| @@ -90,6 +100,20 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | @@ -90,6 +100,20 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | ||
| 90 | calculateDeliveryPrice(selectedItem, expressTemplateMap, result); | 100 | calculateDeliveryPrice(selectedItem, expressTemplateMap, result); |
| 91 | } | 101 | } |
| 92 | 102 | ||
| 103 | + /** | ||
| 104 | + * 是否全局包邮 | ||
| 105 | + * | ||
| 106 | + * @param param 计算信息 | ||
| 107 | + * @param result 计算结果 | ||
| 108 | + * @return 是否包邮 | ||
| 109 | + */ | ||
| 110 | + private boolean isGlobalExpressFree(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { | ||
| 111 | + TradeConfigDO config = tradeConfigService.getTradeConfig(); | ||
| 112 | + return config != null | ||
| 113 | + && Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) // 开启包邮 | ||
| 114 | + && result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice(); // 满足包邮的价格 | ||
| 115 | + } | ||
| 116 | + | ||
| 93 | private void calculateDeliveryPrice(List<OrderItem> selectedSkus, | 117 | private void calculateDeliveryPrice(List<OrderItem> selectedSkus, |
| 94 | Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap, | 118 | Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap, |
| 95 | TradePriceCalculateRespBO result) { | 119 | TradePriceCalculateRespBO result) { |
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculatorTest.java
| @@ -4,8 +4,10 @@ import cn.hutool.core.map.MapUtil; | @@ -4,8 +4,10 @@ import cn.hutool.core.map.MapUtil; | ||
| 4 | import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | 4 | import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; |
| 5 | import cn.iocoder.yudao.module.member.api.address.AddressApi; | 5 | import cn.iocoder.yudao.module.member.api.address.AddressApi; |
| 6 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; | 6 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; |
| 7 | +import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO; | ||
| 7 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; | 8 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; |
| 8 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; | 9 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 10 | +import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; | ||
| 9 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; | 11 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; |
| 10 | import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; | 12 | import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; |
| 11 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; | 13 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| @@ -34,10 +36,14 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { | @@ -34,10 +36,14 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { | ||
| 34 | 36 | ||
| 35 | @InjectMocks | 37 | @InjectMocks |
| 36 | private TradeDeliveryPriceCalculator calculator; | 38 | private TradeDeliveryPriceCalculator calculator; |
| 39 | + | ||
| 37 | @Mock | 40 | @Mock |
| 38 | private AddressApi addressApi; | 41 | private AddressApi addressApi; |
| 42 | + | ||
| 39 | @Mock | 43 | @Mock |
| 40 | private DeliveryExpressTemplateService deliveryExpressTemplateService; | 44 | private DeliveryExpressTemplateService deliveryExpressTemplateService; |
| 45 | + @Mock | ||
| 46 | + private TradeConfigService tradeConfigService; | ||
| 41 | 47 | ||
| 42 | private TradePriceCalculateReqBO reqBO; | 48 | private TradePriceCalculateReqBO reqBO; |
| 43 | private TradePriceCalculateRespBO resultBO; | 49 | private TradePriceCalculateRespBO resultBO; |
| @@ -90,8 +96,36 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { | @@ -90,8 +96,36 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { | ||
| 90 | } | 96 | } |
| 91 | 97 | ||
| 92 | @Test | 98 | @Test |
| 99 | + @DisplayName("全场包邮") | ||
| 100 | + public void testCalculate_expressGlobalFree() { | ||
| 101 | + // mock 方法(全场包邮) | ||
| 102 | + when(tradeConfigService.getTradeConfig()).thenReturn(new TradeConfigDO().setDeliveryExpressFreeEnabled(true) | ||
| 103 | + .setDeliveryExpressFreePrice(2200)); | ||
| 104 | + | ||
| 105 | + // 调用 | ||
| 106 | + calculator.calculate(reqBO, resultBO); | ||
| 107 | + TradePriceCalculateRespBO.Price price = resultBO.getPrice(); | ||
| 108 | + assertThat(price) | ||
| 109 | + .extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice") | ||
| 110 | + .containsExactly(2200, 0, 0, 0, 0, 2200); | ||
| 111 | + assertThat(resultBO.getItems()).hasSize(3); | ||
| 112 | + // 断言:SKU1 | ||
| 113 | + assertThat(resultBO.getItems().get(0)) | ||
| 114 | + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") | ||
| 115 | + .containsExactly(100, 2, 0, 0, 0, 0, 200); | ||
| 116 | + // 断言:SKU2 | ||
| 117 | + assertThat(resultBO.getItems().get(1)) | ||
| 118 | + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") | ||
| 119 | + .containsExactly(200, 10, 0, 0, 0, 0, 2000); | ||
| 120 | + // 断言:SKU3 未选中 | ||
| 121 | + assertThat(resultBO.getItems().get(2)) | ||
| 122 | + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice") | ||
| 123 | + .containsExactly(300, 1, 0, 0, 0, 0, 300); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + @Test | ||
| 93 | @DisplayName("按件计算运费不包邮的情况") | 127 | @DisplayName("按件计算运费不包邮的情况") |
| 94 | - public void testCalculateByExpressTemplateCharge() { | 128 | + public void testCalculate_expressTemplateCharge() { |
| 95 | // SKU 1 : 100 * 2 = 200 | 129 | // SKU 1 : 100 * 2 = 200 |
| 96 | // SKU 2 :200 * 10 = 2000 | 130 | // SKU 2 :200 * 10 = 2000 |
| 97 | // 运费 首件 1000 + 续件 2000 = 3000 | 131 | // 运费 首件 1000 + 续件 2000 = 3000 |
| @@ -123,7 +157,7 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { | @@ -123,7 +157,7 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { | ||
| 123 | 157 | ||
| 124 | @Test | 158 | @Test |
| 125 | @DisplayName("按件计算运费包邮的情况") | 159 | @DisplayName("按件计算运费包邮的情况") |
| 126 | - public void testCalculateByExpressTemplateFree() { | 160 | + public void testCalculate_expressTemplateFree() { |
| 127 | // SKU 1 : 100 * 2 = 200 | 161 | // SKU 1 : 100 * 2 = 200 |
| 128 | // SKU 2 :200 * 10 = 2000 | 162 | // SKU 2 :200 * 10 = 2000 |
| 129 | // 运费 0 | 163 | // 运费 0 |