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 52 */
53 53 private Integer originalPrice;
54 54 /**
55   - * 商品优惠(总),单位:分
  55 + * 订单优惠(总),单位:分
56 56 *
57 57 * 订单级优惠:对主订单的优惠,常见如:订单满 200 元减 10 元;订单满 80 包邮。
58 58 *
... ... @@ -101,9 +101,13 @@ public class PriceCalculateRespDTO {
101 101 * 订单商品 SKU
102 102 */
103 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 113 private Integer count;
... ... @@ -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 150 * 对应 taobao 的 order.part_mjz_discount 字段
  151 + * 淘宝说明:子订单分摊优惠基础逻辑:一般正常优惠券和满减优惠按照子订单的金额进行分摊,特殊情况如果优惠券是指定商品使用的,只会分摊到对应商品子订单上不分摊。
147 152 */
148 153 private Integer orderPartPrice;
149 154 /**
150 155 * 分摊后子订单实付金额(总),单位:分
151 156 *
  157 + * = {@link #payPrice}
  158 + * - {@link #orderPartPrice}
  159 + *
152 160 * 对应 taobao 的 divide_order_fee 字段
153 161 */
154 162 private Integer orderDividePrice;
... ... @@ -156,7 +164,7 @@ public class PriceCalculateRespDTO {
156 164 }
157 165  
158 166 /**
159   - * 营销活动
  167 + * 营销明细
160 168 */
161 169 @Data
162 170 public static class Promotion {
... ... @@ -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 203 * 匹配的商品 SKU 数组
196 204 */
... ... @@ -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 17  
18 18 DISCOUNT_ACTIVITY(1, "限时折扣"),
19 19 REWARD_ACTIVITY(2, "满减送"),
  20 +
  21 + MEMBER(3, "会员折扣"),
20 22 ;
21 23  
22 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 19 default PriceCalculateRespDTO convert(PriceCalculateReqDTO calculateReqDTO, List<ProductSkuRespDTO> skuList) {
20 20 // 创建 PriceCalculateRespDTO 对象
21 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 27 // 创建它的 OrderItem 属性
27 28 Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(),
28 29 PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount);
29 30 skuList.forEach(sku -> {
30 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 36 priceCalculate.getOrder().getItems().add(orderItem);
  37 + // 补充价格信息到 Order 中
  38 + order.setOriginalPrice(order.getOriginalPrice() + orderItem.getOriginalPrice()).setPayPrice(order.getOriginalPrice());
36 39 });
37 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 1 package cn.iocoder.yudao.module.market.service.price;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
3 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 5 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
5 6 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
6 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 12 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
8 13 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  14 +import com.google.common.base.Suppliers;
9 15 import org.springframework.stereotype.Service;
10 16 import org.springframework.validation.annotation.Validated;
11 17  
12 18 import javax.annotation.Resource;
13 19 import java.util.List;
14 20 import java.util.Map;
  21 +import java.util.function.Supplier;
15 22  
16 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 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 29 * 价格计算 Service 实现类
21 30 *
  31 + * 优惠计算顺序:min(限时折扣, 会员折扣) > 满减送 > 优惠券。
  32 + * 参考文档:
  33 + * 1. <a href="https://help.youzan.com/displaylist/detail_4_4-1-60384">有赞文档:限时折扣、满减送、优惠券哪个优先计算?</a>
  34 + *
22 35 * @author 芋道源码
23 36 */
24 37 @Service
... ... @@ -26,22 +39,26 @@ import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_E
26 39 public class PriceServiceImpl implements PriceService {
27 40  
28 41 @Resource
  42 + private DiscountService discountService;
  43 +
  44 + @Resource
29 45 private ProductSkuApi productSkuApi;
30 46  
31 47 @Override
32 48 public PriceCalculateRespDTO calculatePrice(PriceCalculateReqDTO calculateReqDTO) {
33 49 // 获得商品 SKU 数组
34   - List<ProductSkuRespDTO> skuList = checkProductSkus(calculateReqDTO);
  50 + List<ProductSkuRespDTO> skuList = checkSkus(calculateReqDTO);
35 51 // 初始化 PriceCalculateRespDTO 对象
36 52 PriceCalculateRespDTO priceCalculate = PriceConvert.INSTANCE.convert(calculateReqDTO, skuList);
37 53  
38   - // 计算【限时折扣】促销 TODO 待实现
  54 + // 计算商品级别的价格
  55 + calculatePriceForSkuLevel(calculateReqDTO.getUserId(), priceCalculate);
39 56 // 计算【满减送】促销 TODO 待实现
40 57 // 计算【优惠劵】促销 TODO 待实现
41 58 return priceCalculate;
42 59 }
43 60  
44   - private List<ProductSkuRespDTO> checkProductSkus(PriceCalculateReqDTO calculateReqDTO) {
  61 + private List<ProductSkuRespDTO> checkSkus(PriceCalculateReqDTO calculateReqDTO) {
45 62 // 获得商品 SKU 数组
46 63 Map<Long, Integer> skuIdCountMap = CollectionUtils.convertMap(calculateReqDTO.getItems(),
47 64 PriceCalculateReqDTO.Item::getSkuId, PriceCalculateReqDTO.Item::getCount);
... ... @@ -58,4 +75,107 @@ public class PriceServiceImpl implements PriceService {
58 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 +}
... ...