Commit f01c600492b29c0e24a99c1e77150a679846729f

Authored by puhui999
1 parent f72dd272

【功能完善】商城: APP 完善积分商城活动订单价格计算

Showing 18 changed files with 237 additions and 18 deletions
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.point;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;
  4 +
  5 +/**
  6 + * 积分商城活动 API 接口
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +public interface PointActivityApi {
  11 +
  12 + /**
  13 + * 【下单前】校验是否参与积分商城活动
  14 + *
  15 + * 如果校验失败,则抛出业务异常
  16 + *
  17 + * @param activityId 活动编号
  18 + * @param skuId SKU 编号
  19 + * @param count 数量
  20 + * @return 积分商城商品信息
  21 + */
  22 + PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count);
  23 +
  24 +
  25 +}
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.point.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 校验参与积分商城 Response DTO
  7 + */
  8 +@Data
  9 +public class PointValidateJoinRespDTO {
  10 +
  11 + /**
  12 + * 可兑换次数
  13 + */
  14 + private Integer count;
  15 + /**
  16 + * 所需兑换积分
  17 + */
  18 + private Integer point;
  19 + /**
  20 + * 所需兑换金额,单位:分
  21 + */
  22 + private Integer price;
  23 +
  24 +}
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
@@ -50,6 +50,10 @@ public interface ErrorCodeConstants { @@ -50,6 +50,10 @@ public interface ErrorCodeConstants {
50 ErrorCode POINT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_007_002, "积分商城活动已关闭,不能修改"); 50 ErrorCode POINT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_007_002, "积分商城活动已关闭,不能修改");
51 ErrorCode POINT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_007_003, "积分商城活动未关闭或未结束,不能删除"); 51 ErrorCode POINT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_007_003, "积分商城活动未关闭或未结束,不能删除");
52 ErrorCode POINT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_007_004, "积分商城活动已关闭,不能重复关闭"); 52 ErrorCode POINT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_007_004, "积分商城活动已关闭,不能重复关闭");
  53 + ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_STATUS_CLOSED = new ErrorCode(1_013_007_005, "积分商品兑换失败,原因:积分商城活动已关闭");
  54 + ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_007_006, "积分商品兑换失败,原因:单次限购超出");
  55 + ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS = new ErrorCode(1_013_007_007, "积分商品兑换失败,原因:商品不存在");
  56 + ErrorCode POINT_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1_013_007_008, "积分商品兑换失败,原因:积分商品库存不足");
53 57
54 // ========== 秒杀活动 1-013-008-000 ========== 58 // ========== 秒杀活动 1-013-008-000 ==========
55 ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_008_000, "秒杀活动不存在"); 59 ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_008_000, "秒杀活动不存在");
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.point;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;
  4 +import cn.iocoder.yudao.module.promotion.service.point.PointActivityService;
  5 +import jakarta.annotation.Resource;
  6 +import org.springframework.stereotype.Service;
  7 +import org.springframework.validation.annotation.Validated;
  8 +
  9 +/**
  10 + * 积分商城活动 Api 接口实现类
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@Service
  15 +@Validated
  16 +public class PointActivityApiImpl implements PointActivityApi {
  17 +
  18 + @Resource
  19 + private PointActivityService pointActivityService;
  20 +
  21 + @Override
  22 + public PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count) {
  23 + return pointActivityService.validateJoinPointActivity(activityId, skuId, count);
  24 + }
  25 +
  26 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java
@@ -29,4 +29,9 @@ public interface PointProductMapper extends BaseMapperX<PointProductDO> { @@ -29,4 +29,9 @@ public interface PointProductMapper extends BaseMapperX<PointProductDO> {
29 .eq(PointProductDO::getActivityId, pointProductDO.getActivityId())); 29 .eq(PointProductDO::getActivityId, pointProductDO.getActivityId()));
30 } 30 }
31 31
  32 + default PointProductDO selectListByActivityIdAndSkuId(Long activityId, Long skuId) {
  33 + return selectOne(PointProductDO::getActivityId, activityId,
  34 + PointProductDO::getSkuId, skuId);
  35 + }
  36 +
32 } 37 }
33 \ No newline at end of file 38 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java
1 package cn.iocoder.yudao.module.promotion.service.point; 1 package cn.iocoder.yudao.module.promotion.service.point;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;
4 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO;
6 import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO;
@@ -78,4 +79,16 @@ public interface PointActivityService { @@ -78,4 +79,16 @@ public interface PointActivityService {
78 */ 79 */
79 List<PointProductDO> getPointProductListByActivityIds(Collection<Long> activityIds); 80 List<PointProductDO> getPointProductListByActivityIds(Collection<Long> activityIds);
80 81
  82 + /**
  83 + * 【下单前】校验是否参与积分商城活动
  84 + *
  85 + * 如果校验失败,则抛出业务异常
  86 + *
  87 + * @param activityId 活动编号
  88 + * @param skuId SKU 编号
  89 + * @param count 数量
  90 + * @return 积分商城商品信息
  91 + */
  92 + PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count);
  93 +
81 } 94 }
82 \ No newline at end of file 95 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
8 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 8 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
9 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; 9 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
10 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 10 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  11 +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;
11 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; 12 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO;
12 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO; 13 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO;
13 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductSaveReqVO; 14 import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductSaveReqVO;
@@ -244,4 +245,28 @@ public class PointActivityServiceImpl implements PointActivityService { @@ -244,4 +245,28 @@ public class PointActivityServiceImpl implements PointActivityService {
244 return pointProductMapper.selectListByActivityId(activityIds); 245 return pointProductMapper.selectListByActivityId(activityIds);
245 } 246 }
246 247
  248 + @Override
  249 + public PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count) {
  250 + // 1. 校验积分商城活动是否存在
  251 + PointActivityDO activity = validatePointActivityExists(activityId);
  252 + if (CommonStatusEnum.isDisable(activity.getStatus())) {
  253 + throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_STATUS_CLOSED);
  254 + }
  255 +
  256 + // 2.1 校验积分商城商品是否存在
  257 + PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(activityId, skuId);
  258 + if (product == null) {
  259 + throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
  260 + }
  261 + // 2.2 超过单次购买限制
  262 + if (count > product.getCount()) {
  263 + throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED);
  264 + }
  265 + // 2.2 校验库存是否充足
  266 + if (count > product.getStock()) {
  267 + throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL);
  268 + }
  269 + return BeanUtils.toBean(product, PointValidateJoinRespDTO.class);
  270 + }
  271 +
247 } 272 }
248 \ No newline at end of file 273 \ No newline at end of file
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -58,11 +58,12 @@ public interface ErrorCodeConstants { @@ -58,11 +58,12 @@ public interface ErrorCodeConstants {
58 58
59 // ========== Price 相关 1-011-003-000 ============ 59 // ========== Price 相关 1-011-003-000 ============
60 ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1_011_003_000, "支付价格计算异常,原因:价格小于等于 0"); 60 ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1_011_003_000, "支付价格计算异常,原因:价格小于等于 0");
61 - ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1_011_003_002, "计算快递运费异常,找不到对应的运费模板");  
62 - ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵");  
63 - ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_005, "参与秒杀的商品,超过了秒杀总限购数量");  
64 - ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_006, "计算快递运费异常,配送方式不匹配");  
65 - ErrorCode PRICE_CALCULATE_COUPON_CAN_NOT_USE = new ErrorCode(1_011_003_007, "该优惠劵无法使用,原因:{}」"); 61 + ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1_011_003_001, "计算快递运费异常,找不到对应的运费模板");
  62 + ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_002, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵");
  63 + ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_003, "参与秒杀的商品,超过了秒杀总限购数量");
  64 + ErrorCode PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_004, "参与积分活动的商品,超过了积分活动商品总限购数量");
  65 + ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_005, "计算快递运费异常,配送方式不匹配");
  66 + ErrorCode PRICE_CALCULATE_COUPON_CAN_NOT_USE = new ErrorCode(1_011_003_006, "该优惠劵无法使用,原因:{}」");
66 67
67 // ========== 物流 Express 模块 1-011-004-000 ========== 68 // ========== 物流 Express 模块 1-011-004-000 ==========
68 ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在"); 69 ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在");
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java
@@ -20,6 +20,7 @@ public enum TradeOrderTypeEnum implements IntArrayValuable { @@ -20,6 +20,7 @@ public enum TradeOrderTypeEnum implements IntArrayValuable {
20 SECKILL(1, "秒杀订单"), 20 SECKILL(1, "秒杀订单"),
21 BARGAIN(2, "砍价订单"), 21 BARGAIN(2, "砍价订单"),
22 COMBINATION(3, "拼团订单"), 22 COMBINATION(3, "拼团订单"),
  23 + POINT(4, "积分商城"),
23 ; 24 ;
24 25
25 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeOrderTypeEnum::getType).toArray(); 26 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeOrderTypeEnum::getType).toArray();
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java
@@ -6,13 +6,13 @@ import cn.iocoder.yudao.framework.common.validation.Mobile; @@ -6,13 +6,13 @@ import cn.iocoder.yudao.framework.common.validation.Mobile;
6 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 6 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
7 import com.fasterxml.jackson.annotation.JsonIgnore; 7 import com.fasterxml.jackson.annotation.JsonIgnore;
8 import io.swagger.v3.oas.annotations.media.Schema; 8 import io.swagger.v3.oas.annotations.media.Schema;
9 -import lombok.Data;  
10 -  
11 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
12 import jakarta.validation.constraints.AssertTrue; 10 import jakarta.validation.constraints.AssertTrue;
13 import jakarta.validation.constraints.Min; 11 import jakarta.validation.constraints.Min;
14 import jakarta.validation.constraints.NotEmpty; 12 import jakarta.validation.constraints.NotEmpty;
15 import jakarta.validation.constraints.NotNull; 13 import jakarta.validation.constraints.NotNull;
  14 +import lombok.Data;
  15 +
16 import java.util.List; 16 import java.util.List;
17 17
18 @Schema(description = "用户 App - 交易订单结算 Request VO") 18 @Schema(description = "用户 App - 交易订单结算 Request VO")
@@ -62,6 +62,10 @@ public class AppTradeOrderSettlementReqVO { @@ -62,6 +62,10 @@ public class AppTradeOrderSettlementReqVO {
62 @Schema(description = "砍价记录编号", example = "123") 62 @Schema(description = "砍价记录编号", example = "123")
63 private Long bargainRecordId; 63 private Long bargainRecordId;
64 64
  65 + // ========== 积分商城活动相关字段 ==========
  66 + @Schema(description = "积分商城活动编号", example = "123")
  67 + private Long pointActivityId;
  68 +
65 @AssertTrue(message = "活动商品每次只能购买一种规格") 69 @AssertTrue(message = "活动商品每次只能购买一种规格")
66 @JsonIgnore 70 @JsonIgnore
67 public boolean isValidActivityItems() { 71 public boolean isValidActivityItems() {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
@@ -219,7 +219,8 @@ public interface TradeOrderConvert { @@ -219,7 +219,8 @@ public interface TradeOrderConvert {
219 .setSeckillActivityId(settlementReqVO.getSeckillActivityId()) 219 .setSeckillActivityId(settlementReqVO.getSeckillActivityId())
220 .setBargainRecordId(settlementReqVO.getBargainRecordId()) 220 .setBargainRecordId(settlementReqVO.getBargainRecordId())
221 .setCombinationActivityId(settlementReqVO.getCombinationActivityId()) 221 .setCombinationActivityId(settlementReqVO.getCombinationActivityId())
222 - .setCombinationHeadId(settlementReqVO.getCombinationHeadId()); 222 + .setCombinationHeadId(settlementReqVO.getCombinationHeadId())
  223 + .setPointActivityId(settlementReqVO.getPointActivityId());
223 // 商品项的构建 224 // 商品项的构建
224 Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId); 225 Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId);
225 for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) { 226 for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java
@@ -42,9 +42,9 @@ public interface TradeOrderQueryService { @@ -42,9 +42,9 @@ public interface TradeOrderQueryService {
42 /** 42 /**
43 * 获得指定用户,指定活动,指定状态的交易订单 43 * 获得指定用户,指定活动,指定状态的交易订单
44 * 44 *
45 - * @param userId 用户编号 45 + * @param userId 用户编号
46 * @param combinationActivityId 活动编号 46 * @param combinationActivityId 活动编号
47 - * @param status 订单状态 47 + * @param status 订单状态
48 * @return 交易订单 48 * @return 交易订单
49 */ 49 */
50 TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status); 50 TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status);
@@ -116,7 +116,7 @@ public interface TradeOrderQueryService { @@ -116,7 +116,7 @@ public interface TradeOrderQueryService {
116 * @param activityId 活动编号 116 * @param activityId 活动编号
117 * @return 秒杀商品数量 117 * @return 秒杀商品数量
118 */ 118 */
119 - int getSeckillProductCount(Long userId, Long activityId); 119 + int getActivityProductCount(Long userId, Long activityId);
120 120
121 // =================== Order Item =================== 121 // =================== Order Item ===================
122 122
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java
@@ -23,10 +23,10 @@ import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClien @@ -23,10 +23,10 @@ import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClien
23 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; 23 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
24 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; 24 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
25 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; 25 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
  26 +import jakarta.annotation.Resource;
26 import org.springframework.cache.annotation.Cacheable; 27 import org.springframework.cache.annotation.Cacheable;
27 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
28 29
29 -import jakarta.annotation.Resource;  
30 import java.util.*; 30 import java.util.*;
31 31
32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -174,7 +174,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { @@ -174,7 +174,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
174 } 174 }
175 175
176 @Override 176 @Override
177 - public int getSeckillProductCount(Long userId, Long activityId) { 177 + public int getActivityProductCount(Long userId, Long activityId) {
178 // 获得订单列表 178 // 获得订单列表
179 List<TradeOrderDO> orders = tradeOrderMapper.selectListByUserIdAndSeckillActivityId(userId, activityId); 179 List<TradeOrderDO> orders = tradeOrderMapper.selectListByUserIdAndSeckillActivityId(userId, activityId);
180 orders.removeIf(order -> TradeOrderStatusEnum.isCanceled(order.getStatus())); // 过滤掉【已取消】的订单 180 orders.removeIf(order -> TradeOrderStatusEnum.isCanceled(order.getStatus())); // 过滤掉【已取消】的订单
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java
1 package cn.iocoder.yudao.module.trade.service.price.bo; 1 package cn.iocoder.yudao.module.trade.service.price.bo;
2 2
3 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 3 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
4 -import lombok.Data;  
5 -  
6 import jakarta.validation.Valid; 4 import jakarta.validation.Valid;
7 import jakarta.validation.constraints.Min; 5 import jakarta.validation.constraints.Min;
8 import jakarta.validation.constraints.NotNull; 6 import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +
9 import java.util.List; 9 import java.util.List;
10 10
11 /** 11 /**
@@ -84,6 +84,12 @@ public class TradePriceCalculateReqBO { @@ -84,6 +84,12 @@ public class TradePriceCalculateReqBO {
84 */ 84 */
85 private Long bargainRecordId; 85 private Long bargainRecordId;
86 86
  87 + // ========== 积分商城活动相关字段 ==========
  88 + /**
  89 + * 积分商城活动编号
  90 + */
  91 + private Long pointActivityId;
  92 +
87 /** 93 /**
88 * 商品 SKU 94 * 商品 SKU
89 */ 95 */
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.price.calculator;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.util.ObjectUtil;
  5 +import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi;
  7 +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO;
  8 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
  9 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
  10 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
  11 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  12 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  13 +import jakarta.annotation.Resource;
  14 +import lombok.extern.slf4j.Slf4j;
  15 +import org.springframework.core.annotation.Order;
  16 +import org.springframework.stereotype.Component;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  19 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT;
  20 +
  21 +/**
  22 + * 积分商城的 {@link TradePriceCalculator} 实现类
  23 + *
  24 + * @author owen
  25 + */
  26 +@Component
  27 +@Order(TradePriceCalculator.ORDER_POINT_ACTIVITY)
  28 +@Slf4j
  29 +public class TradePointActivityPriceCalculator implements TradePriceCalculator {
  30 +
  31 + @Resource
  32 + private PointActivityApi pointActivityApi;
  33 +
  34 + @Resource
  35 + private TradeOrderQueryService tradeOrderQueryService;
  36 +
  37 + @Override
  38 + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
  39 + // 1. 判断订单类型是否为积分商城活动
  40 + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.POINT.getType())) {
  41 + return;
  42 + }
  43 +
  44 + Assert.isTrue(param.getItems().size() == 1, "积分商城兑换商品时,只允许选择一个商品");
  45 + // 2. 校验是否可以参与积分商城活动
  46 + TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0);
  47 + PointValidateJoinRespDTO activity = validateJoinSeckill(
  48 + param.getUserId(), param.getPointActivityId(),
  49 + orderItem.getSkuId(), orderItem.getCount());
  50 +
  51 + // 3.1 记录优惠明细
  52 + int discountPrice = 0;
  53 + Assert.isTrue(activity.getPoint() >= 1, "积分商城商品兑换积分必须大于 1");
  54 + // 情况一:单使用积分兑换
  55 + if (activity.getPrice() == null || activity.getPrice() == 0) {
  56 + discountPrice = orderItem.getPayPrice();
  57 + } else { // 情况二:积分 + 金额
  58 + discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount();
  59 + }
  60 + TradePriceCalculatorHelper.addPromotion(result, orderItem,
  61 + param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(),
  62 + StrUtil.format("积分商城活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)),
  63 + discountPrice);
  64 + // 3.2 更新 SKU 优惠金额
  65 + orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice);
  66 + TradePriceCalculatorHelper.recountPayPrice(orderItem);
  67 + TradePriceCalculatorHelper.recountAllPrice(result);
  68 + }
  69 +
  70 + private PointValidateJoinRespDTO validateJoinSeckill(Long userId, Long activityId, Long skuId, Integer count) {
  71 + // 1. 校验是否可以参与积分商城活动
  72 + PointValidateJoinRespDTO pointValidateJoinRespDTO = pointActivityApi.validateJoinPointActivity(activityId, skuId, count);
  73 + // 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用
  74 + int activityProductCount = tradeOrderQueryService.getActivityProductCount(userId, activityId);
  75 + if (activityProductCount + count > pointValidateJoinRespDTO.getCount()) {
  76 + throw exception(PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT);
  77 + }
  78 + return pointValidateJoinRespDTO;
  79 + }
  80 +
  81 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java
@@ -16,6 +16,7 @@ public interface TradePriceCalculator { @@ -16,6 +16,7 @@ public interface TradePriceCalculator {
16 int ORDER_SECKILL_ACTIVITY = 8; 16 int ORDER_SECKILL_ACTIVITY = 8;
17 int ORDER_BARGAIN_ACTIVITY = 8; 17 int ORDER_BARGAIN_ACTIVITY = 8;
18 int ORDER_COMBINATION_ACTIVITY = 8; 18 int ORDER_COMBINATION_ACTIVITY = 8;
  19 + int ORDER_POINT_ACTIVITY = 8;
19 20
20 int ORDER_DISCOUNT_ACTIVITY = 10; 21 int ORDER_DISCOUNT_ACTIVITY = 10;
21 int ORDER_REWARD_ACTIVITY = 20; 22 int ORDER_REWARD_ACTIVITY = 20;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
@@ -90,6 +90,9 @@ public class TradePriceCalculatorHelper { @@ -90,6 +90,9 @@ public class TradePriceCalculatorHelper {
90 if (param.getBargainRecordId() != null) { 90 if (param.getBargainRecordId() != null) {
91 return TradeOrderTypeEnum.BARGAIN.getType(); 91 return TradeOrderTypeEnum.BARGAIN.getType();
92 } 92 }
  93 + if (param.getPointActivityId() != null) {
  94 + return TradeOrderTypeEnum.POINT.getType();
  95 + }
93 return TradeOrderTypeEnum.NORMAL.getType(); 96 return TradeOrderTypeEnum.NORMAL.getType();
94 } 97 }
95 98
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java
@@ -8,11 +8,10 @@ import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; @@ -8,11 +8,10 @@ import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
8 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 8 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
9 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 9 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
10 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 10 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  11 +import jakarta.annotation.Resource;
11 import org.springframework.core.annotation.Order; 12 import org.springframework.core.annotation.Order;
12 import org.springframework.stereotype.Component; 13 import org.springframework.stereotype.Component;
13 14
14 -import jakarta.annotation.Resource;  
15 -  
16 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 15 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
17 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT; 16 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT;
18 17
@@ -61,7 +60,7 @@ public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator @@ -61,7 +60,7 @@ public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator
61 // 1. 校验是否可以参与秒杀 60 // 1. 校验是否可以参与秒杀
62 SeckillValidateJoinRespDTO seckillActivity = seckillActivityApi.validateJoinSeckill(activityId, skuId, count); 61 SeckillValidateJoinRespDTO seckillActivity = seckillActivityApi.validateJoinSeckill(activityId, skuId, count);
63 // 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用 62 // 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用
64 - int seckillProductCount = tradeOrderQueryService.getSeckillProductCount(userId, activityId); 63 + int seckillProductCount = tradeOrderQueryService.getActivityProductCount(userId, activityId);
65 if (seckillProductCount + count > seckillActivity.getTotalLimitCount()) { 64 if (seckillProductCount + count > seckillActivity.getTotalLimitCount()) {
66 throw exception(PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT); 65 throw exception(PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT);
67 } 66 }