Commit cb995ba04795916362578e1c193ccbdc6c72d0dc

Authored by YunaiV
1 parent fee72677

【代码优化】商城:价格计算相关逻辑

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());