mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 02:28:03 +08:00 
			
		
		
		
	mall + trade:review 物流计算
This commit is contained in:
		| @ -83,10 +83,11 @@ public interface DeliveryExpressTemplateService { | |||||||
|      */ |      */ | ||||||
|     DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); |     DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); | ||||||
|  |  | ||||||
|  |     // TODO @jason 方法名可以改成 getExpressTemplateMapBySpuIdsAndArea | ||||||
|     /** |     /** | ||||||
|      * 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板 |      * 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板 | ||||||
|      * |      * | ||||||
|      * @param ids    SPU 编号列表 |      * @param ids    SPU 编号列表 // TODO @jason:模版编号? | ||||||
|      * @param areaId 区域编号 |      * @param areaId 区域编号 | ||||||
|      * @return Map (spuId -> 运费模板设置) |      * @return Map (spuId -> 运费模板设置) | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -5,7 +5,10 @@ import cn.hutool.core.lang.Assert; | |||||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||||
| import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | ||||||
| import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; | ||||||
| import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.*; | import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO; | ||||||
|  | import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateDetailRespVO; | ||||||
|  | import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO; | ||||||
|  | import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO; | ||||||
| import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; | ||||||
| import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; | ||||||
| import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; | ||||||
| @ -234,14 +237,16 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | |||||||
|         List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet()); |         List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet()); | ||||||
|         Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size()); |         Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size()); | ||||||
|         templateList.forEach(item -> { |         templateList.forEach(item -> { | ||||||
|  |             // TODO @jason:if return ,更简洁哈; | ||||||
|             if (spuMap.containsKey(item.getId())) { |             if (spuMap.containsKey(item.getId())) { | ||||||
|                 ProductSpuRespDTO spuDTO = spuMap.get(item.getId()); |                 ProductSpuRespDTO spu = spuMap.get(item.getId()); | ||||||
|                 SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO() |                 SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO() | ||||||
|                         .setSpuId(spuDTO.getId()).setAreaId(areaId) |                         .setSpuId(spu.getId()).setAreaId(areaId) | ||||||
|                         .setChargeMode(item.getChargeMode()) |                         .setChargeMode(item.getChargeMode()) | ||||||
|  |                         // TODO @jason:是不是只要查询到一个,就不用查询下一个了;TemplateCharge 和 TemplateFree | ||||||
|                         .setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId)) |                         .setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId)) | ||||||
|                         .setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId)); |                         .setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId)); | ||||||
|                 result.put(spuDTO.getId(), bo); |                 result.put(spu.getId(), bo); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|         return result; |         return result; | ||||||
| @ -249,6 +254,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | |||||||
|  |  | ||||||
|     private DeliveryExpressTemplateChargeDO findMatchExpressTemplateCharge(Long templateId, Integer areaId) { |     private DeliveryExpressTemplateChargeDO findMatchExpressTemplateCharge(Long templateId, Integer areaId) { | ||||||
|         List<DeliveryExpressTemplateChargeDO> list = expressTemplateChargeMapper.selectListByTemplateId(templateId); |         List<DeliveryExpressTemplateChargeDO> list = expressTemplateChargeMapper.selectListByTemplateId(templateId); | ||||||
|  |         // TODO @jason:可以使用 CollectionUtils.findFirst() | ||||||
|         for (DeliveryExpressTemplateChargeDO item : list) { |         for (DeliveryExpressTemplateChargeDO item : list) { | ||||||
|             // 第一个匹配的返回。 areaId 不能重复 |             // 第一个匹配的返回。 areaId 不能重复 | ||||||
|             if (item.getAreaIds().contains(areaId)) { |             if (item.getAreaIds().contains(areaId)) { | ||||||
| @ -260,6 +266,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla | |||||||
|  |  | ||||||
|     private DeliveryExpressTemplateFreeDO findMatchExpressTemplateFree(Long templateId, Integer areaId) { |     private DeliveryExpressTemplateFreeDO findMatchExpressTemplateFree(Long templateId, Integer areaId) { | ||||||
|         List<DeliveryExpressTemplateFreeDO> list = expressTemplateFreeMapper.selectListByTemplateId(templateId); |         List<DeliveryExpressTemplateFreeDO> list = expressTemplateFreeMapper.selectListByTemplateId(templateId); | ||||||
|  |         // TODO @jason:可以使用 CollectionUtils.findFirst() | ||||||
|         for (DeliveryExpressTemplateFreeDO item : list) { |         for (DeliveryExpressTemplateFreeDO item : list) { | ||||||
|             // 第一个匹配的返回。 areaId 不能重复 |             // 第一个匹配的返回。 areaId 不能重复 | ||||||
|             if (item.getAreaIds().contains(areaId)) { |             if (item.getAreaIds().contains(areaId)) { | ||||||
|  | |||||||
| @ -13,9 +13,11 @@ import lombok.Data; | |||||||
| @Data | @Data | ||||||
| public class SpuDeliveryExpressTemplateRespBO { | public class SpuDeliveryExpressTemplateRespBO { | ||||||
|  |  | ||||||
|  |     // TODO @jason:是不是可以简单一点;是否包邮;然后直接把  templateCharge、templateFree 需要的字段平铺开 | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 配送计费方式 |      * 配送计费方式 | ||||||
|      * <p> |      * | ||||||
|      * 枚举 {@link DeliveryExpressChargeModeEnum} |      * 枚举 {@link DeliveryExpressChargeModeEnum} | ||||||
|      */ |      */ | ||||||
|     private Integer chargeMode; |     private Integer chargeMode; | ||||||
| @ -30,6 +32,7 @@ public class SpuDeliveryExpressTemplateRespBO { | |||||||
|      */ |      */ | ||||||
|     private DeliveryExpressTemplateFreeDO templateFree; |     private DeliveryExpressTemplateFreeDO templateFree; | ||||||
|  |  | ||||||
|  |     // TODO @jason:下面两个字段不用返回也可以呀 | ||||||
|     /** |     /** | ||||||
|      * SPU 编号 |      * SPU 编号 | ||||||
|      * <p> |      * <p> | ||||||
|  | |||||||
| @ -54,33 +54,34 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | |||||||
|         AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); |         AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); | ||||||
|         Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); |         Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); | ||||||
|  |  | ||||||
|         //1.3 过滤出已选中的商品SKU |         // 2. 过滤出已选中的商品SKU | ||||||
|         List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); |         List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); | ||||||
|  |         Set<Long> spuIds = convertSet(selectedItem, OrderItem::getSpuId); | ||||||
|         Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap = |         Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap = | ||||||
|                 deliveryExpressTemplateService.getExpressTemplateBySpuIdsAndArea( |                 deliveryExpressTemplateService.getExpressTemplateBySpuIdsAndArea(spuIds, address.getAreaId()); | ||||||
|                         convertSet(selectedItem, OrderItem::getSpuId), address.getAreaId()); |  | ||||||
|  |  | ||||||
|         // 1.4 计算配送费用 |         // 3. 计算配送费用 | ||||||
|  |         // TODO @jason:这里应该不能判断空;如果找不到模版,就要报错;不然配送费就亏了 | ||||||
|         if (CollUtil.isNotEmpty(spuExpressTemplateMap)) { |         if (CollUtil.isNotEmpty(spuExpressTemplateMap)) { | ||||||
|             calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result); |             calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void calculateDeliveryPrice(List<OrderItem> selectedSkus, |     private void calculateDeliveryPrice(List<OrderItem> selectedSkus, | ||||||
|                                         Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap, |                                         Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap, | ||||||
|                                         TradePriceCalculateRespBO result) { |                                         TradePriceCalculateRespBO result) { | ||||||
|  |         // 得到 SKU 详情 | ||||||
|  |         // TODO @jason:可以去掉这里的读取;在 TradePriceCalculateRespBO 初始化的时候,把 weight、volume 拿到 | ||||||
|         Set<Long> skuIds = convertSet(selectedSkus, OrderItem::getSkuId); |         Set<Long> skuIds = convertSet(selectedSkus, OrderItem::getSkuId); | ||||||
|         // 得到SKU 详情。得到 重量体积 |  | ||||||
|         Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId); |         Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId); | ||||||
|         // 按spu 来计算商品的运费 一个 spuId 可能对应多条订单商品 SKU, |         // 按 SPU 来计算商品的运费:一个 spuId 可能对应多条订单商品 SKU | ||||||
|         Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId); |         Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId); | ||||||
|  |  | ||||||
|         // 依次计算每个 SPU 的快递运费 |         // 依次计算每个 SPU 的快递运费 | ||||||
|         for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) { |         for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) { | ||||||
|             Long spuId  = entry.getKey(); |             Long spuId  = entry.getKey(); | ||||||
|             List<OrderItem> orderItems = entry.getValue(); |             List<OrderItem> orderItems = entry.getValue(); | ||||||
|  |             // TODO @jason:如果找不到,则打印 error log | ||||||
|             SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId); |             SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId); | ||||||
|             if (templateBO == null) { |             if (templateBO == null) { | ||||||
|                 // 记录错误日志 |                 // 记录错误日志 | ||||||
| @ -93,18 +94,18 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | |||||||
|             double totalVolume = 0; |             double totalVolume = 0; | ||||||
|             for (OrderItem orderItem : orderItems) { |             for (OrderItem orderItem : orderItems) { | ||||||
|                 totalCount += orderItem.getCount(); |                 totalCount += orderItem.getCount(); | ||||||
|                 totalPrice += orderItem.getPayPrice(); // 先按应付总金额来算,后面确认一下 |                 totalPrice += orderItem.getPayPrice(); // 先按应付总金额来算,后面确认一下 TODO @jason:是的哈 | ||||||
|                 ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId()); |                 ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId()); | ||||||
|                 if (skuResp != null) { |                 // TODO @jason:是不是要保持风格统一,都用 += | ||||||
|                     totalWeight = totalWeight + skuResp.getWeight() * orderItem.getCount(); |                 totalWeight = totalWeight + skuResp.getWeight() * orderItem.getCount(); | ||||||
|                     totalVolume = totalVolume + skuResp.getVolume() * orderItem.getCount(); |                 totalVolume = totalVolume + skuResp.getVolume() * orderItem.getCount(); | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             // 优先判断是否包邮. 如果包邮不计算快递运费 |             // 优先判断是否包邮. 如果包邮不计算快递运费 | ||||||
|             if (checkExpressFree(templateBO.getChargeMode(), totalCount, totalWeight, |             if (checkExpressFree(templateBO.getChargeMode(), totalCount, totalWeight, | ||||||
|                             totalVolume, totalPrice, templateBO.getTemplateFree())) { |                             totalVolume, totalPrice, templateBO.getTemplateFree())) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  |             // TODO @jason:这块判断,可以收到 calculateExpressFeeByChargeMode 里;另外找不到,要打 error log | ||||||
|             if (templateBO.getTemplateCharge() == null) { |             if (templateBO.getTemplateCharge() == null) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| @ -175,6 +176,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | |||||||
|      * @param orderItems    SKU 商品 |      * @param orderItems    SKU 商品 | ||||||
|      */ |      */ | ||||||
|     private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) { |     private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) { | ||||||
|  |         // TODO @jason:分摊的话,是不是要按照比例呀?重量、价格、数量等等 | ||||||
|         int dividePrice = deliveryPrice / orderItems.size(); |         int dividePrice = deliveryPrice / orderItems.size(); | ||||||
|         for (OrderItem item : orderItems) { |         for (OrderItem item : orderItems) { | ||||||
|             // 更新快递运费 |             // 更新快递运费 | ||||||
| @ -192,6 +194,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | |||||||
|      * @param totalPrice   总金额 |      * @param totalPrice   总金额 | ||||||
|      * @param templateFree 包邮配置 |      * @param templateFree 包邮配置 | ||||||
|      */ |      */ | ||||||
|  |     // TODO @jason:isExpressFree 更合适;因为 check 是一种校验,往往抛出异常; | ||||||
|     private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight, |     private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight, | ||||||
|                                      double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) { |                                      double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) { | ||||||
|         if (templateFree == null) { |         if (templateFree == null) { | ||||||
| @ -206,7 +209,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { | |||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|             case WEIGHT: |             case WEIGHT: | ||||||
|                 //  freeCount 是不是应该是 double ?? |                 // freeCount 是不是应该是 double ?? | ||||||
|  |                 // TODO @jason:要不配置的时候,把它的单位和商品对齐?到底是 kg、还是斤 | ||||||
|                 if (totalWeight >= templateFree.getFreeCount() |                 if (totalWeight >= templateFree.getFreeCount() | ||||||
|                         && totalPrice >= templateFree.getFreePrice()) { |                         && totalPrice >= templateFree.getFreePrice()) { | ||||||
|                     return true; |                     return true; | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV