mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-30 10:05:59 +08:00 
			
		
		
		
	完成支付单的单体轮廓
This commit is contained in:
		| @ -1,15 +1,19 @@ | ||||
| package cn.iocoder.yudao.coreservice.modules.pay.convert.order; | ||||
| 
 | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO; | ||||
| import org.mapstruct.Mapper; | ||||
| import org.mapstruct.factory.Mappers; | ||||
| 
 | ||||
| @Mapper | ||||
| public interface PayOrderCoreServiceConvert { | ||||
| public interface PayOrderCoreConvert { | ||||
| 
 | ||||
|     PayOrderCoreServiceConvert INSTANCE = Mappers.getMapper(PayOrderCoreServiceConvert.class); | ||||
|     PayOrderCoreConvert INSTANCE = Mappers.getMapper(PayOrderCoreConvert.class); | ||||
| 
 | ||||
|     PayOrderDO convert(PayOrderCreateReqDTO bean); | ||||
| 
 | ||||
|     PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean); | ||||
| 
 | ||||
| } | ||||
| @ -28,12 +28,6 @@ public class PayOrderDO extends BaseDO { | ||||
|      * 订单编号,数据库自增 | ||||
|      */ | ||||
|     private Long id; | ||||
| //    /** | ||||
| //     * 订单号,根据规则生成 | ||||
| //     * | ||||
| //     * 例如说,P202110132239124200055 | ||||
| //     */ | ||||
| //    private String no; | ||||
|     /** | ||||
|      * 商户编号 | ||||
|      * | ||||
| @ -53,7 +47,7 @@ public class PayOrderDO extends BaseDO { | ||||
|      */ | ||||
|     private Long channelId; | ||||
|     /** | ||||
|      * 商户编码 | ||||
|      * 渠道编码 | ||||
|      * | ||||
|      * 枚举 {@link PayChannelCodeEnum} | ||||
|      */ | ||||
| @ -132,6 +126,12 @@ public class PayOrderDO extends BaseDO { | ||||
|      * 页面跳转地址 | ||||
|      */ | ||||
|     private String returnUrl; | ||||
|     /** | ||||
|      * 支付成功的订单拓展单编号 | ||||
|      * | ||||
|      * 关联 {@link PayOrderDO#getId()} | ||||
|      */ | ||||
|     private Long successExtensionId; | ||||
|  | ||||
|     // TODO 芋艿:可能要优化 | ||||
|     /** | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order; | ||||
|  | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum; | ||||
| import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||||
| import com.baomidou.mybatisplus.annotation.TableName; | ||||
| import lombok.Data; | ||||
| @ -7,48 +9,62 @@ import lombok.EqualsAndHashCode; | ||||
| import lombok.experimental.Accessors; | ||||
|  | ||||
| /** | ||||
|  * 交易扩展表 | ||||
|  * 支付订单拓展 DO | ||||
|  * | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @TableName("pay_transaction_extension") | ||||
| @TableName("pay_order_extension") | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @Accessors(chain = true) | ||||
| public class PayOrderExtensionDO extends BaseDO { | ||||
|  | ||||
|     /** | ||||
|      * 编号,自增 | ||||
|      * 订单拓展编号,数据库自增 | ||||
|      */ | ||||
|     private Integer id; | ||||
|     private Long id; | ||||
|     /** | ||||
|      * 交易编号 {@link PayTransactionDO#getId()} | ||||
|      */ | ||||
|     private Integer transactionId; | ||||
|     /** | ||||
|      * 选择的支付渠道 | ||||
|      */ | ||||
|     private Integer payChannel; | ||||
|     /** | ||||
|      * 生成传输给第三方的订单号 | ||||
|      * 订单号,根据规则生成 | ||||
|      * 调用支付渠道时,使用该字段作为对接的订单号。 | ||||
|      * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no | ||||
|      * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no | ||||
|      * | ||||
|      * 唯一索引 | ||||
|      * 例如说,P202110132239124200055 | ||||
|      */ | ||||
|     private String transactionCode; | ||||
|     private String no; | ||||
|     /** | ||||
|      * 扩展内容 | ||||
|      * 订单号 | ||||
|      * | ||||
|      * 异步通知的时候填充回调的数据 | ||||
|      * 关联 {@link PayOrderDO#getId()} | ||||
|      */ | ||||
|     private String extensionData; | ||||
|     private Long orderId; | ||||
|     /** | ||||
|      * 发起交易的 IP | ||||
|      */ | ||||
|     private String createIp; | ||||
|     /** | ||||
|      * 状态 | ||||
|      * 渠道编号 | ||||
|      * | ||||
|      * @see cn.iocoder.mall.payservice.enums.transaction.PayTransactionStatusEnum | ||||
|      * 关联 {@link PayChannelDO#getId()} | ||||
|      */ | ||||
|     private Long channelId; | ||||
|     /** | ||||
|      * 渠道编码 | ||||
|      */ | ||||
|     private Integer channelCode; | ||||
|     /** | ||||
|      * 客户端 IP | ||||
|      */ | ||||
|     private String clientIp; | ||||
|     /** | ||||
|      * 支付状态 | ||||
|      * | ||||
|      * 枚举 {@link PayOrderStatusEnum} | ||||
|      * 注意,只包含上述枚举的 WAITING 和 SUCCESS | ||||
|      */ | ||||
|     private Integer status; | ||||
|     /** | ||||
|      * 支付渠道异步通知的内容 | ||||
|      * | ||||
|      * 在支持成功后,会记录回调的数据 | ||||
|      */ | ||||
|     private String channelCallbackData; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,14 @@ | ||||
| package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant; | ||||
|  | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | ||||
| import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||||
| import org.apache.ibatis.annotations.Mapper; | ||||
|  | ||||
| @Mapper | ||||
| public interface PayChannelCoreMapper extends BaseMapperX<PayChannelDO> { | ||||
|  | ||||
|     default PayChannelDO selectByAppIdAndCode(Long appId, String code) { | ||||
|         return selectOne("app_id", appId, "code", code); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order; | ||||
|  | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO; | ||||
| import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | ||||
| import org.apache.ibatis.annotations.Mapper; | ||||
|  | ||||
| @Mapper | ||||
| public interface PayOrderExtensionCoreMapper extends BaseMapperX<PayOrderExtensionDO> { | ||||
|  | ||||
| } | ||||
| @ -13,4 +13,18 @@ public interface PayErrorCodeConstants { | ||||
|     ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在"); | ||||
|     ErrorCode PAY_APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用"); | ||||
|  | ||||
|     // ========== CHANNEL 模块 1-007-001-000 ========== | ||||
|     ErrorCode PAY_CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在"); | ||||
|     ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001000, "支付渠道已经禁用"); | ||||
|  | ||||
|     // ========== ORDER 模块 1-007-002-000 ========== | ||||
|     ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(100401000, "支付订单不存在"); | ||||
|     ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(100401001, "支付订单不处于待支付"); | ||||
|     ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(100401002, "支付订单不处于已支付"); | ||||
|     ErrorCode PAY_ORDER_ERROR_USER = new ErrorCode(100401003, "支付订单用户不正确"); | ||||
|  | ||||
|     ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(100401050, "支付交易拓展单不存在"); | ||||
|     ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(100401051, "支付交易拓展单不处于待支付"); | ||||
|     ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(100401052, "支付订单不处于已支付"); | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,24 @@ | ||||
| package cn.iocoder.yudao.coreservice.modules.pay.service.merchant; | ||||
|  | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | ||||
| import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||||
|  | ||||
| /** | ||||
|  * 支付渠道 Core Service 接口 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| public interface PayChannelCoreService { | ||||
|  | ||||
|     /** | ||||
|      * 支付渠道的合法性 | ||||
|      * | ||||
|      * 如果不合法,抛出 {@link ServiceException} 业务异常 | ||||
|      * | ||||
|      * @param appId 应用编号 | ||||
|      * @param code 支付渠道 | ||||
|      * @return 渠道信息 | ||||
|      */ | ||||
|     PayChannelDO validPayChannel(Long appId, String code); | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,42 @@ | ||||
| package cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl; | ||||
|  | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant.PayChannelCoreMapper; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import javax.validation.Valid; | ||||
|  | ||||
| import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.*; | ||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||
|  | ||||
| /** | ||||
|  * 支付渠道 Core Service 实现类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Service | ||||
| @Valid | ||||
| @Slf4j | ||||
| public class PayChannelCoreServiceImpl implements PayChannelCoreService { | ||||
|  | ||||
|     @Resource | ||||
|     private PayChannelCoreMapper payChannelCoreMapper; | ||||
|  | ||||
|     @Override | ||||
|     public PayChannelDO validPayChannel(Long appId, String code) { | ||||
|         PayChannelDO channel = payChannelCoreMapper.selectByAppIdAndCode(appId, code); | ||||
|         if (channel == null) { | ||||
|             throw exception(PAY_CHANNEL_NOT_FOUND); | ||||
|         } | ||||
|         if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) { | ||||
|             throw exception(PayErrorCodeConstants.PAY_CHANNEL_IS_DISABLE); | ||||
|         } | ||||
|         return channel; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -17,8 +17,8 @@ public class PayOrderSubmitReqDTO implements Serializable { | ||||
|     /** | ||||
|      * 应用编号 | ||||
|      */ | ||||
|     @NotEmpty(message = "应用编号不能为空") | ||||
|     private String appId; | ||||
|     @NotNull(message = "应用编号不能为空") | ||||
|     private Long appId; | ||||
|  | ||||
|     /** | ||||
|      * 支付单编号 | ||||
|  | ||||
| @ -1,11 +1,17 @@ | ||||
| package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; | ||||
|  | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayOrderCoreServiceConvert; | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.core.util.RandomUtil; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayOrderCoreConvert; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderExtensionCoreMapper; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO; | ||||
| import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO; | ||||
| @ -17,9 +23,16 @@ import org.springframework.stereotype.Service; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import javax.validation.Valid; | ||||
| import java.util.Date; | ||||
|  | ||||
| import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.PAY_ORDER_NOT_FOUND; | ||||
| import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING; | ||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||
|  | ||||
| /** | ||||
|  * 支付订单 Core Service 实现类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Service | ||||
| @Valid | ||||
| @ -28,9 +41,13 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService { | ||||
|  | ||||
|     @Resource | ||||
|     private PayAppCoreService payAppCoreService; | ||||
|     @Resource | ||||
|     private PayChannelCoreService payChannelCoreService; | ||||
|  | ||||
|     @Resource | ||||
|     private PayOrderCoreMapper payOrderCoreMapper; | ||||
|     @Resource | ||||
|     private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper; | ||||
|  | ||||
|     @Override | ||||
|     public Long createPayOrder(PayOrderCreateReqDTO reqDTO) { | ||||
| @ -47,7 +64,8 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService { | ||||
|         } | ||||
|  | ||||
|         // 创建支付交易单 | ||||
|         order = PayOrderCoreServiceConvert.INSTANCE.convert(reqDTO) | ||||
|         // TODO 芋艿:需要看看,还有啥要补全的字段 | ||||
|         order = PayOrderCoreConvert.INSTANCE.convert(reqDTO) | ||||
|                 .setStatus(PayOrderStatusEnum.WAITING.getStatus()) | ||||
|                 .setNotifyUrl(app.getPayNotifyUrl()); | ||||
|         payOrderCoreMapper.insert(order); | ||||
| @ -59,31 +77,50 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService { | ||||
|     public PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO) { | ||||
|         // 校验 App | ||||
|         PayAppDO app = payAppCoreService.validPayApp(reqDTO.getId()); | ||||
|         // TODO 校验支付渠道是否有效 | ||||
|         // 校验支付渠道是否有效 | ||||
|         PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getId(), reqDTO.getChannelCode()); | ||||
|  | ||||
|         // 获得 PayOrderDO ,并校验其是否存在 | ||||
|         PayOrderDO order = payOrderCoreMapper.selectById(reqDTO.getId()); | ||||
|         if (order == null) { // 是否存在 | ||||
|             throw exception(PAY_TRANSACTION_NOT_FOUND); | ||||
|         if (order == null || order.getAppId().equals(reqDTO.getAppId())) { // 是否存在 | ||||
|             throw exception(PAY_ORDER_NOT_FOUND); | ||||
|         } | ||||
|         if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 | ||||
|             throw exception(PAY_TRANSACTION_STATUS_IS_NOT_WAITING); | ||||
|             throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); | ||||
|         } | ||||
|  | ||||
|         // 插入 PayTransactionExtensionDO | ||||
|         PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(submitReqDTO) | ||||
|                 .setTransactionId(payTransaction.getId()).setTransactionCode(generateTransactionCode()) | ||||
|                 .setStatus(PayTransactionStatusEnum.WAITING.getStatus()); | ||||
|         payTransactionExtensionMapper.insert(payTransactionExtensionDO); | ||||
|         // 插入 PayOrderExtensionDO | ||||
|         PayOrderExtensionDO orderExtension = PayOrderCoreConvert.INSTANCE.convert(reqDTO) | ||||
|                 .setOrderId(order.getId()).setNo(generateOrderExtensionNo()) | ||||
|                 .setStatus(PayOrderStatusEnum.WAITING.getStatus()); | ||||
|         payOrderExtensionCoreMapper.insert(orderExtension); | ||||
|  | ||||
|         // 调用三方接口 | ||||
|         AbstractThirdPayClient thirdPayClient = ThirdPayClientFactory.getThirdPayClient(submitReqDTO.getPayChannel()); | ||||
|         CommonResult<String> invokeResult = thirdPayClient.submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null | ||||
|         CommonResult<String> invokeResult = thirdPayClient.submitTransaction(payTransaction, orderExtension, null); // TODO 暂时传入 extra = null | ||||
|         invokeResult.checkError(); | ||||
|  | ||||
|         // TODO 轮询三方接口,是否已经支付的任务 | ||||
|         // 返回成功 | ||||
|         return new PayTransactionSubmitRespDTO().setId(payTransactionExtensionDO.getId()).setInvokeResponse(invokeResult.getData()); | ||||
|         return new PayOrderSubmitRespDTO().setExtensionId(orderExtension.getId()).setInvokeResponse(invokeResult.getData()); | ||||
|     } | ||||
|  | ||||
|     private String generateOrderExtensionNo() { | ||||
| //    wx | ||||
| //    2014 | ||||
| //    10 | ||||
| //    27 | ||||
| //    20 | ||||
| //    09 | ||||
| //    39 | ||||
| //    5522657 | ||||
| //    a690389285100 | ||||
|         // 目前的算法 | ||||
|         // 时间序列,年月日时分秒 14 位 | ||||
|         // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整 | ||||
|         return DateUtil.format(new Date(), "yyyyMMddHHmmss") + // 时间序列 | ||||
|                 RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒 | ||||
|                 ; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -28,6 +28,10 @@ public interface BaseMapperX<T> extends BaseMapper<T> { | ||||
|         return selectOne(new QueryWrapper<T>().eq(field, value)); | ||||
|     } | ||||
|  | ||||
|     default T selectOne(String field1, Object value1, String field2, Object value2) { | ||||
|         return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2)); | ||||
|     } | ||||
|  | ||||
|     default Integer selectCount(String field, Object value) { | ||||
|         return selectCount(new QueryWrapper<T>().eq(field, value)); | ||||
|     } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV