From 5934d6b0299b0091e1b652aeaf1bad7e968c6a1f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 10 Nov 2022 09:27:20 +0800 Subject: [PATCH] =?UTF-8?q?trade=EF=BC=9A=E5=AE=8C=E6=88=90=E4=B8=8B?= =?UTF-8?q?=E5=8D=95=E6=97=B6=EF=BC=8C=E5=88=9B=E5=BB=BA=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=8D=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../convert/order/TradeOrderConvert.java | 23 ++++++ .../trade/convert/pay/PayOrderConvert.java | 30 -------- .../order/config/TradeOrderProperties.java | 15 ++-- .../service/order/TradeOrderServiceImpl.java | 75 ++++--------------- .../service/order/TradeOrderServiceTest.java | 27 ++++++- .../api/order/PayOrderInfoCreateReqDTO.java | 9 +-- .../src/main/resources/application.yaml | 4 +- 7 files changed, 79 insertions(+), 104 deletions(-) delete mode 100644 yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java 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 15e9ae728..7b1ff5aa1 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 @@ -1,24 +1,31 @@ package cn.iocoder.yudao.module.trade.convert.order; +import cn.hutool.core.date.DateUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; +import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO; 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.app.order.vo.AppTradeOrderCreateReqVO; 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.TradeOrderItemRefundStatusEnum; +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; +import java.time.Duration; import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; @Mapper public interface TradeOrderConvert { @@ -67,4 +74,20 @@ public interface TradeOrderConvert { ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean); List convertList(List list); + default PayOrderInfoCreateReqDTO convert(TradeOrderDO tradeOrderDO, List tradeOrderItemDOs, + List spus, TradeOrderProperties tradeOrderProperties) { + PayOrderInfoCreateReqDTO createReqDTO = new PayOrderInfoCreateReqDTO() + .setAppId(tradeOrderProperties.getAppId()).setUserIp(tradeOrderDO.getUserIp()); + // 商户相关字段 + createReqDTO.setMerchantOrderId(String.valueOf(tradeOrderDO.getId())); + String subject = spus.get(0).getName(); + if (spus.size() > 1) { + subject += " 等多件"; + } + createReqDTO.setSubject(subject); + // 订单相关字段 + createReqDTO.setAmount(tradeOrderDO.getPayPrice()).setExpireTime(addTime(tradeOrderProperties.getExpireTime())); + return createReqDTO; + } + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java deleted file mode 100644 index a32d42526..000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/pay/PayOrderConvert.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.trade.convert.pay; - -import cn.hutool.core.date.DateUtil; -import cn.iocoder.yudao.module.pay.api.order.PayOrderInfoCreateReqDTO; -import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.mapstruct.Named; -import org.mapstruct.factory.Mappers; - -import java.util.Date; - -@Mapper -public interface PayOrderConvert { - - PayOrderConvert INSTANCE = Mappers.getMapper(PayOrderConvert.class); - - @Mappings({ - @Mapping(source = "payPrice", target = "amount"), - @Mapping(target = "expireTime", source = "cancelTime" , qualifiedByName = "convertCreateTimeToPayExpireTime") - }) - PayOrderInfoCreateReqDTO convert(TradeOrderDO tradeOrderDO); - - @Named("convertCreateTimeToPayExpireTime") - default Date convertCreateTimeToPayExpireTime(Date cancelTime) { - return DateUtil.offsetMinute(new Date(), 30); - } - -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java index dad8982d2..4d584f4c9 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java @@ -5,8 +5,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; import javax.validation.constraints.NotNull; +import java.time.Duration; /** + * 交易订单的配置项 + * * @author LeeYan9 * @since 2022-09-15 */ @@ -15,16 +18,16 @@ import javax.validation.constraints.NotNull; @Validated public class TradeOrderProperties { - /** - * 商户订单编号 - */ - @NotNull(message = "商户订单编号不能为空") - private String merchantOrderId; - /** * 应用编号 */ @NotNull(message = "应用编号不能为空") private Long appId; + /** + * 支付超时时间 + */ + @NotNull(message = "支付超时时间不能为空") + private Duration expireTime; + } 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 92b4e2ec7..95df76afe 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,10 @@ package cn.iocoder.yudao.module.trade.service.order; -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.text.StrBuilder; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.TerminalEnum; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.module.member.api.address.AddressApi; import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; @@ -30,7 +27,6 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; import cn.iocoder.yudao.module.trade.dal.mysql.orderitem.TradeOrderItemMapper; import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants; -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemRefundStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; @@ -58,10 +54,6 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREAT @Service public class TradeOrderServiceImpl implements TradeOrderService { - // TODO LeeYan9: 静态变量, 需要在最前面哈; 另外, 静态变量的注释最好写下; - private static final String BLANK_PLACEHOLDER = " "; - private static final String MULTIPLIER_PLACEHOLDER = "x"; - @Resource private TradeOrderMapper tradeOrderMapper; @Resource @@ -89,7 +81,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { // 商品 SKU 检查:可售状态、库存 List skus = validateSkuSaleable(createReqVO.getItems()); // 商品 SPU 检查:可售状态 - validateSpuSaleable(convertSet(skus, ProductSkuRespDTO::getSpuId)); + List spus = validateSpuSaleable(convertSet(skus, ProductSkuRespDTO::getSpuId)); // 用户收件地址的校验 AddressRespDTO address = validateAddress(userId, createReqVO.getAddressId()); @@ -102,55 +94,11 @@ public class TradeOrderServiceImpl implements TradeOrderService { List tradeOrderItems = createTradeOrderItems(tradeOrderDO, priceResp.getOrder().getItems(), skus); // 订单创建完后的逻辑 - afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems); + afterCreateTradeOrder(userId, createReqVO, tradeOrderDO, tradeOrderItems, spus); // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! return tradeOrderDO.getId(); } - private void fillPayOrderInfoFromItems(PayOrderInfoCreateReqDTO payOrderInfoCreateReqDTO, - List tradeOrderItems) { - // 填写 商品&应用信息 - payOrderInfoCreateReqDTO.setMerchantOrderId(tradeOrderProperties.getMerchantOrderId()); - payOrderInfoCreateReqDTO.setAppId(tradeOrderProperties.getAppId()); - - // 填写商品信息 - StrBuilder subject = new StrBuilder(); - StrBuilder body = new StrBuilder(); - for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) { - // append subject - subject.append(BLANK_PLACEHOLDER); - subject.append(tradeOrderItem.getName()); - // append body - body.append(BLANK_PLACEHOLDER); - body.append(tradeOrderItem.getName()); - body.append(MULTIPLIER_PLACEHOLDER); - body.append(tradeOrderItem.getCount()); - } - // 设置 subject & body - // TODO @LeeYan9: 可以抽象一个 StrUtils 方法; 或者看看 hutool 有没自带的哈 - payOrderInfoCreateReqDTO.setSubject(StrUtils.maxLength(subject.subString(1), 32)); - payOrderInfoCreateReqDTO.setBody(StrUtils.maxLength(body.subString(1), 128)); - } - - private void xfillItemsInfoFromSkuAndOrder(TradeOrderDO tradeOrderDO, List tradeOrderItems, - Map spuInfos) { - for (TradeOrderItemDO tradeOrderItem : tradeOrderItems) { - // 填充订单信息 - tradeOrderItem.setOrderId(tradeOrderDO.getId()); - tradeOrderItem.setUserId(tradeOrderDO.getUserId()); - // 填充SKU信息 - ProductSkuRespDTO skuInfoRespDTO = spuInfos.get(tradeOrderItem.getSkuId()); - tradeOrderItem.setSpuId(skuInfoRespDTO.getSpuId()); - tradeOrderItem.setPicUrl(skuInfoRespDTO.getPicUrl()); - tradeOrderItem.setName(skuInfoRespDTO.getName()); - tradeOrderItem.setRefundStatus(TradeOrderItemRefundStatusEnum.NONE.getStatus()); - // todo - List property = - BeanUtil.copyToList(skuInfoRespDTO.getProperties(), TradeOrderItemDO.Property.class); - tradeOrderItem.setProperties(property); - } - } - /** * 校验商品 SKU 是否可出售 * @@ -248,7 +196,8 @@ public class TradeOrderServiceImpl implements TradeOrderService { * @param tradeOrderDO 交易订单 */ private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, - TradeOrderDO tradeOrderDO, List tradeOrderItemDOs) { + TradeOrderDO tradeOrderDO, List tradeOrderItemDOs, + List spus) { // 下单时扣减商品库存 productSkuApi.updateSkuStock(new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(tradeOrderItemDOs))); @@ -262,13 +211,21 @@ public class TradeOrderServiceImpl implements TradeOrderService { .setOrderId(tradeOrderDO.getId())); } - // 构建预支付请求参数 - // TODO @LeeYan9: 需要更新到订单上 -// PayOrderInfoCreateReqDTO payOrderCreateReqDTO = PayOrderConvert.INSTANCE.convert(tradeOrderDO); -// fillPayOrderInfoFromItems(payOrderCreateReqDTO, tradeOrderItems); // 生成预支付 + createPayOrder(tradeOrderDO, tradeOrderItemDOs, spus); // 增加订单日志 TODO 芋艿:待实现 } + private void createPayOrder(TradeOrderDO tradeOrderDO, List tradeOrderItemDOs, + List spus) { + // 创建支付单,用于后续的支付 + PayOrderInfoCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert( + tradeOrderDO, tradeOrderItemDOs, spus, tradeOrderProperties); + Long payOrderId = payOrderApi.createPayOrder(payOrderCreateReqDTO); + + // 更新到交易单上 + tradeOrderMapper.updateById(new TradeOrderDO().setId(tradeOrderDO.getId()).setPayOrderId(payOrderId)); + } + } 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 e8c908d5a..669680c3d 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 @@ -25,12 +25,15 @@ import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig; +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatcher; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; +import java.time.Duration; import java.util.Arrays; import java.util.List; @@ -73,6 +76,15 @@ class TradeOrderServiceTest extends BaseDbUnitTest { @MockBean private CouponApi couponApi; + @MockBean + private TradeOrderProperties tradeOrderProperties; + + @BeforeEach + public void setUp() { + when(tradeOrderProperties.getAppId()).thenReturn(888L); + when(tradeOrderProperties.getExpireTime()).thenReturn(Duration.ofDays(1)); + } + @Test public void testCreateTradeOrder_success() { // 准备参数 @@ -92,7 +104,7 @@ class TradeOrderServiceTest extends BaseDbUnitTest { when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02)); // mock 方法(商品 SPU 检查) ProductSpuRespDTO spu01 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(11L) - .setStatus(ProductSpuStatusEnum.ENABLE.getStatus())); + .setStatus(ProductSpuStatusEnum.ENABLE.getStatus()).setName("商品 1")); ProductSpuRespDTO spu02 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(21L) .setStatus(ProductSpuStatusEnum.ENABLE.getStatus())); when(productSpuApi.getSpuList(eq(asSet(11L, 21L)))).thenReturn(Arrays.asList(spu01, spu02)); @@ -120,6 +132,17 @@ class TradeOrderServiceTest extends BaseDbUnitTest { assertEquals(priceCalculateReqDTO.getItems().get(1).getCount(), 4); return true; }))).thenReturn(new PriceCalculateRespDTO().setOrder(priceOrder)); + // mock 方法(创建支付单) + when(payOrderApi.createPayOrder(argThat(createReqDTO -> { + assertEquals(createReqDTO.getAppId(), 888L); + assertEquals(createReqDTO.getUserIp(), userIp); + assertNotNull(createReqDTO.getMerchantOrderId()); // 由于 tradeOrderId 后生成,只能校验非空 + assertEquals(createReqDTO.getSubject(), "商品 1 等多件"); + assertNull(createReqDTO.getBody()); + assertEquals(createReqDTO.getAmount(), 80); + assertNotNull(createReqDTO.getExpireTime()); + return true; + }))).thenReturn(1000L); // 调用方法 Long tradeOrderId = tradeOrderService.createTradeOrder(userId, userIp, reqVO); @@ -147,7 +170,7 @@ class TradeOrderServiceTest extends BaseDbUnitTest { assertEquals(tradeOrderDO.getDiscountPrice(), 0); assertEquals(tradeOrderDO.getAdjustPrice(), 0); assertEquals(tradeOrderDO.getPayPrice(), 80); - assertNull(tradeOrderDO.getPayOrderId()); + assertEquals(tradeOrderDO.getPayOrderId(), 1000L); assertNull(tradeOrderDO.getPayChannel()); assertNull(tradeOrderDO.getDeliveryTemplateId()); assertNull(tradeOrderDO.getExpressNo()); diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderInfoCreateReqDTO.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderInfoCreateReqDTO.java index fda4b3d8d..2e530660b 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderInfoCreateReqDTO.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderInfoCreateReqDTO.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.pay.api.order; import lombok.Data; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.Serializable; @@ -45,7 +45,7 @@ public class PayOrderInfoCreateReqDTO implements Serializable { /** * 商品描述 */ - @NotEmpty(message = "商品描述信息不能为空") +// @NotEmpty(message = "商品描述信息不能为空") // 允许空 @Length(max = 128, message = "商品描述信息长度不能超过128") private String body; @@ -55,8 +55,7 @@ public class PayOrderInfoCreateReqDTO implements Serializable { * 支付金额,单位:分 */ @NotNull(message = "支付金额不能为空") - // TODO @LeeYan9: 是不是 @Min 注解呀, 是 Integer 哈 - @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") + @Min(value = 1, message = "支付金额必须大于零") private Integer amount; /** @@ -65,4 +64,4 @@ public class PayOrderInfoCreateReqDTO implements Serializable { @NotNull(message = "支付过期时间不能为空") private Date expireTime; -} \ No newline at end of file +} diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 62e258e45..878948944 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -97,8 +97,8 @@ yudao: enable: true # 验证码的开关,默认为 true;注意,优先读取数据库 infra_config 的 yudao.captcha.enable,所以请从数据库修改,可能需要重启项目 trade: order: - app-id: 1 - merchant-order-id: 1 + app-id: 1 # 商户编号 + expire-time: 2h # 支付的过期时间 codegen: base-package: ${yudao.info.base-package} db-schemas: ${spring.datasource.dynamic.datasource.master.name}