From f01c600492b29c0e24a99c1e77150a679846729f Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 4 Oct 2024 11:44:58 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E3=80=91=E5=95=86=E5=9F=8E:=20APP=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E7=A7=AF=E5=88=86=E5=95=86=E5=9F=8E=E6=B4=BB=E5=8A=A8=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E4=BB=B7=E6=A0=BC=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../promotion/api/point/PointActivityApi.java | 25 ++++++ .../point/dto/PointValidateJoinRespDTO.java | 24 ++++++ .../promotion/enums/ErrorCodeConstants.java | 4 + .../api/point/PointActivityApiImpl.java | 26 ++++++ .../dal/mysql/point/PointProductMapper.java | 5 ++ .../service/point/PointActivityService.java | 13 +++ .../point/PointActivityServiceImpl.java | 25 ++++++ .../trade/enums/ErrorCodeConstants.java | 11 +-- .../trade/enums/order/TradeOrderTypeEnum.java | 1 + .../vo/AppTradeOrderSettlementReqVO.java | 8 +- .../convert/order/TradeOrderConvert.java | 3 +- .../service/order/TradeOrderQueryService.java | 6 +- .../order/TradeOrderQueryServiceImpl.java | 4 +- .../price/bo/TradePriceCalculateReqBO.java | 10 ++- .../TradePointActivityPriceCalculator.java | 81 +++++++++++++++++++ .../calculator/TradePriceCalculator.java | 1 + .../TradePriceCalculatorHelper.java | 3 + .../TradeSeckillActivityPriceCalculator.java | 5 +- 18 files changed, 237 insertions(+), 18 deletions(-) create mode 100644 yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java create mode 100644 yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java create mode 100644 yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java create mode 100644 yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java new file mode 100644 index 000000000..6e4250c67 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.promotion.api.point; + +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; + +/** + * 积分商城活动 API 接口 + * + * @author HUIHUI + */ +public interface PointActivityApi { + + /** + * 【下单前】校验是否参与积分商城活动 + * + * 如果校验失败,则抛出业务异常 + * + * @param activityId 活动编号 + * @param skuId SKU 编号 + * @param count 数量 + * @return 积分商城商品信息 + */ + PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count); + + +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java new file mode 100644 index 000000000..e3b993461 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.promotion.api.point.dto; + +import lombok.Data; + +/** + * 校验参与积分商城 Response DTO + */ +@Data +public class PointValidateJoinRespDTO { + + /** + * 可兑换次数 + */ + private Integer count; + /** + * 所需兑换积分 + */ + private Integer point; + /** + * 所需兑换金额,单位:分 + */ + private Integer price; + +} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java index af8206d2d..514cb27af 100644 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java +++ b/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 { ErrorCode POINT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_007_002, "积分商城活动已关闭,不能修改"); ErrorCode POINT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_007_003, "积分商城活动未关闭或未结束,不能删除"); ErrorCode POINT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_007_004, "积分商城活动已关闭,不能重复关闭"); + ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_STATUS_CLOSED = new ErrorCode(1_013_007_005, "积分商品兑换失败,原因:积分商城活动已关闭"); + ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_007_006, "积分商品兑换失败,原因:单次限购超出"); + ErrorCode POINT_ACTIVITY_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS = new ErrorCode(1_013_007_007, "积分商品兑换失败,原因:商品不存在"); + ErrorCode POINT_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1_013_007_008, "积分商品兑换失败,原因:积分商品库存不足"); // ========== 秒杀活动 1-013-008-000 ========== ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_008_000, "秒杀活动不存在"); diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java new file mode 100644 index 000000000..729551797 --- /dev/null +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.promotion.api.point; + +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; +import cn.iocoder.yudao.module.promotion.service.point.PointActivityService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +/** + * 积分商城活动 Api 接口实现类 + * + * @author HUIHUI + */ +@Service +@Validated +public class PointActivityApiImpl implements PointActivityApi { + + @Resource + private PointActivityService pointActivityService; + + @Override + public PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count) { + return pointActivityService.validateJoinPointActivity(activityId, skuId, count); + } + +} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java index cfa10a515..1169d8f53 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java +++ b/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 { .eq(PointProductDO::getActivityId, pointProductDO.getActivityId())); } + default PointProductDO selectListByActivityIdAndSkuId(Long activityId, Long skuId) { + return selectOne(PointProductDO::getActivityId, activityId, + PointProductDO::getSkuId, skuId); + } + } \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java index 24facb182..f93a89f3e 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.promotion.service.point; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO; import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; @@ -78,4 +79,16 @@ public interface PointActivityService { */ List getPointProductListByActivityIds(Collection activityIds); + /** + * 【下单前】校验是否参与积分商城活动 + * + * 如果校验失败,则抛出业务异常 + * + * @param activityId 活动编号 + * @param skuId SKU 编号 + * @param count 数量 + * @return 积分商城商品信息 + */ + PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count); + } \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java index bfce27e1c..cd03bc45d 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java +++ b/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; import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; +import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductSaveReqVO; @@ -244,4 +245,28 @@ public class PointActivityServiceImpl implements PointActivityService { return pointProductMapper.selectListByActivityId(activityIds); } + @Override + public PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count) { + // 1. 校验积分商城活动是否存在 + PointActivityDO activity = validatePointActivityExists(activityId); + if (CommonStatusEnum.isDisable(activity.getStatus())) { + throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_STATUS_CLOSED); + } + + // 2.1 校验积分商城商品是否存在 + PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(activityId, skuId); + if (product == null) { + throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS); + } + // 2.2 超过单次购买限制 + if (count > product.getCount()) { + throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED); + } + // 2.2 校验库存是否充足 + if (count > product.getStock()) { + throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); + } + return BeanUtils.toBean(product, PointValidateJoinRespDTO.class); + } + } \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java index 2ab726ec4..be0ef51ae 100644 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java +++ b/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 { // ========== Price 相关 1-011-003-000 ============ ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1_011_003_000, "支付价格计算异常,原因:价格小于等于 0"); - ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1_011_003_002, "计算快递运费异常,找不到对应的运费模板"); - ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵"); - ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_005, "参与秒杀的商品,超过了秒杀总限购数量"); - ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_006, "计算快递运费异常,配送方式不匹配"); - ErrorCode PRICE_CALCULATE_COUPON_CAN_NOT_USE = new ErrorCode(1_011_003_007, "该优惠劵无法使用,原因:{}」"); + ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1_011_003_001, "计算快递运费异常,找不到对应的运费模板"); + ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1_011_003_002, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵"); + ErrorCode PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_003, "参与秒杀的商品,超过了秒杀总限购数量"); + ErrorCode PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT = new ErrorCode(1_011_003_004, "参与积分活动的商品,超过了积分活动商品总限购数量"); + ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TYPE_ILLEGAL = new ErrorCode(1_011_003_005, "计算快递运费异常,配送方式不匹配"); + ErrorCode PRICE_CALCULATE_COUPON_CAN_NOT_USE = new ErrorCode(1_011_003_006, "该优惠劵无法使用,原因:{}」"); // ========== 物流 Express 模块 1-011-004-000 ========== ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1_011_004_000, "快递公司不存在"); diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java index f82071206..0e6aad735 100644 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java +++ b/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 { SECKILL(1, "秒杀订单"), BARGAIN(2, "砍价订单"), COMBINATION(3, "拼团订单"), + POINT(4, "积分商城"), ; public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TradeOrderTypeEnum::getType).toArray(); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java index f846ef1d1..7188bc17c 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java +++ b/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; 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() { diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java index 60b81057f..45462a5be 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java +++ b/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 { .setSeckillActivityId(settlementReqVO.getSeckillActivityId()) .setBargainRecordId(settlementReqVO.getBargainRecordId()) .setCombinationActivityId(settlementReqVO.getCombinationActivityId()) - .setCombinationHeadId(settlementReqVO.getCombinationHeadId()); + .setCombinationHeadId(settlementReqVO.getCombinationHeadId()) + .setPointActivityId(settlementReqVO.getPointActivityId()); // 商品项的构建 Map cartMap = convertMap(cartList, CartDO::getId); for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) { diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java index 445792232..b2b8bca98 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java +++ b/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 { /** * 获得指定用户,指定活动,指定状态的交易订单 * - * @param userId 用户编号 + * @param userId 用户编号 * @param combinationActivityId 活动编号 - * @param status 订单状态 + * @param status 订单状态 * @return 交易订单 */ TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status); @@ -116,7 +116,7 @@ public interface TradeOrderQueryService { * @param activityId 活动编号 * @return 秒杀商品数量 */ - int getSeckillProductCount(Long userId, Long activityId); + int getActivityProductCount(Long userId, Long activityId); // =================== Order Item =================== diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java index 68c549891..04cd1b6ff 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java +++ b/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 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO; import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; +import jakarta.annotation.Resource; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; -import jakarta.annotation.Resource; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -174,7 +174,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { } @Override - public int getSeckillProductCount(Long userId, Long activityId) { + public int getActivityProductCount(Long userId, Long activityId) { // 获得订单列表 List orders = tradeOrderMapper.selectListByUserIdAndSeckillActivityId(userId, activityId); orders.removeIf(order -> TradeOrderStatusEnum.isCanceled(order.getStatus())); // 过滤掉【已取消】的订单 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java index 9b6f1d6bd..ada677f36 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java @@ -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 */ diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java new file mode 100644 index 000000000..a220584c7 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java @@ -0,0 +1,81 @@ +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.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 TradeOrderQueryService tradeOrderQueryService; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 1. 判断订单类型是否为积分商城活动 + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.POINT.getType())) { + 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 = 0; + Assert.isTrue(activity.getPoint() >= 1, "积分商城商品兑换积分必须大于 1"); + // 情况一:单使用积分兑换 + if (activity.getPrice() == null || activity.getPrice() == 0) { + discountPrice = orderItem.getPayPrice(); + } else { // 情况二:积分 + 金额 + discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount(); + } + TradePriceCalculatorHelper.addPromotion(result, orderItem, + param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(), + StrUtil.format("积分商城活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)), + discountPrice); + // 3.2 更新 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.getActivityProductCount(userId, activityId); + if (activityProductCount + count > pointValidateJoinRespDTO.getCount()) { + throw exception(PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT); + } + return pointValidateJoinRespDTO; + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java index 9ed7d9a2f..a37048b3b 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java +++ b/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 { 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; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java index 323b50e93..0b24e2ea0 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java +++ b/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 { if (param.getBargainRecordId() != null) { return TradeOrderTypeEnum.BARGAIN.getType(); } + if (param.getPointActivityId() != null) { + return TradeOrderTypeEnum.POINT.getType(); + } return TradeOrderTypeEnum.NORMAL.getType(); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java index fe984ec0e..e1b4ba194 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java +++ b/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; 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; @@ -61,7 +60,7 @@ public class TradeSeckillActivityPriceCalculator implements TradePriceCalculator // 1. 校验是否可以参与秒杀 SeckillValidateJoinRespDTO seckillActivity = seckillActivityApi.validateJoinSeckill(activityId, skuId, count); // 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用 - int seckillProductCount = tradeOrderQueryService.getSeckillProductCount(userId, activityId); + int seckillProductCount = tradeOrderQueryService.getActivityProductCount(userId, activityId); if (seckillProductCount + count > seckillActivity.getTotalLimitCount()) { throw exception(PRICE_CALCULATE_SECKILL_TOTAL_LIMIT_COUNT); }