Kaynağa Gözat

Pay: 新增转账示例单

jason 1 yıl önce
ebeveyn
işleme
ce6f7ab1b1

+ 78 - 0
sql/mysql/pay_wallet.sql

@@ -73,3 +73,81 @@ CREATE TABLE `pay_wallet_recharge`
     PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB COMMENT='会员钱包充值';
 
+-- ----------------------------
+-- 转账单表
+-- ----------------------------
+DROP TABLE IF EXISTS `pay_transfer`;
+CREATE TABLE `pay_transfer`
+(
+    `id`                   bigint       NOT NULL AUTO_INCREMENT COMMENT '编号',
+    `type`                 int          NOT NULL COMMENT '类型',
+    `app_id`               bigint       NOT NULL COMMENT '应用编号',
+    `merchant_order_id`    varchar(64)  NOT NULL COMMENT '商户订单编号',
+    `price`                int          NOT NULL COMMENT '转账金额,单位:分',
+    `title`                varchar(512) NOT NULL COMMENT '转账标题',
+    `payee_info`           varchar(512) 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 '转账渠道编码',
+    `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 '更新者',
+    `update_time`          datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted`              bit(1)       NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id`            bigint       NOT NULL DEFAULT 0 COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB COMMENT='转账单表';
+
+-- ----------------------------
+-- 转账扩展单
+-- ----------------------------
+DROP TABLE IF EXISTS `pay_transfer_extension`;
+CREATE TABLE `pay_transfer_extension`
+(
+    `id`                   bigint        NOT NULL AUTO_INCREMENT COMMENT '编号',
+    `no`                   varchar(64)   NOT NULL COMMENT '转账单号',
+    `transfer_id`          bigint        NOT NULL COMMENT '转账单编号',
+    `channel_id`           bigint        NOT NULL COMMENT '转账渠道编号',
+    `channel_code`         varchar(32)   NOT NULL COMMENT '转账渠道编码',
+    `channel_extras`       varchar(512)  NULL DEFAULT NULL COMMENT '支付渠道的额外参数',
+    `status`               tinyint       NOT NULL COMMENT '转账状态',
+    `channel_notify_data`  varchar(4096) NULL DEFAULT NULL COMMENT '支付渠道异步通知的内容',
+    `creator`              varchar(64)   NULL DEFAULT '' COMMENT '创建者',
+    `create_time`          datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updater`              varchar(64)   NULL DEFAULT '' COMMENT '更新者',
+    `update_time`          datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    `deleted`              bit(1)        NOT NULL DEFAULT b'0' COMMENT '是否删除',
+    `tenant_id`            bigint        NOT NULL DEFAULT 0 COMMENT '租户编号',
+    PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB COMMENT='转账拓展单表';
+
+-- ----------------------------
+-- Table structure for pay_demo_transfer
+-- ----------------------------
+DROP TABLE IF EXISTS `pay_demo_transfer`;
+CREATE TABLE `pay_demo_transfer`  (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单编号',
+  `user_id` bigint UNSIGNED NOT NULL COMMENT '用户编号',
+  `price` int NOT NULL COMMENT '转账金额,单位:分',
+  `type`  int NOT NULL COMMENT '转账类型',
+  `payee_info` varchar(512) NOT 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 '转账支付成功渠道',
+  `transfer_time` datetime NULL DEFAULT NULL COMMENT '转账支付时间',
+  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '创建者',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '更新者',
+  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+   PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB  COMMENT = '示例业务转账订单\n';
+
+
+ALTER TABLE `pay_channel`
+    MODIFY COLUMN `config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '支付渠道配置' AFTER `app_id`;
+

+ 33 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoTransferController.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.pay.controller.admin.demo;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO;
+import cn.iocoder.yudao.module.pay.service.demo.PayDemoTransferService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "管理后台 - 示例转账单")
+@RestController
+@RequestMapping("/pay/demo-transfer")
+@Validated
+public class PayDemoTransferController {
+    @Resource
+    private PayDemoTransferService demoTransferService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建示例转账订单")
+    public CommonResult<Long> createDemoOrder(@Valid @RequestBody PayDemoTransferCreateReqVO createReqVO) {
+        return success(demoTransferService.createDemoTransfer(getLoginUserId(), createReqVO));
+    }
+}

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

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer;
+
+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.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Map;
+
+/**
+ * @author jason
+ */
+@Schema(description = "管理后台 - 示例转账单创建 Request VO")
+@Data
+public class PayDemoTransferCreateReqVO {
+
+    @Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "转账类型不能为空")
+    @InEnum(PayTransferTypeEnum.class)
+    private Integer transferType;
+
+    @NotNull(message = "转账金额不能为空")
+    @Min(value = 1, message = "转账金额必须大于零")
+    private Integer price;
+
+    @Schema(description = "收款方信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "{'ALIPAY_LOGON_ID':'xxxx'}")
+    @NotEmpty(message = "收款方信息不能为空")
+    private Map<String, String> payeeInfo;
+}

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

@@ -0,0 +1,69 @@
+package cn.iocoder.yudao.module.pay.dal.dataobject.demo;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+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;
+
+/**
+ * 示例转账订单
+ *
+ * 演示业务系统的转账业务
+ */
+@TableName(value ="pay_demo_transfer", autoResultMap = true)
+@KeySequence("pay_demo_transfer_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+public class PayDemoTransferDO extends BaseDO {
+    /**
+     * 订单编号
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 用户编号
+     */
+    private Long userId;
+
+    /**
+     * 转账金额,单位:分
+     */
+    private Integer price;
+
+    /**
+     * 转账类型
+     */
+    private Integer type;
+
+    /**
+     * 收款人信息,不同类型和渠道不同
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, String> payeeInfo;
+
+    /**
+     * 转账状态
+     */
+    private Integer transferStatus;
+
+    /**
+     * 转账订单编号
+     */
+    private Long payTransferId;
+
+    /**
+     * 转账支付成功渠道
+     */
+    private String payChannelCode;
+
+    /**
+     * 转账支付时间
+     */
+    private LocalDateTime transferTime;
+}

+ 14 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/demo/PayDemoTransferMapper.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.pay.dal.mysql.demo;
+
+import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PayDemoTransferMapper extends BaseMapperX<PayDemoTransferDO> {
+
+}
+
+
+
+

+ 22 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferService.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.pay.service.demo;
+
+import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO;
+
+import javax.validation.Valid;
+
+/**
+ * 示例转账业务 Service 接口
+ *
+ * @author jason
+ */
+public interface PayDemoTransferService {
+
+    /**
+     * 创建转账单
+     *
+     * @param userId      用户编号
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createDemoTransfer(Long userId, @Valid PayDemoTransferCreateReqVO createReqVO);
+}

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

@@ -0,0 +1,86 @@
+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.dal.dataobject.demo.PayDemoTransferDO;
+import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoTransferMapper;
+import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.Map;
+
+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;
+
+/**
+ * 示例转账业务 Service 实现类
+ *
+ * @author jason
+ */
+@Service
+@Validated
+public class PayDemoTransferServiceImpl implements PayDemoTransferService {
+
+    /**
+     * 接入的实力应用编号
+
+     * 从 [支付管理 -> 应用信息] 里添加
+     */
+    private static final Long TRANSFER_APP_ID = 8L;
+    @Resource
+    private PayDemoTransferMapper demoTransferMapper;
+    @Resource
+    private PayTransferService transferService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createDemoTransfer(Long userId, @Valid PayDemoTransferCreateReqVO vo) {
+        // 1 校验收款账号
+        validatePayeeInfo(vo.getTransferType(), vo.getPayeeInfo());
+
+        // 2 保存示例转账业务表
+        PayDemoTransferDO demoTransfer = new PayDemoTransferDO().setUserId(userId).setType(vo.getTransferType())
+                .setPrice(vo.getPrice()).setPayeeInfo(vo.getPayeeInfo())
+                .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();
+    }
+
+    private void validatePayeeInfo(Integer transferType, Map<String, String> payeeInfo) {
+        PayTransferTypeEnum transferTypeEnum = ofType(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("待实现");
+            }
+        }
+    }
+}