Commit 24f4fd4fee978ede3621062d88e91641505aa4e9

Authored by YunaiV
1 parent 81126b2b

完成支付单的单体轮廓

yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreServiceConvert.java renamed to yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreConvert.java
1 1 package cn.iocoder.yudao.coreservice.modules.pay.convert.order;
2 2  
3 3 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
  4 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
4 5 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO;
  6 +import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO;
5 7 import org.mapstruct.Mapper;
6 8 import org.mapstruct.factory.Mappers;
7 9  
8 10 @Mapper
9   -public interface PayOrderCoreServiceConvert {
  11 +public interface PayOrderCoreConvert {
10 12  
11   - PayOrderCoreServiceConvert INSTANCE = Mappers.getMapper(PayOrderCoreServiceConvert.class);
  13 + PayOrderCoreConvert INSTANCE = Mappers.getMapper(PayOrderCoreConvert.class);
12 14  
13 15 PayOrderDO convert(PayOrderCreateReqDTO bean);
14 16  
  17 + PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean);
  18 +
15 19 }
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java
... ... @@ -28,12 +28,6 @@ public class PayOrderDO extends BaseDO {
28 28 * 订单编号,数据库自增
29 29 */
30 30 private Long id;
31   -// /**
32   -// * 订单号,根据规则生成
33   -// *
34   -// * 例如说,P202110132239124200055
35   -// */
36   -// private String no;
37 31 /**
38 32 * 商户编号
39 33 *
... ... @@ -53,7 +47,7 @@ public class PayOrderDO extends BaseDO {
53 47 */
54 48 private Long channelId;
55 49 /**
56   - * 商户编码
  50 + * 渠道编码
57 51 *
58 52 * 枚举 {@link PayChannelCodeEnum}
59 53 */
... ... @@ -132,6 +126,12 @@ public class PayOrderDO extends BaseDO {
132 126 * 页面跳转地址
133 127 */
134 128 private String returnUrl;
  129 + /**
  130 + * 支付成功的订单拓展单编号
  131 + *
  132 + * 关联 {@link PayOrderDO#getId()}
  133 + */
  134 + private Long successExtensionId;
135 135  
136 136 // TODO 芋艿:可能要优化
137 137 /**
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderExtensionDO.java
1 1 package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order;
2 2  
  3 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
  4 +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum;
3 5 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 6 import com.baomidou.mybatisplus.annotation.TableName;
5 7 import lombok.Data;
... ... @@ -7,48 +9,62 @@ import lombok.EqualsAndHashCode;
7 9 import lombok.experimental.Accessors;
8 10  
9 11 /**
10   - * 交易扩展表
  12 + * 支付订单拓展 DO
  13 + *
  14 + *
  15 + * @author 芋道源码
11 16 */
12   -@TableName("pay_transaction_extension")
  17 +@TableName("pay_order_extension")
13 18 @Data
14 19 @EqualsAndHashCode(callSuper = true)
15 20 @Accessors(chain = true)
16 21 public class PayOrderExtensionDO extends BaseDO {
17 22  
18 23 /**
19   - * 编号,自增
  24 + * 订单拓展编号,数据库自增
20 25 */
21   - private Integer id;
  26 + private Long id;
22 27 /**
23   - * 交易编号 {@link PayTransactionDO#getId()}
  28 + * 订单号,根据规则生成
  29 + * 调用支付渠道时,使用该字段作为对接的订单号。
  30 + * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no
  31 + * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no
  32 + *
  33 + * 例如说,P202110132239124200055
24 34 */
25   - private Integer transactionId;
  35 + private String no;
26 36 /**
27   - * 选择的支付渠道
  37 + * 订单号
  38 + *
  39 + * 关联 {@link PayOrderDO#getId()}
28 40 */
29   - private Integer payChannel;
  41 + private Long orderId;
30 42 /**
31   - * 生成传输给第三方的订单号
  43 + * 渠道编号
32 44 *
33   - * 唯一索引
  45 + * 关联 {@link PayChannelDO#getId()}
34 46 */
35   - private String transactionCode;
  47 + private Long channelId;
36 48 /**
37   - * 扩展内容
38   - *
39   - * 异步通知的时候填充回调的数据
  49 + * 渠道编码
40 50 */
41   - private String extensionData;
  51 + private Integer channelCode;
42 52 /**
43   - * 发起交易的 IP
  53 + * 客户端 IP
44 54 */
45   - private String createIp;
  55 + private String clientIp;
46 56 /**
47   - * 状态
  57 + * 支付状态
48 58 *
49   - * @see cn.iocoder.mall.payservice.enums.transaction.PayTransactionStatusEnum
  59 + * 枚举 {@link PayOrderStatusEnum}
50 60 * 注意,只包含上述枚举的 WAITING 和 SUCCESS
51 61 */
52 62 private Integer status;
  63 + /**
  64 + * 支付渠道异步通知的内容
  65 + *
  66 + * 在支持成功后,会记录回调的数据
  67 + */
  68 + private String channelCallbackData;
53 69  
54 70 }
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/merchant/PayChannelCoreMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant;
  2 +
  3 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +@Mapper
  8 +public interface PayChannelCoreMapper extends BaseMapperX<PayChannelDO> {
  9 +
  10 + default PayChannelDO selectByAppIdAndCode(Long appId, String code) {
  11 + return selectOne("app_id", appId, "code", code);
  12 + }
  13 +
  14 +}
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayOrderExtensionCoreMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order;
  2 +
  3 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
  4 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +
  9 +@Mapper
  10 +public interface PayOrderExtensionCoreMapper extends BaseMapperX<PayOrderExtensionDO> {
  11 +
  12 +}
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeConstants.java
... ... @@ -13,4 +13,18 @@ public interface PayErrorCodeConstants {
13 13 ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
14 14 ErrorCode PAY_APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用");
15 15  
  16 + // ========== CHANNEL 模块 1-007-001-000 ==========
  17 + ErrorCode PAY_CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在");
  18 + ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001000, "支付渠道已经禁用");
  19 +
  20 + // ========== ORDER 模块 1-007-002-000 ==========
  21 + ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(100401000, "支付订单不存在");
  22 + ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(100401001, "支付订单不处于待支付");
  23 + ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(100401002, "支付订单不处于已支付");
  24 + ErrorCode PAY_ORDER_ERROR_USER = new ErrorCode(100401003, "支付订单用户不正确");
  25 +
  26 + ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(100401050, "支付交易拓展单不存在");
  27 + ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(100401051, "支付交易拓展单不处于待支付");
  28 + ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(100401052, "支付订单不处于已支付");
  29 +
16 30 }
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/PayChannelCoreService.java 0 → 100644
  1 +package cn.iocoder.yudao.coreservice.modules.pay.service.merchant;
  2 +
  3 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
  4 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
  5 +
  6 +/**
  7 + * 支付渠道 Core Service 接口
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public interface PayChannelCoreService {
  12 +
  13 + /**
  14 + * 支付渠道的合法性
  15 + *
  16 + * 如果不合法,抛出 {@link ServiceException} 业务异常
  17 + *
  18 + * @param appId 应用编号
  19 + * @param code 支付渠道
  20 + * @return 渠道信息
  21 + */
  22 + PayChannelDO validPayChannel(Long appId, String code);
  23 +
  24 +}
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayChannelCoreServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl;
  2 +
  3 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
  4 +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant.PayChannelCoreMapper;
  5 +import cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants;
  6 +import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService;
  7 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +import javax.annotation.Resource;
  12 +import javax.validation.Valid;
  13 +
  14 +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.*;
  15 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  16 +
  17 +/**
  18 + * 支付渠道 Core Service 实现类
  19 + *
  20 + * @author 芋道源码
  21 + */
  22 +@Service
  23 +@Valid
  24 +@Slf4j
  25 +public class PayChannelCoreServiceImpl implements PayChannelCoreService {
  26 +
  27 + @Resource
  28 + private PayChannelCoreMapper payChannelCoreMapper;
  29 +
  30 + @Override
  31 + public PayChannelDO validPayChannel(Long appId, String code) {
  32 + PayChannelDO channel = payChannelCoreMapper.selectByAppIdAndCode(appId, code);
  33 + if (channel == null) {
  34 + throw exception(PAY_CHANNEL_NOT_FOUND);
  35 + }
  36 + if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) {
  37 + throw exception(PayErrorCodeConstants.PAY_CHANNEL_IS_DISABLE);
  38 + }
  39 + return channel;
  40 + }
  41 +
  42 +}
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderSubmitReqDTO.java
... ... @@ -17,8 +17,8 @@ public class PayOrderSubmitReqDTO implements Serializable {
17 17 /**
18 18 * 应用编号
19 19 */
20   - @NotEmpty(message = "应用编号不能为空")
21   - private String appId;
  20 + @NotNull(message = "应用编号不能为空")
  21 + private Long appId;
22 22  
23 23 /**
24 24 * 支付单编号
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java
1 1 package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
2 2  
3   -import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayOrderCoreServiceConvert;
  3 +import cn.hutool.core.date.DateUtil;
  4 +import cn.hutool.core.util.RandomUtil;
  5 +import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayOrderCoreConvert;
4 6 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
  7 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
5 8 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
  9 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
6 10 import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper;
  11 +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderExtensionCoreMapper;
7 12 import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum;
8 13 import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService;
  14 +import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService;
9 15 import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService;
10 16 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO;
11 17 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO;
... ... @@ -17,9 +23,16 @@ import org.springframework.stereotype.Service;
17 23  
18 24 import javax.annotation.Resource;
19 25 import javax.validation.Valid;
  26 +import java.util.Date;
  27 +
  28 +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.PAY_ORDER_NOT_FOUND;
  29 +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING;
  30 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20 31  
21 32 /**
22 33 * 支付订单 Core Service 实现类
  34 + *
  35 + * @author 芋道源码
23 36 */
24 37 @Service
25 38 @Valid
... ... @@ -28,9 +41,13 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
28 41  
29 42 @Resource
30 43 private PayAppCoreService payAppCoreService;
  44 + @Resource
  45 + private PayChannelCoreService payChannelCoreService;
31 46  
32 47 @Resource
33 48 private PayOrderCoreMapper payOrderCoreMapper;
  49 + @Resource
  50 + private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper;
34 51  
35 52 @Override
36 53 public Long createPayOrder(PayOrderCreateReqDTO reqDTO) {
... ... @@ -47,7 +64,8 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
47 64 }
48 65  
49 66 // 创建支付交易单
50   - order = PayOrderCoreServiceConvert.INSTANCE.convert(reqDTO)
  67 + // TODO 芋艿:需要看看,还有啥要补全的字段
  68 + order = PayOrderCoreConvert.INSTANCE.convert(reqDTO)
51 69 .setStatus(PayOrderStatusEnum.WAITING.getStatus())
52 70 .setNotifyUrl(app.getPayNotifyUrl());
53 71 payOrderCoreMapper.insert(order);
... ... @@ -59,31 +77,50 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
59 77 public PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO) {
60 78 // 校验 App
61 79 PayAppDO app = payAppCoreService.validPayApp(reqDTO.getId());
62   - // TODO 校验支付渠道是否有效
  80 + // 校验支付渠道是否有效
  81 + PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getId(), reqDTO.getChannelCode());
63 82  
64 83 // 获得 PayOrderDO ,并校验其是否存在
65 84 PayOrderDO order = payOrderCoreMapper.selectById(reqDTO.getId());
66   - if (order == null) { // 是否存在
67   - throw exception(PAY_TRANSACTION_NOT_FOUND);
  85 + if (order == null || order.getAppId().equals(reqDTO.getAppId())) { // 是否存在
  86 + throw exception(PAY_ORDER_NOT_FOUND);
68 87 }
69 88 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
70   - throw exception(PAY_TRANSACTION_STATUS_IS_NOT_WAITING);
  89 + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
71 90 }
72 91  
73   - // 插入 PayTransactionExtensionDO
74   - PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(submitReqDTO)
75   - .setTransactionId(payTransaction.getId()).setTransactionCode(generateTransactionCode())
76   - .setStatus(PayTransactionStatusEnum.WAITING.getStatus());
77   - payTransactionExtensionMapper.insert(payTransactionExtensionDO);
  92 + // 插入 PayOrderExtensionDO
  93 + PayOrderExtensionDO orderExtension = PayOrderCoreConvert.INSTANCE.convert(reqDTO)
  94 + .setOrderId(order.getId()).setNo(generateOrderExtensionNo())
  95 + .setStatus(PayOrderStatusEnum.WAITING.getStatus());
  96 + payOrderExtensionCoreMapper.insert(orderExtension);
78 97  
79 98 // 调用三方接口
80 99 AbstractThirdPayClient thirdPayClient = ThirdPayClientFactory.getThirdPayClient(submitReqDTO.getPayChannel());
81   - CommonResult<String> invokeResult = thirdPayClient.submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null
  100 + CommonResult<String> invokeResult = thirdPayClient.submitTransaction(payTransaction, orderExtension, null); // TODO 暂时传入 extra = null
82 101 invokeResult.checkError();
83 102  
84 103 // TODO 轮询三方接口,是否已经支付的任务
85 104 // 返回成功
86   - return new PayTransactionSubmitRespDTO().setId(payTransactionExtensionDO.getId()).setInvokeResponse(invokeResult.getData());
  105 + return new PayOrderSubmitRespDTO().setExtensionId(orderExtension.getId()).setInvokeResponse(invokeResult.getData());
  106 + }
  107 +
  108 + private String generateOrderExtensionNo() {
  109 +// wx
  110 +// 2014
  111 +// 10
  112 +// 27
  113 +// 20
  114 +// 09
  115 +// 39
  116 +// 5522657
  117 +// a690389285100
  118 + // 目前的算法
  119 + // 时间序列,年月日时分秒 14 位
  120 + // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整
  121 + return DateUtil.format(new Date(), "yyyyMMddHHmmss") + // 时间序列
  122 + RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒
  123 + ;
87 124 }
88 125  
89 126 }
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
... ... @@ -28,6 +28,10 @@ public interface BaseMapperX&lt;T&gt; extends BaseMapper&lt;T&gt; {
28 28 return selectOne(new QueryWrapper<T>().eq(field, value));
29 29 }
30 30  
  31 + default T selectOne(String field1, Object value1, String field2, Object value2) {
  32 + return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2));
  33 + }
  34 +
31 35 default Integer selectCount(String field, Object value) {
32 36 return selectCount(new QueryWrapper<T>().eq(field, value));
33 37 }
... ...