Commit 084d4baba2d9f52e674a4287725e0b26b6f25907

Authored by YunaiV
1 parent a88eda34

price:完成会员价格的计算~

yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/api/price/dto/PriceCalculateRespDTO.java
@@ -52,7 +52,7 @@ public class PriceCalculateRespDTO { @@ -52,7 +52,7 @@ public class PriceCalculateRespDTO {
52 */ 52 */
53 private Integer originalPrice; 53 private Integer originalPrice;
54 /** 54 /**
55 - * 商品优惠(总),单位:分 55 + * 订单优惠(总),单位:分
56 * 56 *
57 * 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。 57 * 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。
58 * 58 *
@@ -101,9 +101,13 @@ public class PriceCalculateRespDTO { @@ -101,9 +101,13 @@ public class PriceCalculateRespDTO {
101 * 订单商品 SKU 101 * 订单商品 SKU
102 */ 102 */
103 @Data 103 @Data
104 - public static class OrderItem extends PriceCalculateReqDTO.Item { 104 + public static class OrderItem {
105 105
106 /** 106 /**
  107 + * SKU 编号
  108 + */
  109 + private Long skuId;
  110 + /**
107 * 购买数量 111 * 购买数量
108 */ 112 */
109 private Integer count; 113 private Integer count;
@@ -141,14 +145,18 @@ public class PriceCalculateRespDTO { @@ -141,14 +145,18 @@ public class PriceCalculateRespDTO {
141 145
142 /** 146 /**
143 * 子订单分摊金额(总),单位:分 147 * 子订单分摊金额(总),单位:分
144 - * 需要分摊 {@link Order#discountPrice}、{@link Order#couponPrice} 148 + * 需要分摊 {@link Order#discountPrice}、{@link Order#couponPrice}、{@link Order#pointPrice}
145 * 149 *
146 * 对应 taobao 的 order.part_mjz_discount 字段 150 * 对应 taobao 的 order.part_mjz_discount 字段
  151 + * 淘宝说明:子订单分摊优惠基础逻辑:一般正常优惠券和满减优惠按照子订单的金额进行分摊,特殊情况如果优惠券是指定商品使用的,只会分摊到对应商品子订单上不分摊。
147 */ 152 */
148 private Integer orderPartPrice; 153 private Integer orderPartPrice;
149 /** 154 /**
150 * 分摊后子订单实付金额(总),单位:分 155 * 分摊后子订单实付金额(总),单位:分
151 * 156 *
  157 + * = {@link #payPrice}
  158 + * - {@link #orderPartPrice}
  159 + *
152 * 对应 taobao 的 divide_order_fee 字段 160 * 对应 taobao 的 divide_order_fee 字段
153 */ 161 */
154 private Integer orderDividePrice; 162 private Integer orderDividePrice;
@@ -156,7 +164,7 @@ public class PriceCalculateRespDTO { @@ -156,7 +164,7 @@ public class PriceCalculateRespDTO {
156 } 164 }
157 165
158 /** 166 /**
159 - * 营销活动 167 + * 营销明细
160 */ 168 */
161 @Data 169 @Data
162 public static class Promotion { 170 public static class Promotion {
@@ -186,11 +194,11 @@ public class PriceCalculateRespDTO { @@ -186,11 +194,11 @@ public class PriceCalculateRespDTO {
186 /** 194 /**
187 * 计算时的原价(总),单位:分 195 * 计算时的原价(总),单位:分
188 */ 196 */
189 - private Integer beforePrice; 197 + private Integer originalPrice;
190 /** 198 /**
191 * 计算时的优惠(总),单位:分 199 * 计算时的优惠(总),单位:分
192 */ 200 */
193 - private Integer afterPrice; 201 + private Integer discountPrice;
194 /** 202 /**
195 * 匹配的商品 SKU 数组 203 * 匹配的商品 SKU 数组
196 */ 204 */
@@ -225,11 +233,11 @@ public class PriceCalculateRespDTO { @@ -225,11 +233,11 @@ public class PriceCalculateRespDTO {
225 /** 233 /**
226 * 计算时的原价(总),单位:分 234 * 计算时的原价(总),单位:分
227 */ 235 */
228 - private Integer beforePrice; 236 + private Integer originalPrice;
229 /** 237 /**
230 * 计算时的优惠(总),单位:分 238 * 计算时的优惠(总),单位:分
231 */ 239 */
232 - private Integer afterPrice; 240 + private Integer discountPrice;
233 241
234 } 242 }
235 243
yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionTypeEnum.java
@@ -17,6 +17,8 @@ public enum PromotionTypeEnum implements IntArrayValuable { @@ -17,6 +17,8 @@ public enum PromotionTypeEnum implements IntArrayValuable {
17 17
18 DISCOUNT_ACTIVITY(1, "限时折扣"), 18 DISCOUNT_ACTIVITY(1, "限时折扣"),
19 REWARD_ACTIVITY(2, "满减送"), 19 REWARD_ACTIVITY(2, "满减送"),
  20 +
  21 + MEMBER(3, "会员折扣"),
20 ; 22 ;
21 23
22 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionTypeEnum::getType).toArray(); 24 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionTypeEnum::getType).toArray();
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/convert/price/PriceConvert.java
@@ -19,20 +19,23 @@ public interface PriceConvert { @@ -19,20 +19,23 @@ public interface PriceConvert {
19 default PriceCalculateRespDTO convert(PriceCalculateReqDTO calculateReqDTO, List<ProductSkuRespDTO> skuList) { 19 default PriceCalculateRespDTO convert(PriceCalculateReqDTO calculateReqDTO, List<ProductSkuRespDTO> skuList) {
20 // 创建 PriceCalculateRespDTO 对象 20 // 创建 PriceCalculateRespDTO 对象
21 PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO(); 21 PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();
22 - priceCalculate.setOrder(new PriceCalculateRespDTO.Order().setOriginalPrice(0).setActivityPrice(0)  
23 - .setDeliveryPrice(0).setPayPrice(0).setItems(new ArrayList<>())  
24 - .setCouponId(calculateReqDTO.getCouponId()));  
25 - priceCalculate.setPromotions(new ArrayList<>()); 22 + // 创建它的 Order 属性
  23 + PriceCalculateRespDTO.Order order = new PriceCalculateRespDTO.Order().setOriginalPrice(0).setDiscountPrice(0)
  24 + .setCouponPrice(0).setPointPrice(0).setDeliveryPrice(0).setPayPrice(0)
  25 + .setItems(new ArrayList<>()).setCouponId(calculateReqDTO.getCouponId());
  26 + priceCalculate.setOrder(order).setPromotions(new ArrayList<>());
26 // 创建它的 OrderItem 属性 27 // 创建它的 OrderItem 属性
27 Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(), 28 Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(),
28 PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount); 29 PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount);
29 skuList.forEach(sku -> { 30 skuList.forEach(sku -> {
30 Integer count = skuIdCountMap.get(sku.getId()); 31 Integer count = skuIdCountMap.get(sku.getId());
31 - PriceCalculateRespDTO.OrderItem orderItem = new PriceCalculateRespDTO.OrderItem().setCount(count)  
32 - .setOriginalUnitPrice(sku.getPrice()).setOriginalPrice(sku.getPrice() * count).setActivityPrice(0);  
33 - orderItem.setPayPrice(orderItem.getPayPrice()).setPayUnitPrice(orderItem.getOriginalUnitPrice())  
34 - .setPayPrice(orderItem.getPayPrice()); 32 + PriceCalculateRespDTO.OrderItem orderItem = new PriceCalculateRespDTO.OrderItem()
  33 + .setSkuId(sku.getId()).setCount(count).setOriginalUnitPrice(sku.getPrice())
  34 + .setOriginalPrice(sku.getPrice() * count).setDiscountPrice(0).setOrderPartPrice(0);
  35 + orderItem.setPayPrice(orderItem.getOriginalPrice()).setOrderDividePrice(orderItem.getOrderDividePrice());
35 priceCalculate.getOrder().getItems().add(orderItem); 36 priceCalculate.getOrder().getItems().add(orderItem);
  37 + // 补充价格信息到 Order 中
  38 + order.setOriginalPrice(order.getOriginalPrice() + orderItem.getOriginalPrice()).setPayPrice(order.getOriginalPrice());
36 }); 39 });
37 return priceCalculate; 40 return priceCalculate;
38 } 41 }
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/DiscountService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.market.service.discount;
  2 +
  3 +import cn.iocoder.yudao.module.market.dal.dataobject.discount.DiscountProductDO;
  4 +
  5 +import java.util.Collection;
  6 +import java.util.Map;
  7 +
  8 +/**
  9 + * 限时折扣 Service 接口
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +public interface DiscountService {
  14 +
  15 + /**
  16 + * 基于指定 SKU 编号数组,获得匹配的限时折扣商品
  17 + *
  18 + * 注意,匹配的条件,仅仅是日期符合,并且处于开启状态
  19 + *
  20 + * @param skuIds SKU 编号数组
  21 + * @return 匹配的限时折扣商品
  22 + */
  23 + Map<Long, DiscountProductDO> getMatchDiscountProducts(Collection<Long> skuIds);
  24 +
  25 +}
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/DiscountServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.market.service.discount;
  2 +
  3 +import cn.iocoder.yudao.module.market.dal.dataobject.discount.DiscountProductDO;
  4 +import org.springframework.stereotype.Service;
  5 +import org.springframework.validation.annotation.Validated;
  6 +
  7 +import java.util.Collection;
  8 +import java.util.HashMap;
  9 +import java.util.Map;
  10 +
  11 +/**
  12 + * 限时折扣 Service 实现类
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Service
  17 +@Validated
  18 +public class DiscountServiceImpl implements DiscountService {
  19 +
  20 + // TODO 芋艿:待实现
  21 + @Override
  22 + public Map<Long, DiscountProductDO> getMatchDiscountProducts(Collection<Long> skuIds) {
  23 + Map<Long, DiscountProductDO> products = new HashMap<>();
  24 + products.put(1L, new DiscountProductDO().setPromotionPrice(100));
  25 + products.put(2L, new DiscountProductDO().setPromotionPrice(50));
  26 + return products;
  27 + }
  28 +
  29 +}
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/discount/package-info.java deleted
1 -/**  
2 - * TODO 占位  
3 - */  
4 -package cn.iocoder.yudao.module.market.service.discount;  
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceServiceImpl.java
1 package cn.iocoder.yudao.module.market.service.price; 1 package cn.iocoder.yudao.module.market.service.price;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO; 5 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
5 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO; 6 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
6 import cn.iocoder.yudao.module.market.convert.price.PriceConvert; 7 import cn.iocoder.yudao.module.market.convert.price.PriceConvert;
  8 +import cn.iocoder.yudao.module.market.dal.dataobject.discount.DiscountProductDO;
  9 +import cn.iocoder.yudao.module.market.enums.common.PromotionLevelEnum;
  10 +import cn.iocoder.yudao.module.market.enums.common.PromotionTypeEnum;
  11 +import cn.iocoder.yudao.module.market.service.discount.DiscountService;
7 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 12 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
8 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 13 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  14 +import com.google.common.base.Suppliers;
9 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
10 import org.springframework.validation.annotation.Validated; 16 import org.springframework.validation.annotation.Validated;
11 17
12 import javax.annotation.Resource; 18 import javax.annotation.Resource;
13 import java.util.List; 19 import java.util.List;
14 import java.util.Map; 20 import java.util.Map;
  21 +import java.util.function.Supplier;
15 22
16 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
17 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; 25 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
  26 +import static java.util.Collections.singletonList;
18 27
19 /** 28 /**
20 * 价格计算 Service 实现类 29 * 价格计算 Service 实现类
21 * 30 *
  31 + * 优惠计算顺序:min(限时折扣, 会员折扣) > 满减送 > 优惠券。
  32 + * 参考文档:
  33 + * 1. <a href="https://help.youzan.com/displaylist/detail_4_4-1-60384">有赞文档:限时折扣、满减送、优惠券哪个优先计算?</a>
  34 + *
22 * @author 芋道源码 35 * @author 芋道源码
23 */ 36 */
24 @Service 37 @Service
@@ -26,22 +39,26 @@ import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_E @@ -26,22 +39,26 @@ import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_E
26 public class PriceServiceImpl implements PriceService { 39 public class PriceServiceImpl implements PriceService {
27 40
28 @Resource 41 @Resource
  42 + private DiscountService discountService;
  43 +
  44 + @Resource
29 private ProductSkuApi productSkuApi; 45 private ProductSkuApi productSkuApi;
30 46
31 @Override 47 @Override
32 public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) { 48 public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) {
33 // 获得商品 SKU 数组 49 // 获得商品 SKU 数组
34 - List<ProductSkuRespDTO> skuList = checkProductSkus(calculateReqDTO); 50 + List<ProductSkuRespDTO> skuList = checkSkus(calculateReqDTO);
35 // 初始化 PriceCalculateRespDTO 对象 51 // 初始化 PriceCalculateRespDTO 对象
36 PriceCalculateRespDTO priceCalculate = PriceConvert.INSTANCE.convert(calculateReqDTO, skuList); 52 PriceCalculateRespDTO priceCalculate = PriceConvert.INSTANCE.convert(calculateReqDTO, skuList);
37 53
38 - // 计算【限时折扣】促销 TODO 待实现 54 + // 计算商品级别的价格
  55 + calculatePriceForSkuLevel(calculateReqDTO.getUserId(), priceCalculate);
39 // 计算【满减送】促销 TODO 待实现 56 // 计算【满减送】促销 TODO 待实现
40 // 计算【优惠劵】促销 TODO 待实现 57 // 计算【优惠劵】促销 TODO 待实现
41 return priceCalculate; 58 return priceCalculate;
42 } 59 }
43 60
44 - private List<ProductSkuRespDTO> checkProductSkus(PriceCalculateReqDTO calculateReqDTO) { 61 + private List<ProductSkuRespDTO> checkSkus(PriceCalculateReqDTO calculateReqDTO) {
45 // 获得商品 SKU 数组 62 // 获得商品 SKU 数组
46 Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(), 63 Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(),
47 PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount); 64 PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount);
@@ -58,4 +75,107 @@ public class PriceServiceImpl implements PriceService { @@ -58,4 +75,107 @@ public class PriceServiceImpl implements PriceService {
58 return skus; 75 return skus;
59 } 76 }
60 77
  78 + /**
  79 + * 计算商品级别的价格,例如说:
  80 + * 1. 会员折扣
  81 + * 2. 限时折扣
  82 + *
  83 + * 其中,会员折扣、限时折扣取最低价
  84 + *
  85 + * @param userId 用户编号
  86 + * @param priceCalculate 价格计算的结果
  87 + */
  88 + private void calculatePriceForSkuLevel(Long userId, PriceCalculateRespDTO priceCalculate) {
  89 + // 获取 SKU 级别的所有优惠信息
  90 + Supplier<Double> memberDiscountSupplier = getMemberDiscountSupplier(userId);
  91 + Map<Long, DiscountProductDO> discountProducts = discountService.getMatchDiscountProducts(
  92 + convertSet(priceCalculate.getOrder().getItems(), PriceCalculateRespDTO.OrderItem::getSkuId));
  93 +
  94 + // 处理每个 SKU 的优惠
  95 + priceCalculate.getOrder().getItems().forEach(orderItem -> {
  96 + // 获取该 SKU 的优惠信息
  97 + Double memberDiscount = memberDiscountSupplier.get();
  98 + DiscountProductDO discountProduct = discountProducts.get(orderItem.getSkuId());
  99 + if (discountProduct != null // 假设优惠价格更贵,则认为没优惠
  100 + && discountProduct.getPromotionPrice() >= orderItem.getOriginalUnitPrice()) {
  101 + discountProduct = null;
  102 + }
  103 + if (memberDiscount == null && discountProduct == null) {
  104 + return;
  105 + }
  106 + // 计算价格,判断选择哪个折扣
  107 + Integer memberPrice = memberDiscount != null ? (int) (orderItem.getPayPrice() * memberDiscount / 100) : null;
  108 + Integer promotionPrice = discountProduct != null ? discountProduct.getPromotionPrice() * orderItem.getCount() : null;
  109 + if (memberPrice == null) {
  110 +
  111 + } else if (promotionPrice == null) {
  112 + calculatePriceByMemberDiscount(orderItem, memberDiscount, memberPrice, priceCalculate);
  113 + } else if (memberPrice < promotionPrice) {
  114 +
  115 + } else {
  116 + calculatePriceByMemberDiscount(orderItem, memberDiscount, memberPrice, priceCalculate);
  117 + }
  118 + });
  119 + }
  120 +
  121 + private void calculatePriceByMemberDiscount(PriceCalculateRespDTO.OrderItem orderItem,
  122 + Double memberDiscount, Integer memberPrice,
  123 + PriceCalculateRespDTO priceCalculate) {
  124 + // 记录优惠明细
  125 + addPromotion(priceCalculate, orderItem, null,
  126 + PromotionTypeEnum.MEMBER.getName(),
  127 + PromotionTypeEnum.MEMBER.getType(), PromotionLevelEnum.SKU.getLevel(), memberPrice,
  128 + true, StrUtil.format("会员折扣:省 {} 元", formatPrice(orderItem.getPayPrice() - memberPrice)));
  129 + // 修改 SKU 的优惠
  130 + modifyOrderItemPayPrice(orderItem, memberPrice, priceCalculate);
  131 + }
  132 +
  133 + private void calculatePriceByDiscountActivity(PriceCalculateRespDTO.OrderItem orderItem,
  134 + DiscountProductDO discountProduct, Integer promotionPrice,
  135 + PriceCalculateRespDTO priceCalculate) {
  136 +
  137 + }
  138 +
  139 + private void addPromotion(PriceCalculateRespDTO priceCalculate, PriceCalculateRespDTO.OrderItem orderItem,
  140 + Long id, String name, Integer type, Integer level,
  141 + Integer newPayPrice, Boolean meet, String meetTip) {
  142 + // 创建营销明细 Item
  143 + PriceCalculateRespDTO.PromotionItem promotionItem = new PriceCalculateRespDTO.PromotionItem().setSkuId(orderItem.getSkuId())
  144 + .setOriginalPrice(orderItem.getPayPrice()).setDiscountPrice(orderItem.getPayPrice() - newPayPrice);
  145 + // 创建营销明细
  146 + PriceCalculateRespDTO.Promotion promotion = new PriceCalculateRespDTO.Promotion()
  147 + .setId(id).setName(name).setType(type).setLevel(level)
  148 + .setOriginalPrice(promotionItem.getOriginalPrice()).setDiscountPrice(promotionItem.getDiscountPrice())
  149 + .setItems(singletonList(promotionItem)).setMeet(meet).setMeetTip(meetTip);
  150 + priceCalculate.getPromotions().add(promotion);
  151 + }
  152 +
  153 + private void modifyOrderItemPayPrice(PriceCalculateRespDTO.OrderItem orderItem, Integer newPayPrice,
  154 + PriceCalculateRespDTO priceCalculate) {
  155 + int diffPayPrice = orderItem.getPayPrice() - newPayPrice;
  156 + // 设置 OrderItem 价格相关字段
  157 + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + diffPayPrice);
  158 + orderItem.setPayPrice(newPayPrice);
  159 + orderItem.setOrderDividePrice(orderItem.getPayPrice() - orderItem.getOrderPartPrice());
  160 + // 设置 Order 相关相关字段
  161 + priceCalculate.getOrder().setPayPrice(priceCalculate.getOrder().getPayPrice() - diffPayPrice);
  162 + }
  163 +
  164 + // TODO 芋艿:提前实现
  165 + private Supplier<Double> getMemberDiscountSupplier(Long userId) {
  166 + return Suppliers.memoize(() -> {
  167 + if (userId == 1) {
  168 + return 90d;
  169 + }
  170 + if (userId == 2) {
  171 + return 80d;
  172 + }
  173 + return null; // 无优惠
  174 + });
  175 + }
  176 +
  177 + private String formatPrice(Integer price) {
  178 + return String.format("%.2f", price / 100d);
  179 + }
  180 +
61 } 181 }
yudao-module-mall/yudao-module-market-biz/src/test/java/cn/iocoder/yudao/module/market/service/price/PriceServiceTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.market.service.price;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  4 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  5 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
  6 +import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
  7 +import cn.iocoder.yudao.module.market.enums.common.PromotionLevelEnum;
  8 +import cn.iocoder.yudao.module.market.enums.common.PromotionTypeEnum;
  9 +import cn.iocoder.yudao.module.market.service.discount.DiscountService;
  10 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  11 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  12 +import org.junit.jupiter.api.Test;
  13 +import org.mockito.InjectMocks;
  14 +import org.mockito.Mock;
  15 +
  16 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  17 +import static java.util.Collections.singletonList;
  18 +import static org.junit.jupiter.api.Assertions.*;
  19 +import static org.mockito.ArgumentMatchers.eq;
  20 +import static org.mockito.Mockito.when;
  21 +
  22 +/**
  23 + * {@link PriceServiceImpl} 的单元测试
  24 + *
  25 + * @author 芋道源码
  26 + */
  27 +public class PriceServiceTest extends BaseMockitoUnitTest {
  28 +
  29 + @InjectMocks
  30 + private PriceServiceImpl priceService;
  31 +
  32 + @Mock
  33 + private DiscountService discountService;
  34 + @Mock
  35 + private ProductSkuApi productSkuApi;
  36 +
  37 + @Test
  38 + public void testCalculatePrice_memberDiscount() {
  39 + // 准备参数
  40 + // TODO 芋艿:userId = 1,实现 9 折;后续改成 mock
  41 + PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(1L)
  42 + .setItems(singletonList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2)));
  43 + // mock 方法(商品 SKU 信息)
  44 + ProductSkuRespDTO productSku = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100));
  45 + when(productSkuApi.getSkuList(eq(SetUtils.asSet(10L)))).thenReturn(singletonList(productSku));
  46 +
  47 + // 调用
  48 + PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO);
  49 + // 断言 Order 部分
  50 + PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
  51 + assertEquals(order.getOriginalPrice(), 200);
  52 + assertEquals(order.getDiscountPrice(), 0);
  53 + assertEquals(order.getPointPrice(), 0);
  54 + assertEquals(order.getDeliveryPrice(), 0);
  55 + assertEquals(order.getPayPrice(), 180);
  56 + assertNull(order.getCouponId());
  57 + // 断言 OrderItem 部分
  58 + PriceCalculateRespDTO.OrderItem orderItem = order.getItems().get(0);
  59 + assertEquals(order.getItems().size(), 1);
  60 + assertEquals(orderItem.getSkuId(), 10L);
  61 + assertEquals(orderItem.getCount(), 2);
  62 + assertEquals(orderItem.getOriginalPrice(), 200);
  63 + assertEquals(orderItem.getOriginalUnitPrice(), 100);
  64 + assertEquals(orderItem.getDiscountPrice(), 20);
  65 + assertEquals(orderItem.getPayPrice(), 180);
  66 + assertEquals(orderItem.getOrderPartPrice(), 0);
  67 + assertEquals(orderItem.getOrderDividePrice(), 180);
  68 + // 断言 Promotion 部分
  69 + PriceCalculateRespDTO.Promotion promotion = priceCalculate.getPromotions().get(0);
  70 + assertEquals(priceCalculate.getPromotions().size(), 1);
  71 + assertNull(promotion.getId());
  72 + assertEquals(promotion.getName(), "会员折扣");
  73 + assertEquals(promotion.getType(), PromotionTypeEnum.MEMBER.getType());
  74 + assertEquals(promotion.getLevel(), PromotionLevelEnum.SKU.getLevel());
  75 + assertEquals(promotion.getOriginalPrice(), 200);
  76 + assertEquals(promotion.getDiscountPrice(), 20);
  77 + assertTrue(promotion.getMeet());
  78 + assertEquals(promotion.getMeetTip(), "会员折扣:省 0.20 元");
  79 + PriceCalculateRespDTO.PromotionItem promotionItem = promotion.getItems().get(0);
  80 + assertEquals(promotion.getItems().size(), 1);
  81 + assertEquals(promotionItem.getSkuId(), 10L);
  82 + assertEquals(promotionItem.getOriginalPrice(), 200);
  83 + assertEquals(promotionItem.getDiscountPrice(), 20);
  84 + }
  85 +
  86 +}