2026-06-24 f4bd1f3c89d906131495a0aca5aaf82966378510
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package cn.iocoder.yudao.module.trade.service.price.calculator;
 
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import jakarta.annotation.Resource;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
 
import java.util.List;
import java.util.Map;
 
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.calculateRatePrice;
import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
 
/**
 * 限时折扣的 {@link TradePriceCalculator} 实现类
 *
 * 由于“会员折扣”和“限时折扣”是冲突,需要选择优惠金额多的,所以也放在这里计算
 *
 * @author 芋道源码
 */
@Component
@Order(TradePriceCalculator.ORDER_DISCOUNT_ACTIVITY)
public class TradeDiscountActivityPriceCalculator implements TradePriceCalculator {
 
    @Resource
    private DiscountActivityApi discountActivityApi;
    @Resource
    private MemberLevelApi memberLevelApi;
    @Resource
    private MemberUserApi memberUserApi;
 
    @Override
    public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
        // 0. 只有【普通】订单,才计算该优惠
        if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
            return;
        }
 
        // 1.1 获得 SKU 对应的限时折扣活动
        List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductListBySkuIds(
                convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId));
        Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId);
        // 1.2 获得会员等级
        MemberLevelRespDTO level = getMemberLevel(param.getUserId());
 
        // 2. 计算每个 SKU 的优惠金额
        result.getItems().forEach(orderItem -> {
            if (!orderItem.getSelected()) {
                return;
            }
            // 2.1 计算限时折扣的优惠金额
            DiscountProductRespDTO discountProduct = discountProductMap.get(orderItem.getSkuId());
            Integer discountPrice = calculateActivityPrice(discountProduct, orderItem);
            // 2.2 计算 VIP 优惠金额
            Integer vipPrice = calculateVipPrice(level, orderItem);
            if (discountPrice <= 0 && vipPrice <= 0) {
                return;
            }
 
            // 3. 选择优惠金额多的
            if (discountPrice > vipPrice) {
                TradePriceCalculatorHelper.addPromotion(result, orderItem,
                        discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
                        StrUtil.format("限时折扣:省 {} 元", formatPrice(discountPrice)),
                        discountPrice);
                // 更新 SKU 优惠金额
                orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice);
            } else {
                assert level != null;
                TradePriceCalculatorHelper.addPromotion(result, orderItem,
                        level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
                        String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
                        vipPrice);
                // 更新 SKU 的优惠金额
                orderItem.setVipPrice(vipPrice);
            }
 
            // 4. 分摊优惠
            TradePriceCalculatorHelper.recountPayPrice(orderItem);
            TradePriceCalculatorHelper.recountAllPrice(result);
        });
    }
 
    /**
     * 获得用户的等级
     *
     * @param userId 用户编号
     * @return 用户等级
     */
    public MemberLevelRespDTO getMemberLevel(Long userId) {
        MemberUserRespDTO user = memberUserApi.getUser(userId);
        if (user == null || user.getLevelId() == null || user.getLevelId() <= 0) {
            return null;
        }
        return memberLevelApi.getMemberLevel(user.getLevelId());
    }
 
    /**
     * 计算优惠活动的价格
     *
     * @param discount 优惠活动
     * @param orderItem 交易项
     * @return 优惠价格
     */
    public Integer calculateActivityPrice(DiscountProductRespDTO discount,
                                           TradePriceCalculateRespBO.OrderItem orderItem) {
        if (discount == null) {
            return 0;
        }
        Integer newPrice = orderItem.getPayPrice();
        if (PromotionDiscountTypeEnum.PRICE.getType().equals(discount.getDiscountType())) { // 减价
            newPrice -= discount.getDiscountPrice() * orderItem.getCount();
        } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discount.getDiscountType())) { // 打折
            newPrice = calculateRatePrice(orderItem.getPayPrice(), discount.getDiscountPercent() / 100.0);
        } else {
            throw new IllegalArgumentException(String.format("优惠活动的商品(%s) 的优惠类型不正确", discount));
        }
        return orderItem.getPayPrice() - newPrice;
    }
 
    /**
     * 计算会员 VIP 的优惠价格
     *
     * @param level 会员等级
     * @param orderItem 交易项
     * @return 优惠价格
     */
    public Integer calculateVipPrice(MemberLevelRespDTO level,
                                      TradePriceCalculateRespBO.OrderItem orderItem) {
        if (level == null
                || CommonStatusEnum.isDisable(level.getStatus())
                || level.getDiscountPercent() == null) {
            return 0;
        }
        Integer newPrice = calculateRatePrice(orderItem.getPayPrice(), level.getDiscountPercent().doubleValue());
        return orderItem.getPayPrice() - newPrice;
    }
 
}