mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-18 19:20:05 +08:00
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
This commit is contained in:
commit
ba380ea615
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.api.sku.dto;
|
||||
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
@ -25,7 +26,7 @@ public class ProductSkuRespDTO {
|
||||
/**
|
||||
* 属性数组
|
||||
*/
|
||||
private List<Property> properties;
|
||||
private List<ProductPropertyValueDetailRespDTO> properties;
|
||||
/**
|
||||
* 销售价格,单位:分
|
||||
*/
|
||||
@ -50,10 +51,6 @@ public class ProductSkuRespDTO {
|
||||
* 库存
|
||||
*/
|
||||
private Integer stock;
|
||||
/**
|
||||
* 预警预存
|
||||
*/
|
||||
private Integer warnStock;
|
||||
/**
|
||||
* 商品重量,单位:kg 千克
|
||||
*/
|
||||
@ -63,30 +60,4 @@ public class ProductSkuRespDTO {
|
||||
*/
|
||||
private Double volume;
|
||||
|
||||
/**
|
||||
* 商品属性
|
||||
*/
|
||||
@Data
|
||||
public static class Property {
|
||||
|
||||
/**
|
||||
* 属性编号
|
||||
*/
|
||||
private Long propertyId;
|
||||
/**
|
||||
* 属性名字
|
||||
*/
|
||||
private String propertyName;
|
||||
|
||||
/**
|
||||
* 属性值编号
|
||||
*/
|
||||
private Long valueId;
|
||||
/**
|
||||
* 属性值名字
|
||||
*/
|
||||
private String valueName;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ public class AppProductSpuDetailRespVO {
|
||||
@Schema(description = "商品名称", required = true, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品简介", required = true, example = "我是一个快乐简介")
|
||||
private String introduction;
|
||||
|
||||
@Schema(description = "商品详情", required = true, example = "我是商品描述")
|
||||
private String description;
|
||||
|
||||
|
@ -141,7 +141,7 @@ public class ProductSpuDO extends BaseDO {
|
||||
/**
|
||||
* 物流配置模板编号
|
||||
*
|
||||
* 关联 { TradeDeliveryExpressTemplateDO#getId()}
|
||||
* 对应 TradeDeliveryExpressTemplateDO 的 id 编号
|
||||
*/
|
||||
private Long deliveryTemplateId;
|
||||
|
||||
|
@ -12,6 +12,7 @@ import java.util.List;
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Deprecated
|
||||
public class PriceCalculateReqDTO {
|
||||
|
||||
/**
|
||||
|
@ -1,32 +1,24 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.price;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.CouponMeetRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.*;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
|
||||
import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
|
||||
import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_VALID_TIME_NOT_NOW;
|
||||
import static java.util.Arrays.asList;
|
||||
@ -46,8 +38,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
|
||||
@InjectMocks
|
||||
private PriceServiceImpl priceService;
|
||||
|
||||
@Mock
|
||||
private DiscountActivityService discountService;
|
||||
@Mock
|
||||
private RewardActivityService rewardActivityService;
|
||||
@Mock
|
||||
@ -55,405 +45,12 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
|
||||
@Mock
|
||||
private ProductSkuApi productSkuApi;
|
||||
|
||||
@Test
|
||||
public void testCalculatePrice_memberDiscount() {
|
||||
// 准备参数
|
||||
// TODO 芋艿:userId = 1,实现 9 折;后续改成 mock
|
||||
PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(1L)
|
||||
.setItems(singletonList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2)));
|
||||
// mock 方法(商品 SKU 信息)
|
||||
ProductSkuRespDTO productSku = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100));
|
||||
when(productSkuApi.getSkuList(eq(asSet(10L)))).thenReturn(singletonList(productSku));
|
||||
|
||||
// 调用
|
||||
//PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法
|
||||
PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();
|
||||
// 断言 Order 部分
|
||||
PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
|
||||
assertEquals(order.getTotalPrice(), 200);
|
||||
assertEquals(order.getDiscountPrice(), 0);
|
||||
assertEquals(order.getPointPrice(), 0);
|
||||
assertEquals(order.getDeliveryPrice(), 0);
|
||||
assertEquals(order.getPayPrice(), 180);
|
||||
assertNull(order.getCouponId());
|
||||
// 断言 OrderItem 部分
|
||||
assertEquals(order.getItems().size(), 1);
|
||||
PriceCalculateRespDTO.OrderItem orderItem = order.getItems().get(0);
|
||||
assertEquals(orderItem.getSkuId(), 10L);
|
||||
assertEquals(orderItem.getCount(), 2);
|
||||
assertEquals(orderItem.getOriginalPrice(), 200);
|
||||
assertEquals(orderItem.getOriginalUnitPrice(), 100);
|
||||
assertEquals(orderItem.getDiscountPrice(), 20);
|
||||
assertEquals(orderItem.getPayPrice(), 180);
|
||||
assertEquals(orderItem.getOrderPartPrice(), 0);
|
||||
assertEquals(orderItem.getOrderDividePrice(), 180);
|
||||
// 断言 Promotion 部分
|
||||
assertEquals(priceCalculate.getPromotions().size(), 1);
|
||||
PriceCalculateRespDTO.Promotion promotion = priceCalculate.getPromotions().get(0);
|
||||
assertNull(promotion.getId());
|
||||
assertEquals(promotion.getName(), "会员折扣");
|
||||
assertEquals(promotion.getType(), PromotionTypeEnum.MEMBER.getType());
|
||||
//assertEquals(promotion.getLevel(), PromotionLevelEnum.SKU.getLevel()); TODO 没有这个枚举类
|
||||
assertEquals(promotion.getTotalPrice(), 200);
|
||||
assertEquals(promotion.getDiscountPrice(), 20);
|
||||
assertTrue(promotion.getMatch());
|
||||
assertEquals(promotion.getDescription(), "会员折扣:省 0.20 元");
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem = promotion.getItems().get(0);
|
||||
assertEquals(promotion.getItems().size(), 1);
|
||||
assertEquals(promotionItem.getSkuId(), 10L);
|
||||
assertEquals(promotionItem.getOriginalPrice(), 200);
|
||||
assertEquals(promotionItem.getDiscountPrice(), 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculatePrice_discountActivity() {
|
||||
// 准备参数
|
||||
PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())
|
||||
.setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),
|
||||
new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3)));
|
||||
// mock 方法(商品 SKU 信息)
|
||||
ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100));
|
||||
ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50));
|
||||
when(productSkuApi.getSkuList(eq(asSet(10L, 20L)))).thenReturn(asList(productSku01, productSku02));
|
||||
// mock 方法(限时折扣 DiscountActivity 信息)TODO 没找到 DiscountProductDetailBO
|
||||
//DiscountProductDetailBO discountProduct01 = randomPojo(DiscountProductDetailBO.class, o -> o.setActivityId(1000L)
|
||||
// .setActivityName("活动 1000 号").setSkuId(10L)
|
||||
// .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(40));
|
||||
//DiscountProductDetailBO discountProduct02 = randomPojo(DiscountProductDetailBO.class, o -> o.setActivityId(2000L)
|
||||
// .setActivityName("活动 2000 号").setSkuId(20L)
|
||||
// .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(60));
|
||||
//when(discountService.getMatchDiscountProductList(eq(asSet(10L, 20L)))).thenReturn(
|
||||
// MapUtil.builder(10L, discountProduct01).put(20L, discountProduct02).map());
|
||||
|
||||
// 10L: 100 * 2 - 40 * 2 = 120
|
||||
// 20L:50 * 3 - 50 * 3 * 0.4 = 90
|
||||
|
||||
// 调用
|
||||
//PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法
|
||||
PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();
|
||||
// 断言 Order 部分
|
||||
PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
|
||||
assertEquals(order.getTotalPrice(), 350);
|
||||
assertEquals(order.getDiscountPrice(), 0);
|
||||
assertEquals(order.getPointPrice(), 0);
|
||||
assertEquals(order.getDeliveryPrice(), 0);
|
||||
assertEquals(order.getPayPrice(), 210);
|
||||
assertNull(order.getCouponId());
|
||||
// 断言 OrderItem 部分
|
||||
assertEquals(order.getItems().size(), 2);
|
||||
PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);
|
||||
assertEquals(orderItem01.getSkuId(), 10L);
|
||||
assertEquals(orderItem01.getCount(), 2);
|
||||
assertEquals(orderItem01.getOriginalPrice(), 200);
|
||||
assertEquals(orderItem01.getOriginalUnitPrice(), 100);
|
||||
assertEquals(orderItem01.getDiscountPrice(), 80);
|
||||
assertEquals(orderItem01.getPayPrice(), 120);
|
||||
assertEquals(orderItem01.getOrderPartPrice(), 0);
|
||||
assertEquals(orderItem01.getOrderDividePrice(), 120);
|
||||
PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);
|
||||
assertEquals(orderItem02.getSkuId(), 20L);
|
||||
assertEquals(orderItem02.getCount(), 3);
|
||||
assertEquals(orderItem02.getOriginalPrice(), 150);
|
||||
assertEquals(orderItem02.getOriginalUnitPrice(), 50);
|
||||
assertEquals(orderItem02.getDiscountPrice(), 60);
|
||||
assertEquals(orderItem02.getPayPrice(), 90);
|
||||
assertEquals(orderItem02.getOrderPartPrice(), 0);
|
||||
assertEquals(orderItem02.getOrderDividePrice(), 90);
|
||||
// 断言 Promotion 部分
|
||||
assertEquals(priceCalculate.getPromotions().size(), 2);
|
||||
PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);
|
||||
assertEquals(promotion01.getId(), 1000L);
|
||||
assertEquals(promotion01.getName(), "活动 1000 号");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType());
|
||||
//assertEquals(promotion01.getLevel(), PromotionLevelEnum.SKU.getLevel()); TODO PromotionLevelEnum 没有这个枚举类
|
||||
assertEquals(promotion01.getTotalPrice(), 200);
|
||||
assertEquals(promotion01.getDiscountPrice(), 80);
|
||||
assertTrue(promotion01.getMatch());
|
||||
assertEquals(promotion01.getDescription(), "限时折扣:省 0.80 元");
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem01 = promotion01.getItems().get(0);
|
||||
assertEquals(promotion01.getItems().size(), 1);
|
||||
assertEquals(promotionItem01.getSkuId(), 10L);
|
||||
assertEquals(promotionItem01.getOriginalPrice(), 200);
|
||||
assertEquals(promotionItem01.getDiscountPrice(), 80);
|
||||
PriceCalculateRespDTO.Promotion promotion02 = priceCalculate.getPromotions().get(1);
|
||||
assertEquals(promotion02.getId(), 2000L);
|
||||
assertEquals(promotion02.getName(), "活动 2000 号");
|
||||
assertEquals(promotion02.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType());
|
||||
//assertEquals(promotion02.getLevel(), PromotionLevelEnum.SKU.getLevel()); TODO PromotionLevelEnum 没有这个枚举类
|
||||
assertEquals(promotion02.getTotalPrice(), 150);
|
||||
assertEquals(promotion02.getDiscountPrice(), 60);
|
||||
assertTrue(promotion02.getMatch());
|
||||
assertEquals(promotion02.getDescription(), "限时折扣:省 0.60 元");
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem02 = promotion02.getItems().get(0);
|
||||
assertEquals(promotion02.getItems().size(), 1);
|
||||
assertEquals(promotionItem02.getSkuId(), 20L);
|
||||
assertEquals(promotionItem02.getOriginalPrice(), 150);
|
||||
assertEquals(promotionItem02.getDiscountPrice(), 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试满减送活动,匹配的情况
|
||||
*/
|
||||
@Test
|
||||
public void testCalculatePrice_rewardActivity() {
|
||||
// 准备参数
|
||||
PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())
|
||||
.setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),
|
||||
new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3),
|
||||
new PriceCalculateReqDTO.Item().setSkuId(30L).setCount(4)));
|
||||
// mock 方法(商品 SKU 信息)
|
||||
ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L));
|
||||
ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L));
|
||||
ProductSkuRespDTO productSku03 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(30L).setPrice(30).setSpuId(3L));
|
||||
when(productSkuApi.getSkuList(eq(asSet(10L, 20L, 30L)))).thenReturn(asList(productSku01, productSku02, productSku03));
|
||||
// mock 方法(限时折扣 DiscountActivity 信息)
|
||||
RewardActivityDO rewardActivity01 = randomPojo(RewardActivityDO.class, o -> o.setId(1000L).setName("活动 1000 号")
|
||||
.setProductSpuIds(asList(10L, 20L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
|
||||
.setRules(singletonList(new RewardActivityDO.Rule().setLimit(200).setDiscountPrice(70))));
|
||||
RewardActivityDO rewardActivity02 = randomPojo(RewardActivityDO.class, o -> o.setId(2000L).setName("活动 2000 号")
|
||||
.setProductSpuIds(singletonList(30L)).setConditionType(PromotionConditionTypeEnum.COUNT.getType())
|
||||
.setRules(asList(new RewardActivityDO.Rule().setLimit(1).setDiscountPrice(10),
|
||||
new RewardActivityDO.Rule().setLimit(2).setDiscountPrice(60), // 最大可满足,因为是 4 个
|
||||
new RewardActivityDO.Rule().setLimit(10).setDiscountPrice(100))));
|
||||
Map<RewardActivityDO, Set<Long>> matchRewardActivities = new LinkedHashMap<>();
|
||||
matchRewardActivities.put(rewardActivity01, asSet(1L, 2L));
|
||||
matchRewardActivities.put(rewardActivity02, asSet(3L));
|
||||
// TODO getMatchRewardActivities 没有这个方法,但是找到了 getMatchRewardActivityList
|
||||
//when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L, 3L)))).thenReturn(matchRewardActivities);
|
||||
|
||||
// 调用
|
||||
//PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法
|
||||
PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();
|
||||
// 断言 Order 部分
|
||||
PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
|
||||
assertEquals(order.getTotalPrice(), 470);
|
||||
assertEquals(order.getDiscountPrice(), 130);
|
||||
assertEquals(order.getPointPrice(), 0);
|
||||
assertEquals(order.getDeliveryPrice(), 0);
|
||||
assertEquals(order.getPayPrice(), 340);
|
||||
assertNull(order.getCouponId());
|
||||
// 断言 OrderItem 部分
|
||||
assertEquals(order.getItems().size(), 3);
|
||||
PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);
|
||||
assertEquals(orderItem01.getSkuId(), 10L);
|
||||
assertEquals(orderItem01.getCount(), 2);
|
||||
assertEquals(orderItem01.getOriginalPrice(), 200);
|
||||
assertEquals(orderItem01.getOriginalUnitPrice(), 100);
|
||||
assertEquals(orderItem01.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem01.getPayPrice(), 200);
|
||||
assertEquals(orderItem01.getOrderPartPrice(), 40);
|
||||
assertEquals(orderItem01.getOrderDividePrice(), 160);
|
||||
PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);
|
||||
assertEquals(orderItem02.getSkuId(), 20L);
|
||||
assertEquals(orderItem02.getCount(), 3);
|
||||
assertEquals(orderItem02.getOriginalPrice(), 150);
|
||||
assertEquals(orderItem02.getOriginalUnitPrice(), 50);
|
||||
assertEquals(orderItem02.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem02.getPayPrice(), 150);
|
||||
assertEquals(orderItem02.getOrderPartPrice(), 30);
|
||||
assertEquals(orderItem02.getOrderDividePrice(), 120);
|
||||
PriceCalculateRespDTO.OrderItem orderItem03 = order.getItems().get(2);
|
||||
assertEquals(orderItem03.getSkuId(), 30L);
|
||||
assertEquals(orderItem03.getCount(), 4);
|
||||
assertEquals(orderItem03.getOriginalPrice(), 120);
|
||||
assertEquals(orderItem03.getOriginalUnitPrice(), 30);
|
||||
assertEquals(orderItem03.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem03.getPayPrice(), 120);
|
||||
assertEquals(orderItem03.getOrderPartPrice(), 60);
|
||||
assertEquals(orderItem03.getOrderDividePrice(), 60);
|
||||
// 断言 Promotion 部分(第一个)
|
||||
assertEquals(priceCalculate.getPromotions().size(), 2);
|
||||
PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);
|
||||
assertEquals(promotion01.getId(), 1000L);
|
||||
assertEquals(promotion01.getName(), "活动 1000 号");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
//assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel()); TODO PromotionLevelEnum 没有这个枚举类
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 70);
|
||||
assertTrue(promotion01.getMatch());
|
||||
assertEquals(promotion01.getDescription(), "满减送:省 0.70 元");
|
||||
assertEquals(promotion01.getItems().size(), 2);
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
|
||||
assertEquals(promotionItem011.getSkuId(), 10L);
|
||||
assertEquals(promotionItem011.getOriginalPrice(), 200);
|
||||
assertEquals(promotionItem011.getDiscountPrice(), 40);
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
|
||||
assertEquals(promotionItem012.getSkuId(), 20L);
|
||||
assertEquals(promotionItem012.getOriginalPrice(), 150);
|
||||
assertEquals(promotionItem012.getDiscountPrice(), 30);
|
||||
// 断言 Promotion 部分(第二个)
|
||||
PriceCalculateRespDTO.Promotion promotion02 = priceCalculate.getPromotions().get(1);
|
||||
assertEquals(promotion02.getId(), 2000L);
|
||||
assertEquals(promotion02.getName(), "活动 2000 号");
|
||||
assertEquals(promotion02.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
//assertEquals(promotion02.getLevel(), PromotionLevelEnum.ORDER.getLevel()); TODO PromotionLevelEnum 没有这个枚举类
|
||||
assertEquals(promotion02.getTotalPrice(), 120);
|
||||
assertEquals(promotion02.getDiscountPrice(), 60);
|
||||
assertTrue(promotion02.getMatch());
|
||||
assertEquals(promotion02.getDescription(), "满减送:省 0.60 元");
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem02 = promotion02.getItems().get(0);
|
||||
assertEquals(promotion02.getItems().size(), 1);
|
||||
assertEquals(promotionItem02.getSkuId(), 30L);
|
||||
assertEquals(promotionItem02.getOriginalPrice(), 120);
|
||||
assertEquals(promotionItem02.getDiscountPrice(), 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试满减送活动,不匹配的情况
|
||||
*/
|
||||
@Test
|
||||
public void testCalculatePrice_rewardActivityNotMeet() {
|
||||
// 准备参数
|
||||
PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())
|
||||
.setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),
|
||||
new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3)));
|
||||
// mock 方法(商品 SKU 信息)
|
||||
ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L));
|
||||
ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L));
|
||||
when(productSkuApi.getSkuList(eq(asSet(10L, 20L)))).thenReturn(asList(productSku01, productSku02));
|
||||
// mock 方法(限时折扣 DiscountActivity 信息)
|
||||
RewardActivityDO rewardActivity01 = randomPojo(RewardActivityDO.class, o -> o.setId(1000L).setName("活动 1000 号")
|
||||
.setProductSpuIds(asList(10L, 20L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
|
||||
.setRules(singletonList(new RewardActivityDO.Rule().setLimit(351).setDiscountPrice(70))));
|
||||
Map<RewardActivityDO, Set<Long>> matchRewardActivities = new LinkedHashMap<>();
|
||||
matchRewardActivities.put(rewardActivity01, asSet(1L, 2L));
|
||||
//TODO getMatchRewardActivities 没有这个方法,但是找到了 getMatchRewardActivityList
|
||||
//when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L)))).thenReturn(matchRewardActivities);
|
||||
|
||||
// 调用
|
||||
//PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法
|
||||
PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();
|
||||
// 断言 Order 部分
|
||||
PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
|
||||
assertEquals(order.getTotalPrice(), 350);
|
||||
assertEquals(order.getDiscountPrice(), 0);
|
||||
assertEquals(order.getPointPrice(), 0);
|
||||
assertEquals(order.getDeliveryPrice(), 0);
|
||||
assertEquals(order.getPayPrice(), 350);
|
||||
assertNull(order.getCouponId());
|
||||
// 断言 OrderItem 部分
|
||||
assertEquals(order.getItems().size(), 2);
|
||||
PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);
|
||||
assertEquals(orderItem01.getSkuId(), 10L);
|
||||
assertEquals(orderItem01.getCount(), 2);
|
||||
assertEquals(orderItem01.getOriginalPrice(), 200);
|
||||
assertEquals(orderItem01.getOriginalUnitPrice(), 100);
|
||||
assertEquals(orderItem01.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem01.getPayPrice(), 200);
|
||||
assertEquals(orderItem01.getOrderPartPrice(), 0);
|
||||
assertEquals(orderItem01.getOrderDividePrice(), 200);
|
||||
PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);
|
||||
assertEquals(orderItem02.getSkuId(), 20L);
|
||||
assertEquals(orderItem02.getCount(), 3);
|
||||
assertEquals(orderItem02.getOriginalPrice(), 150);
|
||||
assertEquals(orderItem02.getOriginalUnitPrice(), 50);
|
||||
assertEquals(orderItem02.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem02.getPayPrice(), 150);
|
||||
assertEquals(orderItem02.getOrderPartPrice(), 0);
|
||||
assertEquals(orderItem02.getOrderDividePrice(), 150);
|
||||
// 断言 Promotion 部分
|
||||
assertEquals(priceCalculate.getPromotions().size(), 1);
|
||||
PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);
|
||||
assertEquals(promotion01.getId(), 1000L);
|
||||
assertEquals(promotion01.getName(), "活动 1000 号");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
//assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel()); TODO PromotionLevelEnum 没有这个枚举类
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 0);
|
||||
assertFalse(promotion01.getMatch());
|
||||
assertEquals(promotion01.getDescription(), "TODO"); // TODO 芋艿:后面再想想
|
||||
assertEquals(promotion01.getItems().size(), 2);
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
|
||||
assertEquals(promotionItem011.getSkuId(), 10L);
|
||||
assertEquals(promotionItem011.getOriginalPrice(), 200);
|
||||
assertEquals(promotionItem011.getDiscountPrice(), 0);
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
|
||||
assertEquals(promotionItem012.getSkuId(), 20L);
|
||||
assertEquals(promotionItem012.getOriginalPrice(), 150);
|
||||
assertEquals(promotionItem012.getDiscountPrice(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculatePrice_coupon() {
|
||||
// 准备参数
|
||||
PriceCalculateReqDTO calculateReqDTO = new PriceCalculateReqDTO().setUserId(randomLongId())
|
||||
.setItems(asList(new PriceCalculateReqDTO.Item().setSkuId(10L).setCount(2),
|
||||
new PriceCalculateReqDTO.Item().setSkuId(20L).setCount(3),
|
||||
new PriceCalculateReqDTO.Item().setSkuId(30L).setCount(4)))
|
||||
.setCouponId(1024L);
|
||||
// mock 方法(商品 SKU 信息)
|
||||
ProductSkuRespDTO productSku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(10L).setPrice(100).setSpuId(1L));
|
||||
ProductSkuRespDTO productSku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(20L).setPrice(50).setSpuId(2L));
|
||||
ProductSkuRespDTO productSku03 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(30L).setPrice(30).setSpuId(3L));
|
||||
when(productSkuApi.getSkuList(eq(asSet(10L, 20L, 30L)))).thenReturn(asList(productSku01, productSku02, productSku03));
|
||||
// mock 方法(优惠劵 Coupon 信息)
|
||||
CouponDO coupon = randomPojo(CouponDO.class, o -> o.setId(1024L).setName("程序员节")
|
||||
.setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L))
|
||||
.setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType())
|
||||
.setDiscountPercent(50).setDiscountLimitPrice(70));
|
||||
when(couponService.validCoupon(eq(1024L), eq(calculateReqDTO.getUserId()))).thenReturn(coupon);
|
||||
|
||||
// 调用
|
||||
//PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); TODO 没有这个方法
|
||||
PriceCalculateRespDTO priceCalculate = new PriceCalculateRespDTO();
|
||||
// 断言 Order 部分
|
||||
PriceCalculateRespDTO.Order order = priceCalculate.getOrder();
|
||||
assertEquals(order.getTotalPrice(), 470);
|
||||
assertEquals(order.getDiscountPrice(), 0);
|
||||
assertEquals(order.getPointPrice(), 0);
|
||||
assertEquals(order.getDeliveryPrice(), 0);
|
||||
assertEquals(order.getPayPrice(), 400);
|
||||
assertEquals(order.getCouponId(), 1024L);
|
||||
assertEquals(order.getCouponPrice(), 70);
|
||||
// 断言 OrderItem 部分
|
||||
assertEquals(order.getItems().size(), 3);
|
||||
PriceCalculateRespDTO.OrderItem orderItem01 = order.getItems().get(0);
|
||||
assertEquals(orderItem01.getSkuId(), 10L);
|
||||
assertEquals(orderItem01.getCount(), 2);
|
||||
assertEquals(orderItem01.getOriginalPrice(), 200);
|
||||
assertEquals(orderItem01.getOriginalUnitPrice(), 100);
|
||||
assertEquals(orderItem01.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem01.getPayPrice(), 200);
|
||||
assertEquals(orderItem01.getOrderPartPrice(), 40);
|
||||
assertEquals(orderItem01.getOrderDividePrice(), 160);
|
||||
PriceCalculateRespDTO.OrderItem orderItem02 = order.getItems().get(1);
|
||||
assertEquals(orderItem02.getSkuId(), 20L);
|
||||
assertEquals(orderItem02.getCount(), 3);
|
||||
assertEquals(orderItem02.getOriginalPrice(), 150);
|
||||
assertEquals(orderItem02.getOriginalUnitPrice(), 50);
|
||||
assertEquals(orderItem02.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem02.getPayPrice(), 150);
|
||||
assertEquals(orderItem02.getOrderPartPrice(), 30);
|
||||
assertEquals(orderItem02.getOrderDividePrice(), 120);
|
||||
PriceCalculateRespDTO.OrderItem orderItem03 = order.getItems().get(2);
|
||||
assertEquals(orderItem03.getSkuId(), 30L);
|
||||
assertEquals(orderItem03.getCount(), 4);
|
||||
assertEquals(orderItem03.getOriginalPrice(), 120);
|
||||
assertEquals(orderItem03.getOriginalUnitPrice(), 30);
|
||||
assertEquals(orderItem03.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem03.getPayPrice(), 120);
|
||||
assertEquals(orderItem03.getOrderPartPrice(), 0);
|
||||
assertEquals(orderItem03.getOrderDividePrice(), 120);
|
||||
// 断言 Promotion 部分
|
||||
assertEquals(priceCalculate.getPromotions().size(), 1);
|
||||
PriceCalculateRespDTO.Promotion promotion01 = priceCalculate.getPromotions().get(0);
|
||||
assertEquals(promotion01.getId(), 1024L);
|
||||
assertEquals(promotion01.getName(), "程序员节");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.COUPON.getType());
|
||||
//assertEquals(promotion01.getLevel(), PromotionLevelEnum.COUPON.getLevel()); TODO PromotionLevelEnum 没有这个枚举类
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 70);
|
||||
assertTrue(promotion01.getMatch());
|
||||
assertEquals(promotion01.getDescription(), "优惠劵:省 0.70 元");
|
||||
assertEquals(promotion01.getItems().size(), 2);
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
|
||||
assertEquals(promotionItem011.getSkuId(), 10L);
|
||||
assertEquals(promotionItem011.getOriginalPrice(), 200);
|
||||
assertEquals(promotionItem011.getDiscountPrice(), 40);
|
||||
PriceCalculateRespDTO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
|
||||
assertEquals(promotionItem012.getSkuId(), 20L);
|
||||
assertEquals(promotionItem012.getOriginalPrice(), 150);
|
||||
assertEquals(promotionItem012.getDiscountPrice(), 30);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.enums.delivery;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@ -35,4 +36,8 @@ public enum DeliveryExpressChargeModeEnum implements IntArrayValuable {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static DeliveryExpressChargeModeEnum valueOf(Integer value) {
|
||||
return ArrayUtil.firstMatch(chargeMode -> chargeMode.getType().equals(value), DeliveryExpressChargeModeEnum.values());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public class DeliveryExpressTemplateController {
|
||||
@Operation(summary = "获得快递运费模板")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')")
|
||||
public CommonResult<DeliveryExpressTemplateRespVO> getDeliveryExpressTemplate(@RequestParam("id") Long id) {
|
||||
public CommonResult<DeliveryExpressTemplateDetailRespVO> getDeliveryExpressTemplate(@RequestParam("id") Long id) {
|
||||
return success(deliveryExpressTemplateService.getDeliveryExpressTemplate(id));
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ public class DeliveryExpressTemplateController {
|
||||
@Operation(summary = "获得快递运费模板列表")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
|
||||
@PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')")
|
||||
public CommonResult<List<DeliveryExpressTemplateSimpleRespVO>> getDeliveryExpressTemplateList(@RequestParam("ids") Collection<Long> ids) {
|
||||
public CommonResult<List<DeliveryExpressTemplateRespVO>> getDeliveryExpressTemplateList(@RequestParam("ids") Collection<Long> ids) {
|
||||
List<DeliveryExpressTemplateDO> list = deliveryExpressTemplateService.getDeliveryExpressTemplateList(ids);
|
||||
return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
@ -83,7 +83,7 @@ public class DeliveryExpressTemplateController {
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得快递运费模板分页")
|
||||
@PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')")
|
||||
public CommonResult<PageResult<DeliveryExpressTemplateSimpleRespVO>> getDeliveryExpressTemplatePage(@Valid DeliveryExpressTemplatePageReqVO pageVO) {
|
||||
public CommonResult<PageResult<DeliveryExpressTemplateRespVO>> getDeliveryExpressTemplatePage(@Valid DeliveryExpressTemplatePageReqVO pageVO) {
|
||||
PageResult<DeliveryExpressTemplateDO> pageResult = deliveryExpressTemplateService.getDeliveryExpressTemplatePage(pageVO);
|
||||
return success(DeliveryExpressTemplateConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 快递运费模板的详细 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DeliveryExpressTemplateDetailRespVO extends DeliveryExpressTemplateBaseVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "371")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "运费模板运费设置", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<ExpressTemplateChargeBaseVO> templateCharge;
|
||||
|
||||
@Schema(description = "运费模板包邮区域", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<ExpressTemplateFreeBaseVO> templateFree;
|
||||
|
||||
}
|
@ -1,28 +1,19 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import lombok.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author jason
|
||||
*/
|
||||
@Schema(description = "管理后台 - 快递运费模板 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DeliveryExpressTemplateRespVO extends DeliveryExpressTemplateBaseVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "371")
|
||||
@Schema(description = "编号,自增", required = true, example = "371")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "运费模板运费设置", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<ExpressTemplateChargeBaseVO> templateCharge;
|
||||
|
||||
@Schema(description = "运费模板包邮区域", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<ExpressTemplateFreeBaseVO> templateFree;
|
||||
@Schema(description = "创建时间", required = true)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// TODO @jason:simplae 是不是不用继承 DeliveryExpressTemplateBaseVO,直接 id name 属性就够了。
|
||||
// @芋艿 这里给列表显示用的。 需要显示计费方式和排序, 所以继承 DeliveryExpressTemplateBaseVO。 这是去掉了包邮区域和 区域运费列表
|
||||
@Schema(description = "管理后台 - 快递运费模板 精简 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class DeliveryExpressTemplateSimpleRespVO extends DeliveryExpressTemplateBaseVO {
|
||||
|
||||
@Schema(description = "编号,自增", required = true, example = "371")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "创建时间", required = true)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
@ -1,29 +1,29 @@
|
||||
### /trade-order/settlement 获得订单结算信息
|
||||
GET {{appApi}}/trade/order/settlement?cartIds=1
|
||||
### /trade-order/settlement 获得订单结算信息(基于商品)
|
||||
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
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### /trade-order/confirm-create-order-info-from-cart 基于购物车,确认创建订单
|
||||
GET {{shop-api-base-url}}/trade-order/confirm-create-order-info-from-cart
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Bearer {{user-access-token}}
|
||||
### /trade-order/settlement 获得订单结算信息(基于购物车)
|
||||
GET {{appApi}}/trade/order/settlement?type=0&items[0].cartId=50&couponId=1
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### /trade-order/create 基于商品,创建订单
|
||||
### /trade-order/create 创建订单(基于商品)
|
||||
POST {{appApi}}/trade/order/create
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
{
|
||||
"type": 0,
|
||||
"addressId": 21,
|
||||
"remark": "我是备注",
|
||||
"fromCart": false,
|
||||
"items": [
|
||||
{
|
||||
"skuId": 29,
|
||||
"count": 1
|
||||
"skuId": 1,
|
||||
"count": 2
|
||||
}
|
||||
]
|
||||
],
|
||||
"remark": "我是备注"
|
||||
}
|
||||
|
||||
### 获得订单交易的分页
|
||||
|
@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
@ -23,15 +22,14 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "用户 App - 交易订单")
|
||||
@ -53,92 +51,16 @@ public class AppTradeOrderController {
|
||||
@GetMapping("/settlement")
|
||||
@Operation(summary = "获得订单结算信息")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeOrderSettlementRespVO> settlementOrder(
|
||||
@Valid AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
if (true) {
|
||||
return success(tradeOrderService.settlementOrder(getLoginUserId(), settlementReqVO));
|
||||
}
|
||||
// return success(tradeOrderService.getOrderConfirmCreateInfo(UserSecurityContextHolder.getUserId(), skuId, quantity, couponCardId));
|
||||
AppTradeOrderSettlementRespVO settlement = new AppTradeOrderSettlementRespVO();
|
||||
|
||||
AppTradeOrderSettlementRespVO.Price price = new AppTradeOrderSettlementRespVO.Price();
|
||||
price.setTotalPrice(1000);
|
||||
price.setDeliveryPrice(200);
|
||||
price.setCouponPrice(100);
|
||||
price.setPointPrice(50);
|
||||
price.setPayPrice(950);
|
||||
|
||||
List<AppTradeOrderSettlementRespVO.Item> skus = new ArrayList<>();
|
||||
|
||||
AppTradeOrderSettlementRespVO.Item item1 = new AppTradeOrderSettlementRespVO.Item();
|
||||
item1.setCartId(1L);
|
||||
item1.setSpuId(2048L);
|
||||
item1.setSpuName("Apple iPhone 12");
|
||||
item1.setSkuId(1024);
|
||||
item1.setPrice(500);
|
||||
item1.setPicUrl("https://pro.crmeb.net/uploads/attach/2022/10/12/0c56f9abb80d2775fc1e80dbe4f8826a.jpg");
|
||||
item1.setCount(2);
|
||||
List<AppProductPropertyValueDetailRespVO> properties1 = new ArrayList<>();
|
||||
AppProductPropertyValueDetailRespVO property1 = new AppProductPropertyValueDetailRespVO();
|
||||
property1.setPropertyId(1L);
|
||||
property1.setPropertyName("尺寸");
|
||||
property1.setValueId(2L);
|
||||
property1.setValueName("大");
|
||||
properties1.add(property1);
|
||||
item1.setProperties(properties1);
|
||||
|
||||
AppTradeOrderSettlementRespVO.Item item2 = new AppTradeOrderSettlementRespVO.Item();
|
||||
item2.setCartId(2L);
|
||||
item2.setSpuId(3072L);
|
||||
item2.setSpuName("Samsung Galaxy S21");
|
||||
item2.setSkuId(2048);
|
||||
item2.setPrice(800);
|
||||
item2.setPicUrl("https://pro.crmeb.net/uploads/attach/2022/10/12/0c56f9abb80d2775fc1e80dbe4f8826a.jpg");
|
||||
item2.setCount(1);
|
||||
List<AppProductPropertyValueDetailRespVO> properties2 = new ArrayList<>();
|
||||
AppProductPropertyValueDetailRespVO property2 = new AppProductPropertyValueDetailRespVO();
|
||||
property2.setPropertyId(10L);
|
||||
property2.setPropertyName("颜色");
|
||||
property2.setValueId(20L);
|
||||
property2.setValueName("白色");
|
||||
properties2.add(property2);
|
||||
item2.setProperties(properties2);
|
||||
|
||||
skus.add(item1);
|
||||
skus.add(item2);
|
||||
|
||||
settlement.setItems(skus);
|
||||
settlement.setPrice(price);
|
||||
|
||||
AppTradeOrderSettlementRespVO.Address address = new AppTradeOrderSettlementRespVO.Address();
|
||||
address.setId(1L);
|
||||
address.setName("John");
|
||||
address.setMobile("18888888888");
|
||||
address.setProvinceId(1L);
|
||||
address.setProvinceName("Beijing");
|
||||
address.setCityId(1L);
|
||||
address.setCityName("Beijing");
|
||||
address.setDistrictId(1L);
|
||||
address.setDistrictName("Chaoyang Distripct");
|
||||
address.setDetailAddress("No. 10, Xinzhong Street, Chaoyang District");
|
||||
address.setDefaulted(true);
|
||||
settlement.setAddress(address);
|
||||
|
||||
return success(settlement);
|
||||
public CommonResult<AppTradeOrderSettlementRespVO> settlementOrder(@Valid AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
return success(tradeOrderService.settlementOrder(getLoginUserId(), settlementReqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建订单")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Long> createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO,
|
||||
HttpServletRequest servletRequest) {
|
||||
return success(1L);
|
||||
// // 获取登录用户、用户 IP 地址
|
||||
// Long loginUserId = getLoginUserId();
|
||||
// String clientIp = ServletUtils.getClientIP(servletRequest);
|
||||
// // 创建交易订单,预支付记录
|
||||
// Long orderId = tradeOrderService.createOrder(loginUserId, clientIp, createReqVO);
|
||||
// return success(orderId);
|
||||
public CommonResult<Long> createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO) {
|
||||
Long orderId = tradeOrderService.createOrder(getLoginUserId(), getClientIP(), createReqVO);
|
||||
return success(orderId);
|
||||
}
|
||||
|
||||
@PostMapping("/update-paid")
|
||||
|
@ -3,26 +3,9 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 交易订单创建 Request VO")
|
||||
@Data
|
||||
public class AppTradeOrderCreateReqVO {
|
||||
|
||||
@Schema(description = "收件地址编号", required = true, example = "1")
|
||||
@NotNull(message = "收件地址不能为空")
|
||||
private Long addressId;
|
||||
|
||||
@Schema(description = "优惠劵编号", example = "1024")
|
||||
private Long couponId;
|
||||
|
||||
@Schema(description = "购物车项的编号数组", required = true, example = "true")
|
||||
@NotEmpty(message = "购物车项不能为空")
|
||||
private List<Long> cartIds;
|
||||
|
||||
// ========== 非 AppTradeOrderSettlementReqVO 字段 ==========
|
||||
public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO {
|
||||
|
||||
@Schema(description = "备注", example = "这个是我的订单哟")
|
||||
private String remark;
|
||||
|
@ -1,23 +1,60 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 交易订单结算 Request VO")
|
||||
@Data
|
||||
public class AppTradeOrderSettlementReqVO {
|
||||
|
||||
@NotNull(message = "交易类型不能为空")
|
||||
@InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "商品项数组", required = true)
|
||||
@NotNull(message = "商品不能为空")
|
||||
private List<Item> items;
|
||||
|
||||
@Schema(description = "收件地址编号", example = "1")
|
||||
private Long addressId;
|
||||
|
||||
@Schema(description = "优惠劵编号", example = "1024")
|
||||
private Long couponId;
|
||||
|
||||
@Schema(description = "购物车项的编号数组", required = true, example = "true")
|
||||
@NotEmpty(message = "购物车项不能为空")
|
||||
private List<Long> cartIds;
|
||||
@Data
|
||||
@Schema(description = "用户 App - 商品项")
|
||||
@Valid
|
||||
public static class Item {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", example = "2048")
|
||||
private Long skuId;
|
||||
@Schema(description = "购买数量", example = "1")
|
||||
@Min(value = 1, message = "购买数量最小值为 {value}")
|
||||
private Integer count;
|
||||
|
||||
@Schema(description = "购物车项的编号", example = "1024")
|
||||
private Long cartId;
|
||||
|
||||
@AssertTrue(message = "商品不正确")
|
||||
@JsonIgnore
|
||||
public boolean isValid() {
|
||||
// 组合一:skuId + count 使用商品 SKU
|
||||
if (skuId != null && count != null) {
|
||||
return true;
|
||||
}
|
||||
// 组合二:cartId 使用购物车项
|
||||
return cartId != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -91,17 +91,17 @@ public class AppTradeOrderSettlementRespVO {
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "省份编号", required = true, example = "1")
|
||||
private Long provinceId;
|
||||
private Integer provinceId;
|
||||
@Schema(description = "省份名字", required = true, example = "北京")
|
||||
private String provinceName;
|
||||
|
||||
@Schema(description = "城市编号", required = true, example = "1")
|
||||
private Long cityId;
|
||||
private Integer cityId;
|
||||
@Schema(description = "城市名字", required = true, example = "北京")
|
||||
private String cityName;
|
||||
|
||||
@Schema(description = "地区编号", required = true, example = "1")
|
||||
private Long districtId;
|
||||
private Integer districtId;
|
||||
@Schema(description = "地区名字", required = true, example = "朝阳区")
|
||||
private String districtName;
|
||||
|
||||
|
@ -22,18 +22,18 @@ public interface DeliveryExpressTemplateConvert {
|
||||
|
||||
DeliveryExpressTemplateDO convert(DeliveryExpressTemplateUpdateReqVO bean);
|
||||
|
||||
DeliveryExpressTemplateSimpleRespVO convert(DeliveryExpressTemplateDO bean);
|
||||
DeliveryExpressTemplateRespVO convert(DeliveryExpressTemplateDO bean);
|
||||
|
||||
DeliveryExpressTemplateRespVO convert2(DeliveryExpressTemplateDO bean);
|
||||
DeliveryExpressTemplateDetailRespVO convert2(DeliveryExpressTemplateDO bean);
|
||||
|
||||
List<DeliveryExpressTemplateSimpleRespVO> convertList(List<DeliveryExpressTemplateDO> list);
|
||||
List<DeliveryExpressTemplateRespVO> convertList(List<DeliveryExpressTemplateDO> list);
|
||||
|
||||
PageResult<DeliveryExpressTemplateSimpleRespVO> convertPage(PageResult<DeliveryExpressTemplateDO> page);
|
||||
PageResult<DeliveryExpressTemplateRespVO> convertPage(PageResult<DeliveryExpressTemplateDO> page);
|
||||
|
||||
default DeliveryExpressTemplateRespVO convert(DeliveryExpressTemplateDO bean,
|
||||
List<DeliveryExpressTemplateChargeDO> chargeList,
|
||||
List<DeliveryExpressTemplateFreeDO> freeList){
|
||||
DeliveryExpressTemplateRespVO respVO = convert2(bean);
|
||||
default DeliveryExpressTemplateDetailRespVO convert(DeliveryExpressTemplateDO bean,
|
||||
List<DeliveryExpressTemplateChargeDO> chargeList,
|
||||
List<DeliveryExpressTemplateFreeDO> freeList){
|
||||
DeliveryExpressTemplateDetailRespVO respVO = convert2(bean);
|
||||
respVO.setTemplateCharge(convertTemplateChargeList(chargeList));
|
||||
respVO.setTemplateFree(convertTemplateFreeList(freeList));
|
||||
return respVO;
|
||||
|
@ -3,29 +3,28 @@ package cn.iocoder.yudao.module.trade.convert.order;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
@ -48,32 +47,32 @@ public interface TradeOrderConvert {
|
||||
@Mapping(source = "createReqVO.couponId", target = "couponId"),
|
||||
@Mapping(target = "remark", ignore = true),
|
||||
@Mapping(source = "createReqVO.remark", target = "userRemark"),
|
||||
@Mapping(source = "createReqVO.type", target = "type"),
|
||||
@Mapping(source = "calculateRespBO.price.totalPrice", target = "totalPrice"),
|
||||
@Mapping(source = "calculateRespBO.price.discountPrice", target = "discountPrice"),
|
||||
@Mapping(source = "calculateRespBO.price.deliveryPrice", target = "deliveryPrice"),
|
||||
@Mapping(source = "calculateRespBO.price.couponPrice", target = "couponPrice"),
|
||||
@Mapping(source = "calculateRespBO.price.pointPrice", target = "pointPrice"),
|
||||
@Mapping(source = "calculateRespBO.price.payPrice", target = "payPrice"),
|
||||
@Mapping(source = "address.name", target = "receiverName"),
|
||||
@Mapping(source = "address.mobile", target = "receiverMobile"),
|
||||
@Mapping(source = "address.areaId", target = "receiverAreaId"),
|
||||
@Mapping(source = "address.detailAddress", target = "receiverDetailAddress"),
|
||||
})
|
||||
TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO,
|
||||
PriceCalculateRespDTO.Order order, AddressRespDTO address);
|
||||
TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "id", ignore = true),
|
||||
@Mapping(source = "sku.spuId", target = "spuId"),
|
||||
})
|
||||
TradeOrderItemDO convert(PriceCalculateRespDTO.OrderItem orderItem, ProductSkuRespDTO sku);
|
||||
|
||||
default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrderDO,
|
||||
List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
return CollectionUtils.convertList(orderItems, orderItem -> {
|
||||
TradeOrderItemDO tradeOrderItemDO = convert(orderItem, skuMap.get(orderItem.getSkuId()));
|
||||
tradeOrderItemDO.setOrderId(tradeOrderDO.getId());
|
||||
tradeOrderItemDO.setUserId(tradeOrderDO.getUserId());
|
||||
tradeOrderItemDO.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); // 退款信息
|
||||
// tradeOrderItemDO.setCommented(false);
|
||||
return tradeOrderItemDO;
|
||||
default List<TradeOrderItemDO> convertList(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {
|
||||
return CollectionUtils.convertList(calculateRespBO.getItems(), item -> {
|
||||
TradeOrderItemDO orderItem = convert(item);
|
||||
orderItem.setOrderId(tradeOrderDO.getId());
|
||||
orderItem.setUserId(tradeOrderDO.getUserId());
|
||||
orderItem.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
orderItem.setCommentStatus(false);
|
||||
return orderItem;
|
||||
});
|
||||
}
|
||||
TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item);
|
||||
|
||||
@Mapping(source = "userId" , target = "userId")
|
||||
PriceCalculateReqDTO convert(AppTradeOrderCreateReqVO createReqVO, Long userId);
|
||||
@ -85,19 +84,20 @@ public interface TradeOrderConvert {
|
||||
ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean);
|
||||
List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);
|
||||
|
||||
default PayOrderCreateReqDTO convert(TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,
|
||||
List<ProductSpuRespDTO> spus, TradeOrderProperties tradeOrderProperties) {
|
||||
default PayOrderCreateReqDTO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
TradePriceCalculateRespBO calculateRespBO, TradeOrderProperties orderProperties) {
|
||||
PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO()
|
||||
.setAppId(tradeOrderProperties.getAppId()).setUserIp(tradeOrderDO.getUserIp());
|
||||
.setAppId(orderProperties.getAppId()).setUserIp(order.getUserIp());
|
||||
// 商户相关字段
|
||||
createReqDTO.setMerchantOrderId(String.valueOf(tradeOrderDO.getId()));
|
||||
String subject = spus.get(0).getName();
|
||||
if (spus.size() > 1) {
|
||||
createReqDTO.setMerchantOrderId(String.valueOf(order.getId()));
|
||||
String subject = calculateRespBO.getItems().get(0).getSpuName();
|
||||
if (calculateRespBO.getItems().size() > 1) {
|
||||
subject += " 等多件";
|
||||
}
|
||||
createReqDTO.setSubject(subject);
|
||||
createReqDTO.setBody(subject); // TODO 芋艿:临时写死
|
||||
// 订单相关字段
|
||||
createReqDTO.setAmount(tradeOrderDO.getPayPrice()).setExpireTime(addTime(tradeOrderProperties.getExpireTime()));
|
||||
createReqDTO.setAmount(order.getPayPrice()).setExpireTime(addTime(orderProperties.getExpireTime()));
|
||||
return createReqDTO;
|
||||
}
|
||||
|
||||
@ -111,6 +111,7 @@ public interface TradeOrderConvert {
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
// TODO 芋艿:可简化
|
||||
default PageResult<TradeOrderPageItemRespVO> convertPage(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
|
||||
Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
|
||||
@ -147,6 +148,7 @@ public interface TradeOrderConvert {
|
||||
TradeOrderPageItemRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> items);
|
||||
ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean);
|
||||
|
||||
// TODO 芋艿:可简化
|
||||
default TradeOrderDetailRespVO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails, MemberUserRespDTO user) {
|
||||
TradeOrderDetailRespVO orderVO = convert2(order, orderItems);
|
||||
@ -177,6 +179,7 @@ public interface TradeOrderConvert {
|
||||
TradeOrderDetailRespVO convert2(TradeOrderDO order, List<TradeOrderItemDO> items);
|
||||
MemberUserRespVO convert(MemberUserRespDTO bean);
|
||||
|
||||
// TODO 芋艿:可简化
|
||||
default PageResult<AppTradeOrderPageItemRespVO> convertPage02(PageResult<TradeOrderDO> pageResult, List<TradeOrderItemDO> orderItems,
|
||||
List<ProductPropertyValueDetailRespDTO> propertyValueDetails) {
|
||||
Map<Long, List<TradeOrderItemDO>> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId);
|
||||
@ -241,4 +244,45 @@ public interface TradeOrderConvert {
|
||||
|
||||
AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean);
|
||||
|
||||
default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
|
||||
List<TradeCartDO> cartList) {
|
||||
TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO();
|
||||
reqBO.setUserId(userId).setType(settlementReqVO.getType())
|
||||
.setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId())
|
||||
.setItems(new ArrayList<>(settlementReqVO.getItems().size()));
|
||||
// 商品项的构建
|
||||
Map<Long, TradeCartDO> cartMap = convertMap(cartList, TradeCartDO::getId);
|
||||
for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {
|
||||
// 情况一:skuId + count
|
||||
if (item.getSkuId() != null) {
|
||||
reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(item.getSkuId()).setCount(item.getCount())
|
||||
.setSelected(true)); // true 的原因,下单一定选中
|
||||
continue;
|
||||
}
|
||||
// 情况二:cartId
|
||||
TradeCartDO cart = cartMap.get(item.getCartId());
|
||||
if (cart == null) {
|
||||
continue;
|
||||
}
|
||||
reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(cart.getSkuId()).setCount(cart.getCount())
|
||||
.setCartId(item.getCartId()).setSelected(true)); // true 的原因,下单一定选中
|
||||
}
|
||||
return reqBO;
|
||||
}
|
||||
|
||||
default AppTradeOrderSettlementRespVO convert(TradePriceCalculateRespBO calculate, AddressRespDTO address) {
|
||||
AppTradeOrderSettlementRespVO respVO = convert0(calculate, address);
|
||||
if (address != null) {
|
||||
Area area = AreaUtils.getArea(address.getAreaId());
|
||||
respVO.getAddress().setDistrictId(area.getId());
|
||||
respVO.getAddress().setDistrictName(area.getName());
|
||||
respVO.getAddress().setCityId(area.getParent().getId());
|
||||
respVO.getAddress().setCityName(area.getParent().getName());
|
||||
respVO.getAddress().setProvinceId(area.getParent().getParent().getId());
|
||||
respVO.getAddress().setProvinceName(area.getParent().getParent().getName());
|
||||
}
|
||||
return respVO;
|
||||
}
|
||||
AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address);
|
||||
|
||||
}
|
||||
|
@ -166,12 +166,25 @@ public class TradeOrderItemDO extends BaseDO {
|
||||
* 关联 ProductPropertyDO 的 id 编号
|
||||
*/
|
||||
private Long propertyId;
|
||||
/**
|
||||
* 属性名字
|
||||
*
|
||||
* 关联 ProductPropertyDO 的 name 字段
|
||||
*/
|
||||
private String propertyName;
|
||||
|
||||
/**
|
||||
* 属性值编号
|
||||
*
|
||||
* 关联 ProductPropertyValueDO 的 id 编号
|
||||
*/
|
||||
private Long valueId;
|
||||
/**
|
||||
* 属性值名字
|
||||
*
|
||||
* 关联 ProductPropertyValueDO 的 name 字段
|
||||
*/
|
||||
private String valueName;
|
||||
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
public interface TradeCartMapper extends BaseMapperX<TradeCartDO> {
|
||||
@ -70,4 +71,10 @@ public interface TradeCartMapper extends BaseMapperX<TradeCartDO> {
|
||||
update(updateObject, new LambdaQueryWrapper<TradeCartDO>().in(TradeCartDO::getId, ids));
|
||||
}
|
||||
|
||||
default List<TradeCartDO> selectListByUserId(Long userId, Set<Long> ids) {
|
||||
return selectList(new LambdaQueryWrapper<TradeCartDO>()
|
||||
.eq(TradeCartDO::getUserId, userId)
|
||||
.in(TradeCartDO::getId, ids));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,10 +4,13 @@ import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 购物车 Service 接口
|
||||
@ -67,6 +70,15 @@ public interface TradeCartService {
|
||||
*/
|
||||
AppTradeCartListRespVO getCartList(Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户的购物车列表
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param ids 购物项的编号
|
||||
* @return 购物车列表
|
||||
*/
|
||||
List<TradeCartDO> getCartList(Long userId, Set<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得用户的购物车商品 SPU 数量的 Map
|
||||
*
|
||||
|
@ -17,10 +17,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
@ -166,6 +163,14 @@ public class TradeCartServiceImpl implements TradeCartService {
|
||||
return TradeCartConvert.INSTANCE.convertList(carts, spus, skus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TradeCartDO> getCartList(Long userId, Set<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return cartMapper.selectListByUserId(userId, ids);
|
||||
}
|
||||
|
||||
private void deleteCartIfSpuDeleted(List<TradeCartDO> carts, List<ProductSpuRespDTO> spus) {
|
||||
// 如果 SPU 被删除,则删除购物车对应的商品。延迟删除
|
||||
carts.removeIf(cart -> {
|
||||
|
@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.trade.service.delivery;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.expresstemplate.DeliveryExpressTemplateUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
|
||||
|
||||
@ -46,7 +46,7 @@ public interface DeliveryExpressTemplateService {
|
||||
* @param id 编号
|
||||
* @return 快递运费模板详情
|
||||
*/
|
||||
DeliveryExpressTemplateRespVO getDeliveryExpressTemplate(Long id);
|
||||
DeliveryExpressTemplateDetailRespVO getDeliveryExpressTemplate(Long id);
|
||||
|
||||
/**
|
||||
* 获得快递运费模板列表
|
||||
@ -70,4 +70,14 @@ public interface DeliveryExpressTemplateService {
|
||||
* @return 快递运费模板分页
|
||||
*/
|
||||
PageResult<DeliveryExpressTemplateDO> getDeliveryExpressTemplatePage(DeliveryExpressTemplatePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 校验快递运费模板
|
||||
*
|
||||
* 如果校验不通过,抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 异常
|
||||
*
|
||||
* @param templateId 模板编号
|
||||
* @return 快递运费模板
|
||||
*/
|
||||
DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId);
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeliveryExpressTemplateRespVO getDeliveryExpressTemplate(Long id) {
|
||||
public DeliveryExpressTemplateDetailRespVO getDeliveryExpressTemplate(Long id) {
|
||||
List<DeliveryExpressTemplateChargeDO> chargeList = expressTemplateChargeMapper.selectListByTemplateId(id);
|
||||
List<DeliveryExpressTemplateFreeDO> freeList = expressTemplateFreeMapper.selectListByTemplateId(id);
|
||||
DeliveryExpressTemplateDO template = expressTemplateMapper.selectById(id);
|
||||
@ -207,4 +207,13 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla
|
||||
return expressTemplateMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId) {
|
||||
DeliveryExpressTemplateDO template = expressTemplateMapper.selectById(templateId);
|
||||
if (template == null) {
|
||||
throw exception(EXPRESS_TEMPLATE_NOT_EXISTS);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.module.member.api.address.AddressApi;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
@ -18,15 +18,9 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
|
||||
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
|
||||
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
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.*;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
|
||||
import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -71,12 +71,13 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
private TradeOrderItemMapper tradeOrderItemMapper;
|
||||
|
||||
@Resource
|
||||
private PriceApi priceApi;
|
||||
private TradeCartService tradeCartService;
|
||||
@Resource
|
||||
private TradePriceService tradePriceService;
|
||||
|
||||
@Resource
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
@Resource
|
||||
private PayOrderApi payOrderApi;
|
||||
@Resource
|
||||
private AddressApi addressApi;
|
||||
@ -92,76 +93,70 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
|
||||
@Override
|
||||
public AppTradeOrderSettlementRespVO settlementOrder(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
return null;
|
||||
// 1. 获得收货地址
|
||||
AddressRespDTO address = getAddress(userId, settlementReqVO.getAddressId());
|
||||
if (address != null) {
|
||||
settlementReqVO.setAddressId(address.getId());
|
||||
}
|
||||
|
||||
// 2. 计算价格
|
||||
TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, settlementReqVO);
|
||||
|
||||
// 3. 拼接返回
|
||||
return TradeOrderConvert.INSTANCE.convert(calculateRespBO, address);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户地址
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param addressId 地址编号
|
||||
* @return 地址
|
||||
*/
|
||||
private AddressRespDTO getAddress(Long userId, Long addressId) {
|
||||
if (addressId != null) {
|
||||
return addressApi.getAddress(addressId, userId);
|
||||
}
|
||||
return addressApi.getDefaultAddress(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单价格
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param settlementReqVO 结算信息
|
||||
* @return 订单价格
|
||||
*/
|
||||
private TradePriceCalculateRespBO calculatePrice(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
// 1. 如果来自购物车,则获得购物车的商品
|
||||
List<TradeCartDO> cartList = tradeCartService.getCartList(userId,
|
||||
convertSet(settlementReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId));
|
||||
|
||||
// 2. 计算价格
|
||||
TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList);
|
||||
calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程,保证都是选中的
|
||||
"商品({}) 未设置为选中", item.getSkuId()));
|
||||
return tradePriceService.calculatePrice(calculateReqBO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) {
|
||||
// 商品 SKU 检查:可售状态、库存
|
||||
// List<ProductSkuRespDTO> skus = validateSkuSaleable(createReqVO.getItems()); // TODO 芋艿,临时关闭。
|
||||
List<ProductSkuRespDTO> skus = null;
|
||||
// 商品 SPU 检查:可售状态
|
||||
List<ProductSpuRespDTO> spus = validateSpuSaleable(convertSet(skus, ProductSkuRespDTO::getSpuId));
|
||||
// 用户收件地址的校验
|
||||
// 1. 用户收件地址的校验
|
||||
AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId());
|
||||
|
||||
// 价格计算
|
||||
PriceCalculateRespDTO priceResp = priceApi.calculatePrice(TradeOrderConvert.INSTANCE.convert(createReqVO, userId));
|
||||
// 2. 价格计算
|
||||
TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
|
||||
|
||||
// 插入 TradeOrderDO 订单
|
||||
TradeOrderDO tradeOrderDO = createTradeOrder(userId, userIp, createReqVO, priceResp.getOrder(), address);
|
||||
// 插入 TradeOrderItemDO 订单项
|
||||
List<TradeOrderItemDO> tradeOrderItems = createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus);
|
||||
// 3.1 插入 TradeOrderDO 订单
|
||||
TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO, address);
|
||||
// 3.2 插入 TradeOrderItemDO 订单项
|
||||
List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO);
|
||||
|
||||
// 订单创建完后的逻辑
|
||||
afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems, spus);
|
||||
afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO);
|
||||
// TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
|
||||
return tradeOrderDO.getId();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 校验商品 SKU 是否可出售
|
||||
// *
|
||||
// * @param items 商品 SKU
|
||||
// * @return 商品 SKU 数组
|
||||
// */
|
||||
// private List<ProductSkuRespDTO> validateSkuSaleable(List<Item> items) {
|
||||
// List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(items, Item::getSkuId));
|
||||
// // SKU 不存在
|
||||
// if (items.size() != skus.size()) {
|
||||
// throw exception(ORDER_CREATE_SKU_NOT_FOUND);
|
||||
// }
|
||||
// // 校验库存不足
|
||||
// Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
// items.forEach(item -> {
|
||||
// ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
|
||||
// if (item.getCount() > sku.getStock()) {
|
||||
// throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_STOCK_NOT_ENOUGH);
|
||||
// }
|
||||
// });
|
||||
// return skus;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 校验商品 SPU 是否可出售
|
||||
*
|
||||
* @param spuIds 商品 SPU 编号数组
|
||||
* @return 商品 SPU 数组
|
||||
*/
|
||||
private List<ProductSpuRespDTO> validateSpuSaleable(Set<Long> spuIds) {
|
||||
List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(spuIds);
|
||||
// SPU 不存在
|
||||
if (spus.size() != spuIds.size()) {
|
||||
throw exception(ORDER_CREATE_SPU_NOT_FOUND);
|
||||
}
|
||||
// 校验是否存在禁用的 SPU
|
||||
ProductSpuRespDTO spu = CollectionUtils.findFirst(spus,
|
||||
spuDTO -> ObjectUtil.notEqual(ProductSpuStatusEnum.ENABLE.getStatus(), spuDTO.getStatus()));
|
||||
if (spu != null) {
|
||||
throw exception(ErrorCodeConstants.ORDER_CREATE_SPU_NOT_SALE);
|
||||
}
|
||||
return spus;
|
||||
return order.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,33 +168,35 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
*/
|
||||
private AddressRespDTO validateAddress(Long userId, Long addressId) {
|
||||
AddressRespDTO address = addressApi.getAddress(addressId, userId);
|
||||
if (Objects.isNull(address)) {
|
||||
if (address == null) {
|
||||
throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,
|
||||
PriceCalculateRespDTO.Order order, AddressRespDTO address) {
|
||||
TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, order, address);
|
||||
tradeOrderDO.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;
|
||||
tradeOrderDO.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
tradeOrderDO.setType(TradeOrderTypeEnum.NORMAL.getType());
|
||||
tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
|
||||
tradeOrderDO.setProductCount(getSumValue(order.getItems(), PriceCalculateRespDTO.OrderItem::getCount, Integer::sum));
|
||||
tradeOrderDO.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
|
||||
tradeOrderDO.setAdjustPrice(0).setPayed(false); // 支付信息
|
||||
tradeOrderDO.setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus()); // 物流信息
|
||||
tradeOrderDO.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); // 退款信息
|
||||
tradeOrderMapper.insert(tradeOrderDO);
|
||||
return tradeOrderDO;
|
||||
TradePriceCalculateRespBO calculateRespBO, AddressRespDTO address) {
|
||||
TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
|
||||
order.setNo(IdUtil.getSnowflakeNextId() + ""); // TODO @LeeYan9: 思考下, 怎么生成好点哈; 这个是会展示给用户的;
|
||||
order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
order.setType(TradeOrderTypeEnum.NORMAL.getType());
|
||||
order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
|
||||
order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
|
||||
order.setTerminal(TerminalEnum.H5.getTerminal()); // todo 数据来源?
|
||||
// 支付信息
|
||||
order.setAdjustPrice(0).setPayed(false);
|
||||
// 物流信息 TODO 芋艿:暂时写死物流方式;应该是前端选择的
|
||||
order.setDeliveryType(DeliveryTypeEnum.EXPRESS.getMode()).setDeliveryStatus(TradeOrderDeliveryStatusEnum.UNDELIVERED.getStatus());
|
||||
// 退款信息
|
||||
order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
|
||||
tradeOrderMapper.insert(order);
|
||||
return order;
|
||||
}
|
||||
|
||||
private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO,
|
||||
List<PriceCalculateRespDTO.OrderItem> orderItems, List<ProductSkuRespDTO> skus) {
|
||||
List<TradeOrderItemDO> tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus);
|
||||
tradeOrderItemMapper.insertBatch(tradeOrderItemDOs);
|
||||
return tradeOrderItemDOs;
|
||||
private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {
|
||||
List<TradeOrderItemDO> orderItems = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);
|
||||
tradeOrderItemMapper.insertBatch(orderItems);
|
||||
return orderItems;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,12 +207,13 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
* @param userId 用户编号
|
||||
* @param createReqVO 创建订单请求
|
||||
* @param tradeOrderDO 交易订单
|
||||
* @param calculateRespBO 订单价格计算结果
|
||||
*/
|
||||
private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
|
||||
TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,
|
||||
List<ProductSpuRespDTO> spus) {
|
||||
TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems,
|
||||
TradePriceCalculateRespBO calculateRespBO) {
|
||||
// 下单时扣减商品库存
|
||||
productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(tradeOrderItemDOs)));
|
||||
productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(orderItems)));
|
||||
|
||||
// 删除购物车商品 TODO 芋艿:待实现
|
||||
|
||||
@ -228,20 +226,19 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
}
|
||||
|
||||
// 生成预支付
|
||||
createPayOrder(tradeOrderDO, tradeOrderItemDOs, spus);
|
||||
createPayOrder(tradeOrderDO, orderItems, calculateRespBO);
|
||||
|
||||
// 增加订单日志 TODO 芋艿:待实现
|
||||
}
|
||||
|
||||
private void createPayOrder(TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> tradeOrderItemDOs,
|
||||
List<ProductSpuRespDTO> spus) {
|
||||
private void createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems, TradePriceCalculateRespBO calculateRespBO) {
|
||||
// 创建支付单,用于后续的支付
|
||||
PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
|
||||
tradeOrderDO, tradeOrderItemDOs, spus, tradeOrderProperties);
|
||||
order, orderItems, calculateRespBO, tradeOrderProperties);
|
||||
Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO);
|
||||
|
||||
// 更新到交易单上
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId));
|
||||
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.price;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 价格计算 Service 接口
|
||||
*
|
||||
@ -16,6 +18,6 @@ public interface TradePriceService {
|
||||
* @param calculateReqDTO 计算信息
|
||||
* @return 计算结果
|
||||
*/
|
||||
TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqDTO);
|
||||
TradePriceCalculateRespBO calculatePrice(@Valid TradePriceCalculateReqBO calculateReqDTO);
|
||||
|
||||
}
|
||||
|
@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.trade.service.price;
|
||||
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator;
|
||||
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
@ -15,8 +19,8 @@ import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL;
|
||||
|
||||
/**
|
||||
@ -25,22 +29,28 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_C
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class TradePriceServiceImpl implements TradePriceService {
|
||||
|
||||
@Resource
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
|
||||
@Resource
|
||||
private List<TradePriceCalculator> priceCalculators;
|
||||
|
||||
@Override
|
||||
public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) {
|
||||
// 1. 获得商品 SKU 数组
|
||||
List<ProductSkuRespDTO> skuList = checkSkus(calculateReqBO);
|
||||
// 1.1 获得商品 SKU 数组
|
||||
List<ProductSkuRespDTO> skuList = checkSkuList(calculateReqBO);
|
||||
// 1.2 获得商品 SPU 数组
|
||||
List<ProductSpuRespDTO> spuList = checkSpuList(skuList);
|
||||
|
||||
// 2.1 计算价格
|
||||
TradePriceCalculateRespBO calculateRespBO = TradePriceCalculatorHelper
|
||||
.buildCalculateResp(calculateReqBO, skuList);
|
||||
.buildCalculateResp(calculateReqBO, spuList, skuList);
|
||||
priceCalculators.forEach(calculator -> calculator.calculate(calculateReqBO, calculateRespBO));
|
||||
// 2.2 如果最终支付金额小于等于 0,则抛出业务异常
|
||||
if (calculateRespBO.getPrice().getPayPrice() <= 0) {
|
||||
@ -51,7 +61,7 @@ public class TradePriceServiceImpl implements TradePriceService {
|
||||
return calculateRespBO;
|
||||
}
|
||||
|
||||
private List<ProductSkuRespDTO> checkSkus(TradePriceCalculateReqBO reqBO) {
|
||||
private List<ProductSkuRespDTO> checkSkuList(TradePriceCalculateReqBO reqBO) {
|
||||
// 获得商品 SKU 数组
|
||||
Map<Long, Integer> skuIdCountMap = convertMap(reqBO.getItems(),
|
||||
TradePriceCalculateReqBO.Item::getSkuId, TradePriceCalculateReqBO.Item::getCount);
|
||||
@ -70,4 +80,20 @@ public class TradePriceServiceImpl implements TradePriceService {
|
||||
return skus;
|
||||
}
|
||||
|
||||
private List<ProductSpuRespDTO> checkSpuList(List<ProductSkuRespDTO> skuList) {
|
||||
// 获得商品 SPU 数组
|
||||
List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(convertSet(skuList, ProductSkuRespDTO::getSpuId));
|
||||
|
||||
// 校验商品 SPU
|
||||
spus.forEach(spu -> {
|
||||
if (spu == null) {
|
||||
throw exception(SPU_NOT_EXISTS);
|
||||
}
|
||||
if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {
|
||||
throw exception(SPU_NOT_ENABLE);
|
||||
}
|
||||
});
|
||||
return spus;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.bo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
@ -21,7 +23,7 @@ public class TradePriceCalculateReqBO {
|
||||
*
|
||||
* 枚举 {@link TradeOrderTypeEnum}
|
||||
*/
|
||||
private Integer orderType;
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
@ -44,6 +46,21 @@ public class TradePriceCalculateReqBO {
|
||||
*/
|
||||
private Long addressId;
|
||||
|
||||
/**
|
||||
* 配送方式
|
||||
*
|
||||
* 枚举 {@link DeliveryTypeEnum}
|
||||
*/
|
||||
private Integer deliveryType;
|
||||
|
||||
/**
|
||||
* 配送模板编号
|
||||
*
|
||||
* 关联 {@link DeliveryExpressTemplateDO#getId()}
|
||||
*/
|
||||
// TODO @jason:运费模版,是不是每个 SKU 传入哈
|
||||
private Long templateId;
|
||||
|
||||
/**
|
||||
* 商品 SKU 数组
|
||||
*/
|
||||
@ -82,5 +99,4 @@ public class TradePriceCalculateReqBO {
|
||||
private Boolean selected;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.bo;
|
||||
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import lombok.Data;
|
||||
@ -23,7 +24,7 @@ public class TradePriceCalculateRespBO {
|
||||
*
|
||||
* 枚举 {@link TradeOrderTypeEnum}
|
||||
*/
|
||||
private Integer orderType;
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 订单价格
|
||||
@ -163,7 +164,26 @@ public class TradePriceCalculateRespBO {
|
||||
*/
|
||||
private Integer payPrice;
|
||||
|
||||
// TODO 芋艿:这里补充下基本信息,简单一点。
|
||||
// ========== 商品信息 ==========
|
||||
/**
|
||||
* 商品名
|
||||
*/
|
||||
private String spuName;
|
||||
/**
|
||||
* 商品图片
|
||||
*
|
||||
* 优先级:SKU.picUrl > SPU.picUrl
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 分类编号
|
||||
*/
|
||||
private Long categoryId;
|
||||
|
||||
/**
|
||||
* 商品属性数组
|
||||
*/
|
||||
private List<ProductPropertyValueDetailRespDTO> properties;
|
||||
|
||||
}
|
||||
|
||||
@ -189,12 +209,6 @@ public class TradePriceCalculateRespBO {
|
||||
* 枚举 {@link PromotionTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 营销级别
|
||||
*
|
||||
* 枚举 @link PromotionLevelEnum} TODO PromotionLevelEnum 没有这个枚举类
|
||||
*/
|
||||
private Integer level;
|
||||
/**
|
||||
* 计算时的原价(总),单位:分
|
||||
*/
|
||||
|
@ -101,7 +101,8 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
|
||||
CouponRespDTO coupon) {
|
||||
Predicate<TradePriceCalculateRespBO.OrderItem> matchPredicate = TradePriceCalculateRespBO.OrderItem::getSelected;
|
||||
if (PromotionProductScopeEnum.SPU.getScope().equals(coupon.getProductScope())) {
|
||||
matchPredicate = orderItem -> coupon.getProductSpuIds().contains(orderItem.getSpuId());
|
||||
matchPredicate = matchPredicate // 额外加如下条件
|
||||
.and(orderItem -> coupon.getProductSpuIds().contains(orderItem.getSpuId()));
|
||||
}
|
||||
return filterList(result.getItems(), matchPredicate);
|
||||
}
|
||||
|
@ -0,0 +1,234 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.iocoder.yudao.module.member.api.address.AddressApi;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateChargeMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryExpressTemplateFreeMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO.OrderItem;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
|
||||
/**
|
||||
* 运费的 {@link TradePriceCalculator} 实现类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Component
|
||||
@Order(TradePriceCalculator.ORDER_DELIVERY)
|
||||
public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
|
||||
|
||||
@Resource
|
||||
private AddressApi addressApi;
|
||||
@Resource
|
||||
private ProductSkuApi productSkuApi;
|
||||
|
||||
@Resource
|
||||
private DeliveryExpressTemplateService deliveryExpressTemplateService;
|
||||
// TODO @jason:走 Service 哈。Mapper 只允许自己的 Service 调用,保护好数据结构;
|
||||
@Resource
|
||||
private DeliveryExpressTemplateChargeMapper templateChargeMapper;
|
||||
@Resource
|
||||
private DeliveryExpressTemplateFreeMapper templateFreeMapper;
|
||||
|
||||
@Override
|
||||
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
|
||||
// 1.1 判断配送方式
|
||||
if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (param.getTemplateId() == null || param.getAddressId() == null) {
|
||||
return;
|
||||
}
|
||||
// 1.2 校验运费模板是否存在
|
||||
DeliveryExpressTemplateDO template = deliveryExpressTemplateService.validateDeliveryExpressTemplate(param.getTemplateId());
|
||||
|
||||
// 得到包邮配置
|
||||
List<DeliveryExpressTemplateFreeDO> expressTemplateFreeList = templateFreeMapper.selectListByTemplateId(template.getId());
|
||||
Map<Integer, DeliveryExpressTemplateFreeDO> areaTemplateFreeMap = new HashMap<>();
|
||||
expressTemplateFreeList.forEach(item -> {
|
||||
for (Integer areaId : item.getAreaIds()) {
|
||||
// TODO 需要保证 areaId 不能重复
|
||||
if (!areaTemplateFreeMap.containsKey(areaId)) {
|
||||
areaTemplateFreeMap.put(areaId, item);
|
||||
}
|
||||
}
|
||||
});
|
||||
// 得到快递运费配置
|
||||
List<DeliveryExpressTemplateChargeDO> expressTemplateChargeList = templateChargeMapper.selectListByTemplateId(template.getId());
|
||||
Map<Integer, DeliveryExpressTemplateChargeDO> areaTemplateChargeMap = new HashMap<>();
|
||||
expressTemplateChargeList.forEach(item -> {
|
||||
for (Integer areaId : item.getAreaIds()) {
|
||||
// areaId 不能重复
|
||||
if (!areaTemplateChargeMap.containsKey(areaId)) {
|
||||
areaTemplateChargeMap.put(areaId, item);
|
||||
}
|
||||
}
|
||||
});
|
||||
// 得到收件地址区域
|
||||
AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
|
||||
// 1.3 计算快递费用
|
||||
calculateDeliveryPrice(address.getAreaId(), template.getChargeMode(),
|
||||
areaTemplateFreeMap, areaTemplateChargeMap, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验订单是否满足包邮条件
|
||||
*
|
||||
* @param receiverAreaId 收件人地区的区域编号
|
||||
* @param chargeMode 配送计费方式
|
||||
* @param areaTemplateFreeMap 运费模板包邮区域设置 Map
|
||||
* @param areaTemplateChargeMap 运费模板快递费用设置 Map
|
||||
*/
|
||||
private void calculateDeliveryPrice(Integer receiverAreaId,
|
||||
Integer chargeMode,
|
||||
Map<Integer, DeliveryExpressTemplateFreeDO> areaTemplateFreeMap,
|
||||
Map<Integer, DeliveryExpressTemplateChargeDO> areaTemplateChargeMap,
|
||||
TradePriceCalculateRespBO result) {
|
||||
// 过滤出已选中的商品SKU
|
||||
List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
|
||||
Set<Long> skuIds = convertSet(selectedItem, OrderItem::getSkuId);
|
||||
// 得到SKU 详情。得到 重量体积
|
||||
Map<Long, ProductSkuRespDTO> skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId);
|
||||
// 一个 spuId 可能对应多条订单商品 SKU
|
||||
// TODO @jason:得确认下,按照 sku 算,还是 spu 算;
|
||||
Map<Long, List<OrderItem>> spuIdItemMap = convertMultiMap(selectedItem, OrderItem::getSpuId);
|
||||
// 依次计算每个 SPU 的快递运费
|
||||
for (Map.Entry<Long, List<OrderItem>> entry : spuIdItemMap.entrySet()) {
|
||||
List<OrderItem> orderItems = entry.getValue();
|
||||
// 总件数, 总金额, 总重量, 总体积
|
||||
int totalCount = 0;
|
||||
int totalPrice = 0;
|
||||
double totalWeight = 0;
|
||||
double totalVolume = 0;
|
||||
for (OrderItem orderItem : orderItems) {
|
||||
totalCount += orderItem.getCount();
|
||||
totalPrice += orderItem.getPrice(); // TODO jason:应该按照 payPrice?
|
||||
ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId());
|
||||
if (skuResp != null) {
|
||||
totalWeight = totalWeight + skuResp.getWeight(); // TODO @jason:* 数量
|
||||
totalVolume = totalVolume + skuResp.getVolume();
|
||||
}
|
||||
}
|
||||
// 优先判断是否包邮. 如果包邮不计算快递运费
|
||||
if (areaTemplateFreeMap.containsKey(receiverAreaId) &&
|
||||
checkExpressFree(chargeMode, totalCount, totalWeight,
|
||||
totalVolume, totalPrice, areaTemplateFreeMap.get(receiverAreaId))) {
|
||||
continue;
|
||||
}
|
||||
// 计算快递运费
|
||||
// TODO @jason:貌似也可以抽成 checkExpressFree 类似方法
|
||||
if (areaTemplateChargeMap.containsKey(receiverAreaId)) {
|
||||
DeliveryExpressTemplateChargeDO templateCharge = areaTemplateChargeMap.get(receiverAreaId);
|
||||
DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
|
||||
switch (chargeModeEnum) {
|
||||
case PIECE: {
|
||||
calculateExpressFeeBySpu(totalCount, templateCharge, orderItems);
|
||||
break;
|
||||
}
|
||||
case WEIGHT: {
|
||||
calculateExpressFeeBySpu(totalWeight, templateCharge, orderItems);
|
||||
break;
|
||||
}
|
||||
case VOLUME: {
|
||||
calculateExpressFeeBySpu(totalVolume, templateCharge, orderItems);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 spu 来计算快递费用
|
||||
*
|
||||
* @param total 总件数/总重量/总体积
|
||||
* @param templateCharge 快递运费配置
|
||||
* @param orderItems SKU 商品项目
|
||||
*/
|
||||
private void calculateExpressFeeBySpu(double total, DeliveryExpressTemplateChargeDO templateCharge, List<OrderItem> orderItems) {
|
||||
int deliveryPrice;
|
||||
if (total <= templateCharge.getStartCount()) {
|
||||
deliveryPrice = templateCharge.getStartPrice();
|
||||
} else {
|
||||
double remainWeight = total - templateCharge.getStartCount();
|
||||
// 剩余重量/ 续件 = 续件的次数. 向上取整
|
||||
int extraNum = (int) Math.ceil(remainWeight / templateCharge.getExtraCount());
|
||||
int extraPrice = templateCharge.getExtraPrice() * extraNum;
|
||||
deliveryPrice = templateCharge.getStartPrice() + extraPrice;
|
||||
}
|
||||
// TODO @芋艿 分摊快递费用到 SKU. 是不是搞复杂了;
|
||||
// TODO @jason:因为退费的时候,可能按照 SKU 考虑退费金额
|
||||
divideDeliveryPrice(deliveryPrice, orderItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快递运费分摊到每个 SKU 商品上
|
||||
*
|
||||
* @param deliveryPrice 快递运费
|
||||
* @param orderItems SKU 商品
|
||||
*/
|
||||
private void divideDeliveryPrice(int deliveryPrice, List<OrderItem> orderItems) {
|
||||
int dividePrice = deliveryPrice / orderItems.size();
|
||||
for (OrderItem item : orderItems) {
|
||||
// 更新快递运费
|
||||
item.setDeliveryPrice(dividePrice);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否包邮
|
||||
*
|
||||
* @param chargeMode 配送计费方式
|
||||
* @param totalCount 总件数
|
||||
* @param totalWeight 总重量
|
||||
* @param totalVolume 总体积
|
||||
* @param totalPrice 总金额
|
||||
* @param templateFree 包邮配置
|
||||
*/
|
||||
private boolean checkExpressFree(Integer chargeMode, int totalCount, double totalWeight,
|
||||
double totalVolume, int totalPrice, DeliveryExpressTemplateFreeDO templateFree) {
|
||||
DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
|
||||
switch (chargeModeEnum) {
|
||||
case PIECE:
|
||||
// 两个条件都满足才包邮
|
||||
if (totalCount >= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case WEIGHT:
|
||||
// freeCount 是不是应该是 double ??
|
||||
if (totalWeight >= templateFree.getFreeCount()
|
||||
&& totalPrice >= templateFree.getFreePrice()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VOLUME:
|
||||
if (totalVolume >= templateFree.getFreeCount()
|
||||
&& totalPrice >= templateFree.getFreePrice()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -53,10 +53,13 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
|
||||
Integer newDiscountPrice = orderItem.getPayPrice() - newPayPrice;
|
||||
|
||||
// 3.1 记录优惠明细
|
||||
TradePriceCalculatorHelper.addPromotion(result, orderItem,
|
||||
discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
|
||||
StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
|
||||
newDiscountPrice);
|
||||
if (orderItem.getSelected()) {
|
||||
// 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
|
||||
TradePriceCalculatorHelper.addPromotion(result, orderItem,
|
||||
discountProduct.getActivityId(), discountProduct.getActivityName(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
|
||||
StrUtil.format("限时折扣:省 {} 元", formatPrice(newDiscountPrice)),
|
||||
newDiscountPrice);
|
||||
}
|
||||
// 3.2 更新 SKU 优惠金额
|
||||
orderItem.setDiscountPrice(orderItem.getDiscountPrice() + newDiscountPrice);
|
||||
TradePriceCalculatorHelper.recountPayPrice(orderItem);
|
||||
|
@ -13,6 +13,12 @@ public interface TradePriceCalculator {
|
||||
int ORDER_DISCOUNT_ACTIVITY = 10;
|
||||
int ORDER_REWARD_ACTIVITY = 20;
|
||||
int ORDER_COUPON = 30;
|
||||
/**
|
||||
* 快递运费的计算
|
||||
*
|
||||
* 放在各种营销活动、优惠劵后面 TODO
|
||||
*/
|
||||
int ORDER_DELIVERY = 40;
|
||||
|
||||
void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result);
|
||||
|
||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
|
||||
@ -24,25 +25,42 @@ import static java.util.Collections.singletonList;
|
||||
public class TradePriceCalculatorHelper {
|
||||
|
||||
public static TradePriceCalculateRespBO buildCalculateResp(TradePriceCalculateReqBO param,
|
||||
List<ProductSkuRespDTO> skuList) {
|
||||
List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) {
|
||||
// 创建 PriceCalculateRespDTO 对象
|
||||
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO();
|
||||
result.setOrderType(param.getOrderType());
|
||||
result.setType(param.getType());
|
||||
result.setPromotions(new ArrayList<>());
|
||||
|
||||
// 创建它的 OrderItem 属性
|
||||
Map<Long, TradePriceCalculateReqBO.Item> skuItemMap = convertMap(param.getItems(),
|
||||
TradePriceCalculateReqBO.Item::getSkuId);
|
||||
result.setItems(new ArrayList<>(skuItemMap.size()));
|
||||
skuList.forEach(sku -> {
|
||||
TradePriceCalculateReqBO.Item skuItem = skuItemMap.get(sku.getId());
|
||||
TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem()
|
||||
// SKU 字段
|
||||
.setSpuId(sku.getSpuId()).setSkuId(sku.getId())
|
||||
.setCount(skuItem.getCount()).setCartId(skuItem.getCartId()).setSelected(skuItem.getSelected())
|
||||
// 价格字段
|
||||
.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * skuItem.getCount())
|
||||
.setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0);
|
||||
result.setItems(new ArrayList<>(param.getItems().size()));
|
||||
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skuList, ProductSkuRespDTO::getId);
|
||||
param.getItems().forEach(item -> {
|
||||
ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
|
||||
if (sku == null) {
|
||||
return;
|
||||
}
|
||||
ProductSpuRespDTO spu = spuMap.get(sku.getSpuId());
|
||||
if (spu == null) {
|
||||
return;
|
||||
}
|
||||
// 商品项
|
||||
TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem();
|
||||
result.getItems().add(orderItem);
|
||||
orderItem.setSpuId(sku.getSpuId()).setSkuId(sku.getId())
|
||||
.setCount(item.getCount()).setCartId(item.getCartId()).setSelected(item.getSelected());
|
||||
// sku 价格
|
||||
orderItem.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * item.getCount())
|
||||
.setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0);
|
||||
// sku 信息
|
||||
orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties());
|
||||
// spu 信息
|
||||
orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId());
|
||||
if (orderItem.getPicUrl() == null) {
|
||||
orderItem.setPicUrl(spu.getPicUrl());
|
||||
}
|
||||
});
|
||||
|
||||
// 创建它的 Price 属性
|
||||
result.setPrice(new TradePriceCalculateRespBO.Price());
|
||||
recountAllPrice(result);
|
||||
@ -86,6 +104,31 @@ public class TradePriceCalculatorHelper {
|
||||
- orderItem.getPointPrice());
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算每个订单项的支付金额
|
||||
*
|
||||
* 【目前主要是单测使用】
|
||||
*
|
||||
* @param orderItems 订单项数组
|
||||
*/
|
||||
public static void recountPayPrice(List<TradePriceCalculateRespBO.OrderItem> orderItems) {
|
||||
orderItems.forEach(orderItem -> {
|
||||
if (orderItem.getDiscountPrice() == null) {
|
||||
orderItem.setDiscountPrice(0);
|
||||
}
|
||||
if (orderItem.getDeliveryPrice() == null) {
|
||||
orderItem.setDeliveryPrice(0);
|
||||
}
|
||||
if (orderItem.getCouponPrice() == null) {
|
||||
orderItem.setCouponPrice(0);
|
||||
}
|
||||
if (orderItem.getPointPrice() == null) {
|
||||
orderItem.setPointPrice(0);
|
||||
}
|
||||
recountPayPrice(orderItem);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算已选中的订单项,总支付金额
|
||||
*
|
||||
@ -94,7 +137,7 @@ public class TradePriceCalculatorHelper {
|
||||
*/
|
||||
public static Integer calculateTotalPayPrice(List<TradePriceCalculateRespBO.OrderItem> orderItems) {
|
||||
return getSumValue(orderItems,
|
||||
orderItem -> orderItem.getSelected() ? orderItem.getPayPrice() : 0, // 未选中的情况下,不计算支付金额
|
||||
orderItem -> orderItem.getSelected() ? orderItem.getPayPrice() : 0, // 未选中的情况下,不计算支付金额
|
||||
Integer::sum);
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,9 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
|
||||
// 1.2 获得最大匹配的满减送活动的规则
|
||||
RewardActivityMatchRespDTO.Rule rule = getMaxMatchRewardActivityRule(rewardActivity, orderItems);
|
||||
if (rule == null) {
|
||||
TradePriceCalculatorHelper.addNotMatchPromotion(result, orderItems,
|
||||
rewardActivity.getId(), rewardActivity.getName(), PromotionTypeEnum.REWARD_ACTIVITY.getType(),
|
||||
getRewardActivityNotMeetTip(rewardActivity));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -69,8 +69,7 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
|
||||
.setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png"));
|
||||
// mock 方法(交易订单项)
|
||||
TradeOrderItemDO orderItem = randomPojo(TradeOrderItemDO.class, o -> {
|
||||
//o.setOrderId(111L).setUserId(userId).setOrderDividePrice(200); TODO DO 中没有 orderDividePrice 属性
|
||||
o.setOrderId(111L).setUserId(userId);
|
||||
o.setOrderId(111L).setUserId(userId).setPayPrice(200);
|
||||
o.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
});
|
||||
when(tradeOrderService.getOrderItem(eq(1024L), eq(1L)))
|
||||
|
@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
|
||||
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
|
||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
@ -101,10 +102,10 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
|
||||
// mock 方法(商品 SKU 检查)
|
||||
ProductSkuRespDTO sku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(1L).setSpuId(11L)
|
||||
.setPrice(50).setStock(100)
|
||||
.setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(111L).setValueId(222L))));
|
||||
.setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(111L).setValueId(222L))));
|
||||
ProductSkuRespDTO sku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(2L).setSpuId(21L)
|
||||
.setPrice(20).setStock(50))
|
||||
.setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(333L).setValueId(444L)));
|
||||
.setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(333L).setValueId(444L)));
|
||||
when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02));
|
||||
// mock 方法(商品 SPU 检查)
|
||||
ProductSpuRespDTO spu01 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(11L)
|
||||
@ -114,7 +115,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
|
||||
when(productSpuApi.getSpuList(eq(asSet(11L, 21L)))).thenReturn(Arrays.asList(spu01, spu02));
|
||||
// mock 方法(用户收件地址的校验)
|
||||
AddressRespDTO addressRespDTO = new AddressRespDTO().setId(10L).setUserId(userId).setName("芋艿")
|
||||
.setMobile("15601691300").setAreaId(3306L).setPostCode("85757").setDetailAddress("土豆村");
|
||||
.setMobile("15601691300").setAreaId(3306).setPostCode("85757").setDetailAddress("土豆村");
|
||||
when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(addressRespDTO);
|
||||
// mock 方法(价格计算)
|
||||
PriceCalculateRespDTO.OrderItem priceOrderItem01 = new PriceCalculateRespDTO.OrderItem()
|
||||
|
@ -0,0 +1,135 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link TradePriceServiceImpl} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class TradePriceServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private TradePriceServiceImpl tradePriceService;
|
||||
|
||||
@Mock
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Mock
|
||||
private ProductSpuApi productSpuApi;
|
||||
@Mock
|
||||
private List<TradePriceCalculator> priceCalculators;
|
||||
|
||||
@Test
|
||||
public void testCalculatePrice() {
|
||||
// 准备参数
|
||||
TradePriceCalculateReqBO calculateReqBO = new TradePriceCalculateReqBO()
|
||||
.setType(TradeOrderTypeEnum.NORMAL.getType()).setUserId(10L)
|
||||
.setCouponId(20L).setAddressId(30L)
|
||||
.setItems(Arrays.asList(
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(100L).setCount(1).setSelected(true),
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(200L).setCount(3).setSelected(true),
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(300L).setCount(6).setCartId(233L).setSelected(false)
|
||||
));
|
||||
// mock 方法
|
||||
List<ProductSkuRespDTO> skuList = Arrays.asList(
|
||||
new ProductSkuRespDTO().setId(100L).setStock(500).setPrice(1000).setPicUrl("https://t.cn/1.png").setSpuId(1001L)
|
||||
.setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(1L).setPropertyName("颜色")
|
||||
.setValueId(2L).setValueName("红色"))),
|
||||
new ProductSkuRespDTO().setId(200L).setStock(400).setPrice(2000).setPicUrl("https://t.cn/2.png").setSpuId(1001L)
|
||||
.setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(1L).setPropertyName("颜色")
|
||||
.setValueId(3L).setValueName("黄色"))),
|
||||
new ProductSkuRespDTO().setId(300L).setStock(600).setPrice(3000).setPicUrl("https://t.cn/3.png").setSpuId(1001L)
|
||||
.setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(1L).setPropertyName("颜色")
|
||||
.setValueId(4L).setValueName("黑色")))
|
||||
);
|
||||
when(productSkuApi.getSkuList(Mockito.eq(asSet(100L, 200L, 300L)))).thenReturn(skuList);
|
||||
when(productSpuApi.getSpuList(Mockito.eq(asSet(1001L))))
|
||||
.thenReturn(singletonList(new ProductSpuRespDTO().setId(1001L).setName("小菜").setCategoryId(666L)
|
||||
.setStatus(ProductSpuStatusEnum.ENABLE.getStatus())));
|
||||
|
||||
// 调用
|
||||
TradePriceCalculateRespBO calculateRespBO = tradePriceService.calculatePrice(calculateReqBO);
|
||||
// 断言
|
||||
assertEquals(TradeOrderTypeEnum.NORMAL.getType(), calculateRespBO.getType());
|
||||
assertEquals(0, calculateRespBO.getPromotions().size());
|
||||
assertNull(calculateRespBO.getCouponId());
|
||||
// 断言:订单价格
|
||||
assertEquals(7000, calculateRespBO.getPrice().getTotalPrice());
|
||||
assertEquals(0, calculateRespBO.getPrice().getDiscountPrice());
|
||||
assertEquals(0, calculateRespBO.getPrice().getDeliveryPrice());
|
||||
assertEquals(0, calculateRespBO.getPrice().getCouponPrice());
|
||||
assertEquals(0, calculateRespBO.getPrice().getPointPrice());
|
||||
assertEquals(7000, calculateRespBO.getPrice().getPayPrice());
|
||||
// 断言:SKU 1
|
||||
assertEquals(1001L, calculateRespBO.getItems().get(0).getSpuId());
|
||||
assertEquals(100L, calculateRespBO.getItems().get(0).getSkuId());
|
||||
assertEquals(1, calculateRespBO.getItems().get(0).getCount());
|
||||
assertNull(calculateRespBO.getItems().get(0).getCartId());
|
||||
assertTrue(calculateRespBO.getItems().get(0).getSelected());
|
||||
assertEquals(1000, calculateRespBO.getItems().get(0).getPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(0).getDiscountPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(0).getDeliveryPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(0).getCouponPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(0).getPointPrice());
|
||||
assertEquals(1000, calculateRespBO.getItems().get(0).getPayPrice());
|
||||
assertEquals("小菜", calculateRespBO.getItems().get(0).getSpuName());
|
||||
assertEquals("https://t.cn/1.png", calculateRespBO.getItems().get(0).getPicUrl());
|
||||
assertEquals(666L, calculateRespBO.getItems().get(0).getCategoryId());
|
||||
assertEquals(skuList.get(0).getProperties(), calculateRespBO.getItems().get(0).getProperties());
|
||||
// 断言:SKU 2
|
||||
assertEquals(1001L, calculateRespBO.getItems().get(1).getSpuId());
|
||||
assertEquals(200L, calculateRespBO.getItems().get(1).getSkuId());
|
||||
assertEquals(3, calculateRespBO.getItems().get(1).getCount());
|
||||
assertNull(calculateRespBO.getItems().get(1).getCartId());
|
||||
assertTrue(calculateRespBO.getItems().get(1).getSelected());
|
||||
assertEquals(2000, calculateRespBO.getItems().get(1).getPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(1).getDiscountPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(1).getDeliveryPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(1).getCouponPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(1).getPointPrice());
|
||||
assertEquals(6000, calculateRespBO.getItems().get(1).getPayPrice());
|
||||
assertEquals("小菜", calculateRespBO.getItems().get(1).getSpuName());
|
||||
assertEquals("https://t.cn/2.png", calculateRespBO.getItems().get(1).getPicUrl());
|
||||
assertEquals(666L, calculateRespBO.getItems().get(1).getCategoryId());
|
||||
assertEquals(skuList.get(1).getProperties(), calculateRespBO.getItems().get(1).getProperties());
|
||||
// 断言:SKU 3
|
||||
assertEquals(1001L, calculateRespBO.getItems().get(2).getSpuId());
|
||||
assertEquals(300L, calculateRespBO.getItems().get(2).getSkuId());
|
||||
assertEquals(6, calculateRespBO.getItems().get(2).getCount());
|
||||
assertEquals(233L, calculateRespBO.getItems().get(2).getCartId());
|
||||
assertFalse(calculateRespBO.getItems().get(2).getSelected());
|
||||
assertEquals(3000, calculateRespBO.getItems().get(2).getPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(2).getDiscountPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(2).getDeliveryPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(2).getCouponPrice());
|
||||
assertEquals(0, calculateRespBO.getItems().get(2).getPointPrice());
|
||||
assertEquals(18000, calculateRespBO.getItems().get(2).getPayPrice());
|
||||
assertEquals("小菜", calculateRespBO.getItems().get(2).getSpuName());
|
||||
assertEquals("https://t.cn/3.png", calculateRespBO.getItems().get(2).getPicUrl());
|
||||
assertEquals(666L, calculateRespBO.getItems().get(2).getCategoryId());
|
||||
assertEquals(skuList.get(2).getProperties(), calculateRespBO.getItems().get(2).getProperties());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link TradeCouponPriceCalculator} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private TradeCouponPriceCalculator tradeCouponPriceCalculator;
|
||||
|
||||
@Mock
|
||||
private CouponApi couponApi;
|
||||
|
||||
@Test
|
||||
public void testCalculate() {
|
||||
// 准备参数
|
||||
TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
|
||||
.setUserId(233L).setCouponId(1024L)
|
||||
.setItems(asList(
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), // 匹配优惠劵
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(true), // 匹配优惠劵
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true), // 不匹配优惠劵
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(40L).setCount(5).setSelected(false) // 匹配优惠劵,但是未选中
|
||||
));
|
||||
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
|
||||
.setPrice(new TradePriceCalculateRespBO.Price())
|
||||
.setPromotions(new ArrayList<>())
|
||||
.setItems(asList(
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
|
||||
.setPrice(100).setSpuId(1L),
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(true)
|
||||
.setPrice(50).setSpuId(2L),
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(30L).setCount(4).setSelected(true)
|
||||
.setPrice(30).setSpuId(3L),
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(40L).setCount(5).setSelected(false)
|
||||
.setPrice(60).setSpuId(1L)
|
||||
));
|
||||
// 保证价格被初始化上
|
||||
TradePriceCalculatorHelper.recountPayPrice(result.getItems());
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
|
||||
// mock 方法(优惠劵 Coupon 信息)
|
||||
CouponRespDTO coupon = randomPojo(CouponRespDTO.class, o -> o.setId(1024L).setName("程序员节")
|
||||
.setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductSpuIds(asList(1L, 2L))
|
||||
.setUsePrice(350).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType())
|
||||
.setDiscountPercent(50).setDiscountLimitPrice(70));
|
||||
when(couponApi.validateCoupon(eq(new CouponValidReqDTO().setId(1024L).setUserId(233L)))).thenReturn(coupon);
|
||||
|
||||
// 调用
|
||||
tradeCouponPriceCalculator.calculate(param, result);
|
||||
// 断言
|
||||
assertEquals(result.getCouponId(), 1024L);
|
||||
// 断言:Price 部分
|
||||
TradePriceCalculateRespBO.Price price = result.getPrice();
|
||||
assertEquals(price.getTotalPrice(), 470);
|
||||
assertEquals(price.getDiscountPrice(), 0);
|
||||
assertEquals(price.getPointPrice(), 0);
|
||||
assertEquals(price.getDeliveryPrice(), 0);
|
||||
assertEquals(price.getCouponPrice(), 70);
|
||||
assertEquals(price.getPayPrice(), 400);
|
||||
// 断言:SKU 1
|
||||
TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
|
||||
assertEquals(orderItem01.getSkuId(), 10L);
|
||||
assertEquals(orderItem01.getCount(), 2);
|
||||
assertEquals(orderItem01.getPrice(), 100);
|
||||
assertEquals(orderItem01.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem01.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem01.getCouponPrice(), 40);
|
||||
assertEquals(orderItem01.getPointPrice(), 0);
|
||||
assertEquals(orderItem01.getPayPrice(), 160);
|
||||
// 断言:SKU 2
|
||||
TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
|
||||
assertEquals(orderItem02.getSkuId(), 20L);
|
||||
assertEquals(orderItem02.getCount(), 3);
|
||||
assertEquals(orderItem02.getPrice(), 50);
|
||||
assertEquals(orderItem02.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem02.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem02.getCouponPrice(), 30);
|
||||
assertEquals(orderItem02.getPointPrice(), 0);
|
||||
assertEquals(orderItem02.getPayPrice(), 120);
|
||||
// 断言:SKU 3
|
||||
TradePriceCalculateRespBO.OrderItem orderItem03 = result.getItems().get(2);
|
||||
assertEquals(orderItem03.getSkuId(), 30L);
|
||||
assertEquals(orderItem03.getCount(), 4);
|
||||
assertEquals(orderItem03.getPrice(), 30);
|
||||
assertEquals(orderItem03.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem03.getCouponPrice(), 0);
|
||||
assertEquals(orderItem03.getPointPrice(), 0);
|
||||
assertEquals(orderItem03.getPayPrice(), 120);
|
||||
// 断言:SKU 4
|
||||
TradePriceCalculateRespBO.OrderItem orderItem04 = result.getItems().get(3);
|
||||
assertEquals(orderItem04.getSkuId(), 40L);
|
||||
assertEquals(orderItem04.getCount(), 5);
|
||||
assertEquals(orderItem04.getPrice(), 60);
|
||||
assertEquals(orderItem04.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem04.getCouponPrice(), 0);
|
||||
assertEquals(orderItem04.getPointPrice(), 0);
|
||||
assertEquals(orderItem04.getPayPrice(), 300);
|
||||
// 断言:Promotion 部分
|
||||
assertEquals(result.getPromotions().size(), 1);
|
||||
TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
|
||||
assertEquals(promotion01.getId(), 1024L);
|
||||
assertEquals(promotion01.getName(), "程序员节");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.COUPON.getType());
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 70);
|
||||
assertTrue(promotion01.getMatch());
|
||||
assertEquals(promotion01.getDescription(), "优惠劵:省 0.70 元");
|
||||
assertEquals(promotion01.getItems().size(), 2);
|
||||
TradePriceCalculateRespBO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
|
||||
assertEquals(promotionItem011.getSkuId(), 10L);
|
||||
assertEquals(promotionItem011.getTotalPrice(), 200);
|
||||
assertEquals(promotionItem011.getDiscountPrice(), 40);
|
||||
TradePriceCalculateRespBO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
|
||||
assertEquals(promotionItem012.getSkuId(), 20L);
|
||||
assertEquals(promotionItem012.getTotalPrice(), 150);
|
||||
assertEquals(promotionItem012.getDiscountPrice(), 30);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link TradeDiscountActivityPriceCalculator} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class TradeDiscountActivityPriceCalculatorTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private TradeDiscountActivityPriceCalculator tradeDiscountActivityPriceCalculator;
|
||||
|
||||
@Mock
|
||||
private DiscountActivityApi discountActivityApi;
|
||||
|
||||
@Test
|
||||
public void testCalculate() {
|
||||
// 准备参数
|
||||
TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
|
||||
.setItems(asList(
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), // 匹配活动,且已选中
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(false) // 匹配活动,但未选中
|
||||
));
|
||||
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
|
||||
.setPrice(new TradePriceCalculateRespBO.Price())
|
||||
.setPromotions(new ArrayList<>())
|
||||
.setItems(asList(
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
|
||||
.setPrice(100),
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(false)
|
||||
.setPrice(50)
|
||||
));
|
||||
// 保证价格被初始化上
|
||||
TradePriceCalculatorHelper.recountPayPrice(result.getItems());
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
|
||||
// mock 方法(限时折扣活动)
|
||||
when(discountActivityApi.getMatchDiscountProductList(eq(asSet(10L, 20L)))).thenReturn(asList(
|
||||
randomPojo(DiscountProductRespDTO.class, o -> o.setActivityId(1000L)
|
||||
.setActivityName("活动 1000 号").setSkuId(10L)
|
||||
.setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(40)),
|
||||
randomPojo(DiscountProductRespDTO.class, o -> o.setActivityId(2000L)
|
||||
.setActivityName("活动 2000 号").setSkuId(20L)
|
||||
.setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(60))
|
||||
));
|
||||
// 10L: 100 * 2 - 40 * 2 = 120
|
||||
// 20L:50 * 3 - 50 * 3 * 0.4 = 90
|
||||
|
||||
// 调用
|
||||
tradeDiscountActivityPriceCalculator.calculate(param, result);
|
||||
// 断言:Price 部分
|
||||
TradePriceCalculateRespBO.Price price = result.getPrice();
|
||||
assertEquals(price.getTotalPrice(), 200);
|
||||
assertEquals(price.getDiscountPrice(), 80);
|
||||
assertEquals(price.getPointPrice(), 0);
|
||||
assertEquals(price.getDeliveryPrice(), 0);
|
||||
assertEquals(price.getCouponPrice(), 0);
|
||||
assertEquals(price.getPayPrice(), 120);
|
||||
assertNull(result.getCouponId());
|
||||
// 断言:SKU 1
|
||||
assertEquals(result.getItems().size(), 2);
|
||||
TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
|
||||
assertEquals(orderItem01.getSkuId(), 10L);
|
||||
assertEquals(orderItem01.getCount(), 2);
|
||||
assertEquals(orderItem01.getPrice(), 100);
|
||||
assertEquals(orderItem01.getDiscountPrice(), 80);
|
||||
assertEquals(orderItem01.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem01.getCouponPrice(), 0);
|
||||
assertEquals(orderItem01.getPointPrice(), 0);
|
||||
assertEquals(orderItem01.getPayPrice(), 120);
|
||||
// 断言:SKU 2
|
||||
TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
|
||||
assertEquals(orderItem02.getSkuId(), 20L);
|
||||
assertEquals(orderItem02.getCount(), 3);
|
||||
assertEquals(orderItem02.getPrice(), 50);
|
||||
assertEquals(orderItem02.getDiscountPrice(), 60);
|
||||
assertEquals(orderItem02.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem02.getCouponPrice(), 0);
|
||||
assertEquals(orderItem02.getPointPrice(), 0);
|
||||
assertEquals(orderItem02.getPayPrice(), 90);
|
||||
// 断言:Promotion 部分
|
||||
assertEquals(result.getPromotions().size(), 1);
|
||||
TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
|
||||
assertEquals(promotion01.getId(), 1000L);
|
||||
assertEquals(promotion01.getName(), "活动 1000 号");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType());
|
||||
assertEquals(promotion01.getTotalPrice(), 200);
|
||||
assertEquals(promotion01.getDiscountPrice(), 80);
|
||||
assertTrue(promotion01.getMatch());
|
||||
assertEquals(promotion01.getDescription(), "限时折扣:省 0.80 元");
|
||||
TradePriceCalculateRespBO.PromotionItem promotionItem01 = promotion01.getItems().get(0);
|
||||
assertEquals(promotion01.getItems().size(), 1);
|
||||
assertEquals(promotionItem01.getSkuId(), 10L);
|
||||
assertEquals(promotionItem01.getTotalPrice(), 200);
|
||||
assertEquals(promotionItem01.getDiscountPrice(), 80);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link TradeRewardActivityPriceCalculator} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private TradeRewardActivityPriceCalculator tradeRewardActivityPriceCalculator;
|
||||
|
||||
@Mock
|
||||
private RewardActivityApi rewardActivityApi;
|
||||
|
||||
@Test
|
||||
public void testCalculate_match() {
|
||||
// 准备参数
|
||||
TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
|
||||
.setItems(asList(
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), // 匹配活动 1
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(true), // 匹配活动 1
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true) // 匹配活动 2
|
||||
));
|
||||
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
|
||||
.setPrice(new TradePriceCalculateRespBO.Price())
|
||||
.setPromotions(new ArrayList<>())
|
||||
.setItems(asList(
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
|
||||
.setPrice(100).setSpuId(1L),
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(true)
|
||||
.setPrice(50).setSpuId(2L),
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(30L).setCount(4).setSelected(true)
|
||||
.setPrice(30).setSpuId(3L)
|
||||
));
|
||||
// 保证价格被初始化上
|
||||
TradePriceCalculatorHelper.recountPayPrice(result.getItems());
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
|
||||
// mock 方法(限时折扣 DiscountActivity 信息)
|
||||
when(rewardActivityApi.getMatchRewardActivityList(eq(asSet(1L, 2L, 3L)))).thenReturn(asList(
|
||||
randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(1000L).setName("活动 1000 号")
|
||||
.setSpuIds(asList(1L, 2L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
|
||||
.setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(200).setDiscountPrice(70)))),
|
||||
randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(2000L).setName("活动 2000 号")
|
||||
.setSpuIds(singletonList(3L)).setConditionType(PromotionConditionTypeEnum.COUNT.getType())
|
||||
.setRules(asList(new RewardActivityMatchRespDTO.Rule().setLimit(1).setDiscountPrice(10),
|
||||
new RewardActivityMatchRespDTO.Rule().setLimit(2).setDiscountPrice(60), // 最大可满足,因为是 4 个
|
||||
new RewardActivityMatchRespDTO.Rule().setLimit(10).setDiscountPrice(100))))
|
||||
));
|
||||
|
||||
// 调用
|
||||
tradeRewardActivityPriceCalculator.calculate(param, result);
|
||||
// 断言 Order 部分
|
||||
TradePriceCalculateRespBO.Price price = result.getPrice();
|
||||
assertEquals(price.getTotalPrice(), 470);
|
||||
assertEquals(price.getDiscountPrice(), 130);
|
||||
assertEquals(price.getPointPrice(), 0);
|
||||
assertEquals(price.getDeliveryPrice(), 0);
|
||||
assertEquals(price.getCouponPrice(), 0);
|
||||
assertEquals(price.getPayPrice(), 340);
|
||||
assertNull(result.getCouponId());
|
||||
// 断言:SKU 1
|
||||
assertEquals(result.getItems().size(), 3);
|
||||
TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
|
||||
assertEquals(orderItem01.getSkuId(), 10L);
|
||||
assertEquals(orderItem01.getCount(), 2);
|
||||
assertEquals(orderItem01.getPrice(), 100);
|
||||
assertEquals(orderItem01.getDiscountPrice(), 40);
|
||||
assertEquals(orderItem01.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem01.getCouponPrice(), 0);
|
||||
assertEquals(orderItem01.getPointPrice(), 0);
|
||||
assertEquals(orderItem01.getPayPrice(), 160);
|
||||
// 断言:SKU 2
|
||||
TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
|
||||
assertEquals(orderItem02.getSkuId(), 20L);
|
||||
assertEquals(orderItem02.getCount(), 3);
|
||||
assertEquals(orderItem02.getPrice(), 50);
|
||||
assertEquals(orderItem02.getDiscountPrice(), 30);
|
||||
assertEquals(orderItem02.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem02.getCouponPrice(), 0);
|
||||
assertEquals(orderItem02.getPointPrice(), 0);
|
||||
assertEquals(orderItem02.getPayPrice(), 120);
|
||||
// 断言:SKU 3
|
||||
TradePriceCalculateRespBO.OrderItem orderItem03 = result.getItems().get(2);
|
||||
assertEquals(orderItem03.getSkuId(), 30L);
|
||||
assertEquals(orderItem03.getCount(), 4);
|
||||
assertEquals(orderItem03.getPrice(), 30);
|
||||
assertEquals(orderItem03.getDiscountPrice(), 60);
|
||||
assertEquals(orderItem03.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem03.getCouponPrice(), 0);
|
||||
assertEquals(orderItem03.getPointPrice(), 0);
|
||||
assertEquals(orderItem03.getPayPrice(), 60);
|
||||
// 断言:Promotion 部分(第一个)
|
||||
assertEquals(result.getPromotions().size(), 2);
|
||||
TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
|
||||
assertEquals(promotion01.getId(), 1000L);
|
||||
assertEquals(promotion01.getName(), "活动 1000 号");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 70);
|
||||
assertTrue(promotion01.getMatch());
|
||||
assertEquals(promotion01.getDescription(), "满减送:省 0.70 元");
|
||||
assertEquals(promotion01.getItems().size(), 2);
|
||||
TradePriceCalculateRespBO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
|
||||
assertEquals(promotionItem011.getSkuId(), 10L);
|
||||
assertEquals(promotionItem011.getTotalPrice(), 200);
|
||||
assertEquals(promotionItem011.getDiscountPrice(), 40);
|
||||
TradePriceCalculateRespBO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
|
||||
assertEquals(promotionItem012.getSkuId(), 20L);
|
||||
assertEquals(promotionItem012.getTotalPrice(), 150);
|
||||
assertEquals(promotionItem012.getDiscountPrice(), 30);
|
||||
// 断言:Promotion 部分(第二个)
|
||||
TradePriceCalculateRespBO.Promotion promotion02 = result.getPromotions().get(1);
|
||||
assertEquals(promotion02.getId(), 2000L);
|
||||
assertEquals(promotion02.getName(), "活动 2000 号");
|
||||
assertEquals(promotion02.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
assertEquals(promotion02.getTotalPrice(), 120);
|
||||
assertEquals(promotion02.getDiscountPrice(), 60);
|
||||
assertTrue(promotion02.getMatch());
|
||||
assertEquals(promotion02.getDescription(), "满减送:省 0.60 元");
|
||||
TradePriceCalculateRespBO.PromotionItem promotionItem02 = promotion02.getItems().get(0);
|
||||
assertEquals(promotion02.getItems().size(), 1);
|
||||
assertEquals(promotionItem02.getSkuId(), 30L);
|
||||
assertEquals(promotionItem02.getTotalPrice(), 120);
|
||||
assertEquals(promotionItem02.getDiscountPrice(), 60);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculate_notMatch() {
|
||||
// 准备参数
|
||||
TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
|
||||
.setItems(asList(
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true),
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(true),
|
||||
new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true)
|
||||
));
|
||||
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
|
||||
.setPrice(new TradePriceCalculateRespBO.Price())
|
||||
.setPromotions(new ArrayList<>())
|
||||
.setItems(asList(
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
|
||||
.setPrice(100).setSpuId(1L),
|
||||
new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(true)
|
||||
.setPrice(50).setSpuId(2L)
|
||||
));
|
||||
// 保证价格被初始化上
|
||||
TradePriceCalculatorHelper.recountPayPrice(result.getItems());
|
||||
TradePriceCalculatorHelper.recountAllPrice(result);
|
||||
|
||||
// mock 方法(限时折扣 DiscountActivity 信息)
|
||||
when(rewardActivityApi.getMatchRewardActivityList(eq(asSet(1L, 2L)))).thenReturn(singletonList(
|
||||
randomPojo(RewardActivityMatchRespDTO.class, o -> o.setId(1000L).setName("活动 1000 号")
|
||||
.setSpuIds(asList(1L, 2L)).setConditionType(PromotionConditionTypeEnum.PRICE.getType())
|
||||
.setRules(singletonList(new RewardActivityMatchRespDTO.Rule().setLimit(351).setDiscountPrice(70))))
|
||||
));
|
||||
|
||||
// 调用
|
||||
tradeRewardActivityPriceCalculator.calculate(param, result);
|
||||
// 断言 Order 部分
|
||||
TradePriceCalculateRespBO.Price price = result.getPrice();
|
||||
assertEquals(price.getTotalPrice(), 350);
|
||||
assertEquals(price.getDiscountPrice(), 0);
|
||||
assertEquals(price.getPointPrice(), 0);
|
||||
assertEquals(price.getDeliveryPrice(), 0);
|
||||
assertEquals(price.getCouponPrice(), 0);
|
||||
assertEquals(price.getPayPrice(), 350);
|
||||
assertNull(result.getCouponId());
|
||||
// 断言:SKU 1
|
||||
assertEquals(result.getItems().size(), 2);
|
||||
TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
|
||||
assertEquals(orderItem01.getSkuId(), 10L);
|
||||
assertEquals(orderItem01.getCount(), 2);
|
||||
assertEquals(orderItem01.getPrice(), 100);
|
||||
assertEquals(orderItem01.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem01.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem01.getCouponPrice(), 0);
|
||||
assertEquals(orderItem01.getPointPrice(), 0);
|
||||
assertEquals(orderItem01.getPayPrice(), 200);
|
||||
// 断言:SKU 2
|
||||
TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
|
||||
assertEquals(orderItem02.getSkuId(), 20L);
|
||||
assertEquals(orderItem02.getCount(), 3);
|
||||
assertEquals(orderItem02.getPrice(), 50);
|
||||
assertEquals(orderItem02.getDiscountPrice(), 0);
|
||||
assertEquals(orderItem02.getDeliveryPrice(), 0);
|
||||
assertEquals(orderItem02.getCouponPrice(), 0);
|
||||
assertEquals(orderItem02.getPointPrice(), 0);
|
||||
assertEquals(orderItem02.getPayPrice(), 150);
|
||||
// 断言 Promotion 部分
|
||||
assertEquals(result.getPromotions().size(), 1);
|
||||
TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
|
||||
assertEquals(promotion01.getId(), 1000L);
|
||||
assertEquals(promotion01.getName(), "活动 1000 号");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 0);
|
||||
assertFalse(promotion01.getMatch());
|
||||
assertEquals(promotion01.getDescription(), "TODO"); // TODO 芋艿:后面再想想
|
||||
assertEquals(promotion01.getItems().size(), 2);
|
||||
TradePriceCalculateRespBO.PromotionItem promotionItem011 = promotion01.getItems().get(0);
|
||||
assertEquals(promotionItem011.getSkuId(), 10L);
|
||||
assertEquals(promotionItem011.getTotalPrice(), 200);
|
||||
assertEquals(promotionItem011.getDiscountPrice(), 0);
|
||||
TradePriceCalculateRespBO.PromotionItem promotionItem012 = promotion01.getItems().get(1);
|
||||
assertEquals(promotionItem012.getSkuId(), 20L);
|
||||
assertEquals(promotionItem012.getTotalPrice(), 150);
|
||||
assertEquals(promotionItem012.getDiscountPrice(), 0);
|
||||
}
|
||||
|
||||
}
|
@ -18,4 +18,12 @@ public interface AddressApi {
|
||||
*/
|
||||
AddressRespDTO getAddress(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 获得用户默认收件地址
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 用户收件地址
|
||||
*/
|
||||
AddressRespDTO getDefaultAddress(Long userId);
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class AddressRespDTO {
|
||||
/**
|
||||
* 地区编号
|
||||
*/
|
||||
private Long areaId;
|
||||
private Integer areaId;
|
||||
/**
|
||||
* 邮编
|
||||
*/
|
||||
|
@ -25,4 +25,9 @@ public class AddressApiImpl implements AddressApi {
|
||||
return AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRespDTO getDefaultAddress(Long userId) {
|
||||
return AddressConvert.INSTANCE.convert02(addressService.getDefaultUserAddress(userId));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public class MemberAddressDO extends BaseDO {
|
||||
/**
|
||||
* 地区编号
|
||||
*/
|
||||
private Long areaId;
|
||||
private Integer areaId;
|
||||
/**
|
||||
* 收件详细地址
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user