Commit cb995ba04795916362578e1c193ccbdc6c72d0dc
1 parent
fee72677
【代码优化】商城:价格计算相关逻辑
Showing
9 changed files
with
146 additions
and
192 deletions
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/reward/dto/RewardActivityMatchRespDTO.java
| @@ -100,6 +100,13 @@ public class RewardActivityMatchRespDTO { | @@ -100,6 +100,13 @@ public class RewardActivityMatchRespDTO { | ||
| 100 | */ | 100 | */ |
| 101 | private Map<Long, Integer> giveCouponTemplateCounts; | 101 | private Map<Long, Integer> giveCouponTemplateCounts; |
| 102 | 102 | ||
| 103 | + /** | ||
| 104 | + * 规则描述 | ||
| 105 | + * | ||
| 106 | + * 通过 {@link #limit}、{@link #discountPrice} 等字段进行拼接 | ||
| 107 | + */ | ||
| 108 | + private String description; | ||
| 109 | + | ||
| 103 | } | 110 | } |
| 104 | 111 | ||
| 105 | } | 112 | } |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
| 1 | package cn.iocoder.yudao.module.trade.controller.app.order; | 1 | package cn.iocoder.yudao.module.trade.controller.app.order; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; | 5 | import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; |
| 7 | -import cn.iocoder.yudao.module.member.api.level.MemberLevelApi; | ||
| 8 | -import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; | ||
| 9 | -import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | ||
| 10 | -import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; | ||
| 11 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; | 6 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; |
| 12 | -import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; | ||
| 13 | -import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; | ||
| 14 | -import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi; | ||
| 15 | -import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO; | ||
| 16 | -import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi; | ||
| 17 | -import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; | ||
| 18 | -import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum; | ||
| 19 | -import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; | ||
| 20 | -import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; | ||
| 21 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.*; | 7 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.*; |
| 22 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; | 8 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; |
| 23 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; | 9 | import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; |
| @@ -31,6 +17,7 @@ import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService; | @@ -31,6 +17,7 @@ import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService; | ||
| 31 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; | 17 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; |
| 32 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; | 18 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; |
| 33 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; | 19 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; |
| 20 | +import cn.iocoder.yudao.module.trade.service.price.TradePriceService; | ||
| 34 | import com.google.common.collect.Maps; | 21 | import com.google.common.collect.Maps; |
| 35 | import io.swagger.v3.oas.annotations.Operation; | 22 | import io.swagger.v3.oas.annotations.Operation; |
| 36 | import io.swagger.v3.oas.annotations.Parameter; | 23 | import io.swagger.v3.oas.annotations.Parameter; |
| @@ -41,8 +28,8 @@ import lombok.extern.slf4j.Slf4j; | @@ -41,8 +28,8 @@ import lombok.extern.slf4j.Slf4j; | ||
| 41 | import org.springframework.validation.annotation.Validated; | 28 | import org.springframework.validation.annotation.Validated; |
| 42 | import org.springframework.web.bind.annotation.*; | 29 | import org.springframework.web.bind.annotation.*; |
| 43 | 30 | ||
| 44 | -import java.time.LocalDateTime; | ||
| 45 | -import java.util.*; | 31 | +import java.util.List; |
| 32 | +import java.util.Map; | ||
| 46 | 33 | ||
| 47 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 34 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 48 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | 35 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| @@ -61,23 +48,13 @@ public class AppTradeOrderController { | @@ -61,23 +48,13 @@ public class AppTradeOrderController { | ||
| 61 | private TradeOrderQueryService tradeOrderQueryService; | 48 | private TradeOrderQueryService tradeOrderQueryService; |
| 62 | @Resource | 49 | @Resource |
| 63 | private DeliveryExpressService deliveryExpressService; | 50 | private DeliveryExpressService deliveryExpressService; |
| 64 | - | ||
| 65 | @Resource | 51 | @Resource |
| 66 | private AfterSaleService afterSaleService; | 52 | private AfterSaleService afterSaleService; |
| 67 | - | ||
| 68 | @Resource | 53 | @Resource |
| 69 | - private TradeOrderProperties tradeOrderProperties; | 54 | + private TradePriceService priceService; |
| 70 | 55 | ||
| 71 | @Resource | 56 | @Resource |
| 72 | - private MemberLevelApi memberLevelApi; | ||
| 73 | - @Resource | ||
| 74 | - private MemberUserApi memberUserApi; | ||
| 75 | - @Resource | ||
| 76 | - private DiscountActivityApi discountActivityApi; | ||
| 77 | - @Resource | ||
| 78 | - private RewardActivityApi rewardActivityApi; | ||
| 79 | - @Resource | ||
| 80 | - private ProductSkuApi productKpuApi; | 57 | + private TradeOrderProperties tradeOrderProperties; |
| 81 | 58 | ||
| 82 | @GetMapping("/settlement") | 59 | @GetMapping("/settlement") |
| 83 | @Operation(summary = "获得订单结算信息") | 60 | @Operation(summary = "获得订单结算信息") |
| @@ -86,56 +63,11 @@ public class AppTradeOrderController { | @@ -86,56 +63,11 @@ public class AppTradeOrderController { | ||
| 86 | return success(tradeOrderUpdateService.settlementOrder(getLoginUserId(), settlementReqVO)); | 63 | return success(tradeOrderUpdateService.settlementOrder(getLoginUserId(), settlementReqVO)); |
| 87 | } | 64 | } |
| 88 | 65 | ||
| 89 | - @GetMapping("/settlementProduct") | ||
| 90 | - @Operation(summary = "获得商品结算信息") | ||
| 91 | - public CommonResult<List<AppTradeProductSettlementRespVO>> settlementProduct(@RequestParam("ids") Set<Long> ids) { | ||
| 92 | - List<AppTradeProductSettlementRespVO> appTradeProductSettlementRespVOS = new ArrayList<>(); | ||
| 93 | - MemberLevelRespDTO memberLevel = getMemberLevel(); | ||
| 94 | - ids.forEach(spuId -> { | ||
| 95 | - List<AppTradeProductSettlementRespVO.Sku> skus = new ArrayList<>(); | ||
| 96 | - List<ProductSkuRespDTO> skuList = productKpuApi.getSkuListBySpuId(Collections.singletonList(spuId)); | ||
| 97 | - //查询sku的会员和限时优惠 | ||
| 98 | - skuList.forEach(sku -> { | ||
| 99 | - //查询限时优惠价格 | ||
| 100 | - AppTradeProductSettlementRespVO.Sku skuDiscount = calculateDiscountPrice(sku.getId(), sku.getPrice()); | ||
| 101 | - | ||
| 102 | - //查询会员价 | ||
| 103 | - AppTradeProductSettlementRespVO.Sku skuVip = calculateVipPrice(sku.getId(), sku.getPrice(), memberLevel); | ||
| 104 | - | ||
| 105 | - if(skuDiscount != null && skuVip != null){ | ||
| 106 | - if(skuDiscount.getPrice() > skuVip.getPrice()){ | ||
| 107 | - skus.add(skuVip); | ||
| 108 | - }else{ | ||
| 109 | - skus.add(skuDiscount); | ||
| 110 | - } | ||
| 111 | - }else if(skuDiscount != null){ | ||
| 112 | - skus.add(skuDiscount); | ||
| 113 | - }else if(skuVip != null){ | ||
| 114 | - skus.add(skuVip); | ||
| 115 | - } | ||
| 116 | - | ||
| 117 | - }); | ||
| 118 | - AppTradeProductSettlementRespVO.Reward reward = calculateReward(spuId); | ||
| 119 | - AppTradeProductSettlementRespVO respVO = AppTradeProductSettlementRespVO.builder().id(spuId).skus(skus).build(); | ||
| 120 | - if(reward != null){ | ||
| 121 | - //创建满减活动对象 | ||
| 122 | - respVO.setReward(reward); | ||
| 123 | - } | ||
| 124 | - appTradeProductSettlementRespVOS.add(respVO); | ||
| 125 | - }); | ||
| 126 | - return success(appTradeProductSettlementRespVOS); | ||
| 127 | - } | ||
| 128 | - | ||
| 129 | - private MemberLevelRespDTO getMemberLevel() { | ||
| 130 | - Long userId = getLoginUserId(); | ||
| 131 | - if (userId == null) { | ||
| 132 | - return null; | ||
| 133 | - } | ||
| 134 | - MemberUserRespDTO user = memberUserApi.getUser(userId); | ||
| 135 | - if (user.getLevelId() == null || user.getLevelId() <= 0) { | ||
| 136 | - return null; | ||
| 137 | - } | ||
| 138 | - return memberLevelApi.getMemberLevel(user.getLevelId()); | 66 | + @GetMapping("/settlement-product") |
| 67 | + @Operation(summary = "获得商品结算信息", description = "用于商品列表、商品详情,获得参与活动后的价格信息") | ||
| 68 | + @Parameter(name = "spuIds", description = "商品 SPU 编号数组") | ||
| 69 | + public CommonResult<List<AppTradeProductSettlementRespVO>> settlementProduct(@RequestParam("spuIds") List<Long> spuIds) { | ||
| 70 | + return success(priceService.calculateProductPrice(getLoginUserId(), spuIds)); | ||
| 139 | } | 71 | } |
| 140 | 72 | ||
| 141 | @PostMapping("/create") | 73 | @PostMapping("/create") |
| @@ -265,78 +197,4 @@ public class AppTradeOrderController { | @@ -265,78 +197,4 @@ public class AppTradeOrderController { | ||
| 265 | return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO)); | 197 | return success(tradeOrderUpdateService.createOrderItemCommentByMember(getLoginUserId(), createReqVO)); |
| 266 | } | 198 | } |
| 267 | 199 | ||
| 268 | - /** | ||
| 269 | - * 计算会员 VIP 优惠价格 | ||
| 270 | - * | ||
| 271 | - * @param price 原价 | ||
| 272 | - * @param memberLevel 会员等级 | ||
| 273 | - * @return 优惠价格 | ||
| 274 | - */ | ||
| 275 | - public AppTradeProductSettlementRespVO.Sku calculateVipPrice(Long skuId, Integer price, MemberLevelRespDTO memberLevel) { | ||
| 276 | - if (memberLevel == null || memberLevel.getDiscountPercent() == null) { | ||
| 277 | - return null; | ||
| 278 | - } | ||
| 279 | - Integer newPrice = price * memberLevel.getDiscountPercent() / 100; | ||
| 280 | - return AppTradeProductSettlementRespVO.Sku.builder(). | ||
| 281 | - skuId(skuId). | ||
| 282 | - type(PromotionTypeEnum.MEMBER_LEVEL.getType()). | ||
| 283 | - price(newPrice).build(); | ||
| 284 | - } | ||
| 285 | - | ||
| 286 | - /** | ||
| 287 | - * 计算限时优惠信息 | ||
| 288 | - * | ||
| 289 | - * @param price 原价 | ||
| 290 | - * @param skuId 商品规格id | ||
| 291 | - * @return 优惠价格 | ||
| 292 | - */ | ||
| 293 | - private AppTradeProductSettlementRespVO.Sku calculateDiscountPrice(Long skuId, Integer price) { | ||
| 294 | - if (skuId == null) { | ||
| 295 | - return null; | ||
| 296 | - } | ||
| 297 | - | ||
| 298 | - //根据商品id查询限时优惠 | ||
| 299 | - List<DiscountProductRespDTO> matchDiscountProductList = discountActivityApi.getMatchDiscountProductList(Collections.singletonList(skuId)); | ||
| 300 | - if (matchDiscountProductList != null && !matchDiscountProductList.isEmpty()) { | ||
| 301 | - DiscountProductRespDTO discountProductRespDTO = matchDiscountProductList.get(matchDiscountProductList.size() - 1); | ||
| 302 | - AppTradeProductSettlementRespVO.Sku sku = AppTradeProductSettlementRespVO.Sku.builder(). | ||
| 303 | - skuId(skuId). | ||
| 304 | - discountId(discountProductRespDTO.getId()). | ||
| 305 | - type(PromotionTypeEnum.DISCOUNT_ACTIVITY.getType()). | ||
| 306 | - endTime(discountProductRespDTO.getActivityEndTime()). | ||
| 307 | - build(); | ||
| 308 | - Integer discountType = discountProductRespDTO.getDiscountType(); | ||
| 309 | - if(Objects.equals(PromotionDiscountTypeEnum.PRICE.getType(), discountType)){ | ||
| 310 | - sku.setPrice(price - discountProductRespDTO.getDiscountPrice() * 100); | ||
| 311 | - }else if(Objects.equals(PromotionDiscountTypeEnum.PERCENT.getType(), discountType)){ | ||
| 312 | - Integer newPrice = price * discountProductRespDTO.getDiscountPercent() / 100; | ||
| 313 | - sku.setPrice(price - newPrice); | ||
| 314 | - }else{ | ||
| 315 | - throw new IllegalArgumentException("限时折扣活动类型不存在"); | ||
| 316 | - } | ||
| 317 | - return sku; | ||
| 318 | - } | ||
| 319 | - return null; | ||
| 320 | - } | ||
| 321 | - | ||
| 322 | - /** | ||
| 323 | - * 获取第一层满减活动 | ||
| 324 | - * | ||
| 325 | - * @param spuId 商品规格id | ||
| 326 | - * @return 优惠价格 | ||
| 327 | - */ | ||
| 328 | - private AppTradeProductSettlementRespVO.Reward calculateReward(Long spuId) { | ||
| 329 | - List<RewardActivityMatchRespDTO> matchRewardActivityList = rewardActivityApi.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collections.singletonList(spuId), CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now()); | ||
| 330 | - if(matchRewardActivityList != null && !matchRewardActivityList.isEmpty()){ | ||
| 331 | - RewardActivityMatchRespDTO rewardActivityMatchRespDTO = matchRewardActivityList.get(matchRewardActivityList.size() - 1); | ||
| 332 | - if(rewardActivityMatchRespDTO != null){ | ||
| 333 | - RewardActivityMatchRespDTO.Rule rule = rewardActivityMatchRespDTO.getRules().get(0); | ||
| 334 | - return AppTradeProductSettlementRespVO.Reward.builder(). | ||
| 335 | - rewardActivity("满" + rule.getLimit() / 100 + (Objects.equals(rewardActivityMatchRespDTO.getConditionType(), PromotionConditionTypeEnum.PRICE.getType())?"元":"件"+"减") +rule.getDiscountPrice() / 100) | ||
| 336 | - .id(rewardActivityMatchRespDTO.getId()).build(); | ||
| 337 | - } | ||
| 338 | - } | ||
| 339 | - return null; | ||
| 340 | - } | ||
| 341 | - | ||
| 342 | } | 200 | } |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java
| 1 | package cn.iocoder.yudao.module.trade.controller.app.order.vo; | 1 | package cn.iocoder.yudao.module.trade.controller.app.order.vo; |
| 2 | 2 | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | -import lombok.Builder; | ||
| 5 | import lombok.Data; | 4 | import lombok.Data; |
| 6 | 5 | ||
| 7 | import java.io.Serializable; | 6 | import java.io.Serializable; |
| @@ -10,54 +9,48 @@ import java.util.List; | @@ -10,54 +9,48 @@ import java.util.List; | ||
| 10 | 9 | ||
| 11 | @Schema(description = "用户 App - 商品结算信息 Response VO") | 10 | @Schema(description = "用户 App - 商品结算信息 Response VO") |
| 12 | @Data | 11 | @Data |
| 13 | -@Builder | ||
| 14 | public class AppTradeProductSettlementRespVO { | 12 | public class AppTradeProductSettlementRespVO { |
| 15 | 13 | ||
| 16 | - @Schema(description = "spu 商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 17 | - private Long id; | 14 | + @Schema(description = "SPU 商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 15 | + private Long spuId; | ||
| 18 | 16 | ||
| 19 | - @Schema(description = "满减活动对象", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 20 | - private Reward reward; | ||
| 21 | - | ||
| 22 | - @Schema(description = "sku 活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 17 | + @Schema(description = "SKU 价格信息数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 23 | private List<Sku> skus; | 18 | private List<Sku> skus; |
| 24 | 19 | ||
| 25 | - /** | ||
| 26 | - * 满减活动 | ||
| 27 | - */ | 20 | + @Schema(description = "满减送活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 21 | + private RewardActivity rewardActivity; | ||
| 22 | + | ||
| 23 | + @Schema(description = "满减送活动信息") | ||
| 28 | @Data | 24 | @Data |
| 29 | - @Builder | ||
| 30 | - public static class Reward implements Serializable { | 25 | + public static class RewardActivity { |
| 31 | 26 | ||
| 32 | @Schema(description = "满减活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 27 | @Schema(description = "满减活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 33 | private Long id; | 28 | private Long id; |
| 34 | 29 | ||
| 35 | - @Schema(description = "满减活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 36 | - private String rewardActivity; | 30 | + @Schema(description = "优惠规则描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "满 0.5 元减 0.3") |
| 31 | + private List<String> ruleDescriptions; | ||
| 37 | 32 | ||
| 38 | } | 33 | } |
| 39 | 34 | ||
| 40 | - /** | ||
| 41 | - * SKU 数组 | ||
| 42 | - */ | 35 | + @Schema(description = "SKU 价格信息") |
| 43 | @Data | 36 | @Data |
| 44 | - @Builder | ||
| 45 | public static class Sku implements Serializable { | 37 | public static class Sku implements Serializable { |
| 46 | 38 | ||
| 47 | @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 39 | @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 48 | - private Long skuId; | 40 | + private Long id; |
| 49 | 41 | ||
| 50 | - @Schema(description = "价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 51 | - private Integer price; | 42 | + @Schema(description = "支付价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 43 | + private Integer payPrice; // 优惠后价格 | ||
| 52 | 44 | ||
| 53 | @Schema(description = "营销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 45 | @Schema(description = "营销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 54 | - private Integer type; // 对应 PromotionTypeEnum 枚举 | 46 | + private Integer promotionType; // 对应 PromotionTypeEnum 枚举 |
| 55 | 47 | ||
| 56 | - @Schema(description = "限时优惠id", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 57 | - private Long discountId; | 48 | + @Schema(description = "营销编号", requiredMode = Schema.RequiredMode.REQUIRED) |
| 49 | + private Long promotionId; // 目前只有限时折扣活动的编号 | ||
| 58 | 50 | ||
| 59 | @Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED) | 51 | @Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 60 | - private LocalDateTime endTime; | 52 | + private LocalDateTime promotionEndTime; |
| 61 | 53 | ||
| 62 | } | 54 | } |
| 55 | + | ||
| 63 | } | 56 | } |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
| @@ -166,7 +166,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { | @@ -166,7 +166,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { | ||
| 166 | TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList); | 166 | TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList); |
| 167 | calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程,保证都是选中的 | 167 | calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程,保证都是选中的 |
| 168 | "商品({}) 未设置为选中", item.getSkuId())); | 168 | "商品({}) 未设置为选中", item.getSkuId())); |
| 169 | - return tradePriceService.calculatePrice(calculateReqBO); | 169 | + return tradePriceService.calculateOrderPrice(calculateReqBO); |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | @Override | 172 | @Override |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceService.java
| 1 | package cn.iocoder.yudao.module.trade.service.price; | 1 | package cn.iocoder.yudao.module.trade.service.price; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeProductSettlementRespVO; | ||
| 3 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; | 4 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 4 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; | 5 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 5 | - | ||
| 6 | import jakarta.validation.Valid; | 6 | import jakarta.validation.Valid; |
| 7 | 7 | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 8 | /** | 10 | /** |
| 9 | * 价格计算 Service 接口 | 11 | * 价格计算 Service 接口 |
| 10 | * | 12 | * |
| @@ -13,11 +15,20 @@ import jakarta.validation.Valid; | @@ -13,11 +15,20 @@ import jakarta.validation.Valid; | ||
| 13 | public interface TradePriceService { | 15 | public interface TradePriceService { |
| 14 | 16 | ||
| 15 | /** | 17 | /** |
| 16 | - * 价格计算 | 18 | + * 【订单】价格计算 |
| 17 | * | 19 | * |
| 18 | * @param calculateReqDTO 计算信息 | 20 | * @param calculateReqDTO 计算信息 |
| 19 | * @return 计算结果 | 21 | * @return 计算结果 |
| 20 | */ | 22 | */ |
| 21 | - TradePriceCalculateRespBO calculatePrice(@Valid TradePriceCalculateReqBO calculateReqDTO); | 23 | + TradePriceCalculateRespBO calculateOrderPrice(@Valid TradePriceCalculateReqBO calculateReqDTO); |
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 【商品】价格计算,用于商品列表、商品详情 | ||
| 27 | + * | ||
| 28 | + * @param userId 用户编号,允许为空 | ||
| 29 | + * @param spuIds 商品 SPU 编号数组 | ||
| 30 | + * @return 计算结果 | ||
| 31 | + */ | ||
| 32 | + List<AppTradeProductSettlementRespVO> calculateProductPrice(Long userId, List<Long> spuIds); | ||
| 22 | 33 | ||
| 23 | } | 34 | } |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java
| 1 | package cn.iocoder.yudao.module.trade.service.price; | 1 | package cn.iocoder.yudao.module.trade.service.price; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; | ||
| 3 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; | 5 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
| 4 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; | 6 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 5 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | 7 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| 6 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; | 8 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; |
| 9 | +import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi; | ||
| 10 | +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO; | ||
| 11 | +import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi; | ||
| 12 | +import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; | ||
| 13 | +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; | ||
| 14 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeProductSettlementRespVO; | ||
| 7 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; | 15 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 8 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; | 16 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 17 | +import cn.iocoder.yudao.module.trade.service.price.calculator.TradeDiscountActivityPriceCalculator; | ||
| 9 | import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator; | 18 | import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator; |
| 10 | import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper; | 19 | import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper; |
| 20 | +import jakarta.annotation.Resource; | ||
| 11 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
| 12 | import org.springframework.stereotype.Service; | 22 | import org.springframework.stereotype.Service; |
| 13 | import org.springframework.validation.annotation.Validated; | 23 | import org.springframework.validation.annotation.Validated; |
| 14 | 24 | ||
| 15 | -import jakarta.annotation.Resource; | 25 | +import java.time.LocalDateTime; |
| 16 | import java.util.List; | 26 | import java.util.List; |
| 17 | import java.util.Map; | 27 | import java.util.Map; |
| 18 | 28 | ||
| 19 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 29 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 20 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | ||
| 21 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | 30 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 22 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; | 31 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; |
| 23 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; | 32 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; |
| 24 | import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL; | 33 | import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL; |
| @@ -37,12 +46,19 @@ public class TradePriceServiceImpl implements TradePriceService { | @@ -37,12 +46,19 @@ public class TradePriceServiceImpl implements TradePriceService { | ||
| 37 | private ProductSkuApi productSkuApi; | 46 | private ProductSkuApi productSkuApi; |
| 38 | @Resource | 47 | @Resource |
| 39 | private ProductSpuApi productSpuApi; | 48 | private ProductSpuApi productSpuApi; |
| 49 | + @Resource | ||
| 50 | + private DiscountActivityApi discountActivityApi; | ||
| 51 | + @Resource | ||
| 52 | + private RewardActivityApi rewardActivityApi; | ||
| 40 | 53 | ||
| 41 | @Resource | 54 | @Resource |
| 42 | private List<TradePriceCalculator> priceCalculators; | 55 | private List<TradePriceCalculator> priceCalculators; |
| 43 | 56 | ||
| 57 | + @Resource | ||
| 58 | + private TradeDiscountActivityPriceCalculator discountActivityPriceCalculator; | ||
| 59 | + | ||
| 44 | @Override | 60 | @Override |
| 45 | - public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) { | 61 | + public TradePriceCalculateRespBO calculateOrderPrice(TradePriceCalculateReqBO calculateReqBO) { |
| 46 | // 1.1 获得商品 SKU 数组 | 62 | // 1.1 获得商品 SKU 数组 |
| 47 | List<ProductSkuRespDTO> skuList = checkSkuList(calculateReqBO); | 63 | List<ProductSkuRespDTO> skuList = checkSkuList(calculateReqBO); |
| 48 | // 1.2 获得商品 SPU 数组 | 64 | // 1.2 获得商品 SPU 数组 |
| @@ -85,4 +101,60 @@ public class TradePriceServiceImpl implements TradePriceService { | @@ -85,4 +101,60 @@ public class TradePriceServiceImpl implements TradePriceService { | ||
| 85 | return productSpuApi.validateSpuList(convertSet(skuList, ProductSkuRespDTO::getSpuId)); | 101 | return productSpuApi.validateSpuList(convertSet(skuList, ProductSkuRespDTO::getSpuId)); |
| 86 | } | 102 | } |
| 87 | 103 | ||
| 104 | + @Override | ||
| 105 | + public List<AppTradeProductSettlementRespVO> calculateProductPrice(Long userId, List<Long> spuIds) { | ||
| 106 | + // 1.1 获得 SPU 与 SKU 的映射 | ||
| 107 | + List<ProductSkuRespDTO> allSkuList = productSkuApi.getSkuListBySpuId(spuIds); | ||
| 108 | + Map<Long, List<ProductSkuRespDTO>> spuIdAndSkuListMap = convertMultiMap(allSkuList, ProductSkuRespDTO::getSpuId); | ||
| 109 | + // 1.2 获得会员等级 | ||
| 110 | + MemberLevelRespDTO level = discountActivityPriceCalculator.getMemberLevel(userId); | ||
| 111 | + // 1.3 获得限时折扣活动 | ||
| 112 | + Map<Long, DiscountProductRespDTO> skuIdAndDiscountMap = convertMap( | ||
| 113 | + discountActivityApi.getMatchDiscountProductList(convertSet(allSkuList, ProductSkuRespDTO::getId)), | ||
| 114 | + DiscountProductRespDTO::getSkuId); | ||
| 115 | + // 1.4 获得满减送活动 | ||
| 116 | + // TODO 芋艿:这里是有问题的,后面 fix | ||
| 117 | + Map<Long, RewardActivityMatchRespDTO> rewardActivityMap = convertMap( | ||
| 118 | + rewardActivityApi.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(spuIds, CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now()), | ||
| 119 | + RewardActivityMatchRespDTO::getId); | ||
| 120 | + | ||
| 121 | + // 2. 价格计算 | ||
| 122 | + return convertList(spuIds, spuId -> { | ||
| 123 | + AppTradeProductSettlementRespVO spuVO = new AppTradeProductSettlementRespVO().setSpuId(spuId); | ||
| 124 | + // 2.1 优惠价格 | ||
| 125 | + List<ProductSkuRespDTO> skuList = spuIdAndSkuListMap.get(spuId); | ||
| 126 | + List<AppTradeProductSettlementRespVO.Sku> skuVOList = convertList(skuList, sku -> { | ||
| 127 | + AppTradeProductSettlementRespVO.Sku skuVO = new AppTradeProductSettlementRespVO.Sku() | ||
| 128 | + .setId(sku.getId()).setPayPrice(sku.getPrice()); | ||
| 129 | + TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem() | ||
| 130 | + .setPayPrice(sku.getPrice()).setCount(1); | ||
| 131 | + // 计算限时折扣的优惠价格 | ||
| 132 | + DiscountProductRespDTO discountProduct = skuIdAndDiscountMap.get(orderItem.getSkuId()); | ||
| 133 | + Integer discountPrice = discountActivityPriceCalculator.calculateActivityPrice(discountProduct, orderItem); | ||
| 134 | + // 计算 VIP 优惠金额 | ||
| 135 | + Integer vipPrice = discountActivityPriceCalculator.calculateVipPrice(level, orderItem); | ||
| 136 | + if (discountPrice <= 0 && vipPrice <= 0) { | ||
| 137 | + return skuVO; | ||
| 138 | + } | ||
| 139 | + // 选择一个大的优惠 | ||
| 140 | + if (discountPrice > vipPrice) { | ||
| 141 | + return skuVO.setPayPrice(sku.getPrice() - discountPrice) | ||
| 142 | + .setPromotionType(PromotionTypeEnum.DISCOUNT_ACTIVITY.getType()) | ||
| 143 | + .setPromotionId(discountProduct.getId()).setPromotionEndTime(discountProduct.getActivityEndTime()); | ||
| 144 | + } else { | ||
| 145 | + return skuVO.setPayPrice(sku.getPrice() - vipPrice) | ||
| 146 | + .setPromotionType(PromotionTypeEnum.MEMBER_LEVEL.getType()); | ||
| 147 | + } | ||
| 148 | + }); | ||
| 149 | + spuVO.setSkus(skuVOList); | ||
| 150 | + // 2.2 满减送活动 | ||
| 151 | + RewardActivityMatchRespDTO rewardActivity = rewardActivityMap.get(spuId); | ||
| 152 | + if (rewardActivity != null) { | ||
| 153 | + spuVO.setRewardActivity(new AppTradeProductSettlementRespVO.RewardActivity().setId(rewardActivity.getId()) | ||
| 154 | + .setRuleDescriptions(convertList(rewardActivity.getRules(), RewardActivityMatchRespDTO.Rule::getDescription))); | ||
| 155 | + } | ||
| 156 | + return spuVO; | ||
| 157 | + }); | ||
| 158 | + } | ||
| 159 | + | ||
| 88 | } | 160 | } |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java
| @@ -55,8 +55,7 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato | @@ -55,8 +55,7 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato | ||
| 55 | convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId)); | 55 | convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId)); |
| 56 | Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId); | 56 | Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId); |
| 57 | // 1.2 获得会员等级 | 57 | // 1.2 获得会员等级 |
| 58 | - MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()); | ||
| 59 | - MemberLevelRespDTO level = user != null && user.getLevelId() > 0 ? memberLevelApi.getMemberLevel(user.getLevelId()) : null; | 58 | + MemberLevelRespDTO level = getMemberLevel(param.getUserId()); |
| 60 | 59 | ||
| 61 | // 2. 计算每个 SKU 的优惠金额 | 60 | // 2. 计算每个 SKU 的优惠金额 |
| 62 | result.getItems().forEach(orderItem -> { | 61 | result.getItems().forEach(orderItem -> { |
| @@ -97,13 +96,27 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato | @@ -97,13 +96,27 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato | ||
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | /** | 98 | /** |
| 99 | + * 获得用户的等级 | ||
| 100 | + * | ||
| 101 | + * @param userId 用户编号 | ||
| 102 | + * @return 用户等级 | ||
| 103 | + */ | ||
| 104 | + public MemberLevelRespDTO getMemberLevel(Long userId) { | ||
| 105 | + MemberUserRespDTO user = memberUserApi.getUser(userId); | ||
| 106 | + if (user == null || user.getLevelId() == null || user.getLevelId() <= 0) { | ||
| 107 | + return null; | ||
| 108 | + } | ||
| 109 | + return memberLevelApi.getMemberLevel(user.getLevelId()); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + /** | ||
| 100 | * 计算优惠活动的价格 | 113 | * 计算优惠活动的价格 |
| 101 | * | 114 | * |
| 102 | * @param discount 优惠活动 | 115 | * @param discount 优惠活动 |
| 103 | * @param orderItem 交易项 | 116 | * @param orderItem 交易项 |
| 104 | * @return 优惠价格 | 117 | * @return 优惠价格 |
| 105 | */ | 118 | */ |
| 106 | - private Integer calculateActivityPrice(DiscountProductRespDTO discount, | 119 | + public Integer calculateActivityPrice(DiscountProductRespDTO discount, |
| 107 | TradePriceCalculateRespBO.OrderItem orderItem) { | 120 | TradePriceCalculateRespBO.OrderItem orderItem) { |
| 108 | if (discount == null) { | 121 | if (discount == null) { |
| 109 | return 0; | 122 | return 0; |
| @@ -127,7 +140,7 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato | @@ -127,7 +140,7 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato | ||
| 127 | * @return 优惠价格 | 140 | * @return 优惠价格 |
| 128 | */ | 141 | */ |
| 129 | public Integer calculateVipPrice(MemberLevelRespDTO level, | 142 | public Integer calculateVipPrice(MemberLevelRespDTO level, |
| 130 | - TradePriceCalculateRespBO.OrderItem orderItem) { | 143 | + TradePriceCalculateRespBO.OrderItem orderItem) { |
| 131 | if (level == null || level.getDiscountPercent() == null) { | 144 | if (level == null || level.getDiscountPercent() == null) { |
| 132 | return 0; | 145 | return 0; |
| 133 | } | 146 | } |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
| @@ -54,7 +54,7 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator | @@ -54,7 +54,7 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator | ||
| 54 | return; | 54 | return; |
| 55 | } | 55 | } |
| 56 | // 处理最新的满减送活动 | 56 | // 处理最新的满减送活动 |
| 57 | - if(!rewardActivities.isEmpty()){ | 57 | + if (!rewardActivities.isEmpty()) { |
| 58 | calculate(param, result, rewardActivities.get(0)); | 58 | calculate(param, result, rewardActivities.get(0)); |
| 59 | } | 59 | } |
| 60 | } | 60 | } |
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java
| @@ -72,7 +72,7 @@ public class TradePriceServiceImplTest extends BaseMockitoUnitTest { | @@ -72,7 +72,7 @@ public class TradePriceServiceImplTest extends BaseMockitoUnitTest { | ||
| 72 | .setStatus(ProductSpuStatusEnum.ENABLE.getStatus()))); | 72 | .setStatus(ProductSpuStatusEnum.ENABLE.getStatus()))); |
| 73 | 73 | ||
| 74 | // 调用 | 74 | // 调用 |
| 75 | - TradePriceCalculateRespBO calculateRespBO = tradePriceService.calculatePrice(calculateReqBO); | 75 | + TradePriceCalculateRespBO calculateRespBO = tradePriceService.calculateOrderPrice(calculateReqBO); |
| 76 | // 断言 | 76 | // 断言 |
| 77 | assertEquals(TradeOrderTypeEnum.NORMAL.getType(), calculateRespBO.getType()); | 77 | assertEquals(TradeOrderTypeEnum.NORMAL.getType(), calculateRespBO.getType()); |
| 78 | assertEquals(0, calculateRespBO.getPromotions().size()); | 78 | assertEquals(0, calculateRespBO.getPromotions().size()); |