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 | 35 | @TableId |
| 36 | 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 | 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 6 | 6 | import cn.iocoder.yudao.module.member.api.address.AddressApi; |
| 7 | 7 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; |
| 8 | +import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO; | |
| 8 | 9 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; |
| 9 | 10 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; |
| 10 | 11 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 12 | +import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; | |
| 11 | 13 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; |
| 12 | 14 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService; |
| 13 | 15 | import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; |
| ... | ... | @@ -44,6 +46,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { |
| 44 | 46 | private DeliveryPickUpStoreService deliveryPickUpStoreService; |
| 45 | 47 | @Resource |
| 46 | 48 | private DeliveryExpressTemplateService deliveryExpressTemplateService; |
| 49 | + @Resource | |
| 50 | + private TradeConfigService tradeConfigService; | |
| 47 | 51 | |
| 48 | 52 | @Override |
| 49 | 53 | public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { |
| ... | ... | @@ -70,19 +74,25 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { |
| 70 | 74 | // ========= 快递发货 ========== |
| 71 | 75 | |
| 72 | 76 | private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { |
| 73 | - // 1. 得到收件地址区域 | |
| 77 | + // 0. 得到收件地址区域 | |
| 74 | 78 | if (param.getAddressId() == null) { |
| 75 | 79 | throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY); |
| 76 | 80 | } |
| 77 | 81 | AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); |
| 78 | 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 | 91 | List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); |
| 82 | 92 | Set<Long> deliveryTemplateIds = convertSet(selectedItem, OrderItem::getDeliveryTemplateId); |
| 83 | 93 | Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap = |
| 84 | 94 | deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(deliveryTemplateIds, address.getAreaId()); |
| 85 | - // 3. 计算配送费用 | |
| 95 | + // 2.2 计算配送费用 | |
| 86 | 96 | if (CollUtil.isEmpty(expressTemplateMap)) { |
| 87 | 97 | log.error("[calculate][找不到商品 templateIds {} areaId{} 对应的运费模板]", deliveryTemplateIds, address.getAreaId()); |
| 88 | 98 | throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND); |
| ... | ... | @@ -90,6 +100,20 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { |
| 90 | 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 | 117 | private void calculateDeliveryPrice(List<OrderItem> selectedSkus, |
| 94 | 118 | Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap, |
| 95 | 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 | 4 | import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; |
| 5 | 5 | import cn.iocoder.yudao.module.member.api.address.AddressApi; |
| 6 | 6 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; |
| 7 | +import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO; | |
| 7 | 8 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; |
| 8 | 9 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 10 | +import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; | |
| 9 | 11 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; |
| 10 | 12 | import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; |
| 11 | 13 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| ... | ... | @@ -34,10 +36,14 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { |
| 34 | 36 | |
| 35 | 37 | @InjectMocks |
| 36 | 38 | private TradeDeliveryPriceCalculator calculator; |
| 39 | + | |
| 37 | 40 | @Mock |
| 38 | 41 | private AddressApi addressApi; |
| 42 | + | |
| 39 | 43 | @Mock |
| 40 | 44 | private DeliveryExpressTemplateService deliveryExpressTemplateService; |
| 45 | + @Mock | |
| 46 | + private TradeConfigService tradeConfigService; | |
| 41 | 47 | |
| 42 | 48 | private TradePriceCalculateReqBO reqBO; |
| 43 | 49 | private TradePriceCalculateRespBO resultBO; |
| ... | ... | @@ -90,8 +96,36 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { |
| 90 | 96 | } |
| 91 | 97 | |
| 92 | 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 | 127 | @DisplayName("按件计算运费不包邮的情况") |
| 94 | - public void testCalculateByExpressTemplateCharge() { | |
| 128 | + public void testCalculate_expressTemplateCharge() { | |
| 95 | 129 | // SKU 1 : 100 * 2 = 200 |
| 96 | 130 | // SKU 2 :200 * 10 = 2000 |
| 97 | 131 | // 运费 首件 1000 + 续件 2000 = 3000 |
| ... | ... | @@ -123,7 +157,7 @@ public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest { |
| 123 | 157 | |
| 124 | 158 | @Test |
| 125 | 159 | @DisplayName("按件计算运费包邮的情况") |
| 126 | - public void testCalculateByExpressTemplateFree() { | |
| 160 | + public void testCalculate_expressTemplateFree() { | |
| 127 | 161 | // SKU 1 : 100 * 2 = 200 |
| 128 | 162 | // SKU 2 :200 * 10 = 2000 |
| 129 | 163 | // 运费 0 | ... | ... |