Browse Source

转账 - 动态收款人字段修改

jason 1 year ago
parent
commit
86598dd177

+ 20 - 9
sql/mysql/pay_wallet.sql

@@ -5,18 +5,26 @@ DROP TABLE IF EXISTS `pay_transfer`;
 CREATE TABLE `pay_transfer`
 (
     `id`                   bigint       NOT NULL AUTO_INCREMENT COMMENT '编号',
-    `type`                 int          NOT NULL COMMENT '类型',
+    `no`                   varchar(64)  NOT NULL COMMENT '转账单号',
     `app_id`               bigint       NOT NULL COMMENT '应用编号',
+    `channel_id`           bigint       NOT NULL  COMMENT '转账渠道编号',
+    `channel_code`         varchar(32)  NOT NULL  COMMENT '转账渠道编码',
     `merchant_order_id`    varchar(64)  NOT NULL COMMENT '商户订单编号',
-    `price`                int          NOT NULL COMMENT '转账金额,单位:分',
-    `subject`              varchar(512) NOT NULL COMMENT '转账标题',
-    `payee_info`           varchar(512) NOT NULL COMMENT '收款人信息,不同类型和渠道不同',
+    `type`                 int          NOT NULL COMMENT '类型',
     `status`               tinyint      NOT NULL COMMENT '转账状态',
     `success_time`         datetime     NULL COMMENT '转账成功时间',
-    `extension_id`         bigint       NULL  COMMENT '转账渠道编号',
-    `no`                   varchar(64)  NULL COMMENT '转账单号',
-    `channel_id`           bigint       NULL  COMMENT '转账渠道编号',
-    `channel_code`         varchar(32)  NULL  COMMENT '转账渠道编码',
+    `price`                int          NOT NULL COMMENT '转账金额,单位:分',
+    `subject`              varchar(512) NOT NULL COMMENT '转账标题',
+    `alipay_logon_id`      varchar(64)  NULL COMMENT '支付宝登录号',
+    `alipay_account_name`  varchar(64)  NULL COMMENT '支付宝账号名称',
+    `openid`               varchar(64)  NULL COMMENT '微信 openId',
+    `wx_account_name`      varchar(64)  NULL COMMENT '微信账号名称',
+    `notify_url`           varchar(64)  NULL COMMENT '异步通知商户地址',
+    `channel_extras`       varchar(512) NULL DEFAULT NULL COMMENT '渠道的额外参数',
+    `channel_transfer_no`  varchar(64)  NULL DEFAULT NULL COMMENT '渠道转账单号',
+    `channel_error_code`   varchar(128) NULL DEFAULT NULL COMMENT '调用渠道的错误码',
+    `channel_error_msg`    varchar(256) NULL DEFAULT NULL COMMENT '调用渠道的错误提示',
+    `channel_notify_data`  varchar(4096) NULL DEFAULT NULL COMMENT '渠道的同步/异步通知的内容',
     `creator`              varchar(64)  CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
     `create_time`          datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
     `updater`              varchar(64)  CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
@@ -58,7 +66,10 @@ CREATE TABLE `pay_demo_transfer`  (
   `user_id` bigint UNSIGNED NOT NULL COMMENT '用户编号',
   `price` int NOT NULL COMMENT '转账金额,单位:分',
   `type`  int NOT NULL COMMENT '转账类型',
-  `payee_info` varchar(512) NOT NULL COMMENT '收款人信息,不同类型和渠道不同',
+  `alipay_logon_id`      varchar(64)  NULL COMMENT '支付宝登录号',
+  `alipay_account_name`  varchar(64)  NULL COMMENT '支付宝账号名称',
+  `openid`               varchar(64)  NULL COMMENT '微信 openId',
+  `wx_account_name`      varchar(64)  NULL COMMENT '微信账号名称',
   `transfer_status` tinyint      NOT NULL DEFAULT 0 COMMENT '转账状态',
   `pay_transfer_id` bigint NULL DEFAULT NULL COMMENT '转账订单编号',
   `pay_channel_code` varchar(16)  NULL DEFAULT NULL COMMENT '转账支付成功渠道',

+ 20 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java

@@ -58,6 +58,26 @@ public class PayTransferUnifiedReqDTO {
     @NotEmpty(message = "收款方信息 不能为空")
     private Map<String, String> payeeInfo;
 
+    /**
+     * 支付宝登录号
+     */
+    private String alipayLogonId;
+
+    /**
+     * 支付宝账号名称
+     */
+    private String alipayAccountName;
+
+    /**
+     * 微信 openId
+     */
+    private String openid;
+
+    /**
+     * 微信账号名称
+     */
+    private String wxAccountName;
+
     /**
      * 支付渠道的额外参数
      */

+ 7 - 9
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java

@@ -65,15 +65,13 @@ public interface ErrorCodeConstants {
 
     // ========== 转账模块 1-007-009-000 ==========
     ErrorCode PAY_TRANSFER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_009_000, "发起转账报错,错误码:{},错误提示:{}");
-    ErrorCode PAY_TRANSFER_ALIPAY_LOGIN_ID_IS_EMPTY = new ErrorCode(1_007_009_001, "支付宝登录 ID 不能为空");
-    ErrorCode PAY_TRANSFER_ALIPAY_ACCOUNT_NAME_IS_EMPTY = new ErrorCode(1_007_009_002, "支付宝账号名称不能为空");
-    ErrorCode PAY_TRANSFER_NOT_FOUND = new ErrorCode(1_007_009_003, "转账交易单不存在");
-    ErrorCode PAY_TRANSFER_STATUS_IS_SUCCESS = new ErrorCode(1_007_009_004, "转账单已成功转账");
-    ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_009_005, "转账单不处于待转账");
-    ErrorCode PAY_TRANSFER_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_006, "转账单不处于待转账或转账中");
-    ErrorCode PAY_TRANSFER_EXTENSION_NOT_FOUND = new ErrorCode(1_007_009_007, "转账交易拓展单不存在");
-    ErrorCode PAY_TRANSFER_TYPE_AND_CHANNEL_NOT_MATCH = new ErrorCode(1_007_009_008, "转账类型和转账渠道不匹配");
-    ErrorCode PAY_TRANSFER_EXTENSION_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_009, "转账拓展单不处于待转账或转账中");
+    ErrorCode PAY_TRANSFER_NOT_FOUND = new ErrorCode(1_007_009_001, "转账交易单不存在");
+    ErrorCode PAY_TRANSFER_STATUS_IS_SUCCESS = new ErrorCode(1_007_009_002, "转账单已成功转账");
+    ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_009_003, "转账单不处于待转账");
+    ErrorCode PAY_TRANSFER_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_004, "转账单不处于待转账或转账中");
+    ErrorCode PAY_TRANSFER_EXTENSION_NOT_FOUND = new ErrorCode(1_007_009_005, "转账交易拓展单不存在");
+    ErrorCode PAY_TRANSFER_TYPE_AND_CHANNEL_NOT_MATCH = new ErrorCode(1_007_009_006, "转账类型和转账渠道不匹配");
+    ErrorCode PAY_TRANSFER_EXTENSION_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_007, "转账拓展单不处于待转账或转账中");
 
     // ========== 示例订单 1-007-900-000 ==========
     ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1_007_900_000, "示例订单不存在");

+ 44 - 6
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java

@@ -1,14 +1,15 @@
 package cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer;
 
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
+import javax.validation.Validator;
 import javax.validation.constraints.Min;
-import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
-import java.util.Map;
 
 /**
  * @author jason
@@ -22,13 +23,50 @@ public class PayDemoTransferCreateReqVO {
     @InEnum(PayTransferTypeEnum.class)
     private Integer type;
 
+    @Schema(description = "转账金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
     @NotNull(message = "转账金额不能为空")
     @Min(value = 1, message = "转账金额必须大于零")
     private Integer price;
 
-    // TODO @jason:感觉这个动态字段,晚点改;可能要讨论下怎么搞好;
-    @Schema(description = "收款方信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "{'ALIPAY_LOGON_ID':'xxxx'}")
-    @NotEmpty(message = "收款方信息不能为空")
-    private Map<String, String> payeeInfo;
+    // ========== 支付宝,微信转账相关字段 ==========
+    @Schema(description = "支付宝登录号,支持邮箱和手机号格式", example = "test1@@sandbox.com")
+    @NotBlank(message = "支付宝登录号不能为空", groups = {Alipay.class})
+    private String alipayLogonId;
+
+    @Schema(description = "支付宝账号名称", example = "test1")
+    @NotBlank(message = "支付宝登录号不能为空", groups = {Alipay.class})
+    private String alipayAccountName;
+
+    // ========== 微信转账相关字段 ==========
+    @Schema(description = "微信 openId", example = "oLefc4g5Gxx")
+    @NotBlank(message = "微信 openId 不能为空", groups = {WxPay.class})
+    private String openid;
+
+    @Schema(description = "微信账号名称", example = "oLefc4g5Gjxxxxxx")
+    private String wxAccountName;
+
+    // ========== 转账到银行卡和钱包相关字段 待补充 ==========
+    public interface WxPay {
+    }
+
+    public interface Alipay {
+    }
+
+    public void validate(Validator validator) {
+        PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(type);
+        switch (transferType) {
+            case ALIPAY_BALANCE: {
+                ValidationUtils.validate(validator, this, Alipay.class);
+                break;
+            }
+            case WX_BALANCE: {
+                ValidationUtils.validate(validator, this, WxPay.class);
+                break;
+            }
+            default: {
+                throw new UnsupportedOperationException("待实现");
+            }
+        }
+    }
 
 }

+ 17 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/demo/PayDemoTransferConvert.java

@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.pay.convert.demo;
+
+import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO;
+import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author jason
+ */
+@Mapper
+public interface PayDemoTransferConvert {
+
+    PayDemoTransferConvert INSTANCE = Mappers.getMapper(PayDemoTransferConvert.class);
+
+    PayDemoTransferDO convert(PayDemoTransferCreateReqVO bean);
+}

+ 20 - 7
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/demo/PayDemoTransferDO.java

@@ -1,15 +1,13 @@
 package cn.iocoder.yudao.module.pay.dal.dataobject.demo;
 
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import lombok.Data;
 
 import java.time.LocalDateTime;
-import java.util.Map;
 
 /**
  * 示例转账订单
@@ -39,15 +37,30 @@ public class PayDemoTransferDO extends BaseDO {
 
     /**
      * 转账类型
+     * <p>
+     * 枚举 {@link PayTransferTypeEnum}
      */
     private Integer type;
 
-    // TODO @jason:要不字段还是弄成正确的平铺开?
     /**
-     * 收款人信息,不同类型和渠道不同
+     * 支付宝登录号
      */
-    @TableField(typeHandler = JacksonTypeHandler.class)
-    private Map<String, String> payeeInfo;
+    private String alipayLogonId;
+
+    /**
+     * 支付宝账号名称
+     */
+    private String alipayAccountName;
+
+    /**
+     * 微信 openId
+     */
+    private String openid;
+
+    /**
+     * 微信账号名称
+     */
+    private String wxAccountName;
 
     /**
      * 转账状态

+ 72 - 18
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/transfer/PayTransferDO.java

@@ -31,33 +31,35 @@ public class PayTransferDO extends BaseDO {
      */
     @TableId
     private Long id;
+
+    /**
+     * 转账单号
+     *
+     */
+    private String no;
+
     /**
      * 应用编号
      *
      * 关联 {@link PayAppDO#getId()}
      */
     private Long appId;
+
     /**
      * 转账渠道编号
      *
      * 关联 {@link PayChannelDO#getId()}
      */
     private Long channelId;
+
     /**
      * 转账渠道编码
      *
      * 枚举 {@link PayChannelEnum}
      */
     private String channelCode;
-    /**
-     * 类型
-     *
-     * 枚举 {@link PayTransferTypeEnum}
-     */
-    private Integer type;
 
     // ========== 商户相关字段 ==========
-
     /**
      * 商户订单编号
      *
@@ -65,12 +67,20 @@ public class PayTransferDO extends BaseDO {
      */
     private String merchantOrderId;
 
+    // ========== 转账相关字段 ==========
+
+    /**
+     * 类型
+     *
+     * 枚举 {@link PayTransferTypeEnum}
+     */
+    private Integer type;
+
     /**
      * 转账标题
      */
     private String subject;
 
-    // ========== 转账相关字段 ==========
     /**
      * 转账金额,单位:分
      */
@@ -81,26 +91,70 @@ public class PayTransferDO extends BaseDO {
      * 枚举 {@link PayTransferStatusRespEnum}
      */
     private Integer status;
+
     /**
      * 订单转账成功时间
      */
     private LocalDateTime successTime;
+
+    // ========== 支付宝转账相关字段 ==========
     /**
-     * 转账成功的转账拓展单编号
-     *
-     * 关联 {@link PayTransferExtensionDO#getId()}
+     * 支付宝登录号
      */
-    private Long extensionId;
+    private String alipayLogonId;
+
     /**
-     * 转账成功的转账拓展单号
-     *
-     * 关联 {@link PayTransferExtensionDO#getNo()}
+     * 支付宝账号名称
      */
-    private String no;
+    private String alipayAccountName;
+
+    // ========== 微信转账相关字段 ==========
+    /**
+     * 微信 openId
+     */
+    private String openid;
+
+    /**
+     * 微信账号名称
+     */
+    private String wxAccountName;
+
+    // ========== 其它字段 ==========
+
+    /**
+     * 异步通知地址
+     */
+    private String notifyUrl;
+
+    /**
+     * 用户 IP
+     */
+    private String userIp;
+
     /**
-     * 收款人信息,不同类型和渠道不同
+     * 渠道的额外参数
      */
     @TableField(typeHandler = JacksonTypeHandler.class)
-    private Map<String, String> payeeInfo;
+    private Map<String, String> channelExtras;
+
+    /**
+     * 渠道转账单号
+     */
+    private String channelTransferNo;
+
+    /**
+     * 调用渠道的错误码
+     */
+    private String channelErrorCode;
+    /**
+     * 调用渠道的错误提示
+     */
+    private String channelErrorMsg;
+
+    /**
+     * 渠道的同步/异步通知的内容
+     *
+     */
+    private String channelNotifyData;
 
 }

+ 8 - 46
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java

@@ -1,10 +1,7 @@
 package cn.iocoder.yudao.module.pay.service.demo;
 
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
 import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO;
-import cn.iocoder.yudao.module.pay.convert.transfer.PayTransferConvert;
+import cn.iocoder.yudao.module.pay.convert.demo.PayDemoTransferConvert;
 import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoTransferMapper;
 import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService;
@@ -14,12 +11,8 @@ import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
-import java.util.Map;
+import javax.validation.Validator;
 
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*;
-import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_TRANSFER_ALIPAY_ACCOUNT_NAME_IS_EMPTY;
-import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_TRANSFER_ALIPAY_LOGIN_ID_IS_EMPTY;
 import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.WAITING;
 
 /**
@@ -41,49 +34,18 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
     private PayDemoTransferMapper demoTransferMapper;
     @Resource
     private PayTransferService transferService;
+    @Resource
+    private Validator validator;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createDemoTransfer(Long userId, @Valid PayDemoTransferCreateReqVO vo) {
-        // 1 校验收款账号
-        validatePayeeInfo(vo.getType(), vo.getPayeeInfo());
-
+        // 1 校验参数
+        vo.validate(validator);
         // 2 保存示例转账业务表
-        PayDemoTransferDO demoTransfer = new PayDemoTransferDO().setUserId(userId).setType(vo.getType())
-                .setPrice(vo.getPrice()).setPayeeInfo(vo.getPayeeInfo())
-                .setTransferStatus(WAITING.getStatus());
+        PayDemoTransferDO demoTransfer = PayDemoTransferConvert.INSTANCE.convert(vo)
+                .setUserId(userId).setTransferStatus(WAITING.getStatus());
         demoTransferMapper.insert(demoTransfer);
-
-        // 3.1 创建转账单
-        Long transferId = transferService.createTransfer(PayTransferConvert.INSTANCE.convert(vo)
-                .setAppId(TRANSFER_APP_ID).setTitle("示例转账")
-                .setMerchantOrderId(String.valueOf(demoTransfer.getId())));
-        // 3.2 更新转账单编号
-        demoTransferMapper.updateById(new PayDemoTransferDO().setId(demoTransfer.getId())
-                .setPayTransferId(transferId));
         return demoTransfer.getId();
     }
-
-    // TODO @jason:可以参考 AppBrokerageWithdrawCreateReqVO 搞下字段哈,进行校验
-    // @jason payeeinfo 字段确定改一下
-    private void validatePayeeInfo(Integer transferType, Map<String, String> payeeInfo) {
-        PayTransferTypeEnum transferTypeEnum = typeOf(transferType);
-        switch (transferTypeEnum) {
-            case ALIPAY_BALANCE: {
-                if (StrUtil.isEmpty(MapUtil.getStr(payeeInfo, ALIPAY_LOGON_ID))) {
-                    throw exception(PAY_TRANSFER_ALIPAY_LOGIN_ID_IS_EMPTY);
-                }
-                if (StrUtil.isEmpty(MapUtil.getStr(payeeInfo, ALIPAY_ACCOUNT_NAME))) {
-                    throw exception(PAY_TRANSFER_ALIPAY_ACCOUNT_NAME_IS_EMPTY);
-                }
-                break;
-            }
-            case WX_BALANCE:
-            case BANK_CARD:
-            case WALLET_BALANCE: {
-                throw new UnsupportedOperationException("待实现");
-            }
-        }
-    }
-
 }

+ 3 - 7
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java

@@ -28,7 +28,6 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
@@ -80,7 +79,7 @@ public class PayTransferServiceImpl implements PayTransferService {
         PayTransferUnifiedReqDTO transferUnifiedReq = new PayTransferUnifiedReqDTO()
                 .setOutTransferNo(transferExtension.getNo()).setPrice(transfer.getPrice())
                 .setType(transfer.getType()).setTitle(transfer.getSubject())
-                .setPayeeInfo(transfer.getPayeeInfo()).setUserIp(userIp)
+                .setUserIp(userIp)
                 .setChannelExtras(reqVO.getChannelExtras());
         PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq);
 
@@ -139,10 +138,7 @@ public class PayTransferServiceImpl implements PayTransferService {
         if (transfer == null) {
             throw exception(PAY_TRANSFER_NOT_FOUND);
         }
-        if (isSuccess(transfer.getStatus()) && Objects.equals(transfer.getExtensionId(), transferExtension.getId())) {
-            log.info("[updateTransferSuccess][transfer({}) 已经是已转账,无需更新]", transfer.getId());
-            return true;
-        }
+
         if (!isPendingStatus(transfer.getStatus())) {
             throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING);
         }
@@ -151,7 +147,7 @@ public class PayTransferServiceImpl implements PayTransferService {
                 CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()),
                 new PayTransferDO().setStatus(SUCCESS.getStatus()).setSuccessTime(notify.getSuccessTime())
                         .setChannelId(channel.getId()).setChannelCode(channel.getCode())
-                        .setExtensionId(transferExtension.getId()).setNo(transferExtension.getNo()));
+                        .setNo(transferExtension.getNo()));
         if (updateCounts == 0) {
             throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING);
         }