Bläddra i källkod

price:完成优惠劵的价格计算~

YunaiV 2 år sedan
förälder
incheckning
829bbf58b5

+ 2 - 2
yudao-module-mall/pom.xml

@@ -18,8 +18,8 @@
         商城大模块,由 product 商品、market 营销、trade 交易 coupon等组成
     </description>
     <modules>
-        <module>yudao-module-coupon-api</module>
-        <module>yudao-module-coupon-biz</module>
+<!--        <module>yudao-module-coupon-api</module>-->
+<!--        <module>yudao-module-coupon-biz</module>-->
         <module>yudao-module-market-api</module>
         <module>yudao-module-market-biz</module>
         <module>yudao-module-product-api</module>

+ 8 - 4
yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/ErrorCodeConstants.java

@@ -9,10 +9,14 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  */
 public interface ErrorCodeConstants {
 
-
-    // ========== 促销活动相关  1003001000============
+    // ========== 促销活动相关 1003001000 ============ TODO 芋艿:看看是不是要删除掉
     ErrorCode ACTIVITY_NOT_EXISTS = new ErrorCode(1003001000, "促销活动不存在");
 
-    // ========== banner相关  1003002000============
-    ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1003002000, "Banner不存在");
+    // ========== Banner 相关 1003002000 ============
+    ErrorCode BANNER_NOT_EXISTS = new ErrorCode(1003002000, "Banner 不存在");
+
+    // ========== Coupon 相关 1003003000 ============
+    ErrorCode COUPON_NO_MATCH_SPU = new ErrorCode(1003003000, "优惠劵没有可使用的商品!");
+    ErrorCode COUPON_NO_MATCH_MIN_PRICE = new ErrorCode(1003003000, "不满足优惠劵使用的最低金额");
+
 }

+ 5 - 2
yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionLevelEnum.java

@@ -9,14 +9,17 @@ import java.util.Arrays;
 /**
  * 营销的级别枚举
  *
+ * 参考有赞:<a href="https://img01.yzcdn.cn/upload_files/2021/11/02/FhDjUrNDq-G0wjNdYDtgUX09fdGj.png">营销级别</a>
+ *
  * @author 芋道源码
  */
 @Getter
 @AllArgsConstructor
 public enum PromotionLevelEnum implements IntArrayValuable {
 
-    ORDER(1, "订单级"), // 多个商品,进行组合后优惠
-    SKU(2, "商品级"), // 单个商品,直接优惠
+    ORDER(1, "订单级"), // 多个商品,进行组合后优惠。例如说:满减送、打包一口价、第二件半价
+    SKU(2, "商品级"), // 单个商品,直接优惠。例如说:限时折扣、会员折扣
+    COUPON(3, "优惠劵"), // 多个商品,进行组合后优惠。例如说:优惠劵
     ;
 
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionLevelEnum::getLevel).toArray();

+ 1 - 0
yudao-module-mall/yudao-module-market-api/src/main/java/cn/iocoder/yudao/module/market/enums/common/PromotionTypeEnum.java

@@ -19,6 +19,7 @@ public enum PromotionTypeEnum implements IntArrayValuable {
     REWARD_ACTIVITY(2, "满减送"),
 
     MEMBER(3, "会员折扣"),
+    COUPON(4, "优惠劵")
     ;
 
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionTypeEnum::getType).toArray();

+ 139 - 0
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/dal/dataobject/coupon/CouponDO.java

@@ -0,0 +1,139 @@
+package cn.iocoder.yudao.module.market.dal.dataobject.coupon;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.market.enums.common.PromotionProductScopeEnum;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+import java.util.List;
+
+// TODO 芋艿:待完善该实体
+/**
+ * 优惠劵 DO
+ */
+@TableName("coupon")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+public class CouponDO extends BaseDO {
+
+    // ========== 基本信息 BEGIN ==========
+    /**
+     * 优惠劵编号
+     */
+    private Long id;
+    /**
+     * 优惠劵(码)分组编号,{@link CouponTemplateDO} 的 id
+     */
+    private Integer templateId;
+    /**
+     * 优惠劵名
+     *
+     * 冗余自 {@link CouponTemplateDO} 的 title
+     *
+     * TODO 芋艿,暂时不考虑冗余的更新
+     */
+    private String title;
+//    /**
+//     * 核销码
+//     */
+//    private String verifyCode;
+    /**
+     * 优惠码状态
+     *
+     * 1-未使用
+     * 2-已使用
+     * 3-已失效
+     */
+    private Integer status;
+
+    // ========== 基本信息 END ==========
+
+    // ========== 领取情况 BEGIN ==========
+    /**
+     * 用户编号
+     */
+    private Integer userId;
+    /**
+     * 领取类型
+     *
+     * 1 - 用户主动领取
+     * 2 - 后台自动发放
+     */
+    private Integer takeType;
+    // ========== 领取情况 END ==========
+
+    // ========== 使用规则 BEGIN ==========
+    /**
+     * 是否设置满多少金额可用,单位:分
+     */
+    private Integer priceAvailable;
+    /**
+     * 生效开始时间
+     */
+    private Date validStartTime;
+    /**
+     * 生效结束时间
+     */
+    private Date validEndTime;
+    /**
+     * 商品范围
+     *
+     * 枚举 {@link PromotionProductScopeEnum}
+     */
+    private Integer productScope;
+    /**
+     * 商品 SPU 编号的数组
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private List<Long> spuIds;
+    // ========== 使用规则 END ==========
+
+    // ========== 使用效果 BEGIN ==========
+    /**
+     * 优惠类型
+     *
+     * 1-代金卷
+     * 2-折扣卷
+     */
+    private Integer preferentialType;
+    /**
+     * 折扣
+     */
+    private Integer percentOff;
+    /**
+     * 优惠金额,单位:分。
+     */
+    private Integer priceOff;
+    /**
+     * 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
+     *
+     * 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80  元。
+     */
+    private Integer discountPriceLimit;
+    // ========== 使用效果 END ==========
+
+    // ========== 使用情况 BEGIN ==========
+//    /**
+//     * 使用订单号
+//     */
+//    private Integer usedOrderId; // TODO 芋艿,暂时不考虑这个字段
+//    /**
+//     * 订单中优惠面值,单位:分
+//     */
+//    private Integer usedPrice; // TODO 芋艿,暂时不考虑这个字段
+    /**
+     * 使用时间
+     */
+    private Date usedTime;
+
+    // TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
+
+    // ========== 使用情况 END ==========
+
+}

+ 1 - 1
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/banner/BannerServiceImpl.java

@@ -17,7 +17,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 import static cn.iocoder.yudao.module.market.enums.ErrorCodeConstants.BANNER_NOT_EXISTS;
 
 /**
- * 首页banner 实现类
+ * 首页 banner 实现类
  *
  * @author xia
  */

+ 24 - 0
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/coupon/CouponService.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.market.service.coupon;
+
+import cn.iocoder.yudao.module.market.dal.dataobject.coupon.CouponDO;
+
+/**
+ * 优惠劵 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface CouponService {
+
+    /**
+     * 校验优惠劵,包括状态、有限期
+     *
+     * 1. 如果校验通过,则返回优惠劵信息
+     * 2. 如果校验不通过,则直接抛出业务异常
+     *
+     * @param id 优惠劵编号
+     * @param userId 用户编号
+     * @return 优惠劵信息
+     */
+    CouponDO validCoupon(Long id, Long userId);
+
+}

+ 21 - 0
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/coupon/CouponServiceImpl.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.market.service.coupon;
+
+import cn.iocoder.yudao.module.market.dal.dataobject.coupon.CouponDO;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+/**
+ * 优惠劵 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class CouponServiceImpl implements CouponService {
+
+    // TODO 芋艿:待实现
+    @Override
+    public CouponDO validCoupon(Long id, Long userId) {
+        return null;
+    }
+}

+ 93 - 5
yudao-module-mall/yudao-module-market-biz/src/main/java/cn/iocoder/yudao/module/market/service/price/PriceServiceImpl.java

@@ -7,11 +7,14 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
 import cn.iocoder.yudao.module.market.convert.price.PriceConvert;
+import cn.iocoder.yudao.module.market.dal.dataobject.coupon.CouponDO;
 import cn.iocoder.yudao.module.market.dal.dataobject.discount.DiscountProductDO;
 import cn.iocoder.yudao.module.market.dal.dataobject.reward.RewardActivityDO;
 import cn.iocoder.yudao.module.market.enums.common.PromotionConditionTypeEnum;
 import cn.iocoder.yudao.module.market.enums.common.PromotionLevelEnum;
+import cn.iocoder.yudao.module.market.enums.common.PromotionProductScopeEnum;
 import cn.iocoder.yudao.module.market.enums.common.PromotionTypeEnum;
+import cn.iocoder.yudao.module.market.service.coupon.CouponService;
 import cn.iocoder.yudao.module.market.service.discount.DiscountService;
 import cn.iocoder.yudao.module.market.service.reward.RewardService;
 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
@@ -30,6 +33,8 @@ import java.util.function.Supplier;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
+import static cn.iocoder.yudao.module.market.enums.ErrorCodeConstants.COUPON_NO_MATCH_MIN_PRICE;
+import static cn.iocoder.yudao.module.market.enums.ErrorCodeConstants.COUPON_NO_MATCH_SPU;
 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
 import static java.util.Collections.singletonList;
 
@@ -43,7 +48,7 @@ import static java.util.Collections.singletonList;
  * TODO 芋艿:进一步完善
  * 1. 限时折扣:指定金额、减免金额、折扣
  * 2. 满减送:循环、折扣
- * 3.
+ * 3. 优惠劵:待定
  *
  * @author 芋道源码
  */
@@ -55,6 +60,8 @@ public class PriceServiceImpl implements PriceService {
     private DiscountService discountService;
     @Resource
     private RewardService rewardService;
+    @Resource
+    private CouponService couponService;
 
     @Resource
     private ProductSkuApi productSkuApi;
@@ -70,6 +77,8 @@ public class PriceServiceImpl implements PriceService {
         calculatePriceForSkuLevel(calculateReqDTO.getUserId(), priceCalculate);
         // 计算订单级别的价格
         calculatePriceForOrderLevel(calculateReqDTO.getUserId(), priceCalculate);
+        // 计算优惠劵级别的价格
+        calculatePriceForCouponLevel(calculateReqDTO.getUserId(), calculateReqDTO.getCouponId(), priceCalculate);
         // 计算【优惠劵】促销 TODO 待实现
         return priceCalculate;
     }
@@ -228,9 +237,9 @@ public class PriceServiceImpl implements PriceService {
      */
     private RewardActivityDO.Rule getLastMatchRewardActivityRule(RewardActivityDO rewardActivity,
                                                                  List<PriceCalculateRespDTO.OrderItem> orderItems) {
-        Integer count = CollectionUtils.getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getCount, Integer::sum);
+        Integer count = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getCount, Integer::sum);
         // price 的计算逻辑,使用 orderDividePrice 的原因,主要考虑分摊后,这个才是该 SKU 当前真实的支付总价
-        Integer price = CollectionUtils.getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum);
+        Integer price = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum);
         assert count != null && price != null;
         for (int i = rewardActivity.getRules().size() - 1; i >= 0; i--) {
             RewardActivityDO.Rule rule = rewardActivity.getRules().get(i);
@@ -256,6 +265,65 @@ public class PriceServiceImpl implements PriceService {
         return "TODO"; // TODO 芋艿:后面再想想
     }
 
+    // ========== 计算优惠劵级别的价格 ==========
+
+    private void calculatePriceForCouponLevel(Long userId, Long couponId, PriceCalculateRespDTO priceCalculate) {
+        // 校验优惠劵
+        if (couponId == null) {
+            return;
+        }
+        CouponDO coupon = couponService.validCoupon(couponId, userId);
+
+        // 获得匹配的商品 SKU 数组
+        List<PriceCalculateRespDTO.OrderItem> orderItems = getMatchCouponOrderItems(priceCalculate, coupon);
+        if (CollUtil.isEmpty(orderItems)) {
+            throw exception(COUPON_NO_MATCH_SPU);
+        }
+
+        // 计算是否满足优惠劵的使用金额
+        Integer originPrice = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum);
+        assert originPrice != null;
+        if (originPrice < coupon.getPriceAvailable()) {
+            throw exception(COUPON_NO_MATCH_MIN_PRICE);
+        }
+
+        // 计算可以优惠的金额
+        priceCalculate.getOrder().setCouponId(couponId);
+        Integer couponPrice = getCouponPrice(coupon, originPrice);
+        // 分摊金额
+        // TODO 芋艿:limit 不能超过最大价格
+        List<Integer> couponPartPrices = dividePrice(orderItems, couponPrice);
+        // 记录优惠明细
+        addPromotion(priceCalculate, orderItems, coupon.getId(), coupon.getTitle(),
+                PromotionTypeEnum.COUPON.getType(), PromotionLevelEnum.COUPON.getLevel(), couponPartPrices,
+                true, StrUtil.format("优惠劵:省 {} 元", formatPrice(couponPrice)));
+        // 修改 SKU 的分摊
+        for (int i = 0; i < orderItems.size(); i++) {
+            modifyOrderItemOrderPartPriceFromCouponPrice(orderItems.get(i), couponPartPrices.get(i), priceCalculate);
+        }
+    }
+
+    private List<PriceCalculateRespDTO.OrderItem> getMatchCouponOrderItems(PriceCalculateRespDTO priceCalculate,
+                                                                           CouponDO coupon) {
+        if (PromotionProductScopeEnum.ALL.getScope().equals(coupon.getProductScope())) {
+            return priceCalculate.getOrder().getItems();
+        }
+        return CollectionUtils.filterList(priceCalculate.getOrder().getItems(),
+                orderItem -> coupon.getSpuIds().contains(orderItem.getSpuId()));
+    }
+
+    private Integer getCouponPrice(CouponDO coupon, Integer originPrice) {
+        // TODO 芋艿 getPreferentialType 的枚举判断
+        if (coupon.getPreferentialType().equals(1)) { // 减价
+            return coupon.getPriceOff();
+        } else if (coupon.getPreferentialType().equals(2)) { // 打折
+            Integer couponPrice = originPrice * coupon.getPercentOff() / 100;
+            return coupon.getDiscountPriceLimit() == null ? couponPrice
+                    : Math.min(couponPrice, coupon.getDiscountPriceLimit()); // 优惠上限
+        }
+        throw new IllegalArgumentException(String.format("优惠劵(%s) 的优惠类型不正确", coupon.toString()));
+    }
+
     // ========== 其它相对通用的方法 ==========
 
     /**
@@ -325,7 +393,7 @@ public class PriceServiceImpl implements PriceService {
                 orderItem -> new PriceCalculateRespDTO.PromotionItem().setSkuId(orderItem.getSkuId())
                         .setOriginalPrice(orderItem.getOrderDividePrice()).setDiscountPrice(0));
         // 创建营销明细
-        Integer originalPrice = CollectionUtils.getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum);
+        Integer originalPrice = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum);
         PriceCalculateRespDTO.Promotion promotion = new PriceCalculateRespDTO.Promotion()
                 .setId(id).setName(name).setType(type).setLevel(level)
                 .setOriginalPrice(originalPrice).setDiscountPrice(0)
@@ -357,7 +425,7 @@ public class PriceServiceImpl implements PriceService {
      * 本质:分摊 Order 的 discountPrice 价格,到对应的 OrderItem 的 orderPartPrice 价格中
      *
      * @param orderItem 订单商品 SKU
-     * @param addOrderPartPrice 新增的
+     * @param addOrderPartPrice 新增的 discountPrice 价格
      * @param priceCalculate 价格计算结果
      */
     private void modifyOrderItemOrderPartPriceFromDiscountPrice(PriceCalculateRespDTO.OrderItem orderItem, Integer addOrderPartPrice,
@@ -371,6 +439,26 @@ public class PriceServiceImpl implements PriceService {
         order.setPayPrice(order.getPayPrice() - addOrderPartPrice);
     }
 
+    /**
+     * 修改 OrderItem 的 orderPartPrice 价格,同时会修改 Order 的 couponPrice 价格
+     *
+     * 本质:分摊 Order 的 couponPrice 价格,到对应的 OrderItem 的 orderPartPrice 价格中
+     *
+     * @param orderItem 订单商品 SKU
+     * @param addOrderPartPrice 新增的 couponPrice 价格
+     * @param priceCalculate 价格计算结果
+     */
+    private void modifyOrderItemOrderPartPriceFromCouponPrice(PriceCalculateRespDTO.OrderItem orderItem, Integer addOrderPartPrice,
+                                                              PriceCalculateRespDTO priceCalculate) {
+        // 设置 OrderItem 价格相关字段
+        orderItem.setOrderPartPrice(orderItem.getOrderPartPrice() + addOrderPartPrice);
+        orderItem.setOrderDividePrice(orderItem.getPayPrice() - orderItem.getOrderPartPrice());
+        // 设置 Order 相关相关字段
+        PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
+        order.setCouponPrice(order.getCouponPrice() + addOrderPartPrice);
+        order.setPayPrice(order.getPayPrice() - addOrderPartPrice);
+    }
+
     private List<Integer> dividePrice(List<PriceCalculateRespDTO.OrderItem> orderItems, Integer price) {
         List<Integer> prices = new ArrayList<>(orderItems.size());
         Integer total = getSumValue(orderItems, PriceCalculateRespDTO.OrderItem::getOrderDividePrice, Integer::sum);

+ 89 - 0
yudao-module-mall/yudao-module-market-biz/src/test/java/cn/iocoder/yudao/module/market/service/price/PriceServiceTest.java

@@ -4,11 +4,14 @@ import cn.hutool.core.map.MapUtil;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateReqDTO;
 import cn.iocoder.yudao.module.market.api.price.dto.PriceCalculateRespDTO;
+import cn.iocoder.yudao.module.market.dal.dataobject.coupon.CouponDO;
 import cn.iocoder.yudao.module.market.dal.dataobject.discount.DiscountProductDO;
 import cn.iocoder.yudao.module.market.dal.dataobject.reward.RewardActivityDO;
 import cn.iocoder.yudao.module.market.enums.common.PromotionConditionTypeEnum;
 import cn.iocoder.yudao.module.market.enums.common.PromotionLevelEnum;
+import cn.iocoder.yudao.module.market.enums.common.PromotionProductScopeEnum;
 import cn.iocoder.yudao.module.market.enums.common.PromotionTypeEnum;
+import cn.iocoder.yudao.module.market.service.coupon.CouponService;
 import cn.iocoder.yudao.module.market.service.discount.DiscountService;
 import cn.iocoder.yudao.module.market.service.reward.RewardService;
 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
@@ -45,6 +48,8 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
     @Mock
     private RewardService rewardService;
     @Mock
+    private CouponService couponService;
+    @Mock
     private ProductSkuApi productSkuApi;
 
     @Test
@@ -354,4 +359,88 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
         assertEquals(promotionItem012.getDiscountPrice(), 0);
     }
 
+    @Test
+    public void testCalculatePrice_coupon() {
+        // 准备参数
+        PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())
+                .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),
+                        new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3),
+                        new PriceCalculateReqDTO.Item().setSkuId(30L).setCount(4)))
+                .setCouponId(1024L);
+        // mock 方法(商品 SKU 信息)
+        ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L));
+        ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L));
+        ProductSkuRespDTO productSku03 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(30L).setPrice(30).setSpuId(3L));
+        when(productSkuApi.getSkuList(eq(asSet(10L, 20L, 30L)))).thenReturn(asList(productSku01, productSku02, productSku03));
+        // mock 方法(优惠劵 Coupon 信息)
+        CouponDO coupon = randomPojo(CouponDO.class, o -> o.setId(1024L).setTitle("程序员节")
+                .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setSpuIds(asList(1L, 2L))
+                .setPriceAvailable(350).setPreferentialType(2).setPercentOff(50).setDiscountPriceLimit(70));
+        when(couponService.validCoupon(eq(1024L), eq(calculateReqDTO.getUserId()))).thenReturn(coupon);
+
+        // 200 + 150; 350
+        //
+
+        // 调用
+        PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO);
+        // 断言 Order 部分
+        PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
+        assertEquals(order.getOriginalPrice(), 470);
+        assertEquals(order.getDiscountPrice(), 0);
+        assertEquals(order.getPointPrice(), 0);
+        assertEquals(order.getDeliveryPrice(), 0);
+        assertEquals(order.getPayPrice(), 400);
+        assertEquals(order.getCouponId(), 1024L);
+        assertEquals(order.getCouponPrice(), 70);
+        // 断言 OrderItem 部分
+        assertEquals(order.getItems().size(), 3);
+        PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);
+        assertEquals(orderItem01.getSkuId(), 10L);
+        assertEquals(orderItem01.getCount(), 2);
+        assertEquals(orderItem01.getOriginalPrice(), 200);
+        assertEquals(orderItem01.getOriginalUnitPrice(), 100);
+        assertEquals(orderItem01.getDiscountPrice(), 0);
+        assertEquals(orderItem01.getPayPrice(), 200);
+        assertEquals(orderItem01.getOrderPartPrice(), 40);
+        assertEquals(orderItem01.getOrderDividePrice(), 160);
+        PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);
+        assertEquals(orderItem02.getSkuId(), 20L);
+        assertEquals(orderItem02.getCount(), 3);
+        assertEquals(orderItem02.getOriginalPrice(), 150);
+        assertEquals(orderItem02.getOriginalUnitPrice(), 50);
+        assertEquals(orderItem02.getDiscountPrice(), 0);
+        assertEquals(orderItem02.getPayPrice(), 150);
+        assertEquals(orderItem02.getOrderPartPrice(), 30);
+        assertEquals(orderItem02.getOrderDividePrice(), 120);
+        PriceCalculateRespDTO.OrderItem orderItem03 = order.getItems().get(2);
+        assertEquals(orderItem03.getSkuId(), 30L);
+        assertEquals(orderItem03.getCount(), 4);
+        assertEquals(orderItem03.getOriginalPrice(), 120);
+        assertEquals(orderItem03.getOriginalUnitPrice(), 30);
+        assertEquals(orderItem03.getDiscountPrice(), 0);
+        assertEquals(orderItem03.getPayPrice(), 120);
+        assertEquals(orderItem03.getOrderPartPrice(), 0);
+        assertEquals(orderItem03.getOrderDividePrice(), 120);
+        // 断言 Promotion 部分
+        assertEquals(priceCalculate.getPromotions().size(), 1);
+        PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);
+        assertEquals(promotion01.getId(), 1024L);
+        assertEquals(promotion01.getName(), "程序员节");
+        assertEquals(promotion01.getType(), PromotionTypeEnum.COUPON.getType());
+        assertEquals(promotion01.getLevel(), PromotionLevelEnum.COUPON.getLevel());
+        assertEquals(promotion01.getOriginalPrice(), 350);
+        assertEquals(promotion01.getDiscountPrice(), 70);
+        assertTrue(promotion01.getMeet());
+        assertEquals(promotion01.getMeetTip(), "优惠劵:省 0.70 元");
+        assertEquals(promotion01.getItems().size(), 2);
+        PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
+        assertEquals(promotionItem011.getSkuId(), 10L);
+        assertEquals(promotionItem011.getOriginalPrice(), 200);
+        assertEquals(promotionItem011.getDiscountPrice(), 40);
+        PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
+        assertEquals(promotionItem012.getSkuId(), 20L);
+        assertEquals(promotionItem012.getOriginalPrice(), 150);
+        assertEquals(promotionItem012.getDiscountPrice(), 30);
+    }
+
 }

+ 0 - 5
yudao-server/pom.xml

@@ -56,11 +56,6 @@
             <artifactId>yudao-module-trade-biz</artifactId>
             <version>${revision}</version>
         </dependency>
-        <dependency>
-            <groupId>cn.iocoder.boot</groupId>
-            <artifactId>yudao-module-coupon-biz</artifactId>
-            <version>${revision}</version>
-        </dependency>
         <!-- 数据报表 -->
 <!--        <dependency>-->
 <!--            <groupId>cn.iocoder.boot</groupId>-->