mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-08-15 02:31:53 +08:00
Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17
This commit is contained in:
@@ -6,13 +6,13 @@ import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 交易订单结算 Request VO")
|
||||
@@ -62,6 +62,10 @@ public class AppTradeOrderSettlementReqVO {
|
||||
@Schema(description = "砍价记录编号", example = "123")
|
||||
private Long bargainRecordId;
|
||||
|
||||
// ========== 积分商城活动相关字段 ==========
|
||||
@Schema(description = "积分商城活动编号", example = "123")
|
||||
private Long pointActivityId;
|
||||
|
||||
@AssertTrue(message = "活动商品每次只能购买一种规格")
|
||||
@JsonIgnore
|
||||
public boolean isValidActivityItems() {
|
||||
|
@@ -219,7 +219,8 @@ public interface TradeOrderConvert {
|
||||
.setSeckillActivityId(settlementReqVO.getSeckillActivityId())
|
||||
.setBargainRecordId(settlementReqVO.getBargainRecordId())
|
||||
.setCombinationActivityId(settlementReqVO.getCombinationActivityId())
|
||||
.setCombinationHeadId(settlementReqVO.getCombinationHeadId());
|
||||
.setCombinationHeadId(settlementReqVO.getCombinationHeadId())
|
||||
.setPointActivityId(settlementReqVO.getPointActivityId());
|
||||
// 商品项的构建
|
||||
Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId);
|
||||
for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {
|
||||
|
@@ -353,4 +353,11 @@ public class TradeOrderDO extends BaseDO {
|
||||
*/
|
||||
private Long combinationRecordId;
|
||||
|
||||
/**
|
||||
* 积分商城活动的编号
|
||||
*
|
||||
* 关联 PointActivityDO 的 id 字段
|
||||
*/
|
||||
private Long pointActivityId;
|
||||
|
||||
}
|
||||
|
@@ -42,9 +42,9 @@ public interface TradeOrderQueryService {
|
||||
/**
|
||||
* 获得指定用户,指定活动,指定状态的交易订单
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userId 用户编号
|
||||
* @param combinationActivityId 活动编号
|
||||
* @param status 订单状态
|
||||
* @param status 订单状态
|
||||
* @return 交易订单
|
||||
*/
|
||||
TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status);
|
||||
|
@@ -245,7 +245,10 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
}
|
||||
|
||||
// 3. 生成预支付
|
||||
createPayOrder(order, orderItems);
|
||||
// 特殊情况:积分兑换时,可能支付金额为零
|
||||
if (order.getPayPrice() > 0) {
|
||||
createPayOrder(order, orderItems);
|
||||
}
|
||||
|
||||
// 4. 插入订单日志
|
||||
TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());
|
||||
|
@@ -0,0 +1,71 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order.handler;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 积分商城活动订单的 {@link TradeOrderHandler} 实现类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Component
|
||||
public class TradePointOrderHandler implements TradeOrderHandler {
|
||||
|
||||
@Resource
|
||||
private PointActivityApi pointActivityApi;
|
||||
|
||||
@Override
|
||||
public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
|
||||
if (!TradeOrderTypeEnum.isPoint(order.getType())) {
|
||||
return;
|
||||
}
|
||||
// 明确校验一下
|
||||
Assert.isTrue(orderItems.size() == 1, "积分商城活动兑换商品兑换时,只允许选择一个商品");
|
||||
|
||||
// 扣减积分商城活动的库存
|
||||
pointActivityApi.updatePointStockDecr(order.getPointActivityId(),
|
||||
orderItems.get(0).getSkuId(), orderItems.get(0).getCount());
|
||||
|
||||
// 如果支付金额为 0,则直接设置为已支付
|
||||
if (Objects.equals(order.getPayPrice(), 0)) {
|
||||
order.setPayStatus(true).setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
|
||||
if (!TradeOrderTypeEnum.isPoint(order.getType())) {
|
||||
return;
|
||||
}
|
||||
// 明确校验一下
|
||||
Assert.isTrue(orderItems.size() == 1, "积分商城活动兑换商品兑换时,只允许选择一个商品");
|
||||
|
||||
// 售后的订单项,已经在 afterCancelOrderItem 回滚库存,所以这里不需要重复回滚
|
||||
orderItems = filterOrderItemListByNoneAfterSale(orderItems);
|
||||
if (CollUtil.isEmpty(orderItems)) {
|
||||
return;
|
||||
}
|
||||
afterCancelOrderItem(order, orderItems.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
|
||||
if (!TradeOrderTypeEnum.isPoint(order.getType())) {
|
||||
return;
|
||||
}
|
||||
// 恢复积分商城活动的库存
|
||||
pointActivityApi.updatePointStockIncr(order.getPointActivityId(),
|
||||
orderItem.getSkuId(), orderItem.getCount());
|
||||
}
|
||||
|
||||
}
|
@@ -69,7 +69,8 @@ public class TradePriceServiceImpl implements TradePriceService {
|
||||
.buildCalculateResp(calculateReqBO, spuList, skuList);
|
||||
priceCalculators.forEach(calculator -> calculator.calculate(calculateReqBO, calculateRespBO));
|
||||
// 2.2 如果最终支付金额小于等于 0,则抛出业务异常
|
||||
if (calculateRespBO.getPrice().getPayPrice() <= 0) {
|
||||
if (calculateReqBO.getPointActivityId() == null // 积分订单,允许支付金额为 0
|
||||
&& calculateRespBO.getPrice().getPayPrice() <= 0) {
|
||||
log.error("[calculatePrice][价格计算不正确,请求 calculateReqDTO({}),结果 priceCalculate({})]",
|
||||
calculateReqBO, calculateRespBO);
|
||||
throw exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL);
|
||||
|
@@ -1,11 +1,11 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.bo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -84,6 +84,12 @@ public class TradePriceCalculateReqBO {
|
||||
*/
|
||||
private Long bargainRecordId;
|
||||
|
||||
// ========== 积分商城活动相关字段 ==========
|
||||
/**
|
||||
* 积分商城活动编号
|
||||
*/
|
||||
private Long pointActivityId;
|
||||
|
||||
/**
|
||||
* 商品 SKU
|
||||
*/
|
||||
|
@@ -0,0 +1,94 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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.point.PointActivityApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;
|
||||
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.order.TradeOrderQueryService;
|
||||
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 static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT;
|
||||
|
||||
/**
|
||||
* 积分商城的 {@link TradePriceCalculator} 实现类
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Component
|
||||
@Order(TradePriceCalculator.ORDER_POINT_ACTIVITY)
|
||||
@Slf4j
|
||||
public class TradePointActivityPriceCalculator implements TradePriceCalculator {
|
||||
|
||||
@Resource
|
||||
private PointActivityApi pointActivityApi;
|
||||
@Resource
|
||||
private MemberUserApi memberUserApi;
|
||||
|
||||
@Resource
|
||||
private TradeOrderQueryService tradeOrderQueryService;
|
||||
|
||||
@Override
|
||||
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||
// 1.1 判断订单类型是否为积分商城活动
|
||||
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.POINT.getType())) {
|
||||
return;
|
||||
}
|
||||
// 1.2 初始化积分
|
||||
MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
|
||||
result.setTotalPoint(user.getPoint()).setUsePoint(0);
|
||||
|
||||
// 1.3 校验用户积分余额
|
||||
if (user.getPoint() == null || user.getPoint() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.isTrue(param.getItems().size() == 1, "积分商城兑换商品时,只允许选择一个商品");
|
||||
// 2. 校验是否可以参与积分商城活动
|
||||
TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0);
|
||||
PointValidateJoinRespDTO activity = validateJoinSeckill(
|
||||
param.getUserId(), param.getPointActivityId(),
|
||||
orderItem.getSkuId(), orderItem.getCount());
|
||||
|
||||
// 3.1 记录优惠明细
|
||||
int discountPrice = orderItem.getPayPrice(); // 情况一:单使用积分兑换
|
||||
Assert.isTrue(activity.getPoint() >= 1, "积分商城商品兑换积分必须大于 1");
|
||||
result.setUsePoint(activity.getPoint() * orderItem.getCount());
|
||||
orderItem.setUsePoint(activity.getPoint() * orderItem.getCount());
|
||||
if (activity.getPrice() != null && activity.getPrice() > 0) { // 情况二:积分 + 金额
|
||||
discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount();
|
||||
}
|
||||
// 3.2 记录优惠明细
|
||||
TradePriceCalculatorHelper.addPromotion(result, orderItem,
|
||||
param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(),
|
||||
StrUtil.format("积分商城活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)),
|
||||
discountPrice);
|
||||
|
||||
// 3.3 更新 SKU 优惠金额
|
||||
orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice);
|
||||
TradePriceCalculatorHelper.recountPayPrice(orderItem);
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
}
|
||||
|
||||
private PointValidateJoinRespDTO validateJoinSeckill(Long userId, Long activityId, Long skuId, Integer count) {
|
||||
// 1. 校验是否可以参与积分商城活动
|
||||
PointValidateJoinRespDTO pointValidateJoinRespDTO = pointActivityApi.validateJoinPointActivity(activityId, skuId, count);
|
||||
// 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用
|
||||
int activityProductCount = tradeOrderQueryService.getSeckillProductCount(userId, activityId);
|
||||
if (activityProductCount + count > pointValidateJoinRespDTO.getCount()) {
|
||||
throw exception(PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT);
|
||||
}
|
||||
return pointValidateJoinRespDTO;
|
||||
}
|
||||
|
||||
}
|
@@ -1,12 +1,14 @@
|
||||
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;
|
||||
@@ -37,6 +39,10 @@ public class TradePointUsePriceCalculator implements TradePriceCalculator {
|
||||
|
||||
@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);
|
||||
|
@@ -16,6 +16,7 @@ public interface TradePriceCalculator {
|
||||
int ORDER_SECKILL_ACTIVITY = 8;
|
||||
int ORDER_BARGAIN_ACTIVITY = 8;
|
||||
int ORDER_COMBINATION_ACTIVITY = 8;
|
||||
int ORDER_POINT_ACTIVITY = 8;
|
||||
|
||||
int ORDER_DISCOUNT_ACTIVITY = 10;
|
||||
int ORDER_REWARD_ACTIVITY = 20;
|
||||
|
@@ -90,6 +90,9 @@ public class TradePriceCalculatorHelper {
|
||||
if (param.getBargainRecordId() != null) {
|
||||
return TradeOrderTypeEnum.BARGAIN.getType();
|
||||
}
|
||||
if (param.getPointActivityId() != null) {
|
||||
return TradeOrderTypeEnum.POINT.getType();
|
||||
}
|
||||
return TradeOrderTypeEnum.NORMAL.getType();
|
||||
}
|
||||
|
||||
|
@@ -8,11 +8,10 @@ import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
|
||||
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 jakarta.annotation.Resource;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT;
|
||||
|
||||
|
Reference in New Issue
Block a user