From 2a9a869e01083b87efcdea0135823f02db4f38e9 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 3 Jun 2023 00:46:56 +0800 Subject: [PATCH 1/8] =?UTF-8?q?mall=20+=20trade=EF=BC=9A=E8=8E=B7=E5=BE=97?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=BB=93=E7=AE=97=E4=BF=A1=E6=81=AF=EF=BC=8C?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E6=94=AF=E4=BB=98=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/sku/dto/ProductSkuRespDTO.java | 29 +------ .../app/spu/vo/AppProductSpuDetailRespVO.java | 3 + .../service/price/PriceServiceTest.java | 5 -- .../app/order/AppTradeOrderController.http | 9 ++- .../app/order/AppTradeOrderController.java | 76 +----------------- .../vo/AppTradeOrderSettlementReqVO.java | 45 ++++++++++- .../vo/AppTradeOrderSettlementRespVO.java | 6 +- .../convert/order/TradeOrderConvert.java | 49 +++++++++++- .../trade/dal/mysql/cart/TradeCartMapper.java | 7 ++ .../trade/service/cart/TradeCartService.java | 12 +++ .../service/cart/TradeCartServiceImpl.java | 13 ++- .../service/order/TradeOrderServiceImpl.java | 79 +++++++++++++------ .../service/price/TradePriceService.java | 4 +- .../service/price/TradePriceServiceImpl.java | 38 +++++++-- .../price/bo/TradePriceCalculateReqBO.java | 2 +- .../price/bo/TradePriceCalculateRespBO.java | 30 +++++-- .../TradePriceCalculatorHelper.java | 48 +++++++---- .../aftersale/TradeAfterSaleServiceTest.java | 2 +- .../module/member/api/address/AddressApi.java | 8 ++ .../api/address/dto/AddressRespDTO.java | 2 +- .../member/api/address/AddressApiImpl.java | 5 ++ 21 files changed, 292 insertions(+), 180 deletions(-) diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java index 7f5e7aed8..1875518c3 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java @@ -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 properties; + private List properties; /** * 销售价格,单位:分 */ @@ -63,30 +64,4 @@ public class ProductSkuRespDTO { */ private Double volume; - /** - * 商品属性 - */ - @Data - public static class Property { - - /** - * 属性编号 - */ - private Long propertyId; - /** - * 属性名字 - */ - private String propertyName; - - /** - * 属性值编号 - */ - private Long valueId; - /** - * 属性值名字 - */ - private String valueName; - - } - } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java index 6992d83b7..41e36fb98 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuDetailRespVO.java @@ -18,6 +18,9 @@ public class AppProductSpuDetailRespVO { @Schema(description = "商品名称", required = true, example = "芋道") private String name; + @Schema(description = "商品简介", required = true, example = "我是一个快乐简介") + private String introduction; + @Schema(description = "商品详情", required = true, example = "我是商品描述") private String description; diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java index 929727077..ef2d57631 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java @@ -93,7 +93,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest { assertNull(promotion.getId()); assertEquals(promotion.getName(), "会员折扣"); assertEquals(promotion.getType(), PromotionTypeEnum.MEMBER.getType()); - assertEquals(promotion.getLevel(), PromotionLevelEnum.SKU.getLevel()); assertEquals(promotion.getTotalPrice(), 200); assertEquals(promotion.getDiscountPrice(), 20); assertTrue(promotion.getMatch()); @@ -264,7 +263,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest { assertEquals(promotion01.getId(), 1000L); assertEquals(promotion01.getName(), "活动 1000 号"); assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType()); - assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel()); assertEquals(promotion01.getTotalPrice(), 350); assertEquals(promotion01.getDiscountPrice(), 70); assertTrue(promotion01.getMatch()); @@ -283,7 +281,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest { assertEquals(promotion02.getId(), 2000L); assertEquals(promotion02.getName(), "活动 2000 号"); assertEquals(promotion02.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType()); - assertEquals(promotion02.getLevel(), PromotionLevelEnum.ORDER.getLevel()); assertEquals(promotion02.getTotalPrice(), 120); assertEquals(promotion02.getDiscountPrice(), 60); assertTrue(promotion02.getMatch()); @@ -352,7 +349,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest { assertEquals(promotion01.getId(), 1000L); assertEquals(promotion01.getName(), "活动 1000 号"); assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType()); - assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel()); assertEquals(promotion01.getTotalPrice(), 350); assertEquals(promotion01.getDiscountPrice(), 0); assertFalse(promotion01.getMatch()); @@ -434,7 +430,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest { assertEquals(promotion01.getId(), 1024L); assertEquals(promotion01.getName(), "程序员节"); assertEquals(promotion01.getType(), PromotionTypeEnum.COUPON.getType()); - assertEquals(promotion01.getLevel(), PromotionLevelEnum.COUPON.getLevel()); assertEquals(promotion01.getTotalPrice(), 350); assertEquals(promotion01.getDiscountPrice(), 70); assertTrue(promotion01.getMatch()); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http index 8a3ed4867..8d2af766f 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http @@ -1,5 +1,10 @@ -### /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/settlement 获得订单结算信息(基于购物车) +GET {{appApi}}/trade/order/settlement?type=0&items[0].cartId=50&couponId=1 Authorization: Bearer {{appToken}} tenant-id: {{appTenentId}} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index 738741f6e..a6eb579a5 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 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; @@ -25,7 +24,6 @@ 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; @@ -53,78 +51,8 @@ public class AppTradeOrderController { @GetMapping("/settlement") @Operation(summary = "获得订单结算信息") @PreAuthenticated - public CommonResult 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 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 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 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 settlementOrder(@Valid AppTradeOrderSettlementReqVO settlementReqVO) { + return success(tradeOrderService.settlementOrder(getLoginUserId(), settlementReqVO)); } @PostMapping("/create") diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java index 973db183e..0324b492b 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java @@ -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 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 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; + } + + } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java index b2d4e4108..aaa059ef1 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java @@ -91,17 +91,17 @@ public class AppTradeOrderSettlementRespVO { 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; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java index 38860943c..3ea449f6c 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java @@ -3,6 +3,7 @@ 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; @@ -18,14 +19,15 @@ import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.Prod 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; @@ -241,4 +243,45 @@ public interface TradeOrderConvert { AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean); + default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO, + List cartList) { + TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO(); + reqBO.setUserId(userId).setType(settlementReqVO.getType()) + .setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId()) + .setItems(new ArrayList<>(settlementReqVO.getItems().size())); + // 商品项的构建 + Map 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); + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java index 564a4f0bc..b4727117e 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java @@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; @Mapper public interface TradeCartMapper extends BaseMapperX { @@ -70,4 +71,10 @@ public interface TradeCartMapper extends BaseMapperX { update(updateObject, new LambdaQueryWrapper().in(TradeCartDO::getId, ids)); } + default List selectListByUserId(Long userId, Set ids) { + return selectList(new LambdaQueryWrapper() + .eq(TradeCartDO::getUserId, userId) + .in(TradeCartDO::getId, ids)); + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java index 9a009860d..93152a633 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java @@ -4,10 +4,13 @@ import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO 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 getCartList(Long userId, Set ids); + /** * 获得用户的购物车商品 SPU 数量的 Map * diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java index c6fb3e0ec..6696de319 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java @@ -17,10 +17,7 @@ import org.springframework.transaction.annotation.Transactional; 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 getCartList(Long userId, Set ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return cartMapper.selectListByUserId(userId, ids); + } + private void deleteCartIfSpuDeleted(List carts, List spus) { // 如果 SPU 被删除,则删除购物车对应的商品。延迟删除 carts.removeIf(cart -> { diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java index c4760b4b1..f60b95899 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java @@ -34,6 +34,7 @@ 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; @@ -41,6 +42,10 @@ 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.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; @@ -50,8 +55,7 @@ import java.time.LocalDateTime; 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; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_NOT_FOUND; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; @@ -70,6 +74,11 @@ public class TradeOrderServiceImpl implements TradeOrderService { @Resource private TradeOrderItemMapper tradeOrderItemMapper; + @Resource + private TradeCartService tradeCartService; + @Resource + private TradePriceService tradePriceService; + @Resource private PriceApi priceApi; @Resource @@ -92,7 +101,48 @@ 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 cartList = tradeCartService.getCartList(userId, + convertSet(settlementReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId)); + + // 2. 计算价格 + TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList); + return tradePriceService.calculatePrice(calculateReqBO); } @Override @@ -120,29 +170,6 @@ public class TradeOrderServiceImpl implements TradeOrderService { return tradeOrderDO.getId(); } -// /** -// * 校验商品 SKU 是否可出售 -// * -// * @param items 商品 SKU -// * @return 商品 SKU 数组 -// */ -// private List validateSkuSaleable(List items) { -// List skus = productSkuApi.getSkuList(convertSet(items, Item::getSkuId)); -// // SKU 不存在 -// if (items.size() != skus.size()) { -// throw exception(ORDER_CREATE_SKU_NOT_FOUND); -// } -// // 校验库存不足 -// Map 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 是否可出售 * diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceService.java index 2dd0c41ca..d12451b22 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceService.java @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.price; 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); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java index f0ebce5ba..0e9dc32c2 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java @@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.trade.service.price; 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 priceCalculators; @Override public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) { - // 1. 获得商品 SKU 数组 - List skuList = checkSkus(calculateReqBO); + // 1.1 获得商品 SKU 数组 + List skuList = checkSkuList(calculateReqBO); + // 1.2 获得商品 SPU 数组 + List 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 checkSkus(TradePriceCalculateReqBO reqBO) { + private List checkSkuList(TradePriceCalculateReqBO reqBO) { // 获得商品 SKU 数组 Map skuIdCountMap = convertMap(reqBO.getItems(), TradePriceCalculateReqBO.Item::getSkuId, TradePriceCalculateReqBO.Item::getCount); @@ -70,4 +80,20 @@ public class TradePriceServiceImpl implements TradePriceService { return skus; } + private List checkSpuList(List skuList) { + // 获得商品 SPU 数组 + List 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; + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java index 2a014c0e0..931940793 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java @@ -21,7 +21,7 @@ public class TradePriceCalculateReqBO { * * 枚举 {@link TradeOrderTypeEnum} */ - private Integer orderType; + private Integer type; /** * 用户编号 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java index 1c347a532..fb006b44b 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java @@ -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 properties; } @@ -189,12 +209,6 @@ public class TradePriceCalculateRespBO { * 枚举 {@link PromotionTypeEnum} */ private Integer type; - /** - * 营销级别 - * - * 枚举 {@link PromotionLevelEnum} - */ - private Integer level; /** * 计算时的原价(总),单位:分 */ diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java index 9ed94b692..47a73c0d8 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java @@ -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; @@ -14,6 +15,8 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; import static java.util.Collections.singletonList; +// TODO 芋艿:改成父类 + /** * {@link TradePriceCalculator} 的工具类 * @@ -24,25 +27,42 @@ import static java.util.Collections.singletonList; public class TradePriceCalculatorHelper { public static TradePriceCalculateRespBO buildCalculateResp(TradePriceCalculateReqBO param, - List skuList) { + List spuList, List skuList) { // 创建 PriceCalculateRespDTO 对象 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO(); - result.setOrderType(param.getOrderType()); + result.setType(param.getType()); + result.setPromotions(new ArrayList<>()); + // 创建它的 OrderItem 属性 - Map 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 spuMap = convertMap(spuList, ProductSpuRespDTO::getId); + Map 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); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java index f628cef4a..0602c6649 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceTest.java @@ -69,7 +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); + o.setOrderId(111L).setUserId(userId).setPayPrice(200); o.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); }); when(tradeOrderService.getOrderItem(eq(1024L), eq(1L))) diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java index 75ba02563..658748819 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApi.java @@ -18,4 +18,12 @@ public interface AddressApi { */ AddressRespDTO getAddress(Long id, Long userId); + /** + * 获得用户默认收件地址 + * + * @param userId 用户编号 + * @return 用户收件地址 + */ + AddressRespDTO getDefaultAddress(Long userId); + } diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java index cc8eb4701..3fbb70f80 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/address/dto/AddressRespDTO.java @@ -29,7 +29,7 @@ public class AddressRespDTO { /** * 地区编号 */ - private Long areaId; + private Integer areaId; /** * 邮编 */ diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java index fd0f4843d..b8088a455 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/address/AddressApiImpl.java @@ -25,4 +25,9 @@ public class AddressApiImpl implements AddressApi { return AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id)); } + @Override + public AddressRespDTO getDefaultAddress(Long userId) { + return AddressConvert.INSTANCE.convert02(addressService.getDefaultUserAddress(userId)); + } + } From 636b14cccec2b8f5d30db81e18817d23c31aaf73 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 3 Jun 2023 09:14:51 +0800 Subject: [PATCH 2/8] =?UTF-8?q?mall=20+=20trade=EF=BC=9A=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E9=80=BB=E8=BE=91=EF=BC=8C=E6=8E=A5=E5=85=A5?= =?UTF-8?q?=E6=96=B0=E7=9A=84=E4=BB=B7=E6=A0=BC=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/order/AppTradeOrderController.http | 17 +-- .../app/order/AppTradeOrderController.java | 14 +-- .../order/vo/AppTradeOrderCreateReqVO.java | 19 +-- .../convert/order/TradeOrderConvert.java | 55 ++++---- .../dataobject/order/TradeOrderItemDO.java | 13 ++ .../service/order/TradeOrderServiceImpl.java | 118 +++++++----------- 6 files changed, 96 insertions(+), 140 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http index 8d2af766f..1a11d4eed 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http @@ -8,27 +8,22 @@ GET {{appApi}}/trade/order/settlement?type=0&items[0].cartId=50&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/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": "我是备注" } ### 获得订单交易的分页 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java index a6eb579a5..e3008fdd6 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java @@ -22,7 +22,6 @@ 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.HashMap; import java.util.List; @@ -30,6 +29,7 @@ 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 - 交易订单") @@ -58,15 +58,9 @@ public class AppTradeOrderController { @PostMapping("/create") @Operation(summary = "创建订单") @PreAuthenticated - public CommonResult 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 createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO) { + Long orderId = tradeOrderService.createOrder(getLoginUserId(), getClientIP(), createReqVO); + return success(orderId); } @PostMapping("/update-paid") diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java index 26c48954c..11031a884 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java @@ -3,26 +3,9 @@ package cn.iocoder.yudao.module.trade.controller.app.order.vo; 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 cartIds; - - // ========== 非 AppTradeOrderSettlementReqVO 字段 ========== +public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO { @Schema(description = "备注", example = "这个是我的订单哟") private String remark; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java index 3ea449f6c..c53ae4d0a 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java @@ -9,11 +9,8 @@ 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; @@ -50,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 convertList(TradeOrderDO tradeOrderDO, - List orderItems, List skus) { - Map 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 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); @@ -87,19 +84,20 @@ public interface TradeOrderConvert { ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean); List convertList(List list); - default PayOrderCreateReqDTO convert(TradeOrderDO tradeOrderDO, List tradeOrderItemDOs, - List spus, TradeOrderProperties tradeOrderProperties) { + default PayOrderCreateReqDTO convert(TradeOrderDO order, List 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; } @@ -113,6 +111,7 @@ public interface TradeOrderConvert { .collect(Collectors.toSet()); } + // TODO 芋艿:可简化 default PageResult convertPage(PageResult pageResult, List orderItems, List propertyValueDetails) { Map> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId); @@ -149,6 +148,7 @@ public interface TradeOrderConvert { TradeOrderPageItemRespVO convert(TradeOrderDO order, List items); ProductPropertyValueDetailRespVO convert(ProductPropertyValueDetailRespDTO bean); + // TODO 芋艿:可简化 default TradeOrderDetailRespVO convert(TradeOrderDO order, List orderItems, List propertyValueDetails, MemberUserRespDTO user) { TradeOrderDetailRespVO orderVO = convert2(order, orderItems); @@ -179,6 +179,7 @@ public interface TradeOrderConvert { TradeOrderDetailRespVO convert2(TradeOrderDO order, List items); MemberUserRespVO convert(MemberUserRespDTO bean); + // TODO 芋艿:可简化 default PageResult convertPage02(PageResult pageResult, List orderItems, List propertyValueDetails) { Map> orderItemMap = convertMultiMap(orderItems, TradeOrderItemDO::getOrderId); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java index 323389c94..3f071ae2f 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java @@ -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; } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java index f60b95899..8e75dd985 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java @@ -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; @@ -40,6 +34,7 @@ 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; @@ -55,7 +50,8 @@ import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_NOT_FOUND; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; @@ -79,13 +75,9 @@ public class TradeOrderServiceImpl implements TradeOrderService { @Resource private TradePriceService tradePriceService; - @Resource - private PriceApi priceApi; @Resource private ProductSkuApi productSkuApi; @Resource - private ProductSpuApi productSpuApi; - @Resource private PayOrderApi payOrderApi; @Resource private AddressApi addressApi; @@ -142,53 +134,29 @@ public class TradeOrderServiceImpl implements TradeOrderService { // 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 skus = validateSkuSaleable(createReqVO.getItems()); // TODO 芋艿,临时关闭。 - List skus = null; - // 商品 SPU 检查:可售状态 - List 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 tradeOrderItems = createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus); + // 3.1 插入 TradeOrderDO 订单 + TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO, address); + // 3.2 插入 TradeOrderItemDO 订单项 + List orderItems = createTradeOrderItems(order, calculateRespBO); // 订单创建完后的逻辑 - afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems, spus); + afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO); // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! - return tradeOrderDO.getId(); - } - - /** - * 校验商品 SPU 是否可出售 - * - * @param spuIds 商品 SPU 编号数组 - * @return 商品 SPU 数组 - */ - private List validateSpuSaleable(Set spuIds) { - List 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(); } /** @@ -200,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 createTradeOrderItems(TradeOrderDO tradeOrderDO, - List orderItems, List skus) { - List tradeOrderItemDOs = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, orderItems, skus); - tradeOrderItemMapper.insertBatch(tradeOrderItemDOs); - return tradeOrderItemDOs; + private List createTradeOrderItems(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) { + List orderItems = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO); + tradeOrderItemMapper.insertBatch(orderItems); + return orderItems; } /** @@ -237,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 tradeOrderItemDOs, - List spus) { + TradeOrderDO tradeOrderDO, List orderItems, + TradePriceCalculateRespBO calculateRespBO) { // 下单时扣减商品库存 - productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(tradeOrderItemDOs))); + productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(orderItems))); // 删除购物车商品 TODO 芋艿:待实现 @@ -255,20 +226,19 @@ public class TradeOrderServiceImpl implements TradeOrderService { } // 生成预支付 - createPayOrder(tradeOrderDO, tradeOrderItemDOs, spus); + createPayOrder(tradeOrderDO, orderItems, calculateRespBO); // 增加订单日志 TODO 芋艿:待实现 } - private void createPayOrder(TradeOrderDO tradeOrderDO, List tradeOrderItemDOs, - List spus) { + private void createPayOrder(TradeOrderDO order, List 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 From 1452f6b231bff6fd1977085de6762c59ef01d52d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 3 Jun 2023 11:53:40 +0800 Subject: [PATCH 3/8] =?UTF-8?q?mall=20+=20trade=EF=BC=9A=E5=A2=9E=E8=82=8C?= =?UTF-8?q?=E7=88=B1=20TradeCouponPriceCalculator=20=E7=9A=84=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/sku/dto/ProductSkuRespDTO.java | 4 - .../api/price/dto/PriceCalculateReqDTO.java | 1 + .../service/price/PriceServiceTest.java | 129 ---------------- .../TradeCouponPriceCalculator.java | 3 +- .../TradePriceCalculatorHelper.java | 29 +++- .../TradeRewardActivityPriceCalculator.java | 3 + .../service/order/TradeOrderServiceTest.java | 7 +- .../price/TradePriceServiceImplTest.java | 135 +++++++++++++++++ .../TradeCouponPriceCalculatorTest.java | 143 ++++++++++++++++++ 9 files changed, 314 insertions(+), 140 deletions(-) create mode 100644 yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java create mode 100644 yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java index 1875518c3..20f84f87c 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java @@ -51,10 +51,6 @@ public class ProductSkuRespDTO { * 库存 */ private Integer stock; - /** - * 预警预存 - */ - private Integer warnStock; /** * 商品重量,单位:kg 千克 */ diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java index ce53de50e..4c43ffa81 100644 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java +++ b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/price/dto/PriceCalculateReqDTO.java @@ -12,6 +12,7 @@ import java.util.List; * @author 芋道源码 */ @Data +@Deprecated public class PriceCalculateReqDTO { /** diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java index ef2d57631..79690000a 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java @@ -56,54 +56,6 @@ 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); - // 断言 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.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() { // 准备参数 @@ -364,87 +316,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest { 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); - // 断言 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.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 public void testGetMeetCouponList() { // 准备参数 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java index 52ebf84a0..f00b04b12 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java @@ -101,7 +101,8 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { CouponRespDTO coupon) { Predicate 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); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java index 47a73c0d8..d171e50cf 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java @@ -15,8 +15,6 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; import static java.util.Collections.singletonList; -// TODO 芋艿:改成父类 - /** * {@link TradePriceCalculator} 的工具类 * @@ -106,6 +104,31 @@ public class TradePriceCalculatorHelper { - orderItem.getPointPrice()); } + /** + * 重新计算每个订单项的支付金额 + * + * 【目前主要是单测使用】 + * + * @param orderItems 订单项数组 + */ + public static void recountPayPrice(List 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); + }); + } + /** * 计算已选中的订单项,总支付金额 * @@ -114,7 +137,7 @@ public class TradePriceCalculatorHelper { */ public static Integer calculateTotalPayPrice(List orderItems) { return getSumValue(orderItems, - orderItem -> orderItem.getSelected() ? orderItem.getPayPrice() : 0, // 未选中的情况下,不计算支付金额 + orderItem -> orderItem.getSelected() ? orderItem.getPayPrice() : 0, // 未选中的情况下,不计算支付金额 Integer::sum); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java index b0947fcc3..f2cfe71cd 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java @@ -53,6 +53,9 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator // 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; } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java index 064ec336e..15a55726d 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceTest.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 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() diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java new file mode 100644 index 000000000..473e3920e --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java @@ -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 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 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()); + } + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java new file mode 100644 index 000000000..238d80e0c --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java @@ -0,0 +1,143 @@ +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 芋道源码 + */ +class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { + + @InjectMocks + private TradeCouponPriceCalculator tradeCouponPriceCalculator; + + @Mock + private CouponApi couponApi; + + @Test + void calculate() { + // 准备参数 + 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); + } +} From 0d08c814ec8ad6d367d0dd4b767bccbd3de89b2d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 3 Jun 2023 13:14:47 +0800 Subject: [PATCH 4/8] =?UTF-8?q?mall=20+=20trade=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20TradeDiscountActivityPriceCalculator=20=E7=9A=84=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/price/PriceServiceTest.java | 87 ------------- .../TradeDiscountActivityPriceCalculator.java | 11 +- .../TradeCouponPriceCalculatorTest.java | 5 +- ...deDiscountActivityPriceCalculatorTest.java | 118 ++++++++++++++++++ 4 files changed, 128 insertions(+), 93 deletions(-) create mode 100644 yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculatorTest.java diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java index 79690000a..5b43d2372 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java @@ -47,8 +47,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest { @InjectMocks private PriceServiceImpl priceService; - @Mock - private DiscountActivityService discountService; @Mock private RewardActivityService rewardActivityService; @Mock @@ -56,91 +54,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest { @Mock private ProductSkuApi productSkuApi; - @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 信息) - 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); - // 断言 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()); - 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()); - 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); - } - /** * 测试满减送活动,匹配的情况 */ diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java index 7bcd8ffb7..fe465f37d 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java @@ -53,10 +53,13 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato 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); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java index 238d80e0c..6d5a5c390 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java @@ -36,7 +36,7 @@ class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { private CouponApi couponApi; @Test - void calculate() { + public void testCalculate() { // 准备参数 TradePriceCalculateReqBO param = new TradePriceCalculateReqBO() .setUserId(233L).setCouponId(1024L) @@ -120,7 +120,7 @@ class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { assertEquals(orderItem04.getCouponPrice(), 0); assertEquals(orderItem04.getPointPrice(), 0); assertEquals(orderItem04.getPayPrice(), 300); - // 断言 Promotion 部分 + // 断言:Promotion 部分 assertEquals(result.getPromotions().size(), 1); TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0); assertEquals(promotion01.getId(), 1024L); @@ -140,4 +140,5 @@ class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { assertEquals(promotionItem012.getTotalPrice(), 150); assertEquals(promotionItem012.getDiscountPrice(), 30); } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculatorTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculatorTest.java new file mode 100644 index 000000000..9175e1643 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculatorTest.java @@ -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); + } + +} From 73a781cbbe42373184d82503aeac8767225d8d0e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 3 Jun 2023 13:36:07 +0800 Subject: [PATCH 5/8] =?UTF-8?q?mall=20+=20trade=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20TradeRewardActivityPriceCalculatorTest=20=E7=9A=84=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/price/PriceServiceTest.java | 178 +------------- .../TradeCouponPriceCalculatorTest.java | 2 +- ...radeRewardActivityPriceCalculatorTest.java | 232 ++++++++++++++++++ 3 files changed, 234 insertions(+), 178 deletions(-) create mode 100644 yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculatorTest.java diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java index 5b43d2372..f2817b8fc 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/price/PriceServiceTest.java @@ -1,33 +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.discount.bo.DiscountProductDetailBO; 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; @@ -54,179 +45,12 @@ public class PriceServiceTest extends BaseMockitoUnitTest { @Mock private ProductSkuApi productSkuApi; - /** - * 测试满减送活动,匹配的情况 - */ - @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> matchRewardActivities = new LinkedHashMap<>(); - matchRewardActivities.put(rewardActivity01, asSet(1L, 2L)); - matchRewardActivities.put(rewardActivity02, asSet(3L)); - when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L, 3L)))).thenReturn(matchRewardActivities); - - // 调用 - PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); - // 断言 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.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.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> matchRewardActivities = new LinkedHashMap<>(); - matchRewardActivities.put(rewardActivity01, asSet(1L, 2L)); - when(rewardActivityService.getMatchRewardActivities(eq(asSet(1L, 2L)))).thenReturn(matchRewardActivities); - // 调用 - PriceCalculateRespDTO priceCalculate = priceService.calculatePrice(calculateReqDTO); - // 断言 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.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 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java index 6d5a5c390..ac4872c86 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java @@ -27,7 +27,7 @@ import static org.mockito.Mockito.when; * * @author 芋道源码 */ -class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { +public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { @InjectMocks private TradeCouponPriceCalculator tradeCouponPriceCalculator; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculatorTest.java b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculatorTest.java new file mode 100644 index 000000000..30107d5b4 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculatorTest.java @@ -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); + } + +} From f1fa8eadd23d04327ec50a3c34435078eb33ae59 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Sat, 3 Jun 2023 17:16:18 +0800 Subject: [PATCH 6/8] =?UTF-8?q?mall=20-=20trade=20-=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=20TradeDeliveryPriceCalculator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/dataobject/spu/ProductSpuDO.java | 2 +- .../DeliveryExpressChargeModeEnum.java | 5 + .../DeliveryExpressTemplateService.java | 7 + .../DeliveryExpressTemplateServiceImpl.java | 9 + .../price/bo/TradePriceCalculateReqBO.java | 17 +- .../TradeDeliveryPriceCalculator.java | 228 ++++++++++++++++++ .../calculator/TradePriceCalculator.java | 4 + .../dataobject/address/MemberAddressDO.java | 2 +- 8 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java index 29cfe3a27..5ee4f1d28 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java @@ -141,7 +141,7 @@ public class ProductSpuDO extends BaseDO { /** * 物流配置模板编号 * - * 关联 { TradeDeliveryExpressTemplateDO#getId()} + * 对应 { TradeDeliveryExpressTemplateDO 的 id 编号} */ private Long deliveryTemplateId; diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryExpressChargeModeEnum.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryExpressChargeModeEnum.java index 8ac37e382..a4b5252ee 100644 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryExpressChargeModeEnum.java +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryExpressChargeModeEnum.java @@ -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()); + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java index 20a0a1b62..2b69c6147 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java @@ -63,4 +63,11 @@ public interface DeliveryExpressTemplateService { * @return 快递运费模板分页 */ PageResult getDeliveryExpressTemplatePage(DeliveryExpressTemplatePageReqVO pageReqVO); + + /** + * 校验快递运费模板 + * @param templateId 模板编号 + * @return DeliveryExpressTemplateDO 非空 + */ + DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java index d77bb78c2..50a24e3bf 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java @@ -202,4 +202,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; + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java index 931940793..eb59f22e0 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java @@ -1,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; @@ -44,6 +46,20 @@ public class TradePriceCalculateReqBO { */ private Long addressId; + /** + * 配送方式 + * + * 枚举 {@link DeliveryTypeEnum} + */ + private Integer deliveryType; + + /** + * 配送模板编号 + * + * 关联 {@link DeliveryExpressTemplateDO#getId()} + */ + private Long templateId; + /** * 商品 SKU 数组 */ @@ -82,5 +98,4 @@ public class TradePriceCalculateReqBO { private Boolean selected; } - } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java new file mode 100644 index 000000000..aaefc76c5 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java @@ -0,0 +1,228 @@ +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; + @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 expressTemplateFreeList = templateFreeMapper.selectListByTemplateId(template.getId()); + Map areaTemplateFreeMap = new HashMap<>(); + expressTemplateFreeList.forEach(item -> { + for (Integer areaId : item.getAreaIds()) { + // TODO 需要保证 areaId 不能重复 + if (!areaTemplateFreeMap.containsKey(areaId)) { + areaTemplateFreeMap.put(areaId, item); + } + } + }); + // 得到快递运费配置 + List expressTemplateChargeList = templateChargeMapper.selectListByTemplateId(template.getId()); + Map 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 areaTemplateFreeMap, + Map areaTemplateChargeMap, + TradePriceCalculateRespBO result) { + // 过滤出已选中的商品SKU + List selectedItem = filterList(result.getItems(), OrderItem::getSelected); + Set skuIds = convertSet(selectedItem, OrderItem::getSkuId); + // 得到SKU 详情。得到 重量体积 + Map skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId); + // 一个 spuId 可能对应多条订单商品 SKU + Map> spuIdItemMap = convertMultiMap(selectedItem, OrderItem::getSpuId); + // 依次计算每个 SPU 的快递运费 + for (Map.Entry> entry : spuIdItemMap.entrySet()) { + List orderItems = entry.getValue(); + // 总件数, 总金额, 总重量, 总体积 + int totalCount = 0, totalPrice = 0; + double totalWeight = 0; + double totalVolume = 0; + for (OrderItem orderItem : orderItems) { + totalCount += orderItem.getCount(); + totalPrice += orderItem.getPrice(); + ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId()); + if (skuResp != null) { + totalWeight = totalWeight + skuResp.getWeight(); + totalVolume = totalVolume + skuResp.getVolume(); + } + } + // 优先判断是否包邮. 如果包邮不计算快递运费 + if (areaTemplateFreeMap.containsKey(receiverAreaId) && + checkExpressFree(chargeMode, totalCount, totalWeight, + totalVolume, totalPrice, areaTemplateFreeMap.get(receiverAreaId))) { + continue; + } + // 计算快递运费 + 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 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. 是不是搞复杂了 + divideDeliveryPrice(deliveryPrice, orderItems); + } + + /** + * 快递运费分摊到每个 SKU 商品上 + * + * @param deliveryPrice 快递运费 + * @param orderItems SKU 商品 + */ + private void divideDeliveryPrice(int deliveryPrice, List 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; + } +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java index 1c9b4f988..ba7fd6c8e 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java @@ -11,6 +11,10 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; public interface TradePriceCalculator { int ORDER_DISCOUNT_ACTIVITY = 10; + /** + * TODO @芋艿 快递运费的计算在满减之前。 例如有满多少包邮 + */ + int ORDER_DELIVERY = 15; int ORDER_REWARD_ACTIVITY = 20; int ORDER_COUPON = 30; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/MemberAddressDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/MemberAddressDO.java index 560edbba9..743849421 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/MemberAddressDO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/MemberAddressDO.java @@ -39,7 +39,7 @@ public class MemberAddressDO extends BaseDO { /** * 地区编号 */ - private Long areaId; + private Integer areaId; /** * 收件详细地址 */ From 36ce968893dfc218c6b5946ccb7305af58250ed0 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 3 Jun 2023 18:08:59 +0800 Subject: [PATCH 7/8] =?UTF-8?q?mall=20+=20trade=EF=BC=9Areview=20=E8=BF=90?= =?UTF-8?q?=E8=B4=B9=E4=BB=B7=E6=A0=BC=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/dal/dataobject/spu/ProductSpuDO.java | 2 +- .../delivery/DeliveryExpressTemplateService.java | 5 ++++- .../price/bo/TradePriceCalculateReqBO.java | 1 + .../calculator/TradeDeliveryPriceCalculator.java | 16 +++++++++++----- .../price/calculator/TradePriceCalculator.java | 10 ++++++---- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java index 5ee4f1d28..905bb890b 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java @@ -141,7 +141,7 @@ public class ProductSpuDO extends BaseDO { /** * 物流配置模板编号 * - * 对应 { TradeDeliveryExpressTemplateDO 的 id 编号} + * 对应 TradeDeliveryExpressTemplateDO 的 id 编号 */ private Long deliveryTemplateId; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java index 2b69c6147..e4fb5e69a 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java @@ -66,8 +66,11 @@ public interface DeliveryExpressTemplateService { /** * 校验快递运费模板 + * + * 如果校验不通过,抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 异常 + * * @param templateId 模板编号 - * @return DeliveryExpressTemplateDO 非空 + * @return 快递运费模板 */ DeliveryExpressTemplateDO validateDeliveryExpressTemplate(Long templateId); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java index eb59f22e0..7f9d333ae 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java @@ -58,6 +58,7 @@ public class TradePriceCalculateReqBO { * * 关联 {@link DeliveryExpressTemplateDO#getId()} */ + // TODO @jason:运费模版,是不是每个 SKU 传入哈 private Long templateId; /** diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java index aaefc76c5..1f53bf8cc 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java @@ -34,12 +34,15 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @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 @@ -106,20 +109,22 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { // 得到SKU 详情。得到 重量体积 Map skuRespMap = convertMap(productSkuApi.getSkuList(skuIds), ProductSkuRespDTO::getId); // 一个 spuId 可能对应多条订单商品 SKU + // TODO @jason:得确认下,按照 sku 算,还是 spu 算; Map> spuIdItemMap = convertMultiMap(selectedItem, OrderItem::getSpuId); // 依次计算每个 SPU 的快递运费 for (Map.Entry> entry : spuIdItemMap.entrySet()) { List orderItems = entry.getValue(); // 总件数, 总金额, 总重量, 总体积 - int totalCount = 0, totalPrice = 0; + int totalCount = 0; + int totalPrice = 0; double totalWeight = 0; double totalVolume = 0; for (OrderItem orderItem : orderItems) { totalCount += orderItem.getCount(); - totalPrice += orderItem.getPrice(); + totalPrice += orderItem.getPrice(); // TODO jason:应该按照 payPrice? ProductSkuRespDTO skuResp = skuRespMap.get(orderItem.getSkuId()); if (skuResp != null) { - totalWeight = totalWeight + skuResp.getWeight(); + totalWeight = totalWeight + skuResp.getWeight(); // TODO @jason:* 数量 totalVolume = totalVolume + skuResp.getVolume(); } } @@ -130,6 +135,7 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { continue; } // 计算快递运费 + // TODO @jason:貌似也可以抽成 checkExpressFree 类似方法 if (areaTemplateChargeMap.containsKey(receiverAreaId)) { DeliveryExpressTemplateChargeDO templateCharge = areaTemplateChargeMap.get(receiverAreaId); DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode); @@ -170,8 +176,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { int extraPrice = templateCharge.getExtraPrice() * extraNum; deliveryPrice = templateCharge.getStartPrice() + extraPrice; } - // - // TODO @芋艿 分摊快递费用到 SKU. 是不是搞复杂了 + // TODO @芋艿 分摊快递费用到 SKU. 是不是搞复杂了; + // TODO @jason:因为退费的时候,可能按照 SKU 考虑退费金额 divideDeliveryPrice(deliveryPrice, orderItems); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java index ba7fd6c8e..92ae9c2ee 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java @@ -11,12 +11,14 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; public interface TradePriceCalculator { int ORDER_DISCOUNT_ACTIVITY = 10; - /** - * TODO @芋艿 快递运费的计算在满减之前。 例如有满多少包邮 - */ - int ORDER_DELIVERY = 15; int ORDER_REWARD_ACTIVITY = 20; int ORDER_COUPON = 30; + /** + * 快递运费的计算 + * + * 放在各种营销活动、优惠劵后面 TODO + */ + int ORDER_DELIVERY = 40; void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result); From 910d374ceee729396afd932ab8665706ab46a11d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 3 Jun 2023 19:56:23 +0800 Subject: [PATCH 8/8] =?UTF-8?q?mall=20+=20trade=EF=BC=9Areview=20=E8=BF=90?= =?UTF-8?q?=E8=B4=B9=E4=BB=B7=E6=A0=BC=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeliveryExpressTemplateController.java | 6 ++--- .../DeliveryExpressTemplateDetailRespVO.java | 25 +++++++++++++++++++ .../DeliveryExpressTemplateRespVO.java | 19 ++++---------- .../DeliveryExpressTemplateSimpleRespVO.java | 21 ---------------- .../DeliveryExpressTemplateConvert.java | 16 ++++++------ .../DeliveryExpressTemplateService.java | 4 +-- .../DeliveryExpressTemplateServiceImpl.java | 2 +- 7 files changed, 44 insertions(+), 49 deletions(-) create mode 100644 yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java delete mode 100644 yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressTemplateController.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressTemplateController.java index 1dcab4055..7a6068827 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressTemplateController.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryExpressTemplateController.java @@ -57,7 +57,7 @@ public class DeliveryExpressTemplateController { @Operation(summary = "获得快递运费模板") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')") - public CommonResult getDeliveryExpressTemplate(@RequestParam("id") Long id) { + public CommonResult getDeliveryExpressTemplate(@RequestParam("id") Long id) { return success(deliveryExpressTemplateService.getDeliveryExpressTemplate(id)); } @@ -65,7 +65,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> getDeliveryExpressTemplateList(@RequestParam("ids") Collection ids) { + public CommonResult> getDeliveryExpressTemplateList(@RequestParam("ids") Collection ids) { List list = deliveryExpressTemplateService.getDeliveryExpressTemplateList(ids); return success(DeliveryExpressTemplateConvert.INSTANCE.convertList(list)); } @@ -73,7 +73,7 @@ public class DeliveryExpressTemplateController { @GetMapping("/page") @Operation(summary = "获得快递运费模板分页") @PreAuthorize("@ss.hasPermission('trade:delivery:express-template:query')") - public CommonResult> getDeliveryExpressTemplatePage(@Valid DeliveryExpressTemplatePageReqVO pageVO) { + public CommonResult> getDeliveryExpressTemplatePage(@Valid DeliveryExpressTemplatePageReqVO pageVO) { PageResult pageResult = deliveryExpressTemplateService.getDeliveryExpressTemplatePage(pageVO); return success(DeliveryExpressTemplateConvert.INSTANCE.convertPage(pageResult)); } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java new file mode 100644 index 000000000..44cb042f9 --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateDetailRespVO.java @@ -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 templateCharge; + + @Schema(description = "运费模板包邮区域", requiredMode = Schema.RequiredMode.REQUIRED) + private List templateFree; + +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java index a03a8e61b..222cab9fd 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateRespVO.java @@ -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 templateCharge; - - @Schema(description = "运费模板包邮区域", requiredMode = Schema.RequiredMode.REQUIRED) - private List templateFree; + @Schema(description = "创建时间", required = true) + private LocalDateTime createTime; } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java deleted file mode 100644 index 957ce3c08..000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/expresstemplate/DeliveryExpressTemplateSimpleRespVO.java +++ /dev/null @@ -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; - -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java index 424c7e132..af247367c 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryExpressTemplateConvert.java @@ -22,18 +22,18 @@ public interface DeliveryExpressTemplateConvert { DeliveryExpressTemplateDO convert(DeliveryExpressTemplateUpdateReqVO bean); - DeliveryExpressTemplateSimpleRespVO convert(DeliveryExpressTemplateDO bean); + DeliveryExpressTemplateRespVO convert(DeliveryExpressTemplateDO bean); - DeliveryExpressTemplateRespVO convert2(DeliveryExpressTemplateDO bean); + DeliveryExpressTemplateDetailRespVO convert2(DeliveryExpressTemplateDO bean); - List convertList(List list); + List convertList(List list); - PageResult convertPage(PageResult page); + PageResult convertPage(PageResult page); - default DeliveryExpressTemplateRespVO convert(DeliveryExpressTemplateDO bean, - List chargeList, - List freeList){ - DeliveryExpressTemplateRespVO respVO = convert2(bean); + default DeliveryExpressTemplateDetailRespVO convert(DeliveryExpressTemplateDO bean, + List chargeList, + List freeList){ + DeliveryExpressTemplateDetailRespVO respVO = convert2(bean); respVO.setTemplateCharge(convertTemplateChargeList(chargeList)); respVO.setTemplateFree(convertTemplateFreeList(freeList)); return respVO; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java index e4fb5e69a..46a2d89b6 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateService.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.trade.service.delivery; 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); /** * 获得快递运费模板列表 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java index 50a24e3bf..3e71bb1c9 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryExpressTemplateServiceImpl.java @@ -185,7 +185,7 @@ public class DeliveryExpressTemplateServiceImpl implements DeliveryExpressTempla } @Override - public DeliveryExpressTemplateRespVO getDeliveryExpressTemplate(Long id) { + public DeliveryExpressTemplateDetailRespVO getDeliveryExpressTemplate(Long id) { List chargeList = expressTemplateChargeMapper.selectListByTemplateId(id); List freeList = expressTemplateFreeMapper.selectListByTemplateId(id); DeliveryExpressTemplateDO template = expressTemplateMapper.selectById(id);