Commit 66210e5ee2664a3c6cf20fe7f4be93c12d1ca911

Authored by YunaiV
2 parents bb8e44a2 dde89d51

Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

Showing 11 changed files with 304 additions and 95 deletions
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -50,8 +50,10 @@ public interface ErrorCodeConstants { @@ -50,8 +50,10 @@ public interface ErrorCodeConstants {
50 // TODO @jason:最好每个模块一段哈。express 一个;exmpresstemplate 一个;pickup 一个 50 // TODO @jason:最好每个模块一段哈。express 一个;exmpresstemplate 一个;pickup 一个
51 ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011003001, "已经存在该编码的快递公司"); 51 ErrorCode EXPRESS_CODE_DUPLICATE = new ErrorCode(1011003001, "已经存在该编码的快递公司");
52 ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011003002, "运费模板不存在"); 52 ErrorCode EXPRESS_TEMPLATE_NOT_EXISTS = new ErrorCode(1011003002, "运费模板不存在");
53 - ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011003002, "已经存在该运费模板名");  
54 - ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011003003, "自提门店不存在"); 53 + ErrorCode EXPRESS_TEMPLATE_NAME_DUPLICATE = new ErrorCode(1011003003, "已经存在该运费模板名");
  54 + ErrorCode DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY = new ErrorCode(1011003004, "计算快递运费时,收件人地址编号为空");
  55 + ErrorCode PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND = new ErrorCode(1011003005, "找不到到商品对应的运费模板");
  56 + ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011003006, "自提门店不存在");
55 57
56 // ========== Price 相关 1011004000 ============ 58 // ========== Price 相关 1011004000 ============
57 ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011004000, "支付价格计算异常,原因:价格小于等于 0"); 59 ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011004000, "支付价格计算异常,原因:价格小于等于 0");
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java
@@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplat @@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplat
6 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO; 6 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
  9 +import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO;
  10 +import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
9 import org.mapstruct.Mapper; 11 import org.mapstruct.Mapper;
10 import org.mapstruct.factory.Mappers; 12 import org.mapstruct.factory.Mappers;
11 13
@@ -47,6 +49,8 @@ public interface DeliveryExpressTemplateConvert { @@ -47,6 +49,8 @@ public interface DeliveryExpressTemplateConvert {
47 49
48 DeliveryExpressTemplateChargeDO convertTemplateCharge(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateChargeUpdateVO vo); 50 DeliveryExpressTemplateChargeDO convertTemplateCharge(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateChargeUpdateVO vo);
49 51
  52 + DeliveryExpressTemplateChargeBO convertTemplateCharge(DeliveryExpressTemplateChargeDO bean);
  53 +
50 default List<DeliveryExpressTemplateChargeDO> convertTemplateChargeList(Long templateId, Integer chargeMode, List<ExpressTemplateChargeBaseVO> list) { 54 default List<DeliveryExpressTemplateChargeDO> convertTemplateChargeList(Long templateId, Integer chargeMode, List<ExpressTemplateChargeBaseVO> list) {
51 return CollectionUtils.convertList(list, vo -> convertTemplateCharge(templateId, chargeMode, vo)); 55 return CollectionUtils.convertList(list, vo -> convertTemplateCharge(templateId, chargeMode, vo));
52 } 56 }
@@ -57,6 +61,8 @@ public interface DeliveryExpressTemplateConvert { @@ -57,6 +61,8 @@ public interface DeliveryExpressTemplateConvert {
57 61
58 DeliveryExpressTemplateFreeDO convertTemplateFree(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateFreeUpdateVO vo); 62 DeliveryExpressTemplateFreeDO convertTemplateFree(DeliveryExpressTemplateUpdateReqVO.ExpressTemplateFreeUpdateVO vo);
59 63
  64 + DeliveryExpressTemplateFreeBO convertTemplateFree(DeliveryExpressTemplateFreeDO bean);
  65 +
60 List<ExpressTemplateChargeBaseVO> convertTemplateChargeList(List<DeliveryExpressTemplateChargeDO> list); 66 List<ExpressTemplateChargeBaseVO> convertTemplateChargeList(List<DeliveryExpressTemplateChargeDO> list);
61 67
62 List<ExpressTemplateFreeBaseVO> convertTemplateFreeList(List<DeliveryExpressTemplateFreeDO> list); 68 List<ExpressTemplateFreeBaseVO> convertTemplateFreeList(List<DeliveryExpressTemplateFreeDO> list);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java
@@ -83,14 +83,13 @@ public interface DeliveryExpressTemplateService { @@ -83,14 +83,13 @@ public interface DeliveryExpressTemplateService {
83 */ 83 */
84 DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); 84 DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId);
85 85
86 - // TODO @jason 方法名可以改成 getExpressTemplateMapBySpuIdsAndArea  
87 /** 86 /**
88 * 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板 87 * 基于指定的 SPU 编号数组和收件人地址区域编号. 获取匹配运费模板
89 * 88 *
90 - * @param ids SPU 编号列表 // TODO @jason:模版编号? 89 + * @param spuIds SPU 编号列表
91 * @param areaId 区域编号 90 * @param areaId 区域编号
92 * @return Map (spuId -> 运费模板设置) 91 * @return Map (spuId -> 运费模板设置)
93 */ 92 */
94 - Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateBySpuIdsAndArea(Collection<Long> ids, Integer areaId); 93 + Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateMapBySpuIdsAndArea(Collection<Long> spuIds, Integer areaId);
95 94
96 } 95 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java
@@ -15,6 +15,8 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemp @@ -15,6 +15,8 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemp
15 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper; 15 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper;
16 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper; 16 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper;
17 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateMapper; 17 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateMapper;
  18 +import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO;
  19 +import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
18 import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO; 20 import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
19 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
20 import org.springframework.transaction.annotation.Transactional; 22 import org.springframework.transaction.annotation.Transactional;
@@ -24,8 +26,7 @@ import javax.annotation.Resource; @@ -24,8 +26,7 @@ import javax.annotation.Resource;
24 import java.util.*; 26 import java.util.*;
25 27
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 28 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;  
28 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
29 import static cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressTemplateConvert.INSTANCE; 30 import static cn.iocoder.yudao.module.trade.convert.delivery.DeliveryExpressTemplateConvert.INSTANCE;
30 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_TEMPLATE_NAME_DUPLICATE; 31 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_TEMPLATE_NAME_DUPLICATE;
31 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_TEMPLATE_NOT_EXISTS; 32 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_TEMPLATE_NOT_EXISTS;
@@ -227,7 +228,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla @@ -227,7 +228,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
227 } 228 }
228 229
229 @Override 230 @Override
230 - public Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateBySpuIdsAndArea(Collection<Long> spuIds, Integer areaId) { 231 + public Map<Long, SpuDeliveryExpressTemplateRespBO> getExpressTemplateMapBySpuIdsAndArea(Collection<Long> spuIds, Integer areaId) {
231 Assert.notNull(areaId, "区域编号 {} 不能为空", areaId); 232 Assert.notNull(areaId, "区域编号 {} 不能为空", areaId);
232 List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds); 233 List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
233 if (CollUtil.isEmpty(spuList)) { 234 if (CollUtil.isEmpty(spuList)) {
@@ -237,43 +238,32 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla @@ -237,43 +238,32 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
237 List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet()); 238 List<DeliveryExpressTemplateDO> templateList = expressTemplateMapper.selectBatchIds(spuMap.keySet());
238 Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size()); 239 Map<Long, SpuDeliveryExpressTemplateRespBO> result = new HashMap<>(templateList.size());
239 templateList.forEach(item -> { 240 templateList.forEach(item -> {
240 - // TODO @jason:if return ,更简洁哈;  
241 - if (spuMap.containsKey(item.getId())) {  
242 - ProductSpuRespDTO spu = spuMap.get(item.getId());  
243 - SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO()  
244 - .setSpuId(spu.getId()).setAreaId(areaId)  
245 - .setChargeMode(item.getChargeMode())  
246 - // TODO @jason:是不是只要查询到一个,就不用查询下一个了;TemplateCharge 和 TemplateFree  
247 - .setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId))  
248 - .setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId));  
249 - result.put(spu.getId(), bo); 241 + ProductSpuRespDTO spu = spuMap.get(item.getId());
  242 + if (spu == null) {
  243 + return;
250 } 244 }
  245 + SpuDeliveryExpressTemplateRespBO bo = new SpuDeliveryExpressTemplateRespBO()
  246 + .setChargeMode(item.getChargeMode())
  247 + // TODO @jason:是不是只要查询到一个,就不用查询下一个了;TemplateCharge 和 TemplateFree
  248 + // @芋艿 包邮的优先级> 费用的优先级 所以两个都要查询
  249 + .setTemplateCharge(findMatchExpressTemplateCharge(item.getId(), areaId))
  250 + .setTemplateFree(findMatchExpressTemplateFree(item.getId(), areaId));
  251 + result.put(spu.getId(), bo);
251 }); 252 });
252 return result; 253 return result;
253 } 254 }
254 255
255 - private DeliveryExpressTemplateChargeDO findMatchExpressTemplateCharge(Long templateId, Integer areaId) {  
256 - List<DeliveryExpressTemplateChargeDO> list = expressTemplateChargeMapper.selectListByTemplateId(templateId);  
257 - // TODO @jason:可以使用 CollectionUtils.findFirst()  
258 - for (DeliveryExpressTemplateChargeDO item : list) {  
259 - // 第一个匹配的返回。 areaId 不能重复  
260 - if (item.getAreaIds().contains(areaId)) {  
261 - return item;  
262 - }  
263 - }  
264 - return null; 256 + private DeliveryExpressTemplateChargeBO findMatchExpressTemplateCharge(Long templateId, Integer areaId) {
  257 + return INSTANCE.convertTemplateCharge(findFirst(
  258 + expressTemplateChargeMapper.selectListByTemplateId(templateId), item -> item.getAreaIds().contains(areaId)
  259 + )
  260 + );
265 } 261 }
266 262
267 - private DeliveryExpressTemplateFreeDO findMatchExpressTemplateFree(Long templateId, Integer areaId) {  
268 - List<DeliveryExpressTemplateFreeDO> list = expressTemplateFreeMapper.selectListByTemplateId(templateId);  
269 - // TODO @jason:可以使用 CollectionUtils.findFirst()  
270 - for (DeliveryExpressTemplateFreeDO item : list) {  
271 - // 第一个匹配的返回。 areaId 不能重复  
272 - if (item.getAreaIds().contains(areaId)) {  
273 - return item;  
274 - }  
275 - }  
276 - return null; 263 + private DeliveryExpressTemplateFreeBO findMatchExpressTemplateFree(Long templateId, Integer areaId) {
  264 + return INSTANCE.convertTemplateFree(findFirst(
  265 + expressTemplateFreeMapper.selectListByTemplateId(templateId), item -> item.getAreaIds().contains(areaId)
  266 + ));
277 } 267 }
278 268
279 } 269 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/DeliveryExpressTemplateChargeBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.delivery.bo;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 快递运费模板费用配置 BO
  7 + *
  8 + * @author jason
  9 + */
  10 +@Data
  11 +public class DeliveryExpressTemplateChargeBO {
  12 +
  13 + /**
  14 + * 首件数量(件数,重量,或体积)
  15 + */
  16 + private Double startCount;
  17 + /**
  18 + * 起步价,单位:分
  19 + */
  20 + private Integer startPrice;
  21 + /**
  22 + * 续件数量(件, 重量,或体积)
  23 + */
  24 + private Double extraCount;
  25 + /**
  26 + * 额外价,单位:分
  27 + */
  28 + private Integer extraPrice;
  29 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/DeliveryExpressTemplateFreeBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.delivery.bo;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 快递运费模板包邮配置 BO
  7 + *
  8 + * @author jason
  9 + */
  10 +@Data
  11 +public class DeliveryExpressTemplateFreeBO {
  12 +
  13 + /**
  14 + * 包邮金额,单位:分
  15 + *
  16 + * 订单总金额 > 包邮金额时,才免运费
  17 + */
  18 + private Integer freePrice;
  19 +
  20 + /**
  21 + * 包邮件数
  22 + *
  23 + * 订单总件数 > 包邮件数时,才免运费
  24 + */
  25 + private Integer freeCount;
  26 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/bo/SpuDeliveryExpressTemplateRespBO.java
1 package cn.iocoder.yudao.module.trade.service.delivery.bo; 1 package cn.iocoder.yudao.module.trade.service.delivery.bo;
2 2
3 -import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;  
4 -import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;  
5 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; 3 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
6 import lombok.Data; 4 import lombok.Data;
7 5
@@ -13,8 +11,6 @@ import lombok.Data; @@ -13,8 +11,6 @@ import lombok.Data;
13 @Data 11 @Data
14 public class SpuDeliveryExpressTemplateRespBO { 12 public class SpuDeliveryExpressTemplateRespBO {
15 13
16 - // TODO @jason:是不是可以简单一点;是否包邮;然后直接把 templateCharge、templateFree 需要的字段平铺开  
17 -  
18 /** 14 /**
19 * 配送计费方式 15 * 配送计费方式
20 * 16 *
@@ -25,24 +21,11 @@ public class SpuDeliveryExpressTemplateRespBO { @@ -25,24 +21,11 @@ public class SpuDeliveryExpressTemplateRespBO {
25 /** 21 /**
26 * 运费模板快递运费设置 22 * 运费模板快递运费设置
27 */ 23 */
28 - private DeliveryExpressTemplateChargeDO templateCharge; 24 + private DeliveryExpressTemplateChargeBO templateCharge;
29 25
30 /** 26 /**
31 * 运费模板包邮设置 27 * 运费模板包邮设置
32 */ 28 */
33 - private DeliveryExpressTemplateFreeDO templateFree;  
34 -  
35 - // TODO @jason:下面两个字段不用返回也可以呀  
36 - /**  
37 - * SPU 编号  
38 - * <p>  
39 - * 关联 ProductSpuDO 的 id 编号  
40 - */  
41 - private Long spuId;  
42 -  
43 - /**  
44 - * 区域编号  
45 - */  
46 - private Integer areaId; 29 + private DeliveryExpressTemplateFreeBO templateFree;
47 30
48 } 31 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
@@ -164,6 +164,15 @@ public class TradePriceCalculateRespBO { @@ -164,6 +164,15 @@ public class TradePriceCalculateRespBO {
164 */ 164 */
165 private Integer payPrice; 165 private Integer payPrice;
166 166
  167 + /**
  168 + * 商品重量,单位:kg 千克
  169 + */
  170 + private Double weight;
  171 + /**
  172 + * 商品体积,单位:m^3 平米
  173 + */
  174 + private Double volume;
  175 +
167 // ========== 商品信息 ========== 176 // ========== 商品信息 ==========
168 /** 177 /**
169 * 商品名 178 * 商品名
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
@@ -4,17 +4,16 @@ import cn.hutool.core.collection.CollUtil; @@ -4,17 +4,16 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
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.product.api.sku.ProductSkuApi;  
8 -import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;  
9 -import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;  
10 -import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;  
11 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; 7 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
12 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 8 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
13 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; 9 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
  10 +import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO;
  11 +import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
14 import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO; 12 import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
15 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 13 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
16 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 14 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
17 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem; 15 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem;
  16 +import lombok.extern.slf4j.Slf4j;
18 import org.springframework.core.annotation.Order; 17 import org.springframework.core.annotation.Order;
19 import org.springframework.stereotype.Component; 18 import org.springframework.stereotype.Component;
20 19
@@ -23,7 +22,10 @@ import java.util.List; @@ -23,7 +22,10 @@ import java.util.List;
23 import java.util.Map; 22 import java.util.Map;
24 import java.util.Set; 23 import java.util.Set;
25 24
  25 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  27 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY;
  28 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND;
27 29
28 /** 30 /**
29 * 运费的 {@link TradePriceCalculator} 实现类 31 * 运费的 {@link TradePriceCalculator} 实现类
@@ -32,13 +34,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -32,13 +34,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
32 */ 34 */
33 @Component 35 @Component
34 @Order(TradePriceCalculator.ORDER_DELIVERY) 36 @Order(TradePriceCalculator.ORDER_DELIVERY)
  37 +@Slf4j
35 public class TradeDeliveryPriceCalculator implements TradePriceCalculator { 38 public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
36 39
37 @Resource 40 @Resource
38 private AddressApi addressApi; 41 private AddressApi addressApi;
39 @Resource 42 @Resource
40 - private ProductSkuApi productSkuApi;  
41 - @Resource  
42 private DeliveryExpressTemplateService deliveryExpressTemplateService; 43 private DeliveryExpressTemplateService deliveryExpressTemplateService;
43 44
44 @Override 45 @Override
@@ -47,10 +48,10 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -47,10 +48,10 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
47 if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) { 48 if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) {
48 return; 49 return;
49 } 50 }
50 - // 1.2 得到收件地址区域  
51 if (param.getAddressId() == null) { 51 if (param.getAddressId() == null) {
52 - return; 52 + throw exception(DELIVERY_EXPRESS_USER_ADDRESS_IS_EMPTY);
53 } 53 }
  54 + // 1.2 得到收件地址区域
54 AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); 55 AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
55 Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); 56 Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId());
56 57
@@ -58,33 +59,27 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -58,33 +59,27 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
58 List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected); 59 List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
59 Set<Long> spuIds = convertSet(selectedItem, OrderItem::getSpuId); 60 Set<Long> spuIds = convertSet(selectedItem, OrderItem::getSpuId);
60 Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap = 61 Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap =
61 - deliveryExpressTemplateService.getExpressTemplateBySpuIdsAndArea(spuIds, address.getAreaId());  
62 - 62 + deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(spuIds, address.getAreaId());
63 // 3. 计算配送费用 63 // 3. 计算配送费用
64 - // TODO @jason:这里应该不能判断空;如果找不到模版,就要报错;不然配送费就亏了  
65 - if (CollUtil.isNotEmpty(spuExpressTemplateMap)) {  
66 - calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result); 64 + if (CollUtil.isEmpty(spuExpressTemplateMap)) {
  65 + log.error("找不到商品 SPU ID {}, area Id {} ,对应的运费模板", spuIds, address.getAreaId());
  66 + throw exception(PRODUCT_EXPRESS_TEMPLATE_NOT_FOUND);
67 } 67 }
  68 + calculateDeliveryPrice(selectedItem, spuExpressTemplateMap, result);
68 } 69 }
69 70
70 private void calculateDeliveryPrice(List<OrderItem> selectedSkus, 71 private void calculateDeliveryPrice(List<OrderItem> selectedSkus,
71 Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap, 72 Map<Long, SpuDeliveryExpressTemplateRespBO> spuExpressTemplateMap,
72 TradePriceCalculateRespBO result) { 73 TradePriceCalculateRespBO result) {
73 - // 得到 SKU 详情  
74 - // TODO @jason:可以去掉这里的读取;在 TradePriceCalculateRespBO 初始化的时候,把 weight、volume 拿到  
75 - Set<Long> skuIds = convertSet(selectedSkus, OrderItem::getSkuId);  
76 - Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId);  
77 // 按 SPU 来计算商品的运费:一个 spuId 可能对应多条订单商品 SKU 74 // 按 SPU 来计算商品的运费:一个 spuId 可能对应多条订单商品 SKU
78 Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId); 75 Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedSkus, OrderItem::getSpuId);
79 -  
80 // 依次计算每个 SPU 的快递运费 76 // 依次计算每个 SPU 的快递运费
81 for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) { 77 for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) {
82 Long spuId = entry.getKey(); 78 Long spuId = entry.getKey();
83 List<OrderItem> orderItems = entry.getValue(); 79 List<OrderItem> orderItems = entry.getValue();
84 - // TODO @jason:如果找不到,则打印 error log  
85 SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId); 80 SpuDeliveryExpressTemplateRespBO templateBO = spuExpressTemplateMap.get(spuId);
86 if (templateBO == null) { 81 if (templateBO == null) {
87 - // 记录错误日志 82 + log.error("不能计算快递运费。不能找到 spuId : {}. 对应的运费模板配置 Resp BO", spuId);
88 continue; 83 continue;
89 } 84 }
90 // 总件数, 总金额, 总重量, 总体积 85 // 总件数, 总金额, 总重量, 总体积
@@ -93,22 +88,16 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -93,22 +88,16 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
93 double totalWeight = 0; 88 double totalWeight = 0;
94 double totalVolume = 0; 89 double totalVolume = 0;
95 for (OrderItem orderItem : orderItems) { 90 for (OrderItem orderItem : orderItems) {
96 - totalCount += orderItem.getCount();  
97 - totalPrice += orderItem.getPayPrice(); // 先按应付总金额来算,后面确认一下 TODO @jason:是的哈  
98 - ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId());  
99 - // TODO @jason:是不是要保持风格统一,都用 +=  
100 - totalWeight = totalWeight + skuResp.getWeight() * orderItem.getCount();  
101 - totalVolume = totalVolume + skuResp.getVolume() * orderItem.getCount(); 91 + totalCount += orderItem.getCount();
  92 + totalPrice += orderItem.getPayPrice();
  93 + totalWeight += totalWeight + orderItem.getWeight() * orderItem.getCount();
  94 + totalVolume += totalVolume + orderItem.getVolume() * orderItem.getCount();
102 } 95 }
103 // 优先判断是否包邮. 如果包邮不计算快递运费 96 // 优先判断是否包邮. 如果包邮不计算快递运费
104 - if (checkExpressFree(templateBO.getChargeMode(), totalCount, totalWeight, 97 + if (isExpressFree(templateBO.getChargeMode(), totalCount, totalWeight,
105 totalVolume, totalPrice, templateBO.getTemplateFree())) { 98 totalVolume, totalPrice, templateBO.getTemplateFree())) {
106 continue; 99 continue;
107 } 100 }
108 - // TODO @jason:这块判断,可以收到 calculateExpressFeeByChargeMode 里;另外找不到,要打 error log  
109 - if (templateBO.getTemplateCharge() == null) {  
110 - continue;  
111 - }  
112 // 计算快递运费 101 // 计算快递运费
113 calculateExpressFeeByChargeMode(totalCount, totalWeight, totalVolume, 102 calculateExpressFeeByChargeMode(totalCount, totalWeight, totalVolume,
114 templateBO.getChargeMode(), templateBO.getTemplateCharge(), orderItems); 103 templateBO.getChargeMode(), templateBO.getTemplateCharge(), orderItems);
@@ -128,8 +117,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -128,8 +117,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
128 * @param orderItems SKU 商品项目 117 * @param orderItems SKU 商品项目
129 */ 118 */
130 private void calculateExpressFeeByChargeMode(double totalCount, double totalWeight, double totalVolume, 119 private void calculateExpressFeeByChargeMode(double totalCount, double totalWeight, double totalVolume,
131 - int chargeMode, DeliveryExpressTemplateChargeDO templateCharge, 120 + int chargeMode, DeliveryExpressTemplateChargeBO templateCharge,
132 List<OrderItem> orderItems) { 121 List<OrderItem> orderItems) {
  122 + if (templateCharge == null) {
  123 + log.error("计算快递运费时,不能找到对应的快递运费模板费用配置。无法计算以下商品 SKU 项目运费: {}", orderItems);
  124 + return;
  125 + }
133 DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode); 126 DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
134 switch (chargeModeEnum) { 127 switch (chargeModeEnum) {
135 case PIECE: { 128 case PIECE: {
@@ -154,7 +147,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -154,7 +147,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
154 * @param templateCharge 快递运费配置 147 * @param templateCharge 快递运费配置
155 * @param orderItems SKU 商品项目 148 * @param orderItems SKU 商品项目
156 */ 149 */
157 - private void calculateExpressFee(double total, DeliveryExpressTemplateChargeDO templateCharge, List<OrderItem> orderItems) { 150 + private void calculateExpressFee(double total, DeliveryExpressTemplateChargeBO templateCharge, List<OrderItem> orderItems) {
158 int deliveryPrice; 151 int deliveryPrice;
159 if (total <= templateCharge.getStartCount()) { 152 if (total <= templateCharge.getStartCount()) {
160 deliveryPrice = templateCharge.getStartPrice(); 153 deliveryPrice = templateCharge.getStartPrice();
@@ -176,11 +169,14 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -176,11 +169,14 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
176 * @param orderItems SKU 商品 169 * @param orderItems SKU 商品
177 */ 170 */
178 private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) { 171 private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) {
179 - // TODO @jason:分摊的话,是不是要按照比例呀?重量、价格、数量等等 172 + // TODO @jason:分摊的话,是不是要按照比例呀?重量、价格、数量等等,
  173 + // 按比例是不是有点复杂。后面看看是否需要
180 int dividePrice = deliveryPrice / orderItems.size(); 174 int dividePrice = deliveryPrice / orderItems.size();
181 for (OrderItem item : orderItems) { 175 for (OrderItem item : orderItems) {
182 // 更新快递运费 176 // 更新快递运费
183 item.setDeliveryPrice(dividePrice); 177 item.setDeliveryPrice(dividePrice);
  178 +
  179 + TradePriceCalculatorHelper.recountPayPrice(item);
184 } 180 }
185 } 181 }
186 182
@@ -194,9 +190,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -194,9 +190,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
194 * @param totalPrice 总金额 190 * @param totalPrice 总金额
195 * @param templateFree 包邮配置 191 * @param templateFree 包邮配置
196 */ 192 */
197 - // TODO @jason:isExpressFree 更合适;因为 check 是一种校验,往往抛出异常;  
198 - private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight,  
199 - double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) { 193 + private boolean isExpressFree(Integer chargeMode, int totalCount, double totalWeight,
  194 + double totalVolume, int totalPrice, DeliveryExpressTemplateFreeBO templateFree) {
200 if (templateFree == null) { 195 if (templateFree == null) {
201 return false; 196 return false;
202 } 197 }
@@ -211,6 +206,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -211,6 +206,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
211 case WEIGHT: 206 case WEIGHT:
212 // freeCount 是不是应该是 double ?? 207 // freeCount 是不是应该是 double ??
213 // TODO @jason:要不配置的时候,把它的单位和商品对齐?到底是 kg、还是斤 208 // TODO @jason:要不配置的时候,把它的单位和商品对齐?到底是 kg、还是斤
  209 + // TODO @芋艿 目前 包邮 件数/重量/体积 都用的是这个字段
214 if (totalWeight >= templateFree.getFreeCount() 210 if (totalWeight >= templateFree.getFreeCount()
215 && totalPrice >= templateFree.getFreePrice()) { 211 && totalPrice >= templateFree.getFreePrice()) {
216 return true; 212 return true;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
@@ -53,7 +53,8 @@ public class TradePriceCalculatorHelper { @@ -53,7 +53,8 @@ public class TradePriceCalculatorHelper {
53 orderItem.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * item.getCount()) 53 orderItem.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * item.getCount())
54 .setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0); 54 .setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0);
55 // sku 信息 55 // sku 信息
56 - orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties()); 56 + orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties())
  57 + .setWeight(sku.getWeight()).setVolume(sku.getVolume());
57 // spu 信息 58 // spu 信息
58 orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId()); 59 orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId());
59 if (orderItem.getPicUrl() == null) { 60 if (orderItem.getPicUrl() == null) {
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculatorTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.price.calculator;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.member.api.address.AddressApi;
  5 +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
  6 +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
  7 +import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateChargeBO;
  8 +import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateFreeBO;
  9 +import cn.iocoder.yudao.module.trade.service.delivery.bo.SpuDeliveryExpressTemplateRespBO;
  10 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  11 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  12 +import org.junit.jupiter.api.BeforeEach;
  13 +import org.junit.jupiter.api.DisplayName;
  14 +import org.junit.jupiter.api.Test;
  15 +import org.mockito.InjectMocks;
  16 +import org.mockito.Mock;
  17 +
  18 +import java.util.ArrayList;
  19 +import java.util.HashMap;
  20 +import java.util.Map;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  23 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  24 +import static cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum.PIECE;
  25 +import static cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum.EXPRESS;
  26 +import static java.util.Arrays.asList;
  27 +import static org.mockito.ArgumentMatchers.eq;
  28 +import static org.mockito.Mockito.when;
  29 +
  30 +import static org.assertj.core.api.Assertions.assertThat;
  31 +
  32 +/**
  33 + * @author jason
  34 + */
  35 +public class TradeDeliveryPriceCalculatorTest extends BaseMockitoUnitTest {
  36 +
  37 + @InjectMocks
  38 + private TradeDeliveryPriceCalculator calculator;
  39 + @Mock
  40 + private AddressApi addressApi;
  41 + @Mock
  42 + private DeliveryExpressTemplateService deliveryExpressTemplateService;
  43 +
  44 + private TradePriceCalculateReqBO reqBO;
  45 + private TradePriceCalculateRespBO resultBO;
  46 + private AddressRespDTO addressResp;
  47 + private DeliveryExpressTemplateChargeBO chargeBO;
  48 + private DeliveryExpressTemplateFreeBO freeBO;
  49 + private SpuDeliveryExpressTemplateRespBO spuTemplateRespBO;
  50 +
  51 + @BeforeEach
  52 + public void init(){
  53 + // 准备参数
  54 + reqBO = new TradePriceCalculateReqBO()
  55 + .setDeliveryType(EXPRESS.getMode())
  56 + .setAddressId(10L)
  57 + .setUserId(1L)
  58 + .setItems(asList(
  59 + new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true),
  60 + new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(10).setSelected(true),
  61 + new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(false)
  62 + ));
  63 + resultBO = new TradePriceCalculateRespBO()
  64 + .setPrice(new TradePriceCalculateRespBO.Price())
  65 + .setPromotions(new ArrayList<>())
  66 + .setItems(asList(
  67 + new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(10L).setCount(2).setSelected(true)
  68 + .setWeight(10d).setVolume(10d).setPrice(100),
  69 + new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(20L).setCount(10).setSelected(true)
  70 + .setWeight(10d).setVolume(10d).setPrice(200),
  71 + new TradePriceCalculateRespBO.OrderItem().setSpuId(1L).setSkuId(30L).setCount(1).setSelected(false)
  72 + .setWeight(10d).setVolume(10d).setPrice(300)
  73 + ));
  74 + // 保证价格被初始化上
  75 + TradePriceCalculatorHelper.recountPayPrice(resultBO.getItems());
  76 + TradePriceCalculatorHelper.recountAllPrice(resultBO);
  77 + // 准备收件地址数据
  78 + addressResp = randomPojo(AddressRespDTO.class, item -> item.setAreaId(10));
  79 + // 准备运费模板费用配置数据
  80 + chargeBO = randomPojo(DeliveryExpressTemplateChargeBO.class,
  81 + item -> item.setStartCount(10D).setStartPrice(1000).setExtraCount(10D).setExtraPrice(2000));
  82 + // 准备运费模板包邮配置数据 订单总件数 < 包邮件数时 12 < 20
  83 + freeBO = randomPojo(DeliveryExpressTemplateFreeBO.class,
  84 + item -> item.setFreeCount(20).setFreePrice(100));
  85 + // 准备 SP 运费模板 数据
  86 + spuTemplateRespBO = randomPojo(SpuDeliveryExpressTemplateRespBO.class,
  87 + item -> item.setChargeMode(PIECE.getType())
  88 + .setTemplateCharge(chargeBO).setTemplateFree(freeBO));
  89 + }
  90 +
  91 + @Test
  92 + @DisplayName("按件计算运费不包邮的情况")
  93 + public void testCalculateByExpressTemplateCharge() {
  94 + // SKU 1 : 100 * 2 = 200
  95 + // SKU 2 :200 * 10 = 2000
  96 + // 运费 首件 1000 + 续件 2000 = 3000
  97 + Map<Long, SpuDeliveryExpressTemplateRespBO> respMap = new HashMap<>();
  98 + respMap.put(1L, spuTemplateRespBO);
  99 +
  100 + // mock 方法
  101 + when(addressApi.getAddress(eq(10L), eq(1L))).thenReturn(addressResp);
  102 + when(deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(eq(asSet(1L)), eq(10)))
  103 + .thenReturn(respMap);
  104 +
  105 + calculator.calculate(reqBO, resultBO);
  106 +
  107 + TradePriceCalculateRespBO.Price price = resultBO.getPrice();
  108 +
  109 + assertThat(price)
  110 + .extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice")
  111 + .containsExactly(2200, 0, 0, 0, 3000, 5200);
  112 + // 断言:SKU
  113 + assertThat(resultBO.getItems()).hasSize(3);
  114 + // SKU1
  115 + assertThat(resultBO.getItems().get(0))
  116 + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
  117 + .containsExactly(100, 2, 0, 0, 0, 1500, 1700);
  118 + // SKU2
  119 + assertThat(resultBO.getItems().get(1))
  120 + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
  121 + .containsExactly(200, 10, 0, 0, 0, 1500, 3500);
  122 + // SKU3 未选中
  123 + assertThat(resultBO.getItems().get(2))
  124 + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
  125 + .containsExactly(300, 1, 0, 0, 0, 0, 300);
  126 + }
  127 +
  128 + @Test
  129 + @DisplayName("按件计算运费包邮的情况")
  130 + public void testCalculateByExpressTemplateFree() {
  131 + // SKU 1 : 100 * 2 = 200
  132 + // SKU 2 :200 * 10 = 2000
  133 + // 运费 0
  134 + Map<Long, SpuDeliveryExpressTemplateRespBO> respMap = new HashMap<>();
  135 + respMap.put(1L, spuTemplateRespBO);
  136 + // 准备运费模板包邮配置数据 包邮 订单总件数 > 包邮件数时 12 > 10
  137 + freeBO = randomPojo(DeliveryExpressTemplateFreeBO.class,
  138 + item -> item.setFreeCount(10).setFreePrice(1000));
  139 + spuTemplateRespBO.setTemplateFree(freeBO);
  140 + // mock 方法
  141 + when(addressApi.getAddress(eq(10L), eq(1L))).thenReturn(addressResp);
  142 + when(deliveryExpressTemplateService.getExpressTemplateMapBySpuIdsAndArea(eq(asSet(1L)), eq(10)))
  143 + .thenReturn(respMap);
  144 +
  145 + calculator.calculate(reqBO, resultBO);
  146 +
  147 + TradePriceCalculateRespBO.Price price = resultBO.getPrice();
  148 +
  149 + // 断言price
  150 + assertThat(price)
  151 + .extracting("totalPrice","discountPrice","couponPrice","pointPrice","deliveryPrice","payPrice")
  152 + .containsExactly(2200, 0, 0, 0, 0, 2200);
  153 + // 断言:SKU
  154 + assertThat(resultBO.getItems()).hasSize(3);
  155 + // SKU1
  156 + assertThat(resultBO.getItems().get(0))
  157 + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
  158 + .containsExactly(100, 2, 0, 0, 0, 0, 200);
  159 + // SKU2
  160 + assertThat(resultBO.getItems().get(1))
  161 + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
  162 + .containsExactly(200, 10, 0, 0, 0, 0, 2000);
  163 + // SKU3 未选中
  164 + assertThat(resultBO.getItems().get(2))
  165 + .extracting("price", "count","discountPrice" ,"couponPrice", "pointPrice","deliveryPrice","payPrice")
  166 + .containsExactly(300, 1, 0, 0, 0, 0, 300);
  167 + }
  168 +}
0 \ No newline at end of file 169 \ No newline at end of file