Commit 25bea4920c05daa4b9004475aee2c851630e263d
1 parent
f2d803c1
mall + trade:review 物流计算
Showing
4 changed files
with
35 additions
and
20 deletions
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java
| @@ -83,10 +83,11 @@ public interface DeliveryExpressTemplateService { | @@ -83,10 +83,11 @@ public interface DeliveryExpressTemplateService { | ||
| 83 | */ | 83 | */ |
| 84 | DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); | 84 | DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); |
| 85 | 85 | ||
| 86 | + // TODO @jason 方法名可以改成 getExpressTemplateMapBySpuIdsAndArea | ||
| 86 | /** | 87 | /** |
| 87 | * 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板 | 88 | * 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板 |
| 88 | * | 89 | * |
| 89 | - * @param ids SPU 编号列表 | 90 | + * @param ids SPU 编号列表 // TODO @jason:模版编号? |
| 90 | * @param areaId 区域编号 | 91 | * @param areaId 区域编号 |
| 91 | * @return Map (spuId -> 运费模板设置) | 92 | * @return Map (spuId -> 运费模板设置) |
| 92 | */ | 93 | */ |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java
| @@ -5,7 +5,10 @@ import cn.hutool.core.lang.Assert; | @@ -5,7 +5,10 @@ import cn.hutool.core.lang.Assert; | ||
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | 6 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| 7 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; | 7 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; |
| 8 | -import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*; | 8 | +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO; |
| 9 | +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateDetailRespVO; | ||
| 10 | +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO; | ||
| 11 | +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO; | ||
| 9 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; | 12 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; |
| 10 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; | 13 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; |
| 11 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; | 14 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; |
| @@ -234,14 +237,16 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | @@ -234,14 +237,16 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | ||
| 234 | List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet()); | 237 | List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet()); |
| 235 | Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size()); | 238 | Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size()); |
| 236 | templateList.forEach(item -> { | 239 | templateList.forEach(item -> { |
| 240 | + // TODO @jason:if return ,更简洁哈; | ||
| 237 | if (spuMap.containsKey(item.getId())) { | 241 | if (spuMap.containsKey(item.getId())) { |
| 238 | - ProductSpuRespDTO spuDTO = spuMap.get(item.getId()); | 242 | + ProductSpuRespDTO spu = spuMap.get(item.getId()); |
| 239 | SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO() | 243 | SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO() |
| 240 | - .setSpuId(spuDTO.getId()).setAreaId(areaId) | 244 | + .setSpuId(spu.getId()).setAreaId(areaId) |
| 241 | .setChargeMode(item.getChargeMode()) | 245 | .setChargeMode(item.getChargeMode()) |
| 246 | + // TODO @jason:是不是只要查询到一个,就不用查询下一个了;TemplateCharge 和 TemplateFree | ||
| 242 | .setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId)) | 247 | .setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId)) |
| 243 | .setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId)); | 248 | .setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId)); |
| 244 | - result.put(spuDTO.getId(), bo); | 249 | + result.put(spu.getId(), bo); |
| 245 | } | 250 | } |
| 246 | }); | 251 | }); |
| 247 | return result; | 252 | return result; |
| @@ -249,6 +254,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | @@ -249,6 +254,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | ||
| 249 | 254 | ||
| 250 | private DeliveryExpressTemplateChargeDO findMatchExpressTemplateCharge(Long templateId, Integer areaId) { | 255 | private DeliveryExpressTemplateChargeDO findMatchExpressTemplateCharge(Long templateId, Integer areaId) { |
| 251 | List<DeliveryExpressTemplateChargeDO> list = expressTemplateChargeMapper.selectListByTemplateId(templateId); | 256 | List<DeliveryExpressTemplateChargeDO> list = expressTemplateChargeMapper.selectListByTemplateId(templateId); |
| 257 | + // TODO @jason:可以使用 CollectionUtils.findFirst() | ||
| 252 | for (DeliveryExpressTemplateChargeDO item : list) { | 258 | for (DeliveryExpressTemplateChargeDO item : list) { |
| 253 | // 第一个匹配的返回。 areaId 不能重复 | 259 | // 第一个匹配的返回。 areaId 不能重复 |
| 254 | if (item.getAreaIds().contains(areaId)) { | 260 | if (item.getAreaIds().contains(areaId)) { |
| @@ -260,6 +266,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | @@ -260,6 +266,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | ||
| 260 | 266 | ||
| 261 | private DeliveryExpressTemplateFreeDO findMatchExpressTemplateFree(Long templateId, Integer areaId) { | 267 | private DeliveryExpressTemplateFreeDO findMatchExpressTemplateFree(Long templateId, Integer areaId) { |
| 262 | List<DeliveryExpressTemplateFreeDO> list = expressTemplateFreeMapper.selectListByTemplateId(templateId); | 268 | List<DeliveryExpressTemplateFreeDO> list = expressTemplateFreeMapper.selectListByTemplateId(templateId); |
| 269 | + // TODO @jason:可以使用 CollectionUtils.findFirst() | ||
| 263 | for (DeliveryExpressTemplateFreeDO item : list) { | 270 | for (DeliveryExpressTemplateFreeDO item : list) { |
| 264 | // 第一个匹配的返回。 areaId 不能重复 | 271 | // 第一个匹配的返回。 areaId 不能重复 |
| 265 | if (item.getAreaIds().contains(areaId)) { | 272 | if (item.getAreaIds().contains(areaId)) { |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/SpuDeliveryExpressTemplateRespBO.java
| @@ -13,9 +13,11 @@ import lombok.Data; | @@ -13,9 +13,11 @@ import lombok.Data; | ||
| 13 | @Data | 13 | @Data |
| 14 | public class SpuDeliveryExpressTemplateRespBO { | 14 | public class SpuDeliveryExpressTemplateRespBO { |
| 15 | 15 | ||
| 16 | + // TODO @jason:是不是可以简单一点;是否包邮;然后直接把 templateCharge、templateFree 需要的字段平铺开 | ||
| 17 | + | ||
| 16 | /** | 18 | /** |
| 17 | * 配送计费方式 | 19 | * 配送计费方式 |
| 18 | - * <p> | 20 | + * |
| 19 | * 枚举 {@link DeliveryExpressChargeModeEnum} | 21 | * 枚举 {@link DeliveryExpressChargeModeEnum} |
| 20 | */ | 22 | */ |
| 21 | private Integer chargeMode; | 23 | private Integer chargeMode; |
| @@ -30,6 +32,7 @@ public class SpuDeliveryExpressTemplateRespBO { | @@ -30,6 +32,7 @@ public class SpuDeliveryExpressTemplateRespBO { | ||
| 30 | */ | 32 | */ |
| 31 | private DeliveryExpressTemplateFreeDO templateFree; | 33 | private DeliveryExpressTemplateFreeDO templateFree; |
| 32 | 34 | ||
| 35 | + // TODO @jason:下面两个字段不用返回也可以呀 | ||
| 33 | /** | 36 | /** |
| 34 | * SPU 编号 | 37 | * SPU 编号 |
| 35 | * <p> | 38 | * <p> |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
| @@ -54,33 +54,34 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | @@ -54,33 +54,34 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | ||
| 54 | AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); | 54 | AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); |
| 55 | Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); | 55 | Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); |
| 56 | 56 | ||
| 57 | - //1.3 过滤出已选中的商品SKU | 57 | + // 2. 过滤出已选中的商品SKU |
| 58 | List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); | 58 | List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); |
| 59 | - | 59 | + Set<Long> spuIds = convertSet(selectedItem, OrderItem::getSpuId); |
| 60 | Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap = | 60 | Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap = |
| 61 | - deliveryExpressTemplateService.getExpressTemplateBySpuIdsAndArea( | ||
| 62 | - convertSet(selectedItem, OrderItem::getSpuId), address.getAreaId()); | 61 | + deliveryExpressTemplateService.getExpressTemplateBySpuIdsAndArea(spuIds, address.getAreaId()); |
| 63 | 62 | ||
| 64 | - // 1.4 计算配送费用 | 63 | + // 3. 计算配送费用 |
| 64 | + // TODO @jason:这里应该不能判断空;如果找不到模版,就要报错;不然配送费就亏了 | ||
| 65 | if (CollUtil.isNotEmpty(spuExpressTemplateMap)) { | 65 | if (CollUtil.isNotEmpty(spuExpressTemplateMap)) { |
| 66 | calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result); | 66 | calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result); |
| 67 | } | 67 | } |
| 68 | - | ||
| 69 | } | 68 | } |
| 70 | 69 | ||
| 71 | private void calculateDeliveryPrice(List<OrderItem> selectedSkus, | 70 | private void calculateDeliveryPrice(List<OrderItem> selectedSkus, |
| 72 | Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap, | 71 | Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap, |
| 73 | TradePriceCalculateRespBO result) { | 72 | TradePriceCalculateRespBO result) { |
| 73 | + // 得到 SKU 详情 | ||
| 74 | + // TODO @jason:可以去掉这里的读取;在 TradePriceCalculateRespBO 初始化的时候,把 weight、volume 拿到 | ||
| 74 | Set<Long> skuIds = convertSet(selectedSkus, OrderItem::getSkuId); | 75 | Set<Long> skuIds = convertSet(selectedSkus, OrderItem::getSkuId); |
| 75 | - // 得到SKU 详情。得到 重量体积 | ||
| 76 | Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId); | 76 | Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId); |
| 77 | - // 按spu 来计算商品的运费 一个 spuId 可能对应多条订单商品 SKU, | 77 | + // 按 SPU 来计算商品的运费:一个 spuId 可能对应多条订单商品 SKU |
| 78 | Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId); | 78 | Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId); |
| 79 | 79 | ||
| 80 | // 依次计算每个 SPU 的快递运费 | 80 | // 依次计算每个 SPU 的快递运费 |
| 81 | for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) { | 81 | for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) { |
| 82 | Long spuId = entry.getKey(); | 82 | Long spuId = entry.getKey(); |
| 83 | List<OrderItem> orderItems = entry.getValue(); | 83 | List<OrderItem> orderItems = entry.getValue(); |
| 84 | + // TODO @jason:如果找不到,则打印 error log | ||
| 84 | SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId); | 85 | SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId); |
| 85 | if (templateBO == null) { | 86 | if (templateBO == null) { |
| 86 | // 记录错误日志 | 87 | // 记录错误日志 |
| @@ -93,18 +94,18 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | @@ -93,18 +94,18 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | ||
| 93 | double totalVolume = 0; | 94 | double totalVolume = 0; |
| 94 | for (OrderItem orderItem : orderItems) { | 95 | for (OrderItem orderItem : orderItems) { |
| 95 | totalCount += orderItem.getCount(); | 96 | totalCount += orderItem.getCount(); |
| 96 | - totalPrice += orderItem.getPayPrice(); // 先按应付总金额来算,后面确认一下 | 97 | + totalPrice += orderItem.getPayPrice(); // 先按应付总金额来算,后面确认一下 TODO @jason:是的哈 |
| 97 | ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId()); | 98 | ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId()); |
| 98 | - if (skuResp != null) { | ||
| 99 | - totalWeight = totalWeight + skuResp.getWeight() * orderItem.getCount(); | ||
| 100 | - totalVolume = totalVolume + skuResp.getVolume() * orderItem.getCount(); | ||
| 101 | - } | 99 | + // TODO @jason:是不是要保持风格统一,都用 += |
| 100 | + totalWeight = totalWeight + skuResp.getWeight() * orderItem.getCount(); | ||
| 101 | + totalVolume = totalVolume + skuResp.getVolume() * orderItem.getCount(); | ||
| 102 | } | 102 | } |
| 103 | // 优先判断是否包邮. 如果包邮不计算快递运费 | 103 | // 优先判断是否包邮. 如果包邮不计算快递运费 |
| 104 | if (checkExpressFree(templateBO.getChargeMode(), totalCount, totalWeight, | 104 | if (checkExpressFree(templateBO.getChargeMode(), totalCount, totalWeight, |
| 105 | totalVolume, totalPrice, templateBO.getTemplateFree())) { | 105 | totalVolume, totalPrice, templateBO.getTemplateFree())) { |
| 106 | continue; | 106 | continue; |
| 107 | } | 107 | } |
| 108 | + // TODO @jason:这块判断,可以收到 calculateExpressFeeByChargeMode 里;另外找不到,要打 error log | ||
| 108 | if (templateBO.getTemplateCharge() == null) { | 109 | if (templateBO.getTemplateCharge() == null) { |
| 109 | continue; | 110 | continue; |
| 110 | } | 111 | } |
| @@ -175,6 +176,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | @@ -175,6 +176,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | ||
| 175 | * @param orderItems SKU 商品 | 176 | * @param orderItems SKU 商品 |
| 176 | */ | 177 | */ |
| 177 | private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) { | 178 | private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) { |
| 179 | + // TODO @jason:分摊的话,是不是要按照比例呀?重量、价格、数量等等 | ||
| 178 | int dividePrice = deliveryPrice / orderItems.size(); | 180 | int dividePrice = deliveryPrice / orderItems.size(); |
| 179 | for (OrderItem item : orderItems) { | 181 | for (OrderItem item : orderItems) { |
| 180 | // 更新快递运费 | 182 | // 更新快递运费 |
| @@ -192,6 +194,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | @@ -192,6 +194,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | ||
| 192 | * @param totalPrice 总金额 | 194 | * @param totalPrice 总金额 |
| 193 | * @param templateFree 包邮配置 | 195 | * @param templateFree 包邮配置 |
| 194 | */ | 196 | */ |
| 197 | + // TODO @jason:isExpressFree 更合适;因为 check 是一种校验,往往抛出异常; | ||
| 195 | private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight, | 198 | private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight, |
| 196 | double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) { | 199 | double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) { |
| 197 | if (templateFree == null) { | 200 | if (templateFree == null) { |
| @@ -206,7 +209,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | @@ -206,7 +209,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | ||
| 206 | } | 209 | } |
| 207 | break; | 210 | break; |
| 208 | case WEIGHT: | 211 | case WEIGHT: |
| 209 | - // freeCount 是不是应该是 double ?? | 212 | + // freeCount 是不是应该是 double ?? |
| 213 | + // TODO @jason:要不配置的时候,把它的单位和商品对齐?到底是 kg、还是斤 | ||
| 210 | if (totalWeight >= templateFree.getFreeCount() | 214 | if (totalWeight >= templateFree.getFreeCount() |
| 211 | && totalPrice >= templateFree.getFreePrice()) { | 215 | && totalPrice >= templateFree.getFreePrice()) { |
| 212 | return true; | 216 | return true; |