package cn.iocoder.yudao.module.trade.service.price.calculator; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.member.api.config.MemberConfigApi; import cn.iocoder.yudao.module.member.api.config.dto.MemberConfigRespDTO; 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.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 lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL; /** * 使用积分的 {@link TradePriceCalculator} 实现类 * * @author owen */ @Component @Order(TradePriceCalculator.ORDER_POINT_USE) @Slf4j public class TradePointUsePriceCalculator implements TradePriceCalculator { @Resource private MemberConfigApi memberConfigApi; @Resource private MemberUserApi memberUserApi; @Override public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { // 判断订单类型是否不为积分商城活动 if (ObjectUtil.equal(result.getType(), TradeOrderTypeEnum.POINT.getType())) { return; } // 0. 初始化积分 MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()); result.setTotalPoint(user.getPoint()).setUsePoint(0); // 1.1 校验是否使用积分 if (!BooleanUtil.isTrue(param.getPointStatus())) { return; } // 1.2 校验积分抵扣是否开启 MemberConfigRespDTO config = memberConfigApi.getConfig(); if (!isDeductPointEnable(config)) { return; } // 1.3 校验用户积分余额 if (user.getPoint() == null || user.getPoint() <= 0) { return; } // 2.1 计算积分优惠金额 int pointPrice = calculatePointPrice(config, user.getPoint(), result); // 2.2 计算分摊的积分、抵扣金额 List orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected); List dividePointPrices = TradePriceCalculatorHelper.dividePrice(orderItems, pointPrice); List divideUsePoints = TradePriceCalculatorHelper.dividePrice(orderItems, result.getUsePoint()); // 3.1 记录优惠明细 TradePriceCalculatorHelper.addPromotion(result, orderItems, param.getUserId(), "积分抵扣", PromotionTypeEnum.POINT.getType(), StrUtil.format("积分抵扣:省 {} 元", TradePriceCalculatorHelper.formatPrice(pointPrice)), dividePointPrices); // 3.2 更新 SKU 优惠金额 for (int i = 0; i < orderItems.size(); i++) { TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); orderItem.setPointPrice(dividePointPrices.get(i)); orderItem.setUsePoint(divideUsePoints.get(i)); TradePriceCalculatorHelper.recountPayPrice(orderItem); } TradePriceCalculatorHelper.recountAllPrice(result); } private boolean isDeductPointEnable(MemberConfigRespDTO config) { return config != null && BooleanUtil.isTrue(config.getPointTradeDeductEnable()) && // 积分功能是否启用 config.getPointTradeDeductUnitPrice() != null && config.getPointTradeDeductUnitPrice() > 0; // 有没有配置:1 积分抵扣多少分 } private Integer calculatePointPrice(MemberConfigRespDTO config, Integer usePoint, TradePriceCalculateRespBO result) { // 每个订单最多可以使用的积分数量 if (config.getPointTradeDeductMaxPrice() != null && config.getPointTradeDeductMaxPrice() > 0) { usePoint = Math.min(usePoint, config.getPointTradeDeductMaxPrice()); } // TODO @疯狂:这里应该是,抵扣到只剩下 0.01; // 积分优惠金额(分) int pointPrice = usePoint * config.getPointTradeDeductUnitPrice(); if (result.getPrice().getPayPrice() <= pointPrice) { // 禁止 0 元购 throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL); } // // 允许0 元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额,这时需要根据支付金额反推使用多少积分 // if (result.getPrice().getPayPrice() < pointPrice) { // pointPrice = result.getPrice().getPayPrice(); // // 反推需要扣除的积分 // usePoint = NumberUtil.toBigDecimal(pointPrice) // .divide(NumberUtil.toBigDecimal(config.getPointTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP) // .intValue(); // } // 记录使用的积分 result.setUsePoint(usePoint); return pointPrice; } }