|
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.lang.Assert;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|
|
import cn.iocoder.yudao.module.member.api.address.AddressApi;
|
|
|
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
|
@@ -55,13 +56,13 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
|
|
return;
|
|
|
}
|
|
|
if (DeliveryTypeEnum.PICK_UP.getType().equals(param.getDeliveryType())) {
|
|
|
- calculateByPickUp(param, result);
|
|
|
+ calculateByPickUp(param);
|
|
|
} else if (DeliveryTypeEnum.EXPRESS.getType().equals(param.getDeliveryType())) {
|
|
|
calculateExpress(param, result);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void calculateByPickUp(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
|
|
+ private void calculateByPickUp(TradePriceCalculateReqBO param) {
|
|
|
if (param.getPickUpStoreId() == null) {
|
|
|
throw exception(PRICE_CALCULATE_DELIVERY_PRICE_PICK_UP_STORE_IS_EMPTY);
|
|
|
}
|
|
@@ -82,12 +83,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
|
|
Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId());
|
|
|
|
|
|
// 情况一:全局包邮
|
|
|
- if (isGlobalExpressFree(param, result)) {
|
|
|
+ if (isGlobalExpressFree(result)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 情况二:
|
|
|
- // 2.1 过滤出已选中的商品SKU
|
|
|
+ // 情况二:快递模版
|
|
|
+ // 2.1 过滤出已选中的商品 SKU
|
|
|
List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
|
|
|
Set<Long> deliveryTemplateIds = convertSet(selectedItem, OrderItem::getDeliveryTemplateId);
|
|
|
Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap =
|
|
@@ -103,11 +104,10 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
|
|
/**
|
|
|
* 是否全局包邮
|
|
|
*
|
|
|
- * @param param 计算信息
|
|
|
* @param result 计算结果
|
|
|
* @return 是否包邮
|
|
|
*/
|
|
|
- private boolean isGlobalExpressFree(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
|
|
+ private boolean isGlobalExpressFree(TradePriceCalculateRespBO result) {
|
|
|
TradeConfigDO config = tradeConfigService.getTradeConfig();
|
|
|
return config != null
|
|
|
&& Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) // 开启包邮
|
|
@@ -118,9 +118,9 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
|
|
Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap,
|
|
|
TradePriceCalculateRespBO result) {
|
|
|
// 按商品运费模板来计算商品的运费:相同的运费模板可能对应多条订单商品 SKU
|
|
|
- Map<Long, List<OrderItem>> tplIdItemMap = convertMultiMap(selectedSkus, OrderItem::getDeliveryTemplateId);
|
|
|
+ Map<Long, List<OrderItem>> template2ItemMap = convertMultiMap(selectedSkus, OrderItem::getDeliveryTemplateId);
|
|
|
// 依次计算快递运费
|
|
|
- for (Map.Entry<Long, List<OrderItem>> entry : tplIdItemMap.entrySet()) {
|
|
|
+ for (Map.Entry<Long, List<OrderItem>> entry : template2ItemMap.entrySet()) {
|
|
|
Long templateId = entry.getKey();
|
|
|
List<OrderItem> orderItems = entry.getValue();
|
|
|
DeliveryExpressTemplateRespBO templateBO = expressTemplateMap.get(templateId);
|
|
@@ -128,30 +128,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
|
|
log.error("[calculateDeliveryPrice][不能计算快递运费,找不到 templateId({}) 对应的运费模板配置]", templateId);
|
|
|
continue;
|
|
|
}
|
|
|
- // 总件数, 总金额, 总重量, 总体积
|
|
|
- int totalCount = 0;
|
|
|
- int totalPrice = 0;
|
|
|
- double totalWeight = 0;
|
|
|
- double totalVolume = 0;
|
|
|
- for (OrderItem orderItem : orderItems) {
|
|
|
- totalCount += orderItem.getCount();
|
|
|
- totalPrice += orderItem.getPayPrice();
|
|
|
- if (orderItem.getWeight() != null) {
|
|
|
- totalWeight += totalWeight + orderItem.getWeight() * orderItem.getCount();
|
|
|
- }
|
|
|
- if (orderItem.getVolume() != null) {
|
|
|
- totalVolume += totalVolume + orderItem.getVolume() * orderItem.getCount();
|
|
|
- }
|
|
|
- }
|
|
|
- // 优先判断是否包邮. 如果包邮不计算快递运费
|
|
|
- if (isExpressFree(templateBO.getChargeMode(), totalCount, totalWeight,
|
|
|
- totalVolume, totalPrice, templateBO.getFree())) {
|
|
|
+ // 1. 优先判断是否包邮。如果包邮不计算快递运费
|
|
|
+ if (isExpressTemplateFree(orderItems, templateBO.getChargeMode(), templateBO.getFree())) {
|
|
|
continue;
|
|
|
}
|
|
|
- // 计算快递运费
|
|
|
- calculateExpressFeeByChargeMode(totalCount, totalWeight, totalVolume,
|
|
|
- templateBO.getChargeMode(), templateBO.getCharge(), orderItems);
|
|
|
-
|
|
|
+ // 2. 计算快递运费
|
|
|
+ calculateExpressFeeByChargeMode(orderItems, templateBO.getChargeMode(), templateBO.getCharge());
|
|
|
}
|
|
|
TradePriceCalculatorHelper.recountAllPrice(result);
|
|
|
}
|
|
@@ -159,73 +141,44 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
|
|
/**
|
|
|
* 按配送方式来计算运费
|
|
|
*
|
|
|
- * @param totalCount 总件数
|
|
|
- * @param totalWeight 总重量
|
|
|
- * @param totalVolume 总体积
|
|
|
+ * @param orderItems SKU 商品项目
|
|
|
* @param chargeMode 配送计费方式
|
|
|
* @param templateCharge 快递运费配置
|
|
|
- * @param orderItems SKU 商品项目
|
|
|
*/
|
|
|
- private void calculateExpressFeeByChargeMode(double totalCount, double totalWeight, double totalVolume,
|
|
|
- int chargeMode, DeliveryExpressTemplateRespBO.Charge templateCharge,
|
|
|
- List<OrderItem> orderItems) {
|
|
|
+ private void calculateExpressFeeByChargeMode(List<OrderItem> orderItems, Integer chargeMode,
|
|
|
+ DeliveryExpressTemplateRespBO.Charge templateCharge) {
|
|
|
if (templateCharge == null) {
|
|
|
log.error("[calculateExpressFeeByChargeMode][计算快递运费时,找不到 SKU({}) 对应的运费模版]", orderItems);
|
|
|
return;
|
|
|
}
|
|
|
- DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
|
|
|
- switch (chargeModeEnum) {
|
|
|
- case PIECE: {
|
|
|
- calculateExpressFee(totalCount, templateCharge, orderItems);
|
|
|
- break;
|
|
|
- }
|
|
|
- case WEIGHT: {
|
|
|
- calculateExpressFee(totalWeight, templateCharge, orderItems);
|
|
|
- break;
|
|
|
- }
|
|
|
- case VOLUME: {
|
|
|
- calculateExpressFee(totalVolume, templateCharge, orderItems);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 计算 SKU 商品快递费用
|
|
|
- *
|
|
|
- * @param total 总件数/总重量/总体积
|
|
|
- * @param templateCharge 快递运费配置
|
|
|
- * @param orderItems SKU 商品项目
|
|
|
- */
|
|
|
- private void calculateExpressFee(double total, DeliveryExpressTemplateRespBO.Charge templateCharge, List<OrderItem> orderItems) {
|
|
|
+ double totalChargeValue = getTotalChargeValue(orderItems, chargeMode);
|
|
|
+ // 1. 计算 SKU 商品快递费用
|
|
|
int deliveryPrice;
|
|
|
- if (total <= templateCharge.getStartCount()) {
|
|
|
+ if (totalChargeValue <= templateCharge.getStartCount()) {
|
|
|
deliveryPrice = templateCharge.getStartPrice();
|
|
|
} else {
|
|
|
- double remainWeight = total - templateCharge.getStartCount();
|
|
|
+ double remainWeight = totalChargeValue - templateCharge.getStartCount();
|
|
|
// 剩余重量/ 续件 = 续件的次数. 向上取整
|
|
|
int extraNum = (int) Math.ceil(remainWeight / templateCharge.getExtraCount());
|
|
|
int extraPrice = templateCharge.getExtraPrice() * extraNum;
|
|
|
deliveryPrice = templateCharge.getStartPrice() + extraPrice;
|
|
|
}
|
|
|
- // 分摊快递费用到 SKU. 退费的时候,可能按照 SKU 考虑退费金额
|
|
|
- divideDeliveryPrice(deliveryPrice, orderItems);
|
|
|
- }
|
|
|
|
|
|
- /**
|
|
|
- * 快递运费分摊到每个 SKU 商品上
|
|
|
- *
|
|
|
- * @param deliveryPrice 快递运费
|
|
|
- * @param orderItems SKU 商品
|
|
|
- */
|
|
|
- private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) {
|
|
|
- // TODO @jason:分摊的话,是不是要按照比例呀?重量、价格、数量等等,
|
|
|
- // 按比例是不是有点复杂。后面看看是否需要;
|
|
|
- // TODO 可以看看别的项目怎么搞的哈。
|
|
|
- int dividePrice = deliveryPrice / orderItems.size();
|
|
|
- for (OrderItem item : orderItems) {
|
|
|
+ // 2. 分摊快递费用到 SKU. 退费的时候,可能按照 SKU 考虑退费金额
|
|
|
+ int remainPrice = deliveryPrice;
|
|
|
+ for (int i = 0; i < orderItems.size(); i++) {
|
|
|
+ TradePriceCalculateRespBO.OrderItem item = orderItems.get(i);
|
|
|
+ int partPrice;
|
|
|
+ double chargeValue = getChargeValue(item, chargeMode);
|
|
|
+ if (i < orderItems.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减
|
|
|
+ partPrice = (int) (deliveryPrice * (chargeValue / totalChargeValue));
|
|
|
+ remainPrice -= partPrice;
|
|
|
+ } else {
|
|
|
+ partPrice = remainPrice;
|
|
|
+ }
|
|
|
+ Assert.isTrue(partPrice >= 0, "分摊金额必须大于等于 0");
|
|
|
// 更新快递运费
|
|
|
- item.setDeliveryPrice(dividePrice);
|
|
|
+ item.setDeliveryPrice(partPrice);
|
|
|
TradePriceCalculatorHelper.recountPayPrice(item);
|
|
|
}
|
|
|
}
|
|
@@ -234,42 +187,38 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
|
|
* 检查是否包邮
|
|
|
*
|
|
|
* @param chargeMode 配送计费方式
|
|
|
- * @param totalCount 总件数
|
|
|
- * @param totalWeight 总重量
|
|
|
- * @param totalVolume 总体积
|
|
|
- * @param totalPrice 总金额
|
|
|
* @param templateFree 包邮配置
|
|
|
*/
|
|
|
- private boolean isExpressFree(Integer chargeMode, int totalCount, double totalWeight,
|
|
|
- double totalVolume, int totalPrice, DeliveryExpressTemplateRespBO.Free templateFree) {
|
|
|
+ private boolean isExpressTemplateFree(List<OrderItem> orderItems, Integer chargeMode,
|
|
|
+ DeliveryExpressTemplateRespBO.Free templateFree) {
|
|
|
if (templateFree == null) {
|
|
|
return false;
|
|
|
}
|
|
|
+ double totalChargeValue = getTotalChargeValue(orderItems, chargeMode);
|
|
|
+ double totalPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
|
|
|
+ return totalChargeValue >= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice();
|
|
|
+ }
|
|
|
+
|
|
|
+ private double getTotalChargeValue(List<OrderItem> orderItems, Integer chargeMode) {
|
|
|
+ double total = 0;
|
|
|
+ for (OrderItem orderItem : orderItems) {
|
|
|
+ total += getChargeValue(orderItem, chargeMode);
|
|
|
+ }
|
|
|
+ return total;
|
|
|
+ }
|
|
|
+
|
|
|
+ private double getChargeValue(OrderItem orderItem, Integer chargeMode) {
|
|
|
DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
|
|
|
switch (chargeModeEnum) {
|
|
|
- case PIECE:
|
|
|
- // 两个条件都满足才包邮
|
|
|
- if (totalCount >= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice()) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- break;
|
|
|
+ case COUNT:
|
|
|
+ return orderItem.getCount();
|
|
|
case WEIGHT:
|
|
|
- // freeCount 是不是应该是 double ??
|
|
|
- // TODO @jason:要不配置的时候,把它的单位和商品对齐?到底是 kg、还是斤
|
|
|
- // TODO @芋艿 目前 包邮 件数/重量/体积 都用的是这个字段
|
|
|
- // TODO @jason:那要不快递模版也改成 kg?这样是不是就不用 double ?
|
|
|
- if (totalWeight >= templateFree.getFreeCount()
|
|
|
- && totalPrice >= templateFree.getFreePrice()) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- break;
|
|
|
+ return orderItem.getWeight() != null ? orderItem.getWeight() * orderItem.getCount() : 0;
|
|
|
case VOLUME:
|
|
|
- if (totalVolume >= templateFree.getFreeCount()
|
|
|
- && totalPrice >= templateFree.getFreePrice()) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- break;
|
|
|
+ return orderItem.getVolume() != null ? orderItem.getVolume() * orderItem.getCount() : 0;
|
|
|
+ default:
|
|
|
+ throw new IllegalArgumentException(StrUtil.format("未知的计费模式({})", chargeMode));
|
|
|
}
|
|
|
- return false;
|
|
|
}
|
|
|
+
|
|
|
}
|