Commit ba380ea615fb6d5f9fcf7a061a0858a88e65bf7a

Authored by puhui999
2 parents e41935ea 910d374c

Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product

# Conflicts:
#	yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
#	yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java
Showing 44 changed files with 1381 additions and 780 deletions
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
1 package cn.iocoder.yudao.module.product.api.sku.dto; 1 package cn.iocoder.yudao.module.product.api.sku.dto;
2 2
  3 +import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
3 import lombok.Data; 4 import lombok.Data;
4 5
5 import java.util.List; 6 import java.util.List;
@@ -25,7 +26,7 @@ public class ProductSkuRespDTO { @@ -25,7 +26,7 @@ public class ProductSkuRespDTO {
25 /** 26 /**
26 * 属性数组 27 * 属性数组
27 */ 28 */
28 - private List<Property> properties; 29 + private List<ProductPropertyValueDetailRespDTO> properties;
29 /** 30 /**
30 * 销售价格,单位:分 31 * 销售价格,单位:分
31 */ 32 */
@@ -51,10 +52,6 @@ public class ProductSkuRespDTO { @@ -51,10 +52,6 @@ public class ProductSkuRespDTO {
51 */ 52 */
52 private Integer stock; 53 private Integer stock;
53 /** 54 /**
54 - * 预警预存  
55 - */  
56 - private Integer warnStock;  
57 - /**  
58 * 商品重量,单位:kg 千克 55 * 商品重量,单位:kg 千克
59 */ 56 */
60 private Double weight; 57 private Double weight;
@@ -63,30 +60,4 @@ public class ProductSkuRespDTO { @@ -63,30 +60,4 @@ public class ProductSkuRespDTO {
63 */ 60 */
64 private Double volume; 61 private Double volume;
65 62
66 - /**  
67 - * 商品属性  
68 - */  
69 - @Data  
70 - public static class Property {  
71 -  
72 - /**  
73 - * 属性编号  
74 - */  
75 - private Long propertyId;  
76 - /**  
77 - * 属性名字  
78 - */  
79 - private String propertyName;  
80 -  
81 - /**  
82 - * 属性值编号  
83 - */  
84 - private Long valueId;  
85 - /**  
86 - * 属性值名字  
87 - */  
88 - private String valueName;  
89 -  
90 - }  
91 -  
92 } 63 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java
@@ -18,6 +18,9 @@ public class AppProductSpuDetailRespVO { @@ -18,6 +18,9 @@ public class AppProductSpuDetailRespVO {
18 @Schema(description = "商品名称", required = true, example = "芋道") 18 @Schema(description = "商品名称", required = true, example = "芋道")
19 private String name; 19 private String name;
20 20
  21 + @Schema(description = "商品简介", required = true, example = "我是一个快乐简介")
  22 + private String introduction;
  23 +
21 @Schema(description = "商品详情", required = true, example = "我是商品描述") 24 @Schema(description = "商品详情", required = true, example = "我是商品描述")
22 private String description; 25 private String description;
23 26
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java
@@ -141,7 +141,7 @@ public class ProductSpuDO extends BaseDO { @@ -141,7 +141,7 @@ public class ProductSpuDO extends BaseDO {
141 /** 141 /**
142 * 物流配置模板编号 142 * 物流配置模板编号
143 * 143 *
144 - * 关联 { TradeDeliveryExpressTemplateDO#getId()} 144 + * 对应 TradeDeliveryExpressTemplateDO 的 id 编号
145 */ 145 */
146 private Long deliveryTemplateId; 146 private Long deliveryTemplateId;
147 147
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java
@@ -12,6 +12,7 @@ import java.util.List; @@ -12,6 +12,7 @@ import java.util.List;
12 * @author 芋道源码 12 * @author 芋道源码
13 */ 13 */
14 @Data 14 @Data
  15 +@Deprecated
15 public class PriceCalculateReqDTO { 16 public class PriceCalculateReqDTO {
16 17
17 /** 18 /**
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java
1 package cn.iocoder.yudao.module.promotion.service.price; 1 package cn.iocoder.yudao.module.promotion.service.price;
2 2
3 -import cn.hutool.core.map.MapUtil;  
4 import cn.iocoder.yudao.framework.common.exception.ServiceException; 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
5 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
6 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 5 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
7 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 6 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
8 import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO; 7 import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO;
9 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; 8 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
10 -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;  
11 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
12 -import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;  
13 -import cn.iocoder.yudao.module.promotion.enums.common.*; 10 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
14 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; 11 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
15 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; 12 import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
16 -import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;  
17 import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService; 13 import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
18 import org.junit.jupiter.api.Test; 14 import org.junit.jupiter.api.Test;
19 import org.mockito.InjectMocks; 15 import org.mockito.InjectMocks;
20 import org.mockito.Mock; 16 import org.mockito.Mock;
21 17
22 -import java.util.LinkedHashMap;  
23 import java.util.List; 18 import java.util.List;
24 -import java.util.Map;  
25 -import java.util.Set;  
26 19
27 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; 20 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
28 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 21 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
29 -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;  
30 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 22 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
31 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_VALID_TIME_NOT_NOW; 23 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_VALID_TIME_NOT_NOW;
32 import static java.util.Arrays.asList; 24 import static java.util.Arrays.asList;
@@ -47,413 +39,18 @@ public class PriceServiceTest extends BaseMockitoUnitTest { @@ -47,413 +39,18 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
47 private PriceServiceImpl priceService; 39 private PriceServiceImpl priceService;
48 40
49 @Mock 41 @Mock
50 - private DiscountActivityService discountService;  
51 - @Mock  
52 private RewardActivityService rewardActivityService; 42 private RewardActivityService rewardActivityService;
53 @Mock 43 @Mock
54 private CouponService couponService; 44 private CouponService couponService;
55 @Mock 45 @Mock
56 private ProductSkuApi productSkuApi; 46 private ProductSkuApi productSkuApi;
57 47
58 - @Test  
59 - public void testCalculatePrice_memberDiscount() {  
60 - // 准备参数  
61 - // TODO 芋艿:userId = 1,实现 9 折;后续改成 mock  
62 - PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(1L)  
63 - .setItems(singletonList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2)));  
64 - // mock 方法(商品 SKU 信息)  
65 - ProductSkuRespDTO productSku = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100));  
66 - when(productSkuApi.getSkuList(eq(asSet(10L)))).thenReturn(singletonList(productSku));  
67 -  
68 - // 调用  
69 - //PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法  
70 - PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();  
71 - // 断言 Order 部分  
72 - PriceCalculateRespDTO.Order order = priceCalculate.getOrder();  
73 - assertEquals(order.getTotalPrice(), 200);  
74 - assertEquals(order.getDiscountPrice(), 0);  
75 - assertEquals(order.getPointPrice(), 0);  
76 - assertEquals(order.getDeliveryPrice(), 0);  
77 - assertEquals(order.getPayPrice(), 180);  
78 - assertNull(order.getCouponId());  
79 - // 断言 OrderItem 部分  
80 - assertEquals(order.getItems().size(), 1);  
81 - PriceCalculateRespDTO.OrderItem orderItem = order.getItems().get(0);  
82 - assertEquals(orderItem.getSkuId(), 10L);  
83 - assertEquals(orderItem.getCount(), 2);  
84 - assertEquals(orderItem.getOriginalPrice(), 200);  
85 - assertEquals(orderItem.getOriginalUnitPrice(), 100);  
86 - assertEquals(orderItem.getDiscountPrice(), 20);  
87 - assertEquals(orderItem.getPayPrice(), 180);  
88 - assertEquals(orderItem.getOrderPartPrice(), 0);  
89 - assertEquals(orderItem.getOrderDividePrice(), 180);  
90 - // 断言 Promotion 部分  
91 - assertEquals(priceCalculate.getPromotions().size(), 1);  
92 - PriceCalculateRespDTO.Promotion promotion = priceCalculate.getPromotions().get(0);  
93 - assertNull(promotion.getId());  
94 - assertEquals(promotion.getName(), "会员折扣");  
95 - assertEquals(promotion.getType(), PromotionTypeEnum.MEMBER.getType());  
96 - //assertEquals(promotion.getLevel(), PromotionLevelEnum.SKU.getLevel()); TODO 没有这个枚举类  
97 - assertEquals(promotion.getTotalPrice(), 200);  
98 - assertEquals(promotion.getDiscountPrice(), 20);  
99 - assertTrue(promotion.getMatch());  
100 - assertEquals(promotion.getDescription(), "会员折扣:省 0.20 元");  
101 - PriceCalculateRespDTO.PromotionItem promotionItem = promotion.getItems().get(0);  
102 - assertEquals(promotion.getItems().size(), 1);  
103 - assertEquals(promotionItem.getSkuId(), 10L);  
104 - assertEquals(promotionItem.getOriginalPrice(), 200);  
105 - assertEquals(promotionItem.getDiscountPrice(), 20);  
106 - }  
107 -  
108 - @Test  
109 - public void testCalculatePrice_discountActivity() {  
110 - // 准备参数  
111 - PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())  
112 - .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),  
113 - new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3)));  
114 - // mock 方法(商品 SKU 信息)  
115 - ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100));  
116 - ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50));  
117 - when(productSkuApi.getSkuList(eq(asSet(10L, 20L)))).thenReturn(asList(productSku01, productSku02));  
118 - // mock 方法(限时折扣 DiscountActivity 信息)TODO 没找到 DiscountProductDetailBO  
119 - //DiscountProductDetailBO discountProduct01 = randomPojo(DiscountProductDetailBO.class, o -> o.setActivityId(1000L)  
120 - // .setActivityName("活动 1000 号").setSkuId(10L)  
121 - // .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(40));  
122 - //DiscountProductDetailBO discountProduct02 = randomPojo(DiscountProductDetailBO.class, o -> o.setActivityId(2000L)  
123 - // .setActivityName("活动 2000 号").setSkuId(20L)  
124 - // .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(60));  
125 - //when(discountService.getMatchDiscountProductList(eq(asSet(10L, 20L)))).thenReturn(  
126 - // MapUtil.builder(10L, discountProduct01).put(20L, discountProduct02).map());  
127 -  
128 - // 10L: 100 * 2 - 40 * 2 = 120  
129 - // 20L:50 * 3 - 50 * 3 * 0.4 = 90  
130 -  
131 - // 调用  
132 - //PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法  
133 - PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();  
134 - // 断言 Order 部分  
135 - PriceCalculateRespDTO.Order order = priceCalculate.getOrder();  
136 - assertEquals(order.getTotalPrice(), 350);  
137 - assertEquals(order.getDiscountPrice(), 0);  
138 - assertEquals(order.getPointPrice(), 0);  
139 - assertEquals(order.getDeliveryPrice(), 0);  
140 - assertEquals(order.getPayPrice(), 210);  
141 - assertNull(order.getCouponId());  
142 - // 断言 OrderItem 部分  
143 - assertEquals(order.getItems().size(), 2);  
144 - PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);  
145 - assertEquals(orderItem01.getSkuId(), 10L);  
146 - assertEquals(orderItem01.getCount(), 2);  
147 - assertEquals(orderItem01.getOriginalPrice(), 200);  
148 - assertEquals(orderItem01.getOriginalUnitPrice(), 100);  
149 - assertEquals(orderItem01.getDiscountPrice(), 80);  
150 - assertEquals(orderItem01.getPayPrice(), 120);  
151 - assertEquals(orderItem01.getOrderPartPrice(), 0);  
152 - assertEquals(orderItem01.getOrderDividePrice(), 120);  
153 - PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);  
154 - assertEquals(orderItem02.getSkuId(), 20L);  
155 - assertEquals(orderItem02.getCount(), 3);  
156 - assertEquals(orderItem02.getOriginalPrice(), 150);  
157 - assertEquals(orderItem02.getOriginalUnitPrice(), 50);  
158 - assertEquals(orderItem02.getDiscountPrice(), 60);  
159 - assertEquals(orderItem02.getPayPrice(), 90);  
160 - assertEquals(orderItem02.getOrderPartPrice(), 0);  
161 - assertEquals(orderItem02.getOrderDividePrice(), 90);  
162 - // 断言 Promotion 部分  
163 - assertEquals(priceCalculate.getPromotions().size(), 2);  
164 - PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);  
165 - assertEquals(promotion01.getId(), 1000L);  
166 - assertEquals(promotion01.getName(), "活动 1000 号");  
167 - assertEquals(promotion01.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType());  
168 - //assertEquals(promotion01.getLevel(), PromotionLevelEnum.SKU.getLevel()); TODO PromotionLevelEnum 没有这个枚举类  
169 - assertEquals(promotion01.getTotalPrice(), 200);  
170 - assertEquals(promotion01.getDiscountPrice(), 80);  
171 - assertTrue(promotion01.getMatch());  
172 - assertEquals(promotion01.getDescription(), "限时折扣:省 0.80 元");  
173 - PriceCalculateRespDTO.PromotionItem promotionItem01 = promotion01.getItems().get(0);  
174 - assertEquals(promotion01.getItems().size(), 1);  
175 - assertEquals(promotionItem01.getSkuId(), 10L);  
176 - assertEquals(promotionItem01.getOriginalPrice(), 200);  
177 - assertEquals(promotionItem01.getDiscountPrice(), 80);  
178 - PriceCalculateRespDTO.Promotion promotion02 = priceCalculate.getPromotions().get(1);  
179 - assertEquals(promotion02.getId(), 2000L);  
180 - assertEquals(promotion02.getName(), "活动 2000 号");  
181 - assertEquals(promotion02.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType());  
182 - //assertEquals(promotion02.getLevel(), PromotionLevelEnum.SKU.getLevel()); TODO PromotionLevelEnum 没有这个枚举类  
183 - assertEquals(promotion02.getTotalPrice(), 150);  
184 - assertEquals(promotion02.getDiscountPrice(), 60);  
185 - assertTrue(promotion02.getMatch());  
186 - assertEquals(promotion02.getDescription(), "限时折扣:省 0.60 元");  
187 - PriceCalculateRespDTO.PromotionItem promotionItem02 = promotion02.getItems().get(0);  
188 - assertEquals(promotion02.getItems().size(), 1);  
189 - assertEquals(promotionItem02.getSkuId(), 20L);  
190 - assertEquals(promotionItem02.getOriginalPrice(), 150);  
191 - assertEquals(promotionItem02.getDiscountPrice(), 60);  
192 - }  
193 -  
194 - /**  
195 - * 测试满减送活动,匹配的情况  
196 - */  
197 - @Test  
198 - public void testCalculatePrice_rewardActivity() {  
199 - // 准备参数  
200 - PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())  
201 - .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),  
202 - new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3),  
203 - new PriceCalculateReqDTO.Item().setSkuId(30L).setCount(4)));  
204 - // mock 方法(商品 SKU 信息)  
205 - ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L));  
206 - ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L));  
207 - ProductSkuRespDTO productSku03 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(30L).setPrice(30).setSpuId(3L));  
208 - when(productSkuApi.getSkuList(eq(asSet(10L, 20L, 30L)))).thenReturn(asList(productSku01, productSku02, productSku03));  
209 - // mock 方法(限时折扣 DiscountActivity 信息)  
210 - RewardActivityDO rewardActivity01 = randomPojo(RewardActivityDO.class, o -> o.setId(1000L).setName("活动 1000 号")  
211 - .setProductSpuIds(asList(10L, 20L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())  
212 - .setRules(singletonList(new RewardActivityDO.Rule().setLimit(200).setDiscountPrice(70))));  
213 - RewardActivityDO rewardActivity02 = randomPojo(RewardActivityDO.class, o -> o.setId(2000L).setName("活动 2000 号")  
214 - .setProductSpuIds(singletonList(30L)).setConditionType(PromotionConditionTypeEnum.COUNT.getType())  
215 - .setRules(asList(new RewardActivityDO.Rule().setLimit(1).setDiscountPrice(10),  
216 - new RewardActivityDO.Rule().setLimit(2).setDiscountPrice(60), // 最大可满足,因为是 4 个  
217 - new RewardActivityDO.Rule().setLimit(10).setDiscountPrice(100))));  
218 - Map<RewardActivityDO, Set<Long>> matchRewardActivities = new LinkedHashMap<>();  
219 - matchRewardActivities.put(rewardActivity01, asSet(1L, 2L));  
220 - matchRewardActivities.put(rewardActivity02, asSet(3L));  
221 - // TODO getMatchRewardActivities 没有这个方法,但是找到了 getMatchRewardActivityList  
222 - //when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L, 3L)))).thenReturn(matchRewardActivities);  
223 -  
224 - // 调用  
225 - //PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法  
226 - PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();  
227 - // 断言 Order 部分  
228 - PriceCalculateRespDTO.Order order = priceCalculate.getOrder();  
229 - assertEquals(order.getTotalPrice(), 470);  
230 - assertEquals(order.getDiscountPrice(), 130);  
231 - assertEquals(order.getPointPrice(), 0);  
232 - assertEquals(order.getDeliveryPrice(), 0);  
233 - assertEquals(order.getPayPrice(), 340);  
234 - assertNull(order.getCouponId());  
235 - // 断言 OrderItem 部分  
236 - assertEquals(order.getItems().size(), 3);  
237 - PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);  
238 - assertEquals(orderItem01.getSkuId(), 10L);  
239 - assertEquals(orderItem01.getCount(), 2);  
240 - assertEquals(orderItem01.getOriginalPrice(), 200);  
241 - assertEquals(orderItem01.getOriginalUnitPrice(), 100);  
242 - assertEquals(orderItem01.getDiscountPrice(), 0);  
243 - assertEquals(orderItem01.getPayPrice(), 200);  
244 - assertEquals(orderItem01.getOrderPartPrice(), 40);  
245 - assertEquals(orderItem01.getOrderDividePrice(), 160);  
246 - PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);  
247 - assertEquals(orderItem02.getSkuId(), 20L);  
248 - assertEquals(orderItem02.getCount(), 3);  
249 - assertEquals(orderItem02.getOriginalPrice(), 150);  
250 - assertEquals(orderItem02.getOriginalUnitPrice(), 50);  
251 - assertEquals(orderItem02.getDiscountPrice(), 0);  
252 - assertEquals(orderItem02.getPayPrice(), 150);  
253 - assertEquals(orderItem02.getOrderPartPrice(), 30);  
254 - assertEquals(orderItem02.getOrderDividePrice(), 120);  
255 - PriceCalculateRespDTO.OrderItem orderItem03 = order.getItems().get(2);  
256 - assertEquals(orderItem03.getSkuId(), 30L);  
257 - assertEquals(orderItem03.getCount(), 4);  
258 - assertEquals(orderItem03.getOriginalPrice(), 120);  
259 - assertEquals(orderItem03.getOriginalUnitPrice(), 30);  
260 - assertEquals(orderItem03.getDiscountPrice(), 0);  
261 - assertEquals(orderItem03.getPayPrice(), 120);  
262 - assertEquals(orderItem03.getOrderPartPrice(), 60);  
263 - assertEquals(orderItem03.getOrderDividePrice(), 60);  
264 - // 断言 Promotion 部分(第一个)  
265 - assertEquals(priceCalculate.getPromotions().size(), 2);  
266 - PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);  
267 - assertEquals(promotion01.getId(), 1000L);  
268 - assertEquals(promotion01.getName(), "活动 1000 号");  
269 - assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());  
270 - //assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel()); TODO PromotionLevelEnum 没有这个枚举类  
271 - assertEquals(promotion01.getTotalPrice(), 350);  
272 - assertEquals(promotion01.getDiscountPrice(), 70);  
273 - assertTrue(promotion01.getMatch());  
274 - assertEquals(promotion01.getDescription(), "满减送:省 0.70 元");  
275 - assertEquals(promotion01.getItems().size(), 2);  
276 - PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0);  
277 - assertEquals(promotionItem011.getSkuId(), 10L);  
278 - assertEquals(promotionItem011.getOriginalPrice(), 200);  
279 - assertEquals(promotionItem011.getDiscountPrice(), 40);  
280 - PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1);  
281 - assertEquals(promotionItem012.getSkuId(), 20L);  
282 - assertEquals(promotionItem012.getOriginalPrice(), 150);  
283 - assertEquals(promotionItem012.getDiscountPrice(), 30);  
284 - // 断言 Promotion 部分(第二个)  
285 - PriceCalculateRespDTO.Promotion promotion02 = priceCalculate.getPromotions().get(1);  
286 - assertEquals(promotion02.getId(), 2000L);  
287 - assertEquals(promotion02.getName(), "活动 2000 号");  
288 - assertEquals(promotion02.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());  
289 - //assertEquals(promotion02.getLevel(), PromotionLevelEnum.ORDER.getLevel()); TODO PromotionLevelEnum 没有这个枚举类  
290 - assertEquals(promotion02.getTotalPrice(), 120);  
291 - assertEquals(promotion02.getDiscountPrice(), 60);  
292 - assertTrue(promotion02.getMatch());  
293 - assertEquals(promotion02.getDescription(), "满减送:省 0.60 元");  
294 - PriceCalculateRespDTO.PromotionItem promotionItem02 = promotion02.getItems().get(0);  
295 - assertEquals(promotion02.getItems().size(), 1);  
296 - assertEquals(promotionItem02.getSkuId(), 30L);  
297 - assertEquals(promotionItem02.getOriginalPrice(), 120);  
298 - assertEquals(promotionItem02.getDiscountPrice(), 60);  
299 - }  
300 -  
301 /** 48 /**
302 * 测试满减送活动,不匹配的情况 49 * 测试满减送活动,不匹配的情况
303 */ 50 */
304 @Test 51 @Test
305 public void testCalculatePrice_rewardActivityNotMeet() { 52 public void testCalculatePrice_rewardActivityNotMeet() {
306 - // 准备参数  
307 - PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())  
308 - .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),  
309 - new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3)));  
310 - // mock 方法(商品 SKU 信息)  
311 - ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L));  
312 - ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L));  
313 - when(productSkuApi.getSkuList(eq(asSet(10L, 20L)))).thenReturn(asList(productSku01, productSku02));  
314 - // mock 方法(限时折扣 DiscountActivity 信息)  
315 - RewardActivityDO rewardActivity01 = randomPojo(RewardActivityDO.class, o -> o.setId(1000L).setName("活动 1000 号")  
316 - .setProductSpuIds(asList(10L, 20L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())  
317 - .setRules(singletonList(new RewardActivityDO.Rule().setLimit(351).setDiscountPrice(70))));  
318 - Map<RewardActivityDO, Set<Long>> matchRewardActivities = new LinkedHashMap<>();  
319 - matchRewardActivities.put(rewardActivity01, asSet(1L, 2L));  
320 - //TODO getMatchRewardActivities 没有这个方法,但是找到了 getMatchRewardActivityList  
321 - //when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L)))).thenReturn(matchRewardActivities);  
322 53
323 - // 调用  
324 - //PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法  
325 - PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();  
326 - // 断言 Order 部分  
327 - PriceCalculateRespDTO.Order order = priceCalculate.getOrder();  
328 - assertEquals(order.getTotalPrice(), 350);  
329 - assertEquals(order.getDiscountPrice(), 0);  
330 - assertEquals(order.getPointPrice(), 0);  
331 - assertEquals(order.getDeliveryPrice(), 0);  
332 - assertEquals(order.getPayPrice(), 350);  
333 - assertNull(order.getCouponId());  
334 - // 断言 OrderItem 部分  
335 - assertEquals(order.getItems().size(), 2);  
336 - PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);  
337 - assertEquals(orderItem01.getSkuId(), 10L);  
338 - assertEquals(orderItem01.getCount(), 2);  
339 - assertEquals(orderItem01.getOriginalPrice(), 200);  
340 - assertEquals(orderItem01.getOriginalUnitPrice(), 100);  
341 - assertEquals(orderItem01.getDiscountPrice(), 0);  
342 - assertEquals(orderItem01.getPayPrice(), 200);  
343 - assertEquals(orderItem01.getOrderPartPrice(), 0);  
344 - assertEquals(orderItem01.getOrderDividePrice(), 200);  
345 - PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);  
346 - assertEquals(orderItem02.getSkuId(), 20L);  
347 - assertEquals(orderItem02.getCount(), 3);  
348 - assertEquals(orderItem02.getOriginalPrice(), 150);  
349 - assertEquals(orderItem02.getOriginalUnitPrice(), 50);  
350 - assertEquals(orderItem02.getDiscountPrice(), 0);  
351 - assertEquals(orderItem02.getPayPrice(), 150);  
352 - assertEquals(orderItem02.getOrderPartPrice(), 0);  
353 - assertEquals(orderItem02.getOrderDividePrice(), 150);  
354 - // 断言 Promotion 部分  
355 - assertEquals(priceCalculate.getPromotions().size(), 1);  
356 - PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);  
357 - assertEquals(promotion01.getId(), 1000L);  
358 - assertEquals(promotion01.getName(), "活动 1000 号");  
359 - assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());  
360 - //assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel()); TODO PromotionLevelEnum 没有这个枚举类  
361 - assertEquals(promotion01.getTotalPrice(), 350);  
362 - assertEquals(promotion01.getDiscountPrice(), 0);  
363 - assertFalse(promotion01.getMatch());  
364 - assertEquals(promotion01.getDescription(), "TODO"); // TODO 芋艿:后面再想想  
365 - assertEquals(promotion01.getItems().size(), 2);  
366 - PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0);  
367 - assertEquals(promotionItem011.getSkuId(), 10L);  
368 - assertEquals(promotionItem011.getOriginalPrice(), 200);  
369 - assertEquals(promotionItem011.getDiscountPrice(), 0);  
370 - PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1);  
371 - assertEquals(promotionItem012.getSkuId(), 20L);  
372 - assertEquals(promotionItem012.getOriginalPrice(), 150);  
373 - assertEquals(promotionItem012.getDiscountPrice(), 0);  
374 - }  
375 -  
376 - @Test  
377 - public void testCalculatePrice_coupon() {  
378 - // 准备参数  
379 - PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())  
380 - .setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),  
381 - new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3),  
382 - new PriceCalculateReqDTO.Item().setSkuId(30L).setCount(4)))  
383 - .setCouponId(1024L);  
384 - // mock 方法(商品 SKU 信息)  
385 - ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L));  
386 - ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L));  
387 - ProductSkuRespDTO productSku03 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(30L).setPrice(30).setSpuId(3L));  
388 - when(productSkuApi.getSkuList(eq(asSet(10L, 20L, 30L)))).thenReturn(asList(productSku01, productSku02, productSku03));  
389 - // mock 方法(优惠劵 Coupon 信息)  
390 - CouponDO coupon = randomPojo(CouponDO.class, o -> o.setId(1024L).setName("程序员节")  
391 - .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L))  
392 - .setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType())  
393 - .setDiscountPercent(50).setDiscountLimitPrice(70));  
394 - when(couponService.validCoupon(eq(1024L), eq(calculateReqDTO.getUserId()))).thenReturn(coupon);  
395 -  
396 - // 调用  
397 - //PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法  
398 - PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();  
399 - // 断言 Order 部分  
400 - PriceCalculateRespDTO.Order order = priceCalculate.getOrder();  
401 - assertEquals(order.getTotalPrice(), 470);  
402 - assertEquals(order.getDiscountPrice(), 0);  
403 - assertEquals(order.getPointPrice(), 0);  
404 - assertEquals(order.getDeliveryPrice(), 0);  
405 - assertEquals(order.getPayPrice(), 400);  
406 - assertEquals(order.getCouponId(), 1024L);  
407 - assertEquals(order.getCouponPrice(), 70);  
408 - // 断言 OrderItem 部分  
409 - assertEquals(order.getItems().size(), 3);  
410 - PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);  
411 - assertEquals(orderItem01.getSkuId(), 10L);  
412 - assertEquals(orderItem01.getCount(), 2);  
413 - assertEquals(orderItem01.getOriginalPrice(), 200);  
414 - assertEquals(orderItem01.getOriginalUnitPrice(), 100);  
415 - assertEquals(orderItem01.getDiscountPrice(), 0);  
416 - assertEquals(orderItem01.getPayPrice(), 200);  
417 - assertEquals(orderItem01.getOrderPartPrice(), 40);  
418 - assertEquals(orderItem01.getOrderDividePrice(), 160);  
419 - PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);  
420 - assertEquals(orderItem02.getSkuId(), 20L);  
421 - assertEquals(orderItem02.getCount(), 3);  
422 - assertEquals(orderItem02.getOriginalPrice(), 150);  
423 - assertEquals(orderItem02.getOriginalUnitPrice(), 50);  
424 - assertEquals(orderItem02.getDiscountPrice(), 0);  
425 - assertEquals(orderItem02.getPayPrice(), 150);  
426 - assertEquals(orderItem02.getOrderPartPrice(), 30);  
427 - assertEquals(orderItem02.getOrderDividePrice(), 120);  
428 - PriceCalculateRespDTO.OrderItem orderItem03 = order.getItems().get(2);  
429 - assertEquals(orderItem03.getSkuId(), 30L);  
430 - assertEquals(orderItem03.getCount(), 4);  
431 - assertEquals(orderItem03.getOriginalPrice(), 120);  
432 - assertEquals(orderItem03.getOriginalUnitPrice(), 30);  
433 - assertEquals(orderItem03.getDiscountPrice(), 0);  
434 - assertEquals(orderItem03.getPayPrice(), 120);  
435 - assertEquals(orderItem03.getOrderPartPrice(), 0);  
436 - assertEquals(orderItem03.getOrderDividePrice(), 120);  
437 - // 断言 Promotion 部分  
438 - assertEquals(priceCalculate.getPromotions().size(), 1);  
439 - PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);  
440 - assertEquals(promotion01.getId(), 1024L);  
441 - assertEquals(promotion01.getName(), "程序员节");  
442 - assertEquals(promotion01.getType(), PromotionTypeEnum.COUPON.getType());  
443 - //assertEquals(promotion01.getLevel(), PromotionLevelEnum.COUPON.getLevel()); TODO PromotionLevelEnum 没有这个枚举类  
444 - assertEquals(promotion01.getTotalPrice(), 350);  
445 - assertEquals(promotion01.getDiscountPrice(), 70);  
446 - assertTrue(promotion01.getMatch());  
447 - assertEquals(promotion01.getDescription(), "优惠劵:省 0.70 元");  
448 - assertEquals(promotion01.getItems().size(), 2);  
449 - PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0);  
450 - assertEquals(promotionItem011.getSkuId(), 10L);  
451 - assertEquals(promotionItem011.getOriginalPrice(), 200);  
452 - assertEquals(promotionItem011.getDiscountPrice(), 40);  
453 - PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1);  
454 - assertEquals(promotionItem012.getSkuId(), 20L);  
455 - assertEquals(promotionItem012.getOriginalPrice(), 150);  
456 - assertEquals(promotionItem012.getDiscountPrice(), 30);  
457 } 54 }
458 55
459 @Test 56 @Test
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryExpressChargeModeEnum.java
1 package cn.iocoder.yudao.module.trade.enums.delivery; 1 package cn.iocoder.yudao.module.trade.enums.delivery;
2 2
  3 +import cn.hutool.core.util.ArrayUtil;
3 import cn.iocoder.yudao.framework.common.core.IntArrayValuable; 4 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4 import lombok.AllArgsConstructor; 5 import lombok.AllArgsConstructor;
5 import lombok.Getter; 6 import lombok.Getter;
@@ -35,4 +36,8 @@ public enum DeliveryExpressChargeModeEnum implements IntArrayValuable { @@ -35,4 +36,8 @@ public enum DeliveryExpressChargeModeEnum implements IntArrayValuable {
35 return ARRAYS; 36 return ARRAYS;
36 } 37 }
37 38
  39 + public static DeliveryExpressChargeModeEnum valueOf(Integer value) {
  40 + return ArrayUtil.firstMatch(chargeMode -> chargeMode.getType().equals(value), DeliveryExpressChargeModeEnum.values());
  41 + }
  42 +
38 } 43 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressTemplateController.java
@@ -58,7 +58,7 @@ public class DeliveryExpressTemplateController { @@ -58,7 +58,7 @@ public class DeliveryExpressTemplateController {
58 @Operation(summary = "获得快递运费模板") 58 @Operation(summary = "获得快递运费模板")
59 @Parameter(name = "id", description = "编号", required = true, example = "1024") 59 @Parameter(name = "id", description = "编号", required = true, example = "1024")
60 @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')") 60 @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')")
61 - public CommonResult<DeliveryExpressTemplateRespVO> getDeliveryExpressTemplate(@RequestParam("id") Long id) { 61 + public CommonResult<DeliveryExpressTemplateDetailRespVO> getDeliveryExpressTemplate(@RequestParam("id") Long id) {
62 return success(deliveryExpressTemplateService.getDeliveryExpressTemplate(id)); 62 return success(deliveryExpressTemplateService.getDeliveryExpressTemplate(id));
63 } 63 }
64 64
@@ -66,7 +66,7 @@ public class DeliveryExpressTemplateController { @@ -66,7 +66,7 @@ public class DeliveryExpressTemplateController {
66 @Operation(summary = "获得快递运费模板列表") 66 @Operation(summary = "获得快递运费模板列表")
67 @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048") 67 @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
68 @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')") 68 @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')")
69 - public CommonResult<List<DeliveryExpressTemplateSimpleRespVO>> getDeliveryExpressTemplateList(@RequestParam("ids") Collection<Long> ids) { 69 + public CommonResult<List<DeliveryExpressTemplateRespVO>> getDeliveryExpressTemplateList(@RequestParam("ids") Collection<Long> ids) {
70 List<DeliveryExpressTemplateDO> list = deliveryExpressTemplateService.getDeliveryExpressTemplateList(ids); 70 List<DeliveryExpressTemplateDO> list = deliveryExpressTemplateService.getDeliveryExpressTemplateList(ids);
71 return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list)); 71 return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list));
72 } 72 }
@@ -83,7 +83,7 @@ public class DeliveryExpressTemplateController { @@ -83,7 +83,7 @@ public class DeliveryExpressTemplateController {
83 @GetMapping("/page") 83 @GetMapping("/page")
84 @Operation(summary = "获得快递运费模板分页") 84 @Operation(summary = "获得快递运费模板分页")
85 @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')") 85 @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')")
86 - public CommonResult<PageResult<DeliveryExpressTemplateSimpleRespVO>> getDeliveryExpressTemplatePage(@Valid DeliveryExpressTemplatePageReqVO pageVO) { 86 + public CommonResult<PageResult<DeliveryExpressTemplateRespVO>> getDeliveryExpressTemplatePage(@Valid DeliveryExpressTemplatePageReqVO pageVO) {
87 PageResult<DeliveryExpressTemplateDO> pageResult = deliveryExpressTemplateService.getDeliveryExpressTemplatePage(pageVO); 87 PageResult<DeliveryExpressTemplateDO> pageResult = deliveryExpressTemplateService.getDeliveryExpressTemplatePage(pageVO);
88 return success(DeliveryExpressTemplateConvert.INSTANCE.convertPage(pageResult)); 88 return success(DeliveryExpressTemplateConvert.INSTANCE.convertPage(pageResult));
89 } 89 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +import java.util.List;
  9 +
  10 +@Schema(description = "管理后台 - 快递运费模板的详细 Response VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +@ToString(callSuper = true)
  14 +public class DeliveryExpressTemplateDetailRespVO extends DeliveryExpressTemplateBaseVO {
  15 +
  16 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "371")
  17 + private Long id;
  18 +
  19 + @Schema(description = "运费模板运费设置", requiredMode = Schema.RequiredMode.REQUIRED)
  20 + private List<ExpressTemplateChargeBaseVO> templateCharge;
  21 +
  22 + @Schema(description = "运费模板包邮区域", requiredMode = Schema.RequiredMode.REQUIRED)
  23 + private List<ExpressTemplateFreeBaseVO> templateFree;
  24 +
  25 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java
1 package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate; 1 package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 -import lombok.Data;  
5 -import lombok.EqualsAndHashCode;  
6 -import lombok.ToString; 4 +import lombok.*;
  5 +import java.time.LocalDateTime;
7 6
8 -import java.util.List;  
9 -  
10 -/**  
11 - * @author jason  
12 - */  
13 @Schema(description = "管理后台 - 快递运费模板 Response VO") 7 @Schema(description = "管理后台 - 快递运费模板 Response VO")
14 @Data 8 @Data
15 @EqualsAndHashCode(callSuper = true) 9 @EqualsAndHashCode(callSuper = true)
16 @ToString(callSuper = true) 10 @ToString(callSuper = true)
17 public class DeliveryExpressTemplateRespVO extends DeliveryExpressTemplateBaseVO { 11 public class DeliveryExpressTemplateRespVO extends DeliveryExpressTemplateBaseVO {
18 12
19 - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "371") 13 + @Schema(description = "编号,自增", required = true, example = "371")
20 private Long id; 14 private Long id;
21 15
22 - @Schema(description = "运费模板运费设置", requiredMode = Schema.RequiredMode.REQUIRED)  
23 - private List<ExpressTemplateChargeBaseVO> templateCharge;  
24 -  
25 - @Schema(description = "运费模板包邮区域", requiredMode = Schema.RequiredMode.REQUIRED)  
26 - private List<ExpressTemplateFreeBaseVO> templateFree; 16 + @Schema(description = "创建时间", required = true)
  17 + private LocalDateTime createTime;
27 18
28 } 19 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java deleted
1 -package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.*;  
5 -import java.time.LocalDateTime;  
6 -  
7 -// TODO @jason:simplae 是不是不用继承 DeliveryExpressTemplateBaseVO,直接 id name 属性就够了。  
8 -// @芋艿 这里给列表显示用的。 需要显示计费方式和排序, 所以继承 DeliveryExpressTemplateBaseVO。 这是去掉了包邮区域和 区域运费列表  
9 -@Schema(description = "管理后台 - 快递运费模板 精简 Response VO")  
10 -@Data  
11 -@EqualsAndHashCode(callSuper = true)  
12 -@ToString(callSuper = true)  
13 -public class DeliveryExpressTemplateSimpleRespVO extends DeliveryExpressTemplateBaseVO {  
14 -  
15 - @Schema(description = "编号,自增", required = true, example = "371")  
16 - private Long id;  
17 -  
18 - @Schema(description = "创建时间", required = true)  
19 - private LocalDateTime createTime;  
20 -  
21 -}  
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http
1 -### /trade-order/settlement 获得订单结算信息  
2 -GET {{appApi}}/trade/order/settlement?cartIds=1 1 +### /trade-order/settlement 获得订单结算信息(基于商品)
  2 +GET {{appApi}}/trade/order/settlement?type=0&items[0].skuId=1&items[0].count=2&items[1].skuId=2&items[1].count=3&couponId=1
3 Authorization: Bearer {{appToken}} 3 Authorization: Bearer {{appToken}}
4 tenant-id: {{appTenentId}} 4 tenant-id: {{appTenentId}}
5 5
6 -### /trade-order/confirm-create-order-info-from-cart 基于购物车,确认创建订单  
7 -GET {{shop-api-base-url}}/trade-order/confirm-create-order-info-from-cart  
8 -Content-Type: application/x-www-form-urlencoded  
9 -Authorization: Bearer {{user-access-token}} 6 +### /trade-order/settlement 获得订单结算信息(基于购物车)
  7 +GET {{appApi}}/trade/order/settlement?type=0&items[0].cartId=50&couponId=1
  8 +Authorization: Bearer {{appToken}}
  9 +tenant-id: {{appTenentId}}
10 10
11 -### /trade-order/create 基于商品,创建订单 11 +### /trade-order/create 创建订单(基于商品)
12 POST {{appApi}}/trade/order/create 12 POST {{appApi}}/trade/order/create
13 Content-Type: application/json 13 Content-Type: application/json
14 Authorization: Bearer {{appToken}} 14 Authorization: Bearer {{appToken}}
15 tenant-id: {{appTenentId}} 15 tenant-id: {{appTenentId}}
16 16
17 { 17 {
  18 + "type": 0,
18 "addressId": 21, 19 "addressId": 21,
19 - "remark": "我是备注",  
20 - "fromCart": false,  
21 "items": [ 20 "items": [
22 { 21 {
23 - "skuId": 29,  
24 - "count": 1 22 + "skuId": 1,
  23 + "count": 2
25 } 24 }
26 - ] 25 + ],
  26 + "remark": "我是备注"
27 } 27 }
28 28
29 ### 获得订单交易的分页 29 ### 获得订单交易的分页
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
@@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
6 import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; 6 import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
7 import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi; 7 import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi;
8 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; 8 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
9 -import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;  
10 import cn.iocoder.yudao.module.trade.controller.app.order.vo.*; 9 import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
11 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; 10 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
12 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 11 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
@@ -23,15 +22,14 @@ import org.springframework.validation.annotation.Validated; @@ -23,15 +22,14 @@ import org.springframework.validation.annotation.Validated;
23 import org.springframework.web.bind.annotation.*; 22 import org.springframework.web.bind.annotation.*;
24 23
25 import javax.annotation.Resource; 24 import javax.annotation.Resource;
26 -import javax.servlet.http.HttpServletRequest;  
27 import javax.validation.Valid; 25 import javax.validation.Valid;
28 -import java.util.ArrayList;  
29 import java.util.HashMap; 26 import java.util.HashMap;
30 import java.util.List; 27 import java.util.List;
31 import java.util.Map; 28 import java.util.Map;
32 29
33 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
34 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 31 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  32 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
35 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 33 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
36 34
37 @Tag(name = "用户 App - 交易订单") 35 @Tag(name = "用户 App - 交易订单")
@@ -53,92 +51,16 @@ public class AppTradeOrderController { @@ -53,92 +51,16 @@ public class AppTradeOrderController {
53 @GetMapping("/settlement") 51 @GetMapping("/settlement")
54 @Operation(summary = "获得订单结算信息") 52 @Operation(summary = "获得订单结算信息")
55 @PreAuthenticated 53 @PreAuthenticated
56 - public CommonResult<AppTradeOrderSettlementRespVO> settlementOrder(  
57 - @Valid AppTradeOrderSettlementReqVO settlementReqVO) {  
58 - if (true) {  
59 - return success(tradeOrderService.settlementOrder(getLoginUserId(), settlementReqVO));  
60 - }  
61 -// return success(tradeOrderService.getOrderConfirmCreateInfo(UserSecurityContextHolder.getUserId(), skuId, quantity, couponCardId));  
62 - AppTradeOrderSettlementRespVO settlement = new AppTradeOrderSettlementRespVO();  
63 -  
64 - AppTradeOrderSettlementRespVO.Price price = new AppTradeOrderSettlementRespVO.Price();  
65 - price.setTotalPrice(1000);  
66 - price.setDeliveryPrice(200);  
67 - price.setCouponPrice(100);  
68 - price.setPointPrice(50);  
69 - price.setPayPrice(950);  
70 -  
71 - List<AppTradeOrderSettlementRespVO.Item> skus = new ArrayList<>();  
72 -  
73 - AppTradeOrderSettlementRespVO.Item item1 = new AppTradeOrderSettlementRespVO.Item();  
74 - item1.setCartId(1L);  
75 - item1.setSpuId(2048L);  
76 - item1.setSpuName("Apple iPhone 12");  
77 - item1.setSkuId(1024);  
78 - item1.setPrice(500);  
79 - item1.setPicUrl("https://pro.crmeb.net/uploads/attach/2022/10/12/0c56f9abb80d2775fc1e80dbe4f8826a.jpg");  
80 - item1.setCount(2);  
81 - List<AppProductPropertyValueDetailRespVO> properties1 = new ArrayList<>();  
82 - AppProductPropertyValueDetailRespVO property1 = new AppProductPropertyValueDetailRespVO();  
83 - property1.setPropertyId(1L);  
84 - property1.setPropertyName("尺寸");  
85 - property1.setValueId(2L);  
86 - property1.setValueName("大");  
87 - properties1.add(property1);  
88 - item1.setProperties(properties1);  
89 -  
90 - AppTradeOrderSettlementRespVO.Item item2 = new AppTradeOrderSettlementRespVO.Item();  
91 - item2.setCartId(2L);  
92 - item2.setSpuId(3072L);  
93 - item2.setSpuName("Samsung Galaxy S21");  
94 - item2.setSkuId(2048);  
95 - item2.setPrice(800);  
96 - item2.setPicUrl("https://pro.crmeb.net/uploads/attach/2022/10/12/0c56f9abb80d2775fc1e80dbe4f8826a.jpg");  
97 - item2.setCount(1);  
98 - List<AppProductPropertyValueDetailRespVO> properties2 = new ArrayList<>();  
99 - AppProductPropertyValueDetailRespVO property2 = new AppProductPropertyValueDetailRespVO();  
100 - property2.setPropertyId(10L);  
101 - property2.setPropertyName("颜色");  
102 - property2.setValueId(20L);  
103 - property2.setValueName("白色");  
104 - properties2.add(property2);  
105 - item2.setProperties(properties2);  
106 -  
107 - skus.add(item1);  
108 - skus.add(item2);  
109 -  
110 - settlement.setItems(skus);  
111 - settlement.setPrice(price);  
112 -  
113 - AppTradeOrderSettlementRespVO.Address address = new AppTradeOrderSettlementRespVO.Address();  
114 - address.setId(1L);  
115 - address.setName("John");  
116 - address.setMobile("18888888888");  
117 - address.setProvinceId(1L);  
118 - address.setProvinceName("Beijing");  
119 - address.setCityId(1L);  
120 - address.setCityName("Beijing");  
121 - address.setDistrictId(1L);  
122 - address.setDistrictName("Chaoyang Distripct");  
123 - address.setDetailAddress("No. 10, Xinzhong Street, Chaoyang District");  
124 - address.setDefaulted(true);  
125 - settlement.setAddress(address);  
126 -  
127 - return success(settlement); 54 + public CommonResult<AppTradeOrderSettlementRespVO> settlementOrder(@Valid AppTradeOrderSettlementReqVO settlementReqVO) {
  55 + return success(tradeOrderService.settlementOrder(getLoginUserId(), settlementReqVO));
128 } 56 }
129 57
130 @PostMapping("/create") 58 @PostMapping("/create")
131 @Operation(summary = "创建订单") 59 @Operation(summary = "创建订单")
132 @PreAuthenticated 60 @PreAuthenticated
133 - public CommonResult<Long> createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO,  
134 - HttpServletRequest servletRequest) {  
135 - return success(1L);  
136 -// // 获取登录用户、用户 IP 地址  
137 -// Long loginUserId = getLoginUserId();  
138 -// String clientIp = ServletUtils.getClientIP(servletRequest);  
139 -// // 创建交易订单,预支付记录  
140 -// Long orderId = tradeOrderService.createOrder(loginUserId, clientIp, createReqVO);  
141 -// return success(orderId); 61 + public CommonResult<Long> createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO) {
  62 + Long orderId = tradeOrderService.createOrder(getLoginUserId(), getClientIP(), createReqVO);
  63 + return success(orderId);
142 } 64 }
143 65
144 @PostMapping("/update-paid") 66 @PostMapping("/update-paid")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java
@@ -3,26 +3,9 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo; @@ -3,26 +3,9 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo;
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
5 5
6 -import javax.validation.constraints.NotEmpty;  
7 -import javax.validation.constraints.NotNull;  
8 -import java.util.List;  
9 -  
10 @Schema(description = "用户 App - 交易订单创建 Request VO") 6 @Schema(description = "用户 App - 交易订单创建 Request VO")
11 @Data 7 @Data
12 -public class AppTradeOrderCreateReqVO {  
13 -  
14 - @Schema(description = "收件地址编号", required = true, example = "1")  
15 - @NotNull(message = "收件地址不能为空")  
16 - private Long addressId;  
17 -  
18 - @Schema(description = "优惠劵编号", example = "1024")  
19 - private Long couponId;  
20 -  
21 - @Schema(description = "购物车项的编号数组", required = true, example = "true")  
22 - @NotEmpty(message = "购物车项不能为空")  
23 - private List<Long> cartIds;  
24 -  
25 - // ========== 非 AppTradeOrderSettlementReqVO 字段 ========== 8 +public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO {
26 9
27 @Schema(description = "备注", example = "这个是我的订单哟") 10 @Schema(description = "备注", example = "这个是我的订单哟")
28 private String remark; 11 private String remark;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.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 cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
  5 +import com.fasterxml.jackson.annotation.JsonIgnore;
3 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 7 import lombok.Data;
5 8
6 -import javax.validation.constraints.NotEmpty; 9 +import javax.validation.Valid;
  10 +import javax.validation.constraints.AssertTrue;
  11 +import javax.validation.constraints.Min;
  12 +import javax.validation.constraints.NotNull;
7 import java.util.List; 13 import java.util.List;
8 14
9 @Schema(description = "用户 App - 交易订单结算 Request VO") 15 @Schema(description = "用户 App - 交易订单结算 Request VO")
10 @Data 16 @Data
11 public class AppTradeOrderSettlementReqVO { 17 public class AppTradeOrderSettlementReqVO {
12 18
  19 + @NotNull(message = "交易类型不能为空")
  20 + @InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}")
  21 + private Integer type;
  22 +
  23 + @Schema(description = "商品项数组", required = true)
  24 + @NotNull(message = "商品不能为空")
  25 + private List<Item> items;
  26 +
13 @Schema(description = "收件地址编号", example = "1") 27 @Schema(description = "收件地址编号", example = "1")
14 private Long addressId; 28 private Long addressId;
15 29
16 @Schema(description = "优惠劵编号", example = "1024") 30 @Schema(description = "优惠劵编号", example = "1024")
17 private Long couponId; 31 private Long couponId;
18 32
19 - @Schema(description = "购物车项的编号数组", required = true, example = "true")  
20 - @NotEmpty(message = "购物车项不能为空")  
21 - private List<Long> cartIds; 33 + @Data
  34 + @Schema(description = "用户 App - 商品项")
  35 + @Valid
  36 + public static class Item {
  37 +
  38 + @Schema(description = "商品 SKU 编号", example = "2048")
  39 + private Long skuId;
  40 + @Schema(description = "购买数量", example = "1")
  41 + @Min(value = 1, message = "购买数量最小值为 {value}")
  42 + private Integer count;
  43 +
  44 + @Schema(description = "购物车项的编号", example = "1024")
  45 + private Long cartId;
  46 +
  47 + @AssertTrue(message = "商品不正确")
  48 + @JsonIgnore
  49 + public boolean isValid() {
  50 + // 组合一:skuId + count 使用商品 SKU
  51 + if (skuId != null && count != null) {
  52 + return true;
  53 + }
  54 + // 组合二:cartId 使用购物车项
  55 + return cartId != null;
  56 + }
  57 +
  58 + }
22 59
23 } 60 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
@@ -91,17 +91,17 @@ public class AppTradeOrderSettlementRespVO { @@ -91,17 +91,17 @@ public class AppTradeOrderSettlementRespVO {
91 private String mobile; 91 private String mobile;
92 92
93 @Schema(description = "省份编号", required = true, example = "1") 93 @Schema(description = "省份编号", required = true, example = "1")
94 - private Long provinceId; 94 + private Integer provinceId;
95 @Schema(description = "省份名字", required = true, example = "北京") 95 @Schema(description = "省份名字", required = true, example = "北京")
96 private String provinceName; 96 private String provinceName;
97 97
98 @Schema(description = "城市编号", required = true, example = "1") 98 @Schema(description = "城市编号", required = true, example = "1")
99 - private Long cityId; 99 + private Integer cityId;
100 @Schema(description = "城市名字", required = true, example = "北京") 100 @Schema(description = "城市名字", required = true, example = "北京")
101 private String cityName; 101 private String cityName;
102 102
103 @Schema(description = "地区编号", required = true, example = "1") 103 @Schema(description = "地区编号", required = true, example = "1")
104 - private Long districtId; 104 + private Integer districtId;
105 @Schema(description = "地区名字", required = true, example = "朝阳区") 105 @Schema(description = "地区名字", required = true, example = "朝阳区")
106 private String districtName; 106 private String districtName;
107 107
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java
@@ -22,18 +22,18 @@ public interface DeliveryExpressTemplateConvert { @@ -22,18 +22,18 @@ public interface DeliveryExpressTemplateConvert {
22 22
23 DeliveryExpressTemplateDO convert(DeliveryExpressTemplateUpdateReqVO bean); 23 DeliveryExpressTemplateDO convert(DeliveryExpressTemplateUpdateReqVO bean);
24 24
25 - DeliveryExpressTemplateSimpleRespVO convert(DeliveryExpressTemplateDO bean); 25 + DeliveryExpressTemplateRespVO convert(DeliveryExpressTemplateDO bean);
26 26
27 - DeliveryExpressTemplateRespVO convert2(DeliveryExpressTemplateDO bean); 27 + DeliveryExpressTemplateDetailRespVO convert2(DeliveryExpressTemplateDO bean);
28 28
29 - List<DeliveryExpressTemplateSimpleRespVO> convertList(List<DeliveryExpressTemplateDO> list); 29 + List<DeliveryExpressTemplateRespVO> convertList(List<DeliveryExpressTemplateDO> list);
30 30
31 - PageResult<DeliveryExpressTemplateSimpleRespVO> convertPage(PageResult<DeliveryExpressTemplateDO> page); 31 + PageResult<DeliveryExpressTemplateRespVO> convertPage(PageResult<DeliveryExpressTemplateDO> page);
32 32
33 - default DeliveryExpressTemplateRespVO convert(DeliveryExpressTemplateDO bean,  
34 - List<DeliveryExpressTemplateChargeDO> chargeList,  
35 - List<DeliveryExpressTemplateFreeDO> freeList){  
36 - DeliveryExpressTemplateRespVO respVO = convert2(bean); 33 + default DeliveryExpressTemplateDetailRespVO convert(DeliveryExpressTemplateDO bean,
  34 + List<DeliveryExpressTemplateChargeDO> chargeList,
  35 + List<DeliveryExpressTemplateFreeDO> freeList){
  36 + DeliveryExpressTemplateDetailRespVO respVO = convert2(bean);
37 respVO.setTemplateCharge(convertTemplateChargeList(chargeList)); 37 respVO.setTemplateCharge(convertTemplateChargeList(chargeList));
38 respVO.setTemplateFree(convertTemplateFreeList(freeList)); 38 respVO.setTemplateFree(convertTemplateFreeList(freeList));
39 return respVO; 39 return respVO;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
@@ -3,29 +3,28 @@ package cn.iocoder.yudao.module.trade.convert.order; @@ -3,29 +3,28 @@ package cn.iocoder.yudao.module.trade.convert.order;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.framework.ip.core.Area;
6 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; 7 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
7 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; 8 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
8 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 9 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
9 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 10 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
10 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; 11 import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
11 -import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;  
12 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; 12 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
13 -import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;  
14 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO; 13 import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
15 -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;  
16 import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO; 14 import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
17 import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO; 15 import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
18 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO; 16 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
19 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO; 17 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
20 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO; 18 import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
21 -import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;  
22 -import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderDetailRespVO;  
23 -import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageItemRespVO; 19 +import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
24 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO; 20 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
  21 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
25 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 22 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
26 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 23 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
27 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; 24 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
28 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; 25 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
  26 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  27 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
29 import org.mapstruct.Mapper; 28 import org.mapstruct.Mapper;
30 import org.mapstruct.Mapping; 29 import org.mapstruct.Mapping;
31 import org.mapstruct.Mappings; 30 import org.mapstruct.Mappings;
@@ -48,32 +47,32 @@ public interface TradeOrderConvert { @@ -48,32 +47,32 @@ public interface TradeOrderConvert {
48 @Mapping(source = "createReqVO.couponId", target = "couponId"), 47 @Mapping(source = "createReqVO.couponId", target = "couponId"),
49 @Mapping(target = "remark", ignore = true), 48 @Mapping(target = "remark", ignore = true),
50 @Mapping(source = "createReqVO.remark", target = "userRemark"), 49 @Mapping(source = "createReqVO.remark", target = "userRemark"),
  50 + @Mapping(source = "createReqVO.type", target = "type"),
  51 + @Mapping(source = "calculateRespBO.price.totalPrice", target = "totalPrice"),
  52 + @Mapping(source = "calculateRespBO.price.discountPrice", target = "discountPrice"),
  53 + @Mapping(source = "calculateRespBO.price.deliveryPrice", target = "deliveryPrice"),
  54 + @Mapping(source = "calculateRespBO.price.couponPrice", target = "couponPrice"),
  55 + @Mapping(source = "calculateRespBO.price.pointPrice", target = "pointPrice"),
  56 + @Mapping(source = "calculateRespBO.price.payPrice", target = "payPrice"),
51 @Mapping(source = "address.name", target = "receiverName"), 57 @Mapping(source = "address.name", target = "receiverName"),
52 @Mapping(source = "address.mobile", target = "receiverMobile"), 58 @Mapping(source = "address.mobile", target = "receiverMobile"),
53 @Mapping(source = "address.areaId", target = "receiverAreaId"), 59 @Mapping(source = "address.areaId", target = "receiverAreaId"),
54 @Mapping(source = "address.detailAddress", target = "receiverDetailAddress"), 60 @Mapping(source = "address.detailAddress", target = "receiverDetailAddress"),
55 }) 61 })
56 TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, 62 TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO,
57 - PriceCalculateRespDTO.Order order, AddressRespDTO address); 63 + TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address);
58 64
59 - @Mappings({  
60 - @Mapping(target = "id", ignore = true),  
61 - @Mapping(source = "sku.spuId", target = "spuId"),  
62 - })  
63 - TradeOrderItemDO convert(PriceCalculateRespDTO.OrderItem orderItem, ProductSkuRespDTO sku);  
64 -  
65 - default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrderDO,  
66 - List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {  
67 - Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);  
68 - return CollectionUtils.convertList(orderItems, orderItem -> {  
69 - TradeOrderItemDO tradeOrderItemDO = convert(orderItem, skuMap.get(orderItem.getSkuId()));  
70 - tradeOrderItemDO.setOrderId(tradeOrderDO.getId());  
71 - tradeOrderItemDO.setUserId(tradeOrderDO.getUserId());  
72 - tradeOrderItemDO.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); // 退款信息  
73 -// tradeOrderItemDO.setCommented(false);  
74 - return tradeOrderItemDO; 65 + default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {
  66 + return CollectionUtils.convertList(calculateRespBO.getItems(), item -> {
  67 + TradeOrderItemDO orderItem = convert(item);
  68 + orderItem.setOrderId(tradeOrderDO.getId());
  69 + orderItem.setUserId(tradeOrderDO.getUserId());
  70 + orderItem.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
  71 + orderItem.setCommentStatus(false);
  72 + return orderItem;
75 }); 73 });
76 } 74 }
  75 + TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item);
77 76
78 @Mapping(source = "userId" , target = "userId") 77 @Mapping(source = "userId" , target = "userId")
79 PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId); 78 PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId);
@@ -85,19 +84,20 @@ public interface TradeOrderConvert { @@ -85,19 +84,20 @@ public interface TradeOrderConvert {
85 ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean); 84 ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean);
86 List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list); 85 List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
87 86
88 - default PayOrderCreateReqDTO convert(TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,  
89 - List<ProductSpuRespDTO> spus, TradeOrderProperties tradeOrderProperties) { 87 + default PayOrderCreateReqDTO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
  88 + TradePriceCalculateRespBO calculateRespBO, TradeOrderProperties orderProperties) {
90 PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO() 89 PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO()
91 - .setAppId(tradeOrderProperties.getAppId()).setUserIp(tradeOrderDO.getUserIp()); 90 + .setAppId(orderProperties.getAppId()).setUserIp(order.getUserIp());
92 // 商户相关字段 91 // 商户相关字段
93 - createReqDTO.setMerchantOrderId(String.valueOf(tradeOrderDO.getId()));  
94 - String subject = spus.get(0).getName();  
95 - if (spus.size() > 1) { 92 + createReqDTO.setMerchantOrderId(String.valueOf(order.getId()));
  93 + String subject = calculateRespBO.getItems().get(0).getSpuName();
  94 + if (calculateRespBO.getItems().size() > 1) {
96 subject += " 等多件"; 95 subject += " 等多件";
97 } 96 }
98 createReqDTO.setSubject(subject); 97 createReqDTO.setSubject(subject);
  98 + createReqDTO.setBody(subject); // TODO 芋艿:临时写死
99 // 订单相关字段 99 // 订单相关字段
100 - createReqDTO.setAmount(tradeOrderDO.getPayPrice()).setExpireTime(addTime(tradeOrderProperties.getExpireTime())); 100 + createReqDTO.setAmount(order.getPayPrice()).setExpireTime(addTime(orderProperties.getExpireTime()));
101 return createReqDTO; 101 return createReqDTO;
102 } 102 }
103 103
@@ -111,6 +111,7 @@ public interface TradeOrderConvert { @@ -111,6 +111,7 @@ public interface TradeOrderConvert {
111 .collect(Collectors.toSet()); 111 .collect(Collectors.toSet());
112 } 112 }
113 113
  114 + // TODO 芋艿:可简化
114 default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems, 115 default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
115 List<ProductPropertyValueDetailRespDTO> propertyValueDetails) { 116 List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
116 Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId); 117 Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
@@ -147,6 +148,7 @@ public interface TradeOrderConvert { @@ -147,6 +148,7 @@ public interface TradeOrderConvert {
147 TradeOrderPageItemRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items); 148 TradeOrderPageItemRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items);
148 ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean); 149 ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
149 150
  151 + // TODO 芋艿:可简化
150 default TradeOrderDetailRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems, 152 default TradeOrderDetailRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
151 List<ProductPropertyValueDetailRespDTO> propertyValueDetails, MemberUserRespDTO user) { 153 List<ProductPropertyValueDetailRespDTO> propertyValueDetails, MemberUserRespDTO user) {
152 TradeOrderDetailRespVO orderVO = convert2(order, orderItems); 154 TradeOrderDetailRespVO orderVO = convert2(order, orderItems);
@@ -177,6 +179,7 @@ public interface TradeOrderConvert { @@ -177,6 +179,7 @@ public interface TradeOrderConvert {
177 TradeOrderDetailRespVO convert2(TradeOrderDO order, List<TradeOrderItemDO> items); 179 TradeOrderDetailRespVO convert2(TradeOrderDO order, List<TradeOrderItemDO> items);
178 MemberUserRespVO convert(MemberUserRespDTO bean); 180 MemberUserRespVO convert(MemberUserRespDTO bean);
179 181
  182 + // TODO 芋艿:可简化
180 default PageResult<AppTradeOrderPageItemRespVO> convertPage02(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems, 183 default PageResult<AppTradeOrderPageItemRespVO> convertPage02(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
181 List<ProductPropertyValueDetailRespDTO> propertyValueDetails) { 184 List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
182 Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId); 185 Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
@@ -241,4 +244,45 @@ public interface TradeOrderConvert { @@ -241,4 +244,45 @@ public interface TradeOrderConvert {
241 244
242 AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean); 245 AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean);
243 246
  247 + default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
  248 + List<TradeCartDO> cartList) {
  249 + TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO();
  250 + reqBO.setUserId(userId).setType(settlementReqVO.getType())
  251 + .setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId())
  252 + .setItems(new ArrayList<>(settlementReqVO.getItems().size()));
  253 + // 商品项的构建
  254 + Map<Long, TradeCartDO> cartMap = convertMap(cartList, TradeCartDO::getId);
  255 + for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {
  256 + // 情况一:skuId + count
  257 + if (item.getSkuId() != null) {
  258 + reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(item.getSkuId()).setCount(item.getCount())
  259 + .setSelected(true)); // true 的原因,下单一定选中
  260 + continue;
  261 + }
  262 + // 情况二:cartId
  263 + TradeCartDO cart = cartMap.get(item.getCartId());
  264 + if (cart == null) {
  265 + continue;
  266 + }
  267 + reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(cart.getSkuId()).setCount(cart.getCount())
  268 + .setCartId(item.getCartId()).setSelected(true)); // true 的原因,下单一定选中
  269 + }
  270 + return reqBO;
  271 + }
  272 +
  273 + default AppTradeOrderSettlementRespVO convert(TradePriceCalculateRespBO calculate, AddressRespDTO address) {
  274 + AppTradeOrderSettlementRespVO respVO = convert0(calculate, address);
  275 + if (address != null) {
  276 + Area area = AreaUtils.getArea(address.getAreaId());
  277 + respVO.getAddress().setDistrictId(area.getId());
  278 + respVO.getAddress().setDistrictName(area.getName());
  279 + respVO.getAddress().setCityId(area.getParent().getId());
  280 + respVO.getAddress().setCityName(area.getParent().getName());
  281 + respVO.getAddress().setProvinceId(area.getParent().getParent().getId());
  282 + respVO.getAddress().setProvinceName(area.getParent().getParent().getName());
  283 + }
  284 + return respVO;
  285 + }
  286 + AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address);
  287 +
244 } 288 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java
@@ -167,11 +167,24 @@ public class TradeOrderItemDO extends BaseDO { @@ -167,11 +167,24 @@ public class TradeOrderItemDO extends BaseDO {
167 */ 167 */
168 private Long propertyId; 168 private Long propertyId;
169 /** 169 /**
  170 + * 属性名字
  171 + *
  172 + * 关联 ProductPropertyDO 的 name 字段
  173 + */
  174 + private String propertyName;
  175 +
  176 + /**
170 * 属性值编号 177 * 属性值编号
171 * 178 *
172 * 关联 ProductPropertyValueDO 的 id 编号 179 * 关联 ProductPropertyValueDO 的 id 编号
173 */ 180 */
174 private Long valueId; 181 private Long valueId;
  182 + /**
  183 + * 属性值名字
  184 + *
  185 + * 关联 ProductPropertyValueDO 的 name 字段
  186 + */
  187 + private String valueName;
175 188
176 } 189 }
177 190
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java
@@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper; @@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
12 import java.util.Collection; 12 import java.util.Collection;
13 import java.util.List; 13 import java.util.List;
14 import java.util.Map; 14 import java.util.Map;
  15 +import java.util.Set;
15 16
16 @Mapper 17 @Mapper
17 public interface TradeCartMapper extends BaseMapperX<TradeCartDO> { 18 public interface TradeCartMapper extends BaseMapperX<TradeCartDO> {
@@ -70,4 +71,10 @@ public interface TradeCartMapper extends BaseMapperX&lt;TradeCartDO&gt; { @@ -70,4 +71,10 @@ public interface TradeCartMapper extends BaseMapperX&lt;TradeCartDO&gt; {
70 update(updateObject, new LambdaQueryWrapper<TradeCartDO>().in(TradeCartDO::getId, ids)); 71 update(updateObject, new LambdaQueryWrapper<TradeCartDO>().in(TradeCartDO::getId, ids));
71 } 72 }
72 73
  74 + default List<TradeCartDO> selectListByUserId(Long userId, Set<Long> ids) {
  75 + return selectList(new LambdaQueryWrapper<TradeCartDO>()
  76 + .eq(TradeCartDO::getUserId, userId)
  77 + .in(TradeCartDO::getId, ids));
  78 + }
  79 +
73 } 80 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java
@@ -4,10 +4,13 @@ import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO @@ -4,10 +4,13 @@ import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO
4 import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO; 4 import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
5 import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO; 5 import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO;
6 import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO; 6 import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
  7 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
7 8
8 import javax.validation.Valid; 9 import javax.validation.Valid;
9 import java.util.Collection; 10 import java.util.Collection;
  11 +import java.util.List;
10 import java.util.Map; 12 import java.util.Map;
  13 +import java.util.Set;
11 14
12 /** 15 /**
13 * 购物车 Service 接口 16 * 购物车 Service 接口
@@ -68,6 +71,15 @@ public interface TradeCartService { @@ -68,6 +71,15 @@ public interface TradeCartService {
68 AppTradeCartListRespVO getCartList(Long userId); 71 AppTradeCartListRespVO getCartList(Long userId);
69 72
70 /** 73 /**
  74 + * 查询用户的购物车列表
  75 + *
  76 + * @param userId 用户编号
  77 + * @param ids 购物项的编号
  78 + * @return 购物车列表
  79 + */
  80 + List<TradeCartDO> getCartList(Long userId, Set<Long> ids);
  81 +
  82 + /**
71 * 获得用户的购物车商品 SPU 数量的 Map 83 * 获得用户的购物车商品 SPU 数量的 Map
72 * 84 *
73 * @param userId 用户编号 85 * @param userId 用户编号
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java
@@ -17,10 +17,7 @@ import org.springframework.transaction.annotation.Transactional; @@ -17,10 +17,7 @@ import org.springframework.transaction.annotation.Transactional;
17 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
18 18
19 import javax.annotation.Resource; 19 import javax.annotation.Resource;
20 -import java.util.Collection;  
21 -import java.util.Comparator;  
22 -import java.util.List;  
23 -import java.util.Map; 20 +import java.util.*;
24 21
25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -166,6 +163,14 @@ public class TradeCartServiceImpl implements TradeCartService { @@ -166,6 +163,14 @@ public class TradeCartServiceImpl implements TradeCartService {
166 return TradeCartConvert.INSTANCE.convertList(carts, spus, skus); 163 return TradeCartConvert.INSTANCE.convertList(carts, spus, skus);
167 } 164 }
168 165
  166 + @Override
  167 + public List<TradeCartDO> getCartList(Long userId, Set<Long> ids) {
  168 + if (CollUtil.isEmpty(ids)) {
  169 + return Collections.emptyList();
  170 + }
  171 + return cartMapper.selectListByUserId(userId, ids);
  172 + }
  173 +
169 private void deleteCartIfSpuDeleted(List<TradeCartDO> carts, List<ProductSpuRespDTO> spus) { 174 private void deleteCartIfSpuDeleted(List<TradeCartDO> carts, List<ProductSpuRespDTO> spus) {
170 // 如果 SPU 被删除,则删除购物车对应的商品。延迟删除 175 // 如果 SPU 被删除,则删除购物车对应的商品。延迟删除
171 carts.removeIf(cart -> { 176 carts.removeIf(cart -> {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java
@@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.trade.service.delivery; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.trade.service.delivery;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO; 4 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO;
  5 +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateDetailRespVO;
5 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO; 6 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO;
6 -import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateRespVO;  
7 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO; 7 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
9 9
@@ -46,7 +46,7 @@ public interface DeliveryExpressTemplateService { @@ -46,7 +46,7 @@ public interface DeliveryExpressTemplateService {
46 * @param id 编号 46 * @param id 编号
47 * @return 快递运费模板详情 47 * @return 快递运费模板详情
48 */ 48 */
49 - DeliveryExpressTemplateRespVO getDeliveryExpressTemplate(Long id); 49 + DeliveryExpressTemplateDetailRespVO getDeliveryExpressTemplate(Long id);
50 50
51 /** 51 /**
52 * 获得快递运费模板列表 52 * 获得快递运费模板列表
@@ -70,4 +70,14 @@ public interface DeliveryExpressTemplateService { @@ -70,4 +70,14 @@ public interface DeliveryExpressTemplateService {
70 * @return 快递运费模板分页 70 * @return 快递运费模板分页
71 */ 71 */
72 PageResult<DeliveryExpressTemplateDO> getDeliveryExpressTemplatePage(DeliveryExpressTemplatePageReqVO pageReqVO); 72 PageResult<DeliveryExpressTemplateDO> getDeliveryExpressTemplatePage(DeliveryExpressTemplatePageReqVO pageReqVO);
  73 +
  74 + /**
  75 + * 校验快递运费模板
  76 + *
  77 + * 如果校验不通过,抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 异常
  78 + *
  79 + * @param templateId 模板编号
  80 + * @return 快递运费模板
  81 + */
  82 + DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId);
73 } 83 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java
@@ -185,7 +185,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla @@ -185,7 +185,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
185 } 185 }
186 186
187 @Override 187 @Override
188 - public DeliveryExpressTemplateRespVO getDeliveryExpressTemplate(Long id) { 188 + public DeliveryExpressTemplateDetailRespVO getDeliveryExpressTemplate(Long id) {
189 List<DeliveryExpressTemplateChargeDO> chargeList = expressTemplateChargeMapper.selectListByTemplateId(id); 189 List<DeliveryExpressTemplateChargeDO> chargeList = expressTemplateChargeMapper.selectListByTemplateId(id);
190 List<DeliveryExpressTemplateFreeDO> freeList = expressTemplateFreeMapper.selectListByTemplateId(id); 190 List<DeliveryExpressTemplateFreeDO> freeList = expressTemplateFreeMapper.selectListByTemplateId(id);
191 DeliveryExpressTemplateDO template = expressTemplateMapper.selectById(id); 191 DeliveryExpressTemplateDO template = expressTemplateMapper.selectById(id);
@@ -207,4 +207,13 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla @@ -207,4 +207,13 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
207 return expressTemplateMapper.selectPage(pageReqVO); 207 return expressTemplateMapper.selectPage(pageReqVO);
208 } 208 }
209 209
  210 + @Override
  211 + public DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId) {
  212 + DeliveryExpressTemplateDO template = expressTemplateMapper.selectById(templateId);
  213 + if (template == null) {
  214 + throw exception(EXPRESS_TEMPLATE_NOT_EXISTS);
  215 + }
  216 + return template;
  217 + }
  218 +
210 } 219 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
1 package cn.iocoder.yudao.module.trade.service.order; 1 package cn.iocoder.yudao.module.trade.service.order;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.util.IdUtil; 5 import cn.hutool.core.util.IdUtil;
5 import cn.hutool.core.util.ObjectUtil; 6 import cn.hutool.core.util.ObjectUtil;
6 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
7 import cn.iocoder.yudao.framework.common.core.KeyValue; 8 import cn.iocoder.yudao.framework.common.core.KeyValue;
8 import cn.iocoder.yudao.framework.common.enums.TerminalEnum; 9 import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
9 import cn.iocoder.yudao.framework.common.pojo.PageResult; 10 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
11 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 11 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
12 import cn.iocoder.yudao.module.member.api.address.AddressApi; 12 import cn.iocoder.yudao.module.member.api.address.AddressApi;
13 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; 13 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
@@ -18,15 +18,9 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; @@ -18,15 +18,9 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
18 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; 18 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
19 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 19 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
20 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 20 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
21 -import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;  
22 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; 21 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
23 -import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;  
24 -import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;  
25 -import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;  
26 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; 22 import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
27 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO; 23 import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
28 -import cn.iocoder.yudao.module.promotion.api.price.PriceApi;  
29 -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;  
30 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; 24 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
31 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; 25 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
32 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; 26 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
@@ -34,13 +28,19 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageRe @@ -34,13 +28,19 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageRe
34 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO; 28 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
35 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; 29 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
36 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 30 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
  31 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
37 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 32 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
38 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 33 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
39 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; 34 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
40 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; 35 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
41 import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants; 36 import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
  37 +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
42 import cn.iocoder.yudao.module.trade.enums.order.*; 38 import cn.iocoder.yudao.module.trade.enums.order.*;
43 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; 39 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
  40 +import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
  41 +import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
  42 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  43 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
44 import lombok.extern.slf4j.Slf4j; 44 import lombok.extern.slf4j.Slf4j;
45 import org.springframework.stereotype.Service; 45 import org.springframework.stereotype.Service;
46 import org.springframework.transaction.annotation.Transactional; 46 import org.springframework.transaction.annotation.Transactional;
@@ -71,11 +71,12 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -71,11 +71,12 @@ public class TradeOrderServiceImpl implements TradeOrderService {
71 private TradeOrderItemMapper tradeOrderItemMapper; 71 private TradeOrderItemMapper tradeOrderItemMapper;
72 72
73 @Resource 73 @Resource
74 - private PriceApi priceApi; 74 + private TradeCartService tradeCartService;
75 @Resource 75 @Resource
76 - private ProductSkuApi productSkuApi; 76 + private TradePriceService tradePriceService;
  77 +
77 @Resource 78 @Resource
78 - private ProductSpuApi productSpuApi; 79 + private ProductSkuApi productSkuApi;
79 @Resource 80 @Resource
80 private PayOrderApi payOrderApi; 81 private PayOrderApi payOrderApi;
81 @Resource 82 @Resource
@@ -92,76 +93,70 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -92,76 +93,70 @@ public class TradeOrderServiceImpl implements TradeOrderService {
92 93
93 @Override 94 @Override
94 public AppTradeOrderSettlementRespVO settlementOrder(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) { 95 public AppTradeOrderSettlementRespVO settlementOrder(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
95 - return null; 96 + // 1. 获得收货地址
  97 + AddressRespDTO address = getAddress(userId, settlementReqVO.getAddressId());
  98 + if (address != null) {
  99 + settlementReqVO.setAddressId(address.getId());
  100 + }
  101 +
  102 + // 2. 计算价格
  103 + TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, settlementReqVO);
  104 +
  105 + // 3. 拼接返回
  106 + return TradeOrderConvert.INSTANCE.convert(calculateRespBO, address);
  107 + }
  108 +
  109 + /**
  110 + * 获得用户地址
  111 + *
  112 + * @param userId 用户编号
  113 + * @param addressId 地址编号
  114 + * @return 地址
  115 + */
  116 + private AddressRespDTO getAddress(Long userId, Long addressId) {
  117 + if (addressId != null) {
  118 + return addressApi.getAddress(addressId, userId);
  119 + }
  120 + return addressApi.getDefaultAddress(userId);
  121 + }
  122 +
  123 + /**
  124 + * 计算订单价格
  125 + *
  126 + * @param userId 用户编号
  127 + * @param settlementReqVO 结算信息
  128 + * @return 订单价格
  129 + */
  130 + private TradePriceCalculateRespBO calculatePrice(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
  131 + // 1. 如果来自购物车,则获得购物车的商品
  132 + List<TradeCartDO> cartList = tradeCartService.getCartList(userId,
  133 + convertSet(settlementReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId));
  134 +
  135 + // 2. 计算价格
  136 + TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList);
  137 + calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程,保证都是选中的
  138 + "商品({}) 未设置为选中", item.getSkuId()));
  139 + return tradePriceService.calculatePrice(calculateReqBO);
96 } 140 }
97 141
98 @Override 142 @Override
99 @Transactional(rollbackFor = Exception.class) 143 @Transactional(rollbackFor = Exception.class)
100 public Long createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) { 144 public Long createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) {
101 - // 商品 SKU 检查:可售状态、库存  
102 -// List<ProductSkuRespDTO> skus = validateSkuSaleable(createReqVO.getItems()); // TODO 芋艿,临时关闭。  
103 - List<ProductSkuRespDTO> skus = null;  
104 - // 商品 SPU 检查:可售状态  
105 - List<ProductSpuRespDTO> spus = validateSpuSaleable(convertSet(skus, ProductSkuRespDTO::getSpuId));  
106 - // 用户收件地址的校验 145 + // 1. 用户收件地址的校验
107 AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId()); 146 AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId());
108 147
109 - // 价格计算  
110 - PriceCalculateRespDTO priceResp = priceApi.calculatePrice(TradeOrderConvert.INSTANCE.convert(createReqVO, userId)); 148 + // 2. 价格计算
  149 + TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
111 150
112 - // 插入 TradeOrderDO 订单  
113 - TradeOrderDO tradeOrderDO = createTradeOrder(userId, userIp, createReqVO, priceResp.getOrder(), address);  
114 - // 插入 TradeOrderItemDO 订单项  
115 - List<TradeOrderItemDO> tradeOrderItems = createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus); 151 + // 3.1 插入 TradeOrderDO 订单
  152 + TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO, address);
  153 + // 3.2 插入 TradeOrderItemDO 订单项
  154 + List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO);
116 155
117 // 订单创建完后的逻辑 156 // 订单创建完后的逻辑
118 - afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems, spus); 157 + afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO);
119 // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! 158 // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
120 - return tradeOrderDO.getId();  
121 - }  
122 -  
123 -// /**  
124 -// * 校验商品 SKU 是否可出售  
125 -// *  
126 -// * @param items 商品 SKU  
127 -// * @return 商品 SKU 数组  
128 -// */  
129 -// private List<ProductSkuRespDTO> validateSkuSaleable(List<Item> items) {  
130 -// List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(items, Item::getSkuId));  
131 -// // SKU 不存在  
132 -// if (items.size() != skus.size()) {  
133 -// throw exception(ORDER_CREATE_SKU_NOT_FOUND);  
134 -// }  
135 -// // 校验库存不足  
136 -// Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);  
137 -// items.forEach(item -> {  
138 -// ProductSkuRespDTO sku = skuMap.get(item.getSkuId());  
139 -// if (item.getCount() > sku.getStock()) {  
140 -// throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_STOCK_NOT_ENOUGH);  
141 -// }  
142 -// });  
143 -// return skus;  
144 -// }  
145 -  
146 - /**  
147 - * 校验商品 SPU 是否可出售  
148 - *  
149 - * @param spuIds 商品 SPU 编号数组  
150 - * @return 商品 SPU 数组  
151 - */  
152 - private List<ProductSpuRespDTO> validateSpuSaleable(Set<Long> spuIds) {  
153 - List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(spuIds);  
154 - // SPU 不存在  
155 - if (spus.size() != spuIds.size()) {  
156 - throw exception(ORDER_CREATE_SPU_NOT_FOUND);  
157 - }  
158 - // 校验是否存在禁用的 SPU  
159 - ProductSpuRespDTO spu = CollectionUtils.findFirst(spus,  
160 - spuDTO -> ObjectUtil.notEqual(ProductSpuStatusEnum.ENABLE.getStatus(), spuDTO.getStatus()));  
161 - if (spu != null) {  
162 - throw exception(ErrorCodeConstants.ORDER_CREATE_SPU_NOT_SALE);  
163 - }  
164 - return spus; 159 + return order.getId();
165 } 160 }
166 161
167 /** 162 /**
@@ -173,33 +168,35 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -173,33 +168,35 @@ public class TradeOrderServiceImpl implements TradeOrderService {
173 */ 168 */
174 private AddressRespDTO validateAddress(Long userId, Long addressId) { 169 private AddressRespDTO validateAddress(Long userId, Long addressId) {
175 AddressRespDTO address = addressApi.getAddress(addressId, userId); 170 AddressRespDTO address = addressApi.getAddress(addressId, userId);
176 - if (Objects.isNull(address)) { 171 + if (address == null) {
177 throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND); 172 throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND);
178 } 173 }
179 return address; 174 return address;
180 } 175 }
181 176
182 private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO, 177 private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,
183 - PriceCalculateRespDTO.Order order, AddressRespDTO address) {  
184 - TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, order, address);  
185 - tradeOrderDO.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;  
186 - tradeOrderDO.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());  
187 - tradeOrderDO.setType(TradeOrderTypeEnum.NORMAL.getType());  
188 - tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());  
189 - tradeOrderDO.setProductCount(getSumValue(order.getItems(), PriceCalculateRespDTO.OrderItem::getCount, Integer::sum));  
190 - tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?  
191 - tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息  
192 - tradeOrderDO.setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus()); // 物流信息  
193 - tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息  
194 - tradeOrderMapper.insert(tradeOrderDO);  
195 - return tradeOrderDO; 178 + TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address) {
  179 + TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
  180 + order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;
  181 + order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
  182 + order.setType(TradeOrderTypeEnum.NORMAL.getType());
  183 + order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
  184 + order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
  185 + order.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
  186 + // 支付信息
  187 + order.setAdjustPrice(0).setPayed(false);
  188 + // 物流信息 TODO 芋艿:暂时写死物流方式;应该是前端选择的
  189 + order.setDeliveryType(DeliveryTypeEnum.EXPRESS.getMode()).setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
  190 + // 退款信息
  191 + order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
  192 + tradeOrderMapper.insert(order);
  193 + return order;
196 } 194 }
197 195
198 - private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO,  
199 - List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {  
200 - List<TradeOrderItemDO> tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus);  
201 - tradeOrderItemMapper.insertBatch(tradeOrderItemDOs);  
202 - return tradeOrderItemDOs; 196 + private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {
  197 + List<TradeOrderItemDO> orderItems = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);
  198 + tradeOrderItemMapper.insertBatch(orderItems);
  199 + return orderItems;
203 } 200 }
204 201
205 /** 202 /**
@@ -210,12 +207,13 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -210,12 +207,13 @@ public class TradeOrderServiceImpl implements TradeOrderService {
210 * @param userId 用户编号 207 * @param userId 用户编号
211 * @param createReqVO 创建订单请求 208 * @param createReqVO 创建订单请求
212 * @param tradeOrderDO 交易订单 209 * @param tradeOrderDO 交易订单
  210 + * @param calculateRespBO 订单价格计算结果
213 */ 211 */
214 private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, 212 private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
215 - TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,  
216 - List<ProductSpuRespDTO> spus) { 213 + TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems,
  214 + TradePriceCalculateRespBO calculateRespBO) {
217 // 下单时扣减商品库存 215 // 下单时扣减商品库存
218 - productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(tradeOrderItemDOs))); 216 + productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(orderItems)));
219 217
220 // 删除购物车商品 TODO 芋艿:待实现 218 // 删除购物车商品 TODO 芋艿:待实现
221 219
@@ -228,20 +226,19 @@ public class TradeOrderServiceImpl implements TradeOrderService { @@ -228,20 +226,19 @@ public class TradeOrderServiceImpl implements TradeOrderService {
228 } 226 }
229 227
230 // 生成预支付 228 // 生成预支付
231 - createPayOrder(tradeOrderDO, tradeOrderItemDOs, spus); 229 + createPayOrder(tradeOrderDO, orderItems, calculateRespBO);
232 230
233 // 增加订单日志 TODO 芋艿:待实现 231 // 增加订单日志 TODO 芋艿:待实现
234 } 232 }
235 233
236 - private void createPayOrder(TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,  
237 - List<ProductSpuRespDTO> spus) { 234 + private void createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems, TradePriceCalculateRespBO calculateRespBO) {
238 // 创建支付单,用于后续的支付 235 // 创建支付单,用于后续的支付
239 PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert( 236 PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
240 - tradeOrderDO, tradeOrderItemDOs, spus, tradeOrderProperties); 237 + order, orderItems, calculateRespBO, tradeOrderProperties);
241 Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO); 238 Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO);
242 239
243 // 更新到交易单上 240 // 更新到交易单上
244 - tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId)); 241 + tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId));
245 } 242 }
246 243
247 @Override 244 @Override
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceService.java
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.price; @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.price;
3 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 3 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
4 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 4 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
5 5
  6 +import javax.validation.Valid;
  7 +
6 /** 8 /**
7 * 价格计算 Service 接口 9 * 价格计算 Service 接口
8 * 10 *
@@ -16,6 +18,6 @@ public interface TradePriceService { @@ -16,6 +18,6 @@ public interface TradePriceService {
16 * @param calculateReqDTO 计算信息 18 * @param calculateReqDTO 计算信息
17 * @return 计算结果 19 * @return 计算结果
18 */ 20 */
19 - TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqDTO); 21 + TradePriceCalculateRespBO calculatePrice(@Valid TradePriceCalculateReqBO calculateReqDTO);
20 22
21 } 23 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java
@@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.trade.service.price; @@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.trade.service.price;
2 2
3 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 3 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
4 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 4 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  5 +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
  6 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  7 +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
5 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 8 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
6 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 9 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
7 import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator; 10 import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator;
8 import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper; 11 import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper;
9 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
10 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
  14 +import org.springframework.validation.annotation.Validated;
11 15
12 import javax.annotation.Resource; 16 import javax.annotation.Resource;
13 import java.util.List; 17 import java.util.List;
@@ -15,8 +19,8 @@ import java.util.Map; @@ -15,8 +19,8 @@ import java.util.Map;
15 19
16 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 20 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
17 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 21 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
18 -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;  
19 -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; 22 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  23 +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
20 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL; 24 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL;
21 25
22 /** 26 /**
@@ -25,22 +29,28 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_C @@ -25,22 +29,28 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_C
25 * @author 芋道源码 29 * @author 芋道源码
26 */ 30 */
27 @Service 31 @Service
  32 +@Validated
28 @Slf4j 33 @Slf4j
29 public class TradePriceServiceImpl implements TradePriceService { 34 public class TradePriceServiceImpl implements TradePriceService {
30 35
31 @Resource 36 @Resource
32 private ProductSkuApi productSkuApi; 37 private ProductSkuApi productSkuApi;
33 @Resource 38 @Resource
  39 + private ProductSpuApi productSpuApi;
  40 +
  41 + @Resource
34 private List<TradePriceCalculator> priceCalculators; 42 private List<TradePriceCalculator> priceCalculators;
35 43
36 @Override 44 @Override
37 public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) { 45 public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) {
38 - // 1. 获得商品 SKU 数组  
39 - List<ProductSkuRespDTO> skuList = checkSkus(calculateReqBO); 46 + // 1.1 获得商品 SKU 数组
  47 + List<ProductSkuRespDTO> skuList = checkSkuList(calculateReqBO);
  48 + // 1.2 获得商品 SPU 数组
  49 + List<ProductSpuRespDTO> spuList = checkSpuList(skuList);
40 50
41 // 2.1 计算价格 51 // 2.1 计算价格
42 TradePriceCalculateRespBO calculateRespBO = TradePriceCalculatorHelper 52 TradePriceCalculateRespBO calculateRespBO = TradePriceCalculatorHelper
43 - .buildCalculateResp(calculateReqBO, skuList); 53 + .buildCalculateResp(calculateReqBO, spuList, skuList);
44 priceCalculators.forEach(calculator -> calculator.calculate(calculateReqBO, calculateRespBO)); 54 priceCalculators.forEach(calculator -> calculator.calculate(calculateReqBO, calculateRespBO));
45 // 2.2 如果最终支付金额小于等于 0,则抛出业务异常 55 // 2.2 如果最终支付金额小于等于 0,则抛出业务异常
46 if (calculateRespBO.getPrice().getPayPrice() <= 0) { 56 if (calculateRespBO.getPrice().getPayPrice() <= 0) {
@@ -51,7 +61,7 @@ public class TradePriceServiceImpl implements TradePriceService { @@ -51,7 +61,7 @@ public class TradePriceServiceImpl implements TradePriceService {
51 return calculateRespBO; 61 return calculateRespBO;
52 } 62 }
53 63
54 - private List<ProductSkuRespDTO> checkSkus(TradePriceCalculateReqBO reqBO) { 64 + private List<ProductSkuRespDTO> checkSkuList(TradePriceCalculateReqBO reqBO) {
55 // 获得商品 SKU 数组 65 // 获得商品 SKU 数组
56 Map<Long, Integer> skuIdCountMap = convertMap(reqBO.getItems(), 66 Map<Long, Integer> skuIdCountMap = convertMap(reqBO.getItems(),
57 TradePriceCalculateReqBO.Item::getSkuId, TradePriceCalculateReqBO.Item::getCount); 67 TradePriceCalculateReqBO.Item::getSkuId, TradePriceCalculateReqBO.Item::getCount);
@@ -70,4 +80,20 @@ public class TradePriceServiceImpl implements TradePriceService { @@ -70,4 +80,20 @@ public class TradePriceServiceImpl implements TradePriceService {
70 return skus; 80 return skus;
71 } 81 }
72 82
  83 + private List<ProductSpuRespDTO> checkSpuList(List<ProductSkuRespDTO> skuList) {
  84 + // 获得商品 SPU 数组
  85 + List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(convertSet(skuList, ProductSkuRespDTO::getSpuId));
  86 +
  87 + // 校验商品 SPU
  88 + spus.forEach(spu -> {
  89 + if (spu == null) {
  90 + throw exception(SPU_NOT_EXISTS);
  91 + }
  92 + if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {
  93 + throw exception(SPU_NOT_ENABLE);
  94 + }
  95 + });
  96 + return spus;
  97 + }
  98 +
73 } 99 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java
1 package cn.iocoder.yudao.module.trade.service.price.bo; 1 package cn.iocoder.yudao.module.trade.service.price.bo;
2 2
  3 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
  4 +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
3 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; 5 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
4 import lombok.Data; 6 import lombok.Data;
5 7
@@ -21,7 +23,7 @@ public class TradePriceCalculateReqBO { @@ -21,7 +23,7 @@ public class TradePriceCalculateReqBO {
21 * 23 *
22 * 枚举 {@link TradeOrderTypeEnum} 24 * 枚举 {@link TradeOrderTypeEnum}
23 */ 25 */
24 - private Integer orderType; 26 + private Integer type;
25 27
26 /** 28 /**
27 * 用户编号 29 * 用户编号
@@ -45,6 +47,21 @@ public class TradePriceCalculateReqBO { @@ -45,6 +47,21 @@ public class TradePriceCalculateReqBO {
45 private Long addressId; 47 private Long addressId;
46 48
47 /** 49 /**
  50 + * 配送方式
  51 + *
  52 + * 枚举 {@link DeliveryTypeEnum}
  53 + */
  54 + private Integer deliveryType;
  55 +
  56 + /**
  57 + * 配送模板编号
  58 + *
  59 + * 关联 {@link DeliveryExpressTemplateDO#getId()}
  60 + */
  61 + // TODO @jason:运费模版,是不是每个 SKU 传入哈
  62 + private Long templateId;
  63 +
  64 + /**
48 * 商品 SKU 数组 65 * 商品 SKU 数组
49 */ 66 */
50 @NotNull(message = "商品数组不能为空") 67 @NotNull(message = "商品数组不能为空")
@@ -82,5 +99,4 @@ public class TradePriceCalculateReqBO { @@ -82,5 +99,4 @@ public class TradePriceCalculateReqBO {
82 private Boolean selected; 99 private Boolean selected;
83 100
84 } 101 }
85 -  
86 } 102 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
1 package cn.iocoder.yudao.module.trade.service.price.bo; 1 package cn.iocoder.yudao.module.trade.service.price.bo;
2 2
  3 +import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
3 import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; 4 import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
4 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; 5 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
5 import lombok.Data; 6 import lombok.Data;
@@ -23,7 +24,7 @@ public class TradePriceCalculateRespBO { @@ -23,7 +24,7 @@ public class TradePriceCalculateRespBO {
23 * 24 *
24 * 枚举 {@link TradeOrderTypeEnum} 25 * 枚举 {@link TradeOrderTypeEnum}
25 */ 26 */
26 - private Integer orderType; 27 + private Integer type;
27 28
28 /** 29 /**
29 * 订单价格 30 * 订单价格
@@ -163,7 +164,26 @@ public class TradePriceCalculateRespBO { @@ -163,7 +164,26 @@ public class TradePriceCalculateRespBO {
163 */ 164 */
164 private Integer payPrice; 165 private Integer payPrice;
165 166
166 - // TODO 芋艿:这里补充下基本信息,简单一点。 167 + // ========== 商品信息 ==========
  168 + /**
  169 + * 商品名
  170 + */
  171 + private String spuName;
  172 + /**
  173 + * 商品图片
  174 + *
  175 + * 优先级:SKU.picUrl > SPU.picUrl
  176 + */
  177 + private String picUrl;
  178 + /**
  179 + * 分类编号
  180 + */
  181 + private Long categoryId;
  182 +
  183 + /**
  184 + * 商品属性数组
  185 + */
  186 + private List<ProductPropertyValueDetailRespDTO> properties;
167 187
168 } 188 }
169 189
@@ -190,12 +210,6 @@ public class TradePriceCalculateRespBO { @@ -190,12 +210,6 @@ public class TradePriceCalculateRespBO {
190 */ 210 */
191 private Integer type; 211 private Integer type;
192 /** 212 /**
193 - * 营销级别  
194 - *  
195 - * 枚举 @link PromotionLevelEnum} TODO PromotionLevelEnum 没有这个枚举类  
196 - */  
197 - private Integer level;  
198 - /**  
199 * 计算时的原价(总),单位:分 213 * 计算时的原价(总),单位:分
200 */ 214 */
201 private Integer totalPrice; 215 private Integer totalPrice;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java
@@ -101,7 +101,8 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { @@ -101,7 +101,8 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
101 CouponRespDTO coupon) { 101 CouponRespDTO coupon) {
102 Predicate<TradePriceCalculateRespBO.OrderItem> matchPredicate = TradePriceCalculateRespBO.OrderItem::getSelected; 102 Predicate<TradePriceCalculateRespBO.OrderItem> matchPredicate = TradePriceCalculateRespBO.OrderItem::getSelected;
103 if (PromotionProductScopeEnum.SPU.getScope().equals(coupon.getProductScope())) { 103 if (PromotionProductScopeEnum.SPU.getScope().equals(coupon.getProductScope())) {
104 - matchPredicate = orderItem -> coupon.getProductSpuIds().contains(orderItem.getSpuId()); 104 + matchPredicate = matchPredicate // 额外加如下条件
  105 + .and(orderItem -> coupon.getProductSpuIds().contains(orderItem.getSpuId()));
105 } 106 }
106 return filterList(result.getItems(), matchPredicate); 107 return filterList(result.getItems(), matchPredicate);
107 } 108 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.price.calculator;
  2 +
  3 +import cn.iocoder.yudao.module.member.api.address.AddressApi;
  4 +import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
  5 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  6 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  7 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
  8 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
  9 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
  10 +import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper;
  11 +import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper;
  12 +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
  13 +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
  14 +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
  15 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  16 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  17 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem;
  18 +import org.springframework.core.annotation.Order;
  19 +import org.springframework.stereotype.Component;
  20 +
  21 +import javax.annotation.Resource;
  22 +import java.util.HashMap;
  23 +import java.util.List;
  24 +import java.util.Map;
  25 +import java.util.Set;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  28 +
  29 +/**
  30 + * 运费的 {@link TradePriceCalculator} 实现类
  31 + *
  32 + * @author jason
  33 + */
  34 +@Component
  35 +@Order(TradePriceCalculator.ORDER_DELIVERY)
  36 +public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
  37 +
  38 + @Resource
  39 + private AddressApi addressApi;
  40 + @Resource
  41 + private ProductSkuApi productSkuApi;
  42 +
  43 + @Resource
  44 + private DeliveryExpressTemplateService deliveryExpressTemplateService;
  45 + // TODO @jason:走 Service 哈。Mapper 只允许自己的 Service 调用,保护好数据结构;
  46 + @Resource
  47 + private DeliveryExpressTemplateChargeMapper templateChargeMapper;
  48 + @Resource
  49 + private DeliveryExpressTemplateFreeMapper templateFreeMapper;
  50 +
  51 + @Override
  52 + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
  53 + // 1.1 判断配送方式
  54 + if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) {
  55 + return;
  56 + }
  57 +
  58 + if (param.getTemplateId() == null || param.getAddressId() == null) {
  59 + return;
  60 + }
  61 + // 1.2 校验运费模板是否存在
  62 + DeliveryExpressTemplateDO template = deliveryExpressTemplateService.validateDeliveryExpressTemplate(param.getTemplateId());
  63 +
  64 + // 得到包邮配置
  65 + List<DeliveryExpressTemplateFreeDO> expressTemplateFreeList = templateFreeMapper.selectListByTemplateId(template.getId());
  66 + Map<Integer, DeliveryExpressTemplateFreeDO> areaTemplateFreeMap = new HashMap<>();
  67 + expressTemplateFreeList.forEach(item -> {
  68 + for (Integer areaId : item.getAreaIds()) {
  69 + // TODO 需要保证 areaId 不能重复
  70 + if (!areaTemplateFreeMap.containsKey(areaId)) {
  71 + areaTemplateFreeMap.put(areaId, item);
  72 + }
  73 + }
  74 + });
  75 + // 得到快递运费配置
  76 + List<DeliveryExpressTemplateChargeDO> expressTemplateChargeList = templateChargeMapper.selectListByTemplateId(template.getId());
  77 + Map<Integer, DeliveryExpressTemplateChargeDO> areaTemplateChargeMap = new HashMap<>();
  78 + expressTemplateChargeList.forEach(item -> {
  79 + for (Integer areaId : item.getAreaIds()) {
  80 + // areaId 不能重复
  81 + if (!areaTemplateChargeMap.containsKey(areaId)) {
  82 + areaTemplateChargeMap.put(areaId, item);
  83 + }
  84 + }
  85 + });
  86 + // 得到收件地址区域
  87 + AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
  88 + // 1.3 计算快递费用
  89 + calculateDeliveryPrice(address.getAreaId(), template.getChargeMode(),
  90 + areaTemplateFreeMap, areaTemplateChargeMap, result);
  91 + }
  92 +
  93 + /**
  94 + * 校验订单是否满足包邮条件
  95 + *
  96 + * @param receiverAreaId 收件人地区的区域编号
  97 + * @param chargeMode 配送计费方式
  98 + * @param areaTemplateFreeMap 运费模板包邮区域设置 Map
  99 + * @param areaTemplateChargeMap 运费模板快递费用设置 Map
  100 + */
  101 + private void calculateDeliveryPrice(Integer receiverAreaId,
  102 + Integer chargeMode,
  103 + Map<Integer, DeliveryExpressTemplateFreeDO> areaTemplateFreeMap,
  104 + Map<Integer, DeliveryExpressTemplateChargeDO> areaTemplateChargeMap,
  105 + TradePriceCalculateRespBO result) {
  106 + // 过滤出已选中的商品SKU
  107 + List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
  108 + Set<Long> skuIds = convertSet(selectedItem, OrderItem::getSkuId);
  109 + // 得到SKU 详情。得到 重量体积
  110 + Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId);
  111 + // 一个 spuId 可能对应多条订单商品 SKU
  112 + // TODO @jason:得确认下,按照 sku 算,还是 spu 算;
  113 + Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedItem, OrderItem::getSpuId);
  114 + // 依次计算每个 SPU 的快递运费
  115 + for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) {
  116 + List<OrderItem> orderItems = entry.getValue();
  117 + // 总件数, 总金额, 总重量, 总体积
  118 + int totalCount = 0;
  119 + int totalPrice = 0;
  120 + double totalWeight = 0;
  121 + double totalVolume = 0;
  122 + for (OrderItem orderItem : orderItems) {
  123 + totalCount += orderItem.getCount();
  124 + totalPrice += orderItem.getPrice(); // TODO jason:应该按照 payPrice?
  125 + ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId());
  126 + if (skuResp != null) {
  127 + totalWeight = totalWeight + skuResp.getWeight(); // TODO @jason:* 数量
  128 + totalVolume = totalVolume + skuResp.getVolume();
  129 + }
  130 + }
  131 + // 优先判断是否包邮. 如果包邮不计算快递运费
  132 + if (areaTemplateFreeMap.containsKey(receiverAreaId) &&
  133 + checkExpressFree(chargeMode, totalCount, totalWeight,
  134 + totalVolume, totalPrice, areaTemplateFreeMap.get(receiverAreaId))) {
  135 + continue;
  136 + }
  137 + // 计算快递运费
  138 + // TODO @jason:貌似也可以抽成 checkExpressFree 类似方法
  139 + if (areaTemplateChargeMap.containsKey(receiverAreaId)) {
  140 + DeliveryExpressTemplateChargeDO templateCharge = areaTemplateChargeMap.get(receiverAreaId);
  141 + DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
  142 + switch (chargeModeEnum) {
  143 + case PIECE: {
  144 + calculateExpressFeeBySpu(totalCount, templateCharge, orderItems);
  145 + break;
  146 + }
  147 + case WEIGHT: {
  148 + calculateExpressFeeBySpu(totalWeight, templateCharge, orderItems);
  149 + break;
  150 + }
  151 + case VOLUME: {
  152 + calculateExpressFeeBySpu(totalVolume, templateCharge, orderItems);
  153 + break;
  154 + }
  155 + }
  156 + }
  157 + }
  158 + TradePriceCalculatorHelper.recountAllPrice(result);
  159 + }
  160 +
  161 + /**
  162 + * 按 spu 来计算快递费用
  163 + *
  164 + * @param total 总件数/总重量/总体积
  165 + * @param templateCharge 快递运费配置
  166 + * @param orderItems SKU 商品项目
  167 + */
  168 + private void calculateExpressFeeBySpu(double total, DeliveryExpressTemplateChargeDO templateCharge, List<OrderItem> orderItems) {
  169 + int deliveryPrice;
  170 + if (total <= templateCharge.getStartCount()) {
  171 + deliveryPrice = templateCharge.getStartPrice();
  172 + } else {
  173 + double remainWeight = total - templateCharge.getStartCount();
  174 + // 剩余重量/ 续件 = 续件的次数. 向上取整
  175 + int extraNum = (int) Math.ceil(remainWeight / templateCharge.getExtraCount());
  176 + int extraPrice = templateCharge.getExtraPrice() * extraNum;
  177 + deliveryPrice = templateCharge.getStartPrice() + extraPrice;
  178 + }
  179 + // TODO @芋艿 分摊快递费用到 SKU. 是不是搞复杂了;
  180 + // TODO @jason:因为退费的时候,可能按照 SKU 考虑退费金额
  181 + divideDeliveryPrice(deliveryPrice, orderItems);
  182 + }
  183 +
  184 + /**
  185 + * 快递运费分摊到每个 SKU 商品上
  186 + *
  187 + * @param deliveryPrice 快递运费
  188 + * @param orderItems SKU 商品
  189 + */
  190 + private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) {
  191 + int dividePrice = deliveryPrice / orderItems.size();
  192 + for (OrderItem item : orderItems) {
  193 + // 更新快递运费
  194 + item.setDeliveryPrice(dividePrice);
  195 + }
  196 + }
  197 +
  198 + /**
  199 + * 检查是否包邮
  200 + *
  201 + * @param chargeMode 配送计费方式
  202 + * @param totalCount 总件数
  203 + * @param totalWeight 总重量
  204 + * @param totalVolume 总体积
  205 + * @param totalPrice 总金额
  206 + * @param templateFree 包邮配置
  207 + */
  208 + private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight,
  209 + double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) {
  210 + DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
  211 + switch (chargeModeEnum) {
  212 + case PIECE:
  213 + // 两个条件都满足才包邮
  214 + if (totalCount >= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice()) {
  215 + return true;
  216 + }
  217 + break;
  218 + case WEIGHT:
  219 + // freeCount 是不是应该是 double ??
  220 + if (totalWeight >= templateFree.getFreeCount()
  221 + && totalPrice >= templateFree.getFreePrice()) {
  222 + return true;
  223 + }
  224 + break;
  225 + case VOLUME:
  226 + if (totalVolume >= templateFree.getFreeCount()
  227 + && totalPrice >= templateFree.getFreePrice()) {
  228 + return true;
  229 + }
  230 + break;
  231 + }
  232 + return false;
  233 + }
  234 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java
@@ -53,10 +53,13 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato @@ -53,10 +53,13 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
53 Integer newDiscountPrice = orderItem.getPayPrice() - newPayPrice; 53 Integer newDiscountPrice = orderItem.getPayPrice() - newPayPrice;
54 54
55 // 3.1 记录优惠明细 55 // 3.1 记录优惠明细
56 - TradePriceCalculatorHelper.addPromotion(result, orderItem,  
57 - discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),  
58 - StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),  
59 - newDiscountPrice); 56 + if (orderItem.getSelected()) {
  57 + // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
  58 + TradePriceCalculatorHelper.addPromotion(result, orderItem,
  59 + discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
  60 + StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
  61 + newDiscountPrice);
  62 + }
60 // 3.2 更新 SKU 优惠金额 63 // 3.2 更新 SKU 优惠金额
61 orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice); 64 orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice);
62 TradePriceCalculatorHelper.recountPayPrice(orderItem); 65 TradePriceCalculatorHelper.recountPayPrice(orderItem);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java
@@ -13,6 +13,12 @@ public interface TradePriceCalculator { @@ -13,6 +13,12 @@ public interface TradePriceCalculator {
13 int ORDER_DISCOUNT_ACTIVITY = 10; 13 int ORDER_DISCOUNT_ACTIVITY = 10;
14 int ORDER_REWARD_ACTIVITY = 20; 14 int ORDER_REWARD_ACTIVITY = 20;
15 int ORDER_COUPON = 30; 15 int ORDER_COUPON = 30;
  16 + /**
  17 + * 快递运费的计算
  18 + *
  19 + * 放在各种营销活动、优惠劵后面 TODO
  20 + */
  21 + int ORDER_DELIVERY = 40;
16 22
17 void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result); 23 void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result);
18 24
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 5 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  6 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
6 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 7 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
7 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 8 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
8 9
@@ -24,25 +25,42 @@ import static java.util.Collections.singletonList; @@ -24,25 +25,42 @@ import static java.util.Collections.singletonList;
24 public class TradePriceCalculatorHelper { 25 public class TradePriceCalculatorHelper {
25 26
26 public static TradePriceCalculateRespBO buildCalculateResp(TradePriceCalculateReqBO param, 27 public static TradePriceCalculateRespBO buildCalculateResp(TradePriceCalculateReqBO param,
27 - List<ProductSkuRespDTO> skuList) { 28 + List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) {
28 // 创建 PriceCalculateRespDTO 对象 29 // 创建 PriceCalculateRespDTO 对象
29 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO(); 30 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO();
30 - result.setOrderType(param.getOrderType()); 31 + result.setType(param.getType());
  32 + result.setPromotions(new ArrayList<>());
  33 +
31 // 创建它的 OrderItem 属性 34 // 创建它的 OrderItem 属性
32 - Map<Long, TradePriceCalculateReqBO.Item> skuItemMap = convertMap(param.getItems(),  
33 - TradePriceCalculateReqBO.Item::getSkuId);  
34 - result.setItems(new ArrayList<>(skuItemMap.size()));  
35 - skuList.forEach(sku -> {  
36 - TradePriceCalculateReqBO.Item skuItem = skuItemMap.get(sku.getId());  
37 - TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem()  
38 - // SKU 字段  
39 - .setSpuId(sku.getSpuId()).setSkuId(sku.getId())  
40 - .setCount(skuItem.getCount()).setCartId(skuItem.getCartId()).setSelected(skuItem.getSelected())  
41 - // 价格字段  
42 - .setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * skuItem.getCount())  
43 - .setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0); 35 + result.setItems(new ArrayList<>(param.getItems().size()));
  36 + Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
  37 + Map<Long, ProductSkuRespDTO> skuMap = convertMap(skuList, ProductSkuRespDTO::getId);
  38 + param.getItems().forEach(item -> {
  39 + ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
  40 + if (sku == null) {
  41 + return;
  42 + }
  43 + ProductSpuRespDTO spu = spuMap.get(sku.getSpuId());
  44 + if (spu == null) {
  45 + return;
  46 + }
  47 + // 商品项
  48 + TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem();
44 result.getItems().add(orderItem); 49 result.getItems().add(orderItem);
  50 + orderItem.setSpuId(sku.getSpuId()).setSkuId(sku.getId())
  51 + .setCount(item.getCount()).setCartId(item.getCartId()).setSelected(item.getSelected());
  52 + // sku 价格
  53 + orderItem.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * item.getCount())
  54 + .setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0);
  55 + // sku 信息
  56 + orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties());
  57 + // spu 信息
  58 + orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId());
  59 + if (orderItem.getPicUrl() == null) {
  60 + orderItem.setPicUrl(spu.getPicUrl());
  61 + }
45 }); 62 });
  63 +
46 // 创建它的 Price 属性 64 // 创建它的 Price 属性
47 result.setPrice(new TradePriceCalculateRespBO.Price()); 65 result.setPrice(new TradePriceCalculateRespBO.Price());
48 recountAllPrice(result); 66 recountAllPrice(result);
@@ -87,6 +105,31 @@ public class TradePriceCalculatorHelper { @@ -87,6 +105,31 @@ public class TradePriceCalculatorHelper {
87 } 105 }
88 106
89 /** 107 /**
  108 + * 重新计算每个订单项的支付金额
  109 + *
  110 + * 【目前主要是单测使用】
  111 + *
  112 + * @param orderItems 订单项数组
  113 + */
  114 + public static void recountPayPrice(List<TradePriceCalculateRespBO.OrderItem> orderItems) {
  115 + orderItems.forEach(orderItem -> {
  116 + if (orderItem.getDiscountPrice() == null) {
  117 + orderItem.setDiscountPrice(0);
  118 + }
  119 + if (orderItem.getDeliveryPrice() == null) {
  120 + orderItem.setDeliveryPrice(0);
  121 + }
  122 + if (orderItem.getCouponPrice() == null) {
  123 + orderItem.setCouponPrice(0);
  124 + }
  125 + if (orderItem.getPointPrice() == null) {
  126 + orderItem.setPointPrice(0);
  127 + }
  128 + recountPayPrice(orderItem);
  129 + });
  130 + }
  131 +
  132 + /**
90 * 计算已选中的订单项,总支付金额 133 * 计算已选中的订单项,总支付金额
91 * 134 *
92 * @param orderItems 订单项数组 135 * @param orderItems 订单项数组
@@ -94,7 +137,7 @@ public class TradePriceCalculatorHelper { @@ -94,7 +137,7 @@ public class TradePriceCalculatorHelper {
94 */ 137 */
95 public static Integer calculateTotalPayPrice(List<TradePriceCalculateRespBO.OrderItem> orderItems) { 138 public static Integer calculateTotalPayPrice(List<TradePriceCalculateRespBO.OrderItem> orderItems) {
96 return getSumValue(orderItems, 139 return getSumValue(orderItems,
97 - orderItem -> orderItem.getSelected() ? orderItem.getPayPrice() : 0, // 未选中的情况下,不计算支付金额 140 + orderItem -> orderItem.getSelected() ? orderItem.getPayPrice() : 0, // 未选中的情况下,不计算支付金额
98 Integer::sum); 141 Integer::sum);
99 } 142 }
100 143
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
@@ -53,6 +53,9 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator @@ -53,6 +53,9 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
53 // 1.2 获得最大匹配的满减送活动的规则 53 // 1.2 获得最大匹配的满减送活动的规则
54 RewardActivityMatchRespDTO.Rule rule = getMaxMatchRewardActivityRule(rewardActivity, orderItems); 54 RewardActivityMatchRespDTO.Rule rule = getMaxMatchRewardActivityRule(rewardActivity, orderItems);
55 if (rule == null) { 55 if (rule == null) {
  56 + TradePriceCalculatorHelper.addNotMatchPromotion(result, orderItems,
  57 + rewardActivity.getId(), rewardActivity.getName(), PromotionTypeEnum.REWARD_ACTIVITY.getType(),
  58 + getRewardActivityNotMeetTip(rewardActivity));
56 return; 59 return;
57 } 60 }
58 61
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java
@@ -69,8 +69,7 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest { @@ -69,8 +69,7 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
69 .setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png")); 69 .setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png"));
70 // mock 方法(交易订单项) 70 // mock 方法(交易订单项)
71 TradeOrderItemDO orderItem = randomPojo(TradeOrderItemDO.class, o -> { 71 TradeOrderItemDO orderItem = randomPojo(TradeOrderItemDO.class, o -> {
72 - //o.setOrderId(111L).setUserId(userId).setOrderDividePrice(200); TODO DO 中没有 orderDividePrice 属性  
73 - o.setOrderId(111L).setUserId(userId); 72 + o.setOrderId(111L).setUserId(userId).setPayPrice(200);
74 o.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); 73 o.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
75 }); 74 });
76 when(tradeOrderService.getOrderItem(eq(1024L), eq(1L))) 75 when(tradeOrderService.getOrderItem(eq(1024L), eq(1L)))
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
8 import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; 8 import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
9 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; 9 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
10 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 10 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
  11 +import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
11 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 12 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
12 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 13 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
13 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; 14 import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
@@ -101,10 +102,10 @@ public class TradeOrderServiceTest extends BaseDbUnitTest { @@ -101,10 +102,10 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
101 // mock 方法(商品 SKU 检查) 102 // mock 方法(商品 SKU 检查)
102 ProductSkuRespDTO sku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(1L).setSpuId(11L) 103 ProductSkuRespDTO sku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(1L).setSpuId(11L)
103 .setPrice(50).setStock(100) 104 .setPrice(50).setStock(100)
104 - .setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(111L).setValueId(222L)))); 105 + .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(111L).setValueId(222L))));
105 ProductSkuRespDTO sku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(2L).setSpuId(21L) 106 ProductSkuRespDTO sku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(2L).setSpuId(21L)
106 .setPrice(20).setStock(50)) 107 .setPrice(20).setStock(50))
107 - .setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(333L).setValueId(444L))); 108 + .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(333L).setValueId(444L)));
108 when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02)); 109 when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02));
109 // mock 方法(商品 SPU 检查) 110 // mock 方法(商品 SPU 检查)
110 ProductSpuRespDTO spu01 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(11L) 111 ProductSpuRespDTO spu01 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(11L)
@@ -114,7 +115,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest { @@ -114,7 +115,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
114 when(productSpuApi.getSpuList(eq(asSet(11L, 21L)))).thenReturn(Arrays.asList(spu01, spu02)); 115 when(productSpuApi.getSpuList(eq(asSet(11L, 21L)))).thenReturn(Arrays.asList(spu01, spu02));
115 // mock 方法(用户收件地址的校验) 116 // mock 方法(用户收件地址的校验)
116 AddressRespDTO addressRespDTO = new AddressRespDTO().setId(10L).setUserId(userId).setName("芋艿") 117 AddressRespDTO addressRespDTO = new AddressRespDTO().setId(10L).setUserId(userId).setName("芋艿")
117 - .setMobile("15601691300").setAreaId(3306L).setPostCode("85757").setDetailAddress("土豆村"); 118 + .setMobile("15601691300").setAreaId(3306).setPostCode("85757").setDetailAddress("土豆村");
118 when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(addressRespDTO); 119 when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(addressRespDTO);
119 // mock 方法(价格计算) 120 // mock 方法(价格计算)
120 PriceCalculateRespDTO.OrderItem priceOrderItem01 = new PriceCalculateRespDTO.OrderItem() 121 PriceCalculateRespDTO.OrderItem priceOrderItem01 = new PriceCalculateRespDTO.OrderItem()
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.price;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
  5 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  6 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  7 +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
  8 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  9 +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
  10 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
  11 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  12 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  13 +import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator;
  14 +import org.junit.jupiter.api.Test;
  15 +import org.mockito.InjectMocks;
  16 +import org.mockito.Mock;
  17 +import org.mockito.Mockito;
  18 +
  19 +import java.util.Arrays;
  20 +import java.util.List;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  23 +import static java.util.Collections.singletonList;
  24 +import static org.junit.jupiter.api.Assertions.*;
  25 +import static org.mockito.Mockito.when;
  26 +
  27 +/**
  28 + * {@link TradePriceServiceImpl} 的单元测试
  29 + *
  30 + * @author 芋道源码
  31 + */
  32 +public class TradePriceServiceImplTest extends BaseMockitoUnitTest {
  33 +
  34 + @InjectMocks
  35 + private TradePriceServiceImpl tradePriceService;
  36 +
  37 + @Mock
  38 + private ProductSkuApi productSkuApi;
  39 + @Mock
  40 + private ProductSpuApi productSpuApi;
  41 + @Mock
  42 + private List<TradePriceCalculator> priceCalculators;
  43 +
  44 + @Test
  45 + public void testCalculatePrice() {
  46 + // 准备参数
  47 + TradePriceCalculateReqBO calculateReqBO = new TradePriceCalculateReqBO()
  48 + .setType(TradeOrderTypeEnum.NORMAL.getType()).setUserId(10L)
  49 + .setCouponId(20L).setAddressId(30L)
  50 + .setItems(Arrays.asList(
  51 + new TradePriceCalculateReqBO.Item().setSkuId(100L).setCount(1).setSelected(true),
  52 + new TradePriceCalculateReqBO.Item().setSkuId(200L).setCount(3).setSelected(true),
  53 + new TradePriceCalculateReqBO.Item().setSkuId(300L).setCount(6).setCartId(233L).setSelected(false)
  54 + ));
  55 + // mock 方法
  56 + List<ProductSkuRespDTO> skuList = Arrays.asList(
  57 + new ProductSkuRespDTO().setId(100L).setStock(500).setPrice(1000).setPicUrl("https://t.cn/1.png").setSpuId(1001L)
  58 + .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(1L).setPropertyName("颜色")
  59 + .setValueId(2L).setValueName("红色"))),
  60 + new ProductSkuRespDTO().setId(200L).setStock(400).setPrice(2000).setPicUrl("https://t.cn/2.png").setSpuId(1001L)
  61 + .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(1L).setPropertyName("颜色")
  62 + .setValueId(3L).setValueName("黄色"))),
  63 + new ProductSkuRespDTO().setId(300L).setStock(600).setPrice(3000).setPicUrl("https://t.cn/3.png").setSpuId(1001L)
  64 + .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(1L).setPropertyName("颜色")
  65 + .setValueId(4L).setValueName("黑色")))
  66 + );
  67 + when(productSkuApi.getSkuList(Mockito.eq(asSet(100L, 200L, 300L)))).thenReturn(skuList);
  68 + when(productSpuApi.getSpuList(Mockito.eq(asSet(1001L))))
  69 + .thenReturn(singletonList(new ProductSpuRespDTO().setId(1001L).setName("小菜").setCategoryId(666L)
  70 + .setStatus(ProductSpuStatusEnum.ENABLE.getStatus())));
  71 +
  72 + // 调用
  73 + TradePriceCalculateRespBO calculateRespBO = tradePriceService.calculatePrice(calculateReqBO);
  74 + // 断言
  75 + assertEquals(TradeOrderTypeEnum.NORMAL.getType(), calculateRespBO.getType());
  76 + assertEquals(0, calculateRespBO.getPromotions().size());
  77 + assertNull(calculateRespBO.getCouponId());
  78 + // 断言:订单价格
  79 + assertEquals(7000, calculateRespBO.getPrice().getTotalPrice());
  80 + assertEquals(0, calculateRespBO.getPrice().getDiscountPrice());
  81 + assertEquals(0, calculateRespBO.getPrice().getDeliveryPrice());
  82 + assertEquals(0, calculateRespBO.getPrice().getCouponPrice());
  83 + assertEquals(0, calculateRespBO.getPrice().getPointPrice());
  84 + assertEquals(7000, calculateRespBO.getPrice().getPayPrice());
  85 + // 断言:SKU 1
  86 + assertEquals(1001L, calculateRespBO.getItems().get(0).getSpuId());
  87 + assertEquals(100L, calculateRespBO.getItems().get(0).getSkuId());
  88 + assertEquals(1, calculateRespBO.getItems().get(0).getCount());
  89 + assertNull(calculateRespBO.getItems().get(0).getCartId());
  90 + assertTrue(calculateRespBO.getItems().get(0).getSelected());
  91 + assertEquals(1000, calculateRespBO.getItems().get(0).getPrice());
  92 + assertEquals(0, calculateRespBO.getItems().get(0).getDiscountPrice());
  93 + assertEquals(0, calculateRespBO.getItems().get(0).getDeliveryPrice());
  94 + assertEquals(0, calculateRespBO.getItems().get(0).getCouponPrice());
  95 + assertEquals(0, calculateRespBO.getItems().get(0).getPointPrice());
  96 + assertEquals(1000, calculateRespBO.getItems().get(0).getPayPrice());
  97 + assertEquals("小菜", calculateRespBO.getItems().get(0).getSpuName());
  98 + assertEquals("https://t.cn/1.png", calculateRespBO.getItems().get(0).getPicUrl());
  99 + assertEquals(666L, calculateRespBO.getItems().get(0).getCategoryId());
  100 + assertEquals(skuList.get(0).getProperties(), calculateRespBO.getItems().get(0).getProperties());
  101 + // 断言:SKU 2
  102 + assertEquals(1001L, calculateRespBO.getItems().get(1).getSpuId());
  103 + assertEquals(200L, calculateRespBO.getItems().get(1).getSkuId());
  104 + assertEquals(3, calculateRespBO.getItems().get(1).getCount());
  105 + assertNull(calculateRespBO.getItems().get(1).getCartId());
  106 + assertTrue(calculateRespBO.getItems().get(1).getSelected());
  107 + assertEquals(2000, calculateRespBO.getItems().get(1).getPrice());
  108 + assertEquals(0, calculateRespBO.getItems().get(1).getDiscountPrice());
  109 + assertEquals(0, calculateRespBO.getItems().get(1).getDeliveryPrice());
  110 + assertEquals(0, calculateRespBO.getItems().get(1).getCouponPrice());
  111 + assertEquals(0, calculateRespBO.getItems().get(1).getPointPrice());
  112 + assertEquals(6000, calculateRespBO.getItems().get(1).getPayPrice());
  113 + assertEquals("小菜", calculateRespBO.getItems().get(1).getSpuName());
  114 + assertEquals("https://t.cn/2.png", calculateRespBO.getItems().get(1).getPicUrl());
  115 + assertEquals(666L, calculateRespBO.getItems().get(1).getCategoryId());
  116 + assertEquals(skuList.get(1).getProperties(), calculateRespBO.getItems().get(1).getProperties());
  117 + // 断言:SKU 3
  118 + assertEquals(1001L, calculateRespBO.getItems().get(2).getSpuId());
  119 + assertEquals(300L, calculateRespBO.getItems().get(2).getSkuId());
  120 + assertEquals(6, calculateRespBO.getItems().get(2).getCount());
  121 + assertEquals(233L, calculateRespBO.getItems().get(2).getCartId());
  122 + assertFalse(calculateRespBO.getItems().get(2).getSelected());
  123 + assertEquals(3000, calculateRespBO.getItems().get(2).getPrice());
  124 + assertEquals(0, calculateRespBO.getItems().get(2).getDiscountPrice());
  125 + assertEquals(0, calculateRespBO.getItems().get(2).getDeliveryPrice());
  126 + assertEquals(0, calculateRespBO.getItems().get(2).getCouponPrice());
  127 + assertEquals(0, calculateRespBO.getItems().get(2).getPointPrice());
  128 + assertEquals(18000, calculateRespBO.getItems().get(2).getPayPrice());
  129 + assertEquals("小菜", calculateRespBO.getItems().get(2).getSpuName());
  130 + assertEquals("https://t.cn/3.png", calculateRespBO.getItems().get(2).getPicUrl());
  131 + assertEquals(666L, calculateRespBO.getItems().get(2).getCategoryId());
  132 + assertEquals(skuList.get(2).getProperties(), calculateRespBO.getItems().get(2).getProperties());
  133 + }
  134 +
  135 +}
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.price.calculator;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
  5 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
  6 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
  7 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
  8 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
  9 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
  10 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  11 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  12 +import org.junit.jupiter.api.Test;
  13 +import org.mockito.InjectMocks;
  14 +import org.mockito.Mock;
  15 +
  16 +import java.util.ArrayList;
  17 +
  18 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  19 +import static java.util.Arrays.asList;
  20 +import static org.junit.jupiter.api.Assertions.assertEquals;
  21 +import static org.junit.jupiter.api.Assertions.assertTrue;
  22 +import static org.mockito.ArgumentMatchers.eq;
  23 +import static org.mockito.Mockito.when;
  24 +
  25 +/**
  26 + * {@link TradeCouponPriceCalculator} 的单元测试类
  27 + *
  28 + * @author 芋道源码
  29 + */
  30 +public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest {
  31 +
  32 + @InjectMocks
  33 + private TradeCouponPriceCalculator tradeCouponPriceCalculator;
  34 +
  35 + @Mock
  36 + private CouponApi couponApi;
  37 +
  38 + @Test
  39 + public void testCalculate() {
  40 + // 准备参数
  41 + TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
  42 + .setUserId(233L).setCouponId(1024L)
  43 + .setItems(asList(
  44 + new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), // 匹配优惠劵
  45 + new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(true), // 匹配优惠劵
  46 + new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true), // 不匹配优惠劵
  47 + new TradePriceCalculateReqBO.Item().setSkuId(40L).setCount(5).setSelected(false) // 匹配优惠劵,但是未选中
  48 + ));
  49 + TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
  50 + .setPrice(new TradePriceCalculateRespBO.Price())
  51 + .setPromotions(new ArrayList<>())
  52 + .setItems(asList(
  53 + new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
  54 + .setPrice(100).setSpuId(1L),
  55 + new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(true)
  56 + .setPrice(50).setSpuId(2L),
  57 + new TradePriceCalculateRespBO.OrderItem().setSkuId(30L).setCount(4).setSelected(true)
  58 + .setPrice(30).setSpuId(3L),
  59 + new TradePriceCalculateRespBO.OrderItem().setSkuId(40L).setCount(5).setSelected(false)
  60 + .setPrice(60).setSpuId(1L)
  61 + ));
  62 + // 保证价格被初始化上
  63 + TradePriceCalculatorHelper.recountPayPrice(result.getItems());
  64 + TradePriceCalculatorHelper.recountAllPrice(result);
  65 +
  66 + // mock 方法(优惠劵 Coupon 信息)
  67 + CouponRespDTO coupon = randomPojo(CouponRespDTO.class, o -> o.setId(1024L).setName("程序员节")
  68 + .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L))
  69 + .setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType())
  70 + .setDiscountPercent(50).setDiscountLimitPrice(70));
  71 + when(couponApi.validateCoupon(eq(new CouponValidReqDTO().setId(1024L).setUserId(233L)))).thenReturn(coupon);
  72 +
  73 + // 调用
  74 + tradeCouponPriceCalculator.calculate(param, result);
  75 + // 断言
  76 + assertEquals(result.getCouponId(), 1024L);
  77 + // 断言:Price 部分
  78 + TradePriceCalculateRespBO.Price price = result.getPrice();
  79 + assertEquals(price.getTotalPrice(), 470);
  80 + assertEquals(price.getDiscountPrice(), 0);
  81 + assertEquals(price.getPointPrice(), 0);
  82 + assertEquals(price.getDeliveryPrice(), 0);
  83 + assertEquals(price.getCouponPrice(), 70);
  84 + assertEquals(price.getPayPrice(), 400);
  85 + // 断言:SKU 1
  86 + TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
  87 + assertEquals(orderItem01.getSkuId(), 10L);
  88 + assertEquals(orderItem01.getCount(), 2);
  89 + assertEquals(orderItem01.getPrice(), 100);
  90 + assertEquals(orderItem01.getDiscountPrice(), 0);
  91 + assertEquals(orderItem01.getDeliveryPrice(), 0);
  92 + assertEquals(orderItem01.getCouponPrice(), 40);
  93 + assertEquals(orderItem01.getPointPrice(), 0);
  94 + assertEquals(orderItem01.getPayPrice(), 160);
  95 + // 断言:SKU 2
  96 + TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
  97 + assertEquals(orderItem02.getSkuId(), 20L);
  98 + assertEquals(orderItem02.getCount(), 3);
  99 + assertEquals(orderItem02.getPrice(), 50);
  100 + assertEquals(orderItem02.getDiscountPrice(), 0);
  101 + assertEquals(orderItem02.getDeliveryPrice(), 0);
  102 + assertEquals(orderItem02.getCouponPrice(), 30);
  103 + assertEquals(orderItem02.getPointPrice(), 0);
  104 + assertEquals(orderItem02.getPayPrice(), 120);
  105 + // 断言:SKU 3
  106 + TradePriceCalculateRespBO.OrderItem orderItem03 = result.getItems().get(2);
  107 + assertEquals(orderItem03.getSkuId(), 30L);
  108 + assertEquals(orderItem03.getCount(), 4);
  109 + assertEquals(orderItem03.getPrice(), 30);
  110 + assertEquals(orderItem03.getDiscountPrice(), 0);
  111 + assertEquals(orderItem03.getCouponPrice(), 0);
  112 + assertEquals(orderItem03.getPointPrice(), 0);
  113 + assertEquals(orderItem03.getPayPrice(), 120);
  114 + // 断言:SKU 4
  115 + TradePriceCalculateRespBO.OrderItem orderItem04 = result.getItems().get(3);
  116 + assertEquals(orderItem04.getSkuId(), 40L);
  117 + assertEquals(orderItem04.getCount(), 5);
  118 + assertEquals(orderItem04.getPrice(), 60);
  119 + assertEquals(orderItem04.getDiscountPrice(), 0);
  120 + assertEquals(orderItem04.getCouponPrice(), 0);
  121 + assertEquals(orderItem04.getPointPrice(), 0);
  122 + assertEquals(orderItem04.getPayPrice(), 300);
  123 + // 断言:Promotion 部分
  124 + assertEquals(result.getPromotions().size(), 1);
  125 + TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
  126 + assertEquals(promotion01.getId(), 1024L);
  127 + assertEquals(promotion01.getName(), "程序员节");
  128 + assertEquals(promotion01.getType(), PromotionTypeEnum.COUPON.getType());
  129 + assertEquals(promotion01.getTotalPrice(), 350);
  130 + assertEquals(promotion01.getDiscountPrice(), 70);
  131 + assertTrue(promotion01.getMatch());
  132 + assertEquals(promotion01.getDescription(), "优惠劵:省 0.70 元");
  133 + assertEquals(promotion01.getItems().size(), 2);
  134 + TradePriceCalculateRespBO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
  135 + assertEquals(promotionItem011.getSkuId(), 10L);
  136 + assertEquals(promotionItem011.getTotalPrice(), 200);
  137 + assertEquals(promotionItem011.getDiscountPrice(), 40);
  138 + TradePriceCalculateRespBO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
  139 + assertEquals(promotionItem012.getSkuId(), 20L);
  140 + assertEquals(promotionItem012.getTotalPrice(), 150);
  141 + assertEquals(promotionItem012.getDiscountPrice(), 30);
  142 + }
  143 +
  144 +}
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculatorTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.price.calculator;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
  5 +import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
  6 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
  7 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
  8 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  9 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  10 +import org.junit.jupiter.api.Test;
  11 +import org.mockito.InjectMocks;
  12 +import org.mockito.Mock;
  13 +
  14 +import java.util.ArrayList;
  15 +
  16 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  17 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  18 +import static java.util.Arrays.asList;
  19 +import static org.junit.jupiter.api.Assertions.*;
  20 +import static org.mockito.ArgumentMatchers.eq;
  21 +import static org.mockito.Mockito.when;
  22 +
  23 +/**
  24 + * {@link TradeDiscountActivityPriceCalculator} 的单元测试类
  25 + *
  26 + * @author 芋道源码
  27 + */
  28 +public class TradeDiscountActivityPriceCalculatorTest extends BaseMockitoUnitTest {
  29 +
  30 + @InjectMocks
  31 + private TradeDiscountActivityPriceCalculator tradeDiscountActivityPriceCalculator;
  32 +
  33 + @Mock
  34 + private DiscountActivityApi discountActivityApi;
  35 +
  36 + @Test
  37 + public void testCalculate() {
  38 + // 准备参数
  39 + TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
  40 + .setItems(asList(
  41 + new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), // 匹配活动,且已选中
  42 + new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(false) // 匹配活动,但未选中
  43 + ));
  44 + TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
  45 + .setPrice(new TradePriceCalculateRespBO.Price())
  46 + .setPromotions(new ArrayList<>())
  47 + .setItems(asList(
  48 + new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
  49 + .setPrice(100),
  50 + new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(false)
  51 + .setPrice(50)
  52 + ));
  53 + // 保证价格被初始化上
  54 + TradePriceCalculatorHelper.recountPayPrice(result.getItems());
  55 + TradePriceCalculatorHelper.recountAllPrice(result);
  56 +
  57 + // mock 方法(限时折扣活动)
  58 + when(discountActivityApi.getMatchDiscountProductList(eq(asSet(10L, 20L)))).thenReturn(asList(
  59 + randomPojo(DiscountProductRespDTO.class, o -> o.setActivityId(1000L)
  60 + .setActivityName("活动 1000 号").setSkuId(10L)
  61 + .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(40)),
  62 + randomPojo(DiscountProductRespDTO.class, o -> o.setActivityId(2000L)
  63 + .setActivityName("活动 2000 号").setSkuId(20L)
  64 + .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(60))
  65 + ));
  66 + // 10L: 100 * 2 - 40 * 2 = 120
  67 + // 20L:50 * 3 - 50 * 3 * 0.4 = 90
  68 +
  69 + // 调用
  70 + tradeDiscountActivityPriceCalculator.calculate(param, result);
  71 + // 断言:Price 部分
  72 + TradePriceCalculateRespBO.Price price = result.getPrice();
  73 + assertEquals(price.getTotalPrice(), 200);
  74 + assertEquals(price.getDiscountPrice(), 80);
  75 + assertEquals(price.getPointPrice(), 0);
  76 + assertEquals(price.getDeliveryPrice(), 0);
  77 + assertEquals(price.getCouponPrice(), 0);
  78 + assertEquals(price.getPayPrice(), 120);
  79 + assertNull(result.getCouponId());
  80 + // 断言:SKU 1
  81 + assertEquals(result.getItems().size(), 2);
  82 + TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
  83 + assertEquals(orderItem01.getSkuId(), 10L);
  84 + assertEquals(orderItem01.getCount(), 2);
  85 + assertEquals(orderItem01.getPrice(), 100);
  86 + assertEquals(orderItem01.getDiscountPrice(), 80);
  87 + assertEquals(orderItem01.getDeliveryPrice(), 0);
  88 + assertEquals(orderItem01.getCouponPrice(), 0);
  89 + assertEquals(orderItem01.getPointPrice(), 0);
  90 + assertEquals(orderItem01.getPayPrice(), 120);
  91 + // 断言:SKU 2
  92 + TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
  93 + assertEquals(orderItem02.getSkuId(), 20L);
  94 + assertEquals(orderItem02.getCount(), 3);
  95 + assertEquals(orderItem02.getPrice(), 50);
  96 + assertEquals(orderItem02.getDiscountPrice(), 60);
  97 + assertEquals(orderItem02.getDeliveryPrice(), 0);
  98 + assertEquals(orderItem02.getCouponPrice(), 0);
  99 + assertEquals(orderItem02.getPointPrice(), 0);
  100 + assertEquals(orderItem02.getPayPrice(), 90);
  101 + // 断言:Promotion 部分
  102 + assertEquals(result.getPromotions().size(), 1);
  103 + TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
  104 + assertEquals(promotion01.getId(), 1000L);
  105 + assertEquals(promotion01.getName(), "活动 1000 号");
  106 + assertEquals(promotion01.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType());
  107 + assertEquals(promotion01.getTotalPrice(), 200);
  108 + assertEquals(promotion01.getDiscountPrice(), 80);
  109 + assertTrue(promotion01.getMatch());
  110 + assertEquals(promotion01.getDescription(), "限时折扣:省 0.80 元");
  111 + TradePriceCalculateRespBO.PromotionItem promotionItem01 = promotion01.getItems().get(0);
  112 + assertEquals(promotion01.getItems().size(), 1);
  113 + assertEquals(promotionItem01.getSkuId(), 10L);
  114 + assertEquals(promotionItem01.getTotalPrice(), 200);
  115 + assertEquals(promotionItem01.getDiscountPrice(), 80);
  116 + }
  117 +
  118 +}
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculatorTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.price.calculator;
  2 +
  3 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
  5 +import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
  6 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
  7 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
  8 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  9 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  10 +import org.junit.jupiter.api.Test;
  11 +import org.mockito.InjectMocks;
  12 +import org.mockito.Mock;
  13 +
  14 +import java.util.ArrayList;
  15 +
  16 +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
  17 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  18 +import static java.util.Arrays.asList;
  19 +import static java.util.Collections.singletonList;
  20 +import static org.junit.jupiter.api.Assertions.*;
  21 +import static org.mockito.ArgumentMatchers.eq;
  22 +import static org.mockito.Mockito.when;
  23 +
  24 +/**
  25 + * {@link TradeRewardActivityPriceCalculator} 的单元测试类
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest {
  30 +
  31 + @InjectMocks
  32 + private TradeRewardActivityPriceCalculator tradeRewardActivityPriceCalculator;
  33 +
  34 + @Mock
  35 + private RewardActivityApi rewardActivityApi;
  36 +
  37 + @Test
  38 + public void testCalculate_match() {
  39 + // 准备参数
  40 + TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
  41 + .setItems(asList(
  42 + new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), // 匹配活动 1
  43 + new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(true), // 匹配活动 1
  44 + new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true) // 匹配活动 2
  45 + ));
  46 + TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
  47 + .setPrice(new TradePriceCalculateRespBO.Price())
  48 + .setPromotions(new ArrayList<>())
  49 + .setItems(asList(
  50 + new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
  51 + .setPrice(100).setSpuId(1L),
  52 + new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(true)
  53 + .setPrice(50).setSpuId(2L),
  54 + new TradePriceCalculateRespBO.OrderItem().setSkuId(30L).setCount(4).setSelected(true)
  55 + .setPrice(30).setSpuId(3L)
  56 + ));
  57 + // 保证价格被初始化上
  58 + TradePriceCalculatorHelper.recountPayPrice(result.getItems());
  59 + TradePriceCalculatorHelper.recountAllPrice(result);
  60 +
  61 + // mock 方法(限时折扣 DiscountActivity 信息)
  62 + when(rewardActivityApi.getMatchRewardActivityList(eq(asSet(1L, 2L, 3L)))).thenReturn(asList(
  63 + randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(1000L).setName("活动 1000 号")
  64 + .setSpuIds(asList(1L, 2L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
  65 + .setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(200).setDiscountPrice(70)))),
  66 + randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(2000L).setName("活动 2000 号")
  67 + .setSpuIds(singletonList(3L)).setConditionType(PromotionConditionTypeEnum.COUNT.getType())
  68 + .setRules(asList(new RewardActivityMatchRespDTO.Rule().setLimit(1).setDiscountPrice(10),
  69 + new RewardActivityMatchRespDTO.Rule().setLimit(2).setDiscountPrice(60), // 最大可满足,因为是 4 个
  70 + new RewardActivityMatchRespDTO.Rule().setLimit(10).setDiscountPrice(100))))
  71 + ));
  72 +
  73 + // 调用
  74 + tradeRewardActivityPriceCalculator.calculate(param, result);
  75 + // 断言 Order 部分
  76 + TradePriceCalculateRespBO.Price price = result.getPrice();
  77 + assertEquals(price.getTotalPrice(), 470);
  78 + assertEquals(price.getDiscountPrice(), 130);
  79 + assertEquals(price.getPointPrice(), 0);
  80 + assertEquals(price.getDeliveryPrice(), 0);
  81 + assertEquals(price.getCouponPrice(), 0);
  82 + assertEquals(price.getPayPrice(), 340);
  83 + assertNull(result.getCouponId());
  84 + // 断言:SKU 1
  85 + assertEquals(result.getItems().size(), 3);
  86 + TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
  87 + assertEquals(orderItem01.getSkuId(), 10L);
  88 + assertEquals(orderItem01.getCount(), 2);
  89 + assertEquals(orderItem01.getPrice(), 100);
  90 + assertEquals(orderItem01.getDiscountPrice(), 40);
  91 + assertEquals(orderItem01.getDeliveryPrice(), 0);
  92 + assertEquals(orderItem01.getCouponPrice(), 0);
  93 + assertEquals(orderItem01.getPointPrice(), 0);
  94 + assertEquals(orderItem01.getPayPrice(), 160);
  95 + // 断言:SKU 2
  96 + TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
  97 + assertEquals(orderItem02.getSkuId(), 20L);
  98 + assertEquals(orderItem02.getCount(), 3);
  99 + assertEquals(orderItem02.getPrice(), 50);
  100 + assertEquals(orderItem02.getDiscountPrice(), 30);
  101 + assertEquals(orderItem02.getDeliveryPrice(), 0);
  102 + assertEquals(orderItem02.getCouponPrice(), 0);
  103 + assertEquals(orderItem02.getPointPrice(), 0);
  104 + assertEquals(orderItem02.getPayPrice(), 120);
  105 + // 断言:SKU 3
  106 + TradePriceCalculateRespBO.OrderItem orderItem03 = result.getItems().get(2);
  107 + assertEquals(orderItem03.getSkuId(), 30L);
  108 + assertEquals(orderItem03.getCount(), 4);
  109 + assertEquals(orderItem03.getPrice(), 30);
  110 + assertEquals(orderItem03.getDiscountPrice(), 60);
  111 + assertEquals(orderItem03.getDeliveryPrice(), 0);
  112 + assertEquals(orderItem03.getCouponPrice(), 0);
  113 + assertEquals(orderItem03.getPointPrice(), 0);
  114 + assertEquals(orderItem03.getPayPrice(), 60);
  115 + // 断言:Promotion 部分(第一个)
  116 + assertEquals(result.getPromotions().size(), 2);
  117 + TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
  118 + assertEquals(promotion01.getId(), 1000L);
  119 + assertEquals(promotion01.getName(), "活动 1000 号");
  120 + assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
  121 + assertEquals(promotion01.getTotalPrice(), 350);
  122 + assertEquals(promotion01.getDiscountPrice(), 70);
  123 + assertTrue(promotion01.getMatch());
  124 + assertEquals(promotion01.getDescription(), "满减送:省 0.70 元");
  125 + assertEquals(promotion01.getItems().size(), 2);
  126 + TradePriceCalculateRespBO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
  127 + assertEquals(promotionItem011.getSkuId(), 10L);
  128 + assertEquals(promotionItem011.getTotalPrice(), 200);
  129 + assertEquals(promotionItem011.getDiscountPrice(), 40);
  130 + TradePriceCalculateRespBO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
  131 + assertEquals(promotionItem012.getSkuId(), 20L);
  132 + assertEquals(promotionItem012.getTotalPrice(), 150);
  133 + assertEquals(promotionItem012.getDiscountPrice(), 30);
  134 + // 断言:Promotion 部分(第二个)
  135 + TradePriceCalculateRespBO.Promotion promotion02 = result.getPromotions().get(1);
  136 + assertEquals(promotion02.getId(), 2000L);
  137 + assertEquals(promotion02.getName(), "活动 2000 号");
  138 + assertEquals(promotion02.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
  139 + assertEquals(promotion02.getTotalPrice(), 120);
  140 + assertEquals(promotion02.getDiscountPrice(), 60);
  141 + assertTrue(promotion02.getMatch());
  142 + assertEquals(promotion02.getDescription(), "满减送:省 0.60 元");
  143 + TradePriceCalculateRespBO.PromotionItem promotionItem02 = promotion02.getItems().get(0);
  144 + assertEquals(promotion02.getItems().size(), 1);
  145 + assertEquals(promotionItem02.getSkuId(), 30L);
  146 + assertEquals(promotionItem02.getTotalPrice(), 120);
  147 + assertEquals(promotionItem02.getDiscountPrice(), 60);
  148 + }
  149 +
  150 + @Test
  151 + public void testCalculate_notMatch() {
  152 + // 准备参数
  153 + TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
  154 + .setItems(asList(
  155 + new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true),
  156 + new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(true),
  157 + new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true)
  158 + ));
  159 + TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
  160 + .setPrice(new TradePriceCalculateRespBO.Price())
  161 + .setPromotions(new ArrayList<>())
  162 + .setItems(asList(
  163 + new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
  164 + .setPrice(100).setSpuId(1L),
  165 + new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(true)
  166 + .setPrice(50).setSpuId(2L)
  167 + ));
  168 + // 保证价格被初始化上
  169 + TradePriceCalculatorHelper.recountPayPrice(result.getItems());
  170 + TradePriceCalculatorHelper.recountAllPrice(result);
  171 +
  172 + // mock 方法(限时折扣 DiscountActivity 信息)
  173 + when(rewardActivityApi.getMatchRewardActivityList(eq(asSet(1L, 2L)))).thenReturn(singletonList(
  174 + randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(1000L).setName("活动 1000 号")
  175 + .setSpuIds(asList(1L, 2L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
  176 + .setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(351).setDiscountPrice(70))))
  177 + ));
  178 +
  179 + // 调用
  180 + tradeRewardActivityPriceCalculator.calculate(param, result);
  181 + // 断言 Order 部分
  182 + TradePriceCalculateRespBO.Price price = result.getPrice();
  183 + assertEquals(price.getTotalPrice(), 350);
  184 + assertEquals(price.getDiscountPrice(), 0);
  185 + assertEquals(price.getPointPrice(), 0);
  186 + assertEquals(price.getDeliveryPrice(), 0);
  187 + assertEquals(price.getCouponPrice(), 0);
  188 + assertEquals(price.getPayPrice(), 350);
  189 + assertNull(result.getCouponId());
  190 + // 断言:SKU 1
  191 + assertEquals(result.getItems().size(), 2);
  192 + TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
  193 + assertEquals(orderItem01.getSkuId(), 10L);
  194 + assertEquals(orderItem01.getCount(), 2);
  195 + assertEquals(orderItem01.getPrice(), 100);
  196 + assertEquals(orderItem01.getDiscountPrice(), 0);
  197 + assertEquals(orderItem01.getDeliveryPrice(), 0);
  198 + assertEquals(orderItem01.getCouponPrice(), 0);
  199 + assertEquals(orderItem01.getPointPrice(), 0);
  200 + assertEquals(orderItem01.getPayPrice(), 200);
  201 + // 断言:SKU 2
  202 + TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
  203 + assertEquals(orderItem02.getSkuId(), 20L);
  204 + assertEquals(orderItem02.getCount(), 3);
  205 + assertEquals(orderItem02.getPrice(), 50);
  206 + assertEquals(orderItem02.getDiscountPrice(), 0);
  207 + assertEquals(orderItem02.getDeliveryPrice(), 0);
  208 + assertEquals(orderItem02.getCouponPrice(), 0);
  209 + assertEquals(orderItem02.getPointPrice(), 0);
  210 + assertEquals(orderItem02.getPayPrice(), 150);
  211 + // 断言 Promotion 部分
  212 + assertEquals(result.getPromotions().size(), 1);
  213 + TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
  214 + assertEquals(promotion01.getId(), 1000L);
  215 + assertEquals(promotion01.getName(), "活动 1000 号");
  216 + assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
  217 + assertEquals(promotion01.getTotalPrice(), 350);
  218 + assertEquals(promotion01.getDiscountPrice(), 0);
  219 + assertFalse(promotion01.getMatch());
  220 + assertEquals(promotion01.getDescription(), "TODO"); // TODO 芋艿:后面再想想
  221 + assertEquals(promotion01.getItems().size(), 2);
  222 + TradePriceCalculateRespBO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
  223 + assertEquals(promotionItem011.getSkuId(), 10L);
  224 + assertEquals(promotionItem011.getTotalPrice(), 200);
  225 + assertEquals(promotionItem011.getDiscountPrice(), 0);
  226 + TradePriceCalculateRespBO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
  227 + assertEquals(promotionItem012.getSkuId(), 20L);
  228 + assertEquals(promotionItem012.getTotalPrice(), 150);
  229 + assertEquals(promotionItem012.getDiscountPrice(), 0);
  230 + }
  231 +
  232 +}
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java
@@ -18,4 +18,12 @@ public interface AddressApi { @@ -18,4 +18,12 @@ public interface AddressApi {
18 */ 18 */
19 AddressRespDTO getAddress(Long id, Long userId); 19 AddressRespDTO getAddress(Long id, Long userId);
20 20
  21 + /**
  22 + * 获得用户默认收件地址
  23 + *
  24 + * @param userId 用户编号
  25 + * @return 用户收件地址
  26 + */
  27 + AddressRespDTO getDefaultAddress(Long userId);
  28 +
21 } 29 }
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java
@@ -29,7 +29,7 @@ public class AddressRespDTO { @@ -29,7 +29,7 @@ public class AddressRespDTO {
29 /** 29 /**
30 * 地区编号 30 * 地区编号
31 */ 31 */
32 - private Long areaId; 32 + private Integer areaId;
33 /** 33 /**
34 * 邮编 34 * 邮编
35 */ 35 */
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java
@@ -25,4 +25,9 @@ public class AddressApiImpl implements AddressApi { @@ -25,4 +25,9 @@ public class AddressApiImpl implements AddressApi {
25 return AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id)); 25 return AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id));
26 } 26 }
27 27
  28 + @Override
  29 + public AddressRespDTO getDefaultAddress(Long userId) {
  30 + return AddressConvert.INSTANCE.convert02(addressService.getDefaultUserAddress(userId));
  31 + }
  32 +
28 } 33 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/MemberAddressDO.java
@@ -39,7 +39,7 @@ public class MemberAddressDO extends BaseDO { @@ -39,7 +39,7 @@ public class MemberAddressDO extends BaseDO {
39 /** 39 /**
40 * 地区编号 40 * 地区编号
41 */ 41 */
42 - private Long areaId; 42 + private Integer areaId;
43 /** 43 /**
44 * 收件详细地址 44 * 收件详细地址
45 */ 45 */