mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 15:21:53 +08:00
code review:钱包、充值、转账的实现
This commit is contained in:
parent
5842a361e2
commit
00e18a480f
@ -77,7 +77,7 @@ ALTER TABLE `pay_channel`
|
||||
MODIFY COLUMN `config` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '支付渠道配置' AFTER `app_id`;
|
||||
|
||||
-- ----------------------------
|
||||
-- 套餐充值表
|
||||
-- 充值套餐表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `pay_wallet_recharge_package`;
|
||||
CREATE TABLE `pay_wallet_recharge_package`
|
||||
@ -94,7 +94,7 @@ CREATE TABLE `pay_wallet_recharge_package`
|
||||
`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='套餐充值表';
|
||||
) ENGINE=InnoDB COMMENT='充值套餐表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for pay_wallet_recharge
|
||||
|
@ -51,7 +51,9 @@ public class PayTransferUnifiedReqDTO {
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 收款方信息,转账类型不同,收款方信息不同
|
||||
* 收款方信息。
|
||||
*
|
||||
* 转账类型 {@link #type} 不同,收款方信息不同
|
||||
*/
|
||||
@NotEmpty(message = "收款方信息 不能为空")
|
||||
private Map<String, String> payeeInfo;
|
||||
|
@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||
|
||||
// TODO @jason:看看能不能融合到 AbstractAlipayPayClient 中。
|
||||
/**
|
||||
* 支付宝转账的 PayClient 实现类
|
||||
*
|
||||
@ -29,6 +30,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
|
||||
*/
|
||||
@Slf4j
|
||||
public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
// TODO @jason:方法之间,要有空格噢
|
||||
public AlipayTransferClient(Long channelId, AlipayPayClientConfig config) {
|
||||
super(channelId, PayChannelEnum.ALIPAY_TRANSFER.getCode(), config);
|
||||
}
|
||||
@ -49,7 +51,7 @@ public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
model.setOrderTitle(reqDTO.getTitle()); // 转账业务的标题,用于在支付宝用户的账单里显示。
|
||||
model.setOutBizNo(reqDTO.getOutTransferNo());
|
||||
model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
|
||||
model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER。
|
||||
model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER
|
||||
model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
|
||||
PayTransferTypeEnum transferType = PayTransferTypeEnum.ofType(reqDTO.getType());
|
||||
switch(transferType) {
|
||||
@ -58,6 +60,7 @@ public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
log.error("[doUnifiedTransfer],支付宝转账不支持的转账类型{}", transferType);
|
||||
throw new UnsupportedOperationException(String.format("支付宝转账不支持转账类型: %s",transferType.getName()));
|
||||
}
|
||||
// TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦?
|
||||
case ALIPAY_BALANCE: {
|
||||
// ② 个性化的参数
|
||||
Participant payeeInfo = new Participant();
|
||||
@ -100,4 +103,5 @@ public class AlipayTransferClient extends AbstractAlipayPayClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -70,4 +70,5 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> {
|
||||
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ public enum PayChannelEnum {
|
||||
ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class),
|
||||
ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class),
|
||||
ALIPAY_BAR("alipay_bar", "支付宝条码支付", AlipayPayClientConfig.class),
|
||||
// TODO @jason:是不是按照微信聊的,合并回 ALIPAY_PC;因为支付宝、微信都是多种支付方式,选择其中的一种即可;
|
||||
ALIPAY_TRANSFER("alipay_transfer", "支付宝转账", AlipayPayClientConfig.class),
|
||||
|
||||
MOCK("mock", "模拟支付", NonePayClientConfig.class),
|
||||
|
@ -18,10 +18,10 @@ public enum PayTransferStatusRespEnum {
|
||||
|
||||
/**
|
||||
* TODO 转账到银行卡. 会有T+0 T+1 到账的请情况。 还未实现
|
||||
* TODO @jason:可以看看其它开源项目,针对这个场景,处理策略是怎么样的?例如说,每天主动轮询?这个状态的单子?
|
||||
*/
|
||||
IN_PROGRESS(10, "转账进行中"),
|
||||
|
||||
|
||||
SUCCESS(20, "转账成功"),
|
||||
/**
|
||||
* 转账关闭 (失败,或者其它情况)
|
||||
|
@ -15,6 +15,7 @@ import java.util.Arrays;
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum PayTransferTypeEnum implements IntArrayValuable {
|
||||
|
||||
ALIPAY_BALANCE(1, "支付宝余额"),
|
||||
WX_BALANCE(2, "微信余额"),
|
||||
BANK_CARD(3, "银行卡"),
|
||||
@ -33,7 +34,9 @@ public enum PayTransferTypeEnum implements IntArrayValuable {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
// TODO @jason:是不是 typeOf 更符合预期哈?
|
||||
public static PayTransferTypeEnum ofType(Integer type) {
|
||||
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,4 +18,5 @@ public interface PayTransferApi {
|
||||
* @return 转账单编号
|
||||
*/
|
||||
Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ public class PayTransferCreateReqDTO {
|
||||
* 类型
|
||||
*/
|
||||
@NotNull(message = "转账类型不能为空")
|
||||
// TODO @jason:枚举的校验
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
@ -34,9 +35,11 @@ public class PayTransferCreateReqDTO {
|
||||
/**
|
||||
* 转账金额,单位:分
|
||||
*/
|
||||
// TODO @jason:这个金额是不是非空哈
|
||||
@Min(value = 1, message = "转账金额必须大于零")
|
||||
private Integer price;
|
||||
|
||||
// TODO @jason:这个标题,是不是不允许空呀
|
||||
/**
|
||||
* 转账标题
|
||||
*/
|
||||
@ -44,4 +47,5 @@ public class PayTransferCreateReqDTO {
|
||||
|
||||
@NotEmpty(message = "收款方信息不能为空")
|
||||
private Map<String, String> payeeInfo;
|
||||
|
||||
}
|
||||
|
@ -27,8 +27,6 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1_007_002_003, "支付订单已经过期");
|
||||
ErrorCode PAY_ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_002_004, "发起支付报错,错误码:{},错误提示:{}");
|
||||
ErrorCode PAY_ORDER_REFUND_FAIL_STATUS_ERROR = new ErrorCode(1_007_002_005, "支付订单退款失败,原因:状态不是已支付或已退款");
|
||||
ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1_007_002_006, "支付订单调价失败,原因:支付订单已付款,不能调价");
|
||||
ErrorCode ORDER_UPDATE_PRICE_FAIL_EQUAL = new ErrorCode(1_007_002_007, "支付订单调价失败,原因:价格没有变化");
|
||||
|
||||
// ========== ORDER 模块(拓展单) 1-007-003-000 ==========
|
||||
ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1_007_003_000, "支付交易拓展单不存在");
|
||||
@ -64,6 +62,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode WALLET_RECHARGE_PACKAGE_AND_PRICE_IS_EMPTY = new ErrorCode(1_007_008_011, "充值金额和充钱套餐不能同时为空");
|
||||
ErrorCode WALLET_RECHARGE_PACKAGE_NOT_FOUND = new ErrorCode(1_007_008_012, "钱包充值套餐不存在");
|
||||
ErrorCode WALLET_RECHARGE_PACKAGE_IS_DISABLE = new ErrorCode(1_007_008_013, "钱包充值套餐已禁用");
|
||||
|
||||
// ========== 转账模块 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 不能为空");
|
||||
|
@ -24,7 +24,13 @@ public enum PayTransferStatusEnum {
|
||||
*/
|
||||
CLOSED(30, "转账关闭");
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final Integer status;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
public static boolean isSuccess(Integer status) {
|
||||
@ -45,4 +51,5 @@ public enum PayTransferStatusEnum {
|
||||
public static boolean isPendingStatus(Integer status) {
|
||||
return Objects.equals(status, WAITING.getStatus()) || Objects.equals(status, IN_PROGRESS.status);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,14 +5,18 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* 转账单 API 实现类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class PayTransferApiImpl implements PayTransferApi {
|
||||
|
||||
@Override
|
||||
public Long createTransfer(PayTransferCreateReqDTO reqDTO) {
|
||||
|
||||
// TODO @jason:貌似没实现噢
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -17,6 +17,7 @@ import java.util.Map;
|
||||
@Data
|
||||
public class PayDemoTransferCreateReqVO {
|
||||
|
||||
// TODO @jason:这个字段,是不是叫 type 就好了。
|
||||
@Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "转账类型不能为空")
|
||||
@InEnum(PayTransferTypeEnum.class)
|
||||
@ -26,7 +27,9 @@ public class PayDemoTransferCreateReqVO {
|
||||
@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;
|
||||
|
||||
}
|
||||
|
@ -29,8 +29,10 @@ public class PayTransferController {
|
||||
|
||||
@PostMapping("/submit")
|
||||
@Operation(summary = "提交转账订单")
|
||||
// TODO @jason:权限的设置
|
||||
public CommonResult<PayTransferSubmitRespVO> submitPayTransfer(@Valid @RequestBody PayTransferSubmitReqVO reqVO) {
|
||||
PayTransferSubmitRespVO respVO = payTransferService.submitTransfer(reqVO, getClientIP());
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,4 +21,5 @@ public class PayTransferSubmitReqVO {
|
||||
|
||||
@Schema(description = "转账渠道的额外参数")
|
||||
private Map<String, String> channelExtras;
|
||||
|
||||
}
|
||||
|
@ -9,4 +9,5 @@ public class PayTransferSubmitRespVO {
|
||||
|
||||
@Schema(description = "转账状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 参见 PayTransferStatusEnum 枚举
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
|
@ -57,4 +57,5 @@ public class PayWalletRechargeController {
|
||||
Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,9 +32,11 @@ public class PayWalletTransactionController {
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得钱包流水分页")
|
||||
// TODO @jason:权限校验,缺一个
|
||||
public CommonResult<PageResult<PayWalletTransactionRespVO>> getWalletTransactionPage(
|
||||
@Valid PayWalletTransactionPageReqVO pageReqVO) {
|
||||
PageResult<PayWalletTransactionDO> result = payWalletTransactionService.getWalletTransactionPage(pageReqVO);
|
||||
return success(PayWalletTransactionConvert.INSTANCE.convertPage2(result));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import lombok.Data;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 套餐充值 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 充值套餐 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
|
@ -5,7 +5,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 套餐充值创建 Request VO")
|
||||
@Schema(description = "管理后台 - 充值套餐创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
|
@ -11,7 +11,7 @@ import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 套餐充值分页 Request VO")
|
||||
@Schema(description = "管理后台 - 充值套餐分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@ -20,6 +20,8 @@ public class WalletRechargePackagePageReqVO extends PageParam {
|
||||
@Schema(description = "套餐名", example = "李四")
|
||||
private String name;
|
||||
|
||||
// TODO @jason:payPrice 和 bonusPrice 可以去掉。。。一般太少检索啦;
|
||||
|
||||
@Schema(description = "支付金额", example = "16454")
|
||||
private Integer payPrice;
|
||||
|
||||
|
@ -4,7 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 套餐充值 Response VO")
|
||||
@Schema(description = "管理后台 - 充值套餐 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
|
@ -7,7 +7,7 @@ import lombok.ToString;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 套餐充值更新 Request VO")
|
||||
@Schema(description = "管理后台 - 充值套餐更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
|
@ -10,4 +10,5 @@ public class PayWalletTransactionPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "钱包编号", example = "1")
|
||||
private Long walletId;
|
||||
|
||||
}
|
||||
|
@ -29,4 +29,7 @@ public class PayWalletTransactionRespVO {
|
||||
|
||||
@Schema(description = "交易时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// TODO @jason:merchantOrderId 字段,需要在 PayWalletTransaction 存储下;然后,前端也返回下这个字段,界面也展示下商户名
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import lombok.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
@ -18,6 +20,7 @@ public class PayWalletPageReqVO extends PageParam {
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户类型", example = "1")
|
||||
@InEnum(UserTypeEnum.class)
|
||||
private Integer userType;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
|
@ -19,4 +19,6 @@ public class PayWalletRespVO extends PayWalletBaseVO {
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// TODO @jason:要不把用户昵称 + avatar 也读取下?
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,9 @@ public class AppPayWalletRechargeCreateReqVO {
|
||||
@Min(value = 1, message = "支付金额必须大于零")
|
||||
private Integer payPrice;
|
||||
|
||||
|
||||
@Schema(description = "充值套餐编号", example = "1024")
|
||||
private Long packageId;
|
||||
|
||||
// TODO @jaosn:写个 AssertTrue 的校验方法,payPrice 和 packageId 必须二选一
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ public interface PayWalletConvert {
|
||||
|
||||
AppPayWalletRespVO convert(PayWalletDO bean);
|
||||
|
||||
PayWalletRespVO convert02(PayWalletDO wallet);
|
||||
PayWalletRespVO convert02(PayWalletDO bean);
|
||||
|
||||
PageResult<PayWalletRespVO> convertPage(PageResult<PayWalletDO> page);
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import java.util.Map;
|
||||
@KeySequence("pay_demo_transfer_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class PayDemoTransferDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
@ -41,6 +42,7 @@ public class PayDemoTransferDO extends BaseDO {
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
// TODO @jason:要不字段还是弄成正确的平铺开?
|
||||
/**
|
||||
* 收款人信息,不同类型和渠道不同
|
||||
*/
|
||||
@ -66,4 +68,5 @@ public class PayDemoTransferDO extends BaseDO {
|
||||
* 转账支付时间
|
||||
*/
|
||||
private LocalDateTime transferTime;
|
||||
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
package cn.iocoder.yudao.module.pay.dal.dataobject.transfer;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@ -26,70 +31,76 @@ public class PayTransferDO extends BaseDO {
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
*
|
||||
* 关联 {@link PayAppDO#getId()}
|
||||
*/
|
||||
private Long appId;
|
||||
/**
|
||||
* 转账渠道编号
|
||||
*
|
||||
* 关联 {@link PayChannelDO#getId()}
|
||||
*/
|
||||
private Long channelId;
|
||||
/**
|
||||
* 转账渠道编码
|
||||
*
|
||||
* 枚举 {@link PayChannelEnum}
|
||||
*/
|
||||
private String channelCode;
|
||||
/**
|
||||
* 类型
|
||||
*
|
||||
* 枚举 {@link PayTransferTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
*/
|
||||
private Long appId;
|
||||
// ========== 商户相关字段 ==========
|
||||
|
||||
/**
|
||||
* 商户订单编号
|
||||
*
|
||||
* 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
|
||||
*/
|
||||
private String merchantOrderId;
|
||||
|
||||
/**
|
||||
* 转账金额,单位:分
|
||||
*/
|
||||
private Integer price;
|
||||
|
||||
// TODO @jason:这个字段,要不要改成 subject。。。和 payorderdo 保持一致;哈哈哈,我也忘记为啥当时选了这个名字了。。。
|
||||
/**
|
||||
* 转账标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
// ========== 转账相关字段 ==========
|
||||
/**
|
||||
* 收款人信息,不同类型和渠道不同
|
||||
* 转账金额,单位:分
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private Map<String, String> payeeInfo;
|
||||
|
||||
private Integer price;
|
||||
/**
|
||||
* 转账状态
|
||||
*
|
||||
* 枚举 {@link PayTransferStatusRespEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 订单转账成功时间
|
||||
*
|
||||
*/
|
||||
private LocalDateTime successTime;
|
||||
|
||||
/**
|
||||
* 转账成功的转账拓展单编号
|
||||
*
|
||||
* 关联 {@link PayTransferExtensionDO#getId()}
|
||||
*/
|
||||
private Long extensionId;
|
||||
|
||||
/**
|
||||
* 转账成功的转账拓展单号
|
||||
*
|
||||
* 关联 {@link PayTransferExtensionDO#getNo()}
|
||||
*/
|
||||
private String no;
|
||||
|
||||
/**
|
||||
* 转账渠道编号
|
||||
* 收款人信息,不同类型和渠道不同
|
||||
*/
|
||||
private Long channelId;
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private Map<String, String> payeeInfo;
|
||||
|
||||
/**
|
||||
* 转账渠道编码
|
||||
*/
|
||||
private String channelCode;
|
||||
}
|
@ -10,6 +10,7 @@ import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
// TODO @jason:转账是不是类似 refund,不用拓展单呀?支付做拓展单的原因,是因为它存在不确定性,可以切换多种;转账和退款,都是明确方式的;
|
||||
/**
|
||||
* 转账拓展单 DO
|
||||
*
|
||||
@ -61,4 +62,5 @@ public class PayTransferExtensionDO extends BaseDO {
|
||||
* 支付渠道异步通知的内容
|
||||
*/
|
||||
private String channelNotifyData;
|
||||
|
||||
}
|
@ -42,7 +42,6 @@ public class PayWalletRechargeDO extends BaseDO {
|
||||
* 实际支付金额
|
||||
*/
|
||||
private Integer payPrice;
|
||||
|
||||
/**
|
||||
* 钱包赠送金额
|
||||
*/
|
||||
@ -50,6 +49,8 @@ public class PayWalletRechargeDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 充值套餐编号
|
||||
*
|
||||
* 关联 {@link PayWalletRechargeDO#getPackageId()} 字段
|
||||
*/
|
||||
private Long packageId;
|
||||
|
||||
|
@ -9,7 +9,7 @@ import lombok.Data;
|
||||
/**
|
||||
* 会员钱包充值套餐 DO
|
||||
*
|
||||
* 通过套餐充值时,可以赠送一定金额;
|
||||
* 通过充值套餐时,可以赠送一定金额;
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
|
@ -8,7 +8,3 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
public interface PayDemoTransferMapper extends BaseMapperX<PayDemoTransferDO> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -14,10 +14,11 @@ public interface PayTransferExtensionMapper extends BaseMapperX<PayTransferExten
|
||||
return selectOne(PayTransferExtensionDO::getNo, no);
|
||||
}
|
||||
|
||||
default int updateByIdAndStatus(Long id, List<Integer> status, PayTransferExtensionDO updateObj){
|
||||
default int updateByIdAndStatus(Long id, List<Integer> whereStatuses, PayTransferExtensionDO updateObj) {
|
||||
return update(updateObj, new LambdaQueryWrapper<PayTransferExtensionDO>()
|
||||
.eq(PayTransferExtensionDO::getId, id).in(PayTransferExtensionDO::getStatus, status));
|
||||
.eq(PayTransferExtensionDO::getId, id).in(PayTransferExtensionDO::getStatus, whereStatuses));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@ public interface PayTransferMapper extends BaseMapperX<PayTransferDO> {
|
||||
return update(updateObj, new LambdaQueryWrapper<PayTransferDO>()
|
||||
.eq(PayTransferDO::getId, id).in(PayTransferDO::getStatus, status));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -70,6 +70,7 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
|
||||
|
||||
/**
|
||||
* 冻结钱包部分余额
|
||||
*
|
||||
* @param id 钱包 id
|
||||
* @param price 冻结金额
|
||||
*/
|
||||
@ -84,6 +85,7 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
|
||||
|
||||
/**
|
||||
* 解冻钱包余额
|
||||
*
|
||||
* @param id 钱包 id
|
||||
* @param price 解冻金额
|
||||
*/
|
||||
@ -98,6 +100,7 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
|
||||
|
||||
/**
|
||||
* 当充值退款时, 更新钱包
|
||||
*
|
||||
* @param id 钱包 id
|
||||
* @param price 退款金额
|
||||
*/
|
||||
|
@ -19,7 +19,3 @@ public interface PayWalletRechargeMapper extends BaseMapperX<PayWalletRechargeDO
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
public interface PayWalletRechargePackageMapper extends BaseMapperX<PayWalletRechargePackageDO> {
|
||||
|
||||
default PageResult<PayWalletRechargePackageDO> selectPage(WalletRechargePackagePageReqVO reqVO) {
|
||||
// TODO @jason:排序按照充值金额
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<PayWalletRechargePackageDO>()
|
||||
.likeIfPresent(PayWalletRechargePackageDO::getName, reqVO.getName())
|
||||
.eqIfPresent(PayWalletRechargePackageDO::getPayPrice, reqVO.getPayPrice())
|
||||
@ -20,8 +21,5 @@ public interface PayWalletRechargePackageMapper extends BaseMapperX<PayWalletRec
|
||||
.betweenIfPresent(PayWalletRechargePackageDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(PayWalletRechargePackageDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -180,4 +180,5 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
|
||||
public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
|
||||
throw new UnsupportedOperationException("待实现");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
|
||||
return demoTransfer.getId();
|
||||
}
|
||||
|
||||
// TODO @jason:可以参考 AppBrokerageWithdrawCreateReqVO 搞下字段哈,进行校验
|
||||
private void validatePayeeInfo(Integer transferType, Map<String, String> payeeInfo) {
|
||||
PayTransferTypeEnum transferTypeEnum = ofType(transferType);
|
||||
switch (transferTypeEnum) {
|
||||
@ -83,4 +84,5 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferSubmi
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 转账 Service 接口
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public interface PayTransferService {
|
||||
@ -29,4 +31,5 @@ public interface PayTransferService {
|
||||
* @return 转账单编号
|
||||
*/
|
||||
Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,10 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.*;
|
||||
|
||||
// TODO @jason:等彻底实现完,单测写写;
|
||||
/**
|
||||
* 转账 Service 实现类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Service
|
||||
@ -47,10 +50,12 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
private PayTransferMapper transferMapper;
|
||||
@Resource
|
||||
private PayTransferExtensionMapper transferExtensionMapper;
|
||||
|
||||
@Resource
|
||||
private PayAppService appService;
|
||||
@Resource
|
||||
private PayChannelService channelService;
|
||||
|
||||
@Resource
|
||||
private PayNoRedisDAO noRedisDAO;
|
||||
|
||||
@ -64,7 +69,7 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
PayChannelDO channel = validateChannelCanSubmit(transfer.getAppId(), reqVO.getChannelCode());
|
||||
PayClient client = channelService.getPayClient(channel.getId());
|
||||
|
||||
// 2 新增转账拓展单
|
||||
// 2. 新增转账拓展单
|
||||
String no = noRedisDAO.generate(TRANSFER_NO_PREFIX);
|
||||
PayTransferExtensionDO transferExtension = new PayTransferExtensionDO().setNo(no)
|
||||
.setTransferId(transfer.getId()).setChannelId(channel.getId())
|
||||
@ -114,6 +119,7 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
// WAITING 状态无需处理
|
||||
// TODO IN_PROGRESS 待处理
|
||||
}
|
||||
|
||||
private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) {
|
||||
// 1. 更新 PayTransferExtensionDO 转账成功
|
||||
PayTransferExtensionDO transferExtension = updateTransferExtensionSuccess(notify);
|
||||
|
@ -31,7 +31,7 @@ public interface PayWalletRechargePackageService {
|
||||
PayWalletRechargePackageDO validWalletRechargePackage(Long packageId);
|
||||
|
||||
/**
|
||||
* 创建套餐充值
|
||||
* 创建充值套餐
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
@ -39,33 +39,25 @@ public interface PayWalletRechargePackageService {
|
||||
Long createWalletRechargePackage(@Valid WalletRechargePackageCreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新套餐充值
|
||||
* 更新充值套餐
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateWalletRechargePackage(@Valid WalletRechargePackageUpdateReqVO updateReqVO);
|
||||
|
||||
|
||||
/**
|
||||
* 删除套餐充值
|
||||
* 删除充值套餐
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteWalletRechargePackage(Long id);
|
||||
|
||||
/**
|
||||
* 获得套餐充值列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 套餐充值列表
|
||||
*/
|
||||
List<PayWalletRechargePackageDO> getWalletRechargePackageList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得套餐充值分页
|
||||
* 获得充值套餐分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 套餐充值分页
|
||||
* @return 充值套餐分页
|
||||
*/
|
||||
PageResult<PayWalletRechargePackageDO> getWalletRechargePackagePage(WalletRechargePackagePageReqVO pageReqVO);
|
||||
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ public class PayWalletRechargePackageServiceImpl implements PayWalletRechargePac
|
||||
return walletRechargePackage.getId();
|
||||
}
|
||||
|
||||
// TODO @jason:校验下,套餐名唯一
|
||||
@Override
|
||||
public void updateWalletRechargePackage(WalletRechargePackageUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
@ -80,14 +81,6 @@ public class PayWalletRechargePackageServiceImpl implements PayWalletRechargePac
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PayWalletRechargePackageDO> getWalletRechargePackageList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return walletRechargePackageMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PayWalletRechargePackageDO> getWalletRechargePackagePage(WalletRechargePackagePageReqVO pageReqVO) {
|
||||
return walletRechargePackageMapper.selectPage(pageReqVO);
|
||||
|
@ -44,7 +44,7 @@ import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*;
|
||||
public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
|
||||
/**
|
||||
* TODO 放到 配置文件中
|
||||
* TODO 芋艿:放到 payconfig
|
||||
*/
|
||||
private static final Long WALLET_PAY_APP_ID = 8L;
|
||||
|
||||
@ -65,11 +65,12 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PayWalletRechargeDO createWalletRecharge(Long userId, Integer userType,
|
||||
AppPayWalletRechargeCreateReqVO reqVO) {
|
||||
// 1.1 校验参数
|
||||
// 1.1 校验参数 TODO @jason:AppPayWalletRechargeCreateReqVO 看下校验;
|
||||
if (Objects.isNull(reqVO.getPayPrice()) && Objects.isNull(reqVO.getPackageId())) {
|
||||
throw exception(WALLET_RECHARGE_PACKAGE_AND_PRICE_IS_EMPTY);
|
||||
}
|
||||
// 1.2 新增钱包充值记录
|
||||
|
||||
// 1.1 计算充值金额
|
||||
int payPrice;
|
||||
int bonusPrice = 0;
|
||||
if (Objects.nonNull(reqVO.getPackageId())) {
|
||||
@ -79,23 +80,22 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
} else {
|
||||
payPrice = reqVO.getPayPrice();
|
||||
}
|
||||
|
||||
// 1.2 插入充值记录
|
||||
PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType);
|
||||
PayWalletRechargeDO walletRecharge = INSTANCE.convert(wallet.getId(), payPrice, bonusPrice, reqVO.getPackageId());
|
||||
walletRechargeMapper.insert(walletRecharge);
|
||||
PayWalletRechargeDO recharge = INSTANCE.convert(wallet.getId(), payPrice, bonusPrice, reqVO.getPackageId());
|
||||
walletRechargeMapper.insert(recharge);
|
||||
|
||||
// 2.1 创建支付单
|
||||
Long payOrderId = payOrderService.createOrder(new PayOrderCreateReqDTO()
|
||||
.setAppId(WALLET_PAY_APP_ID).setUserIp(getClientIP())
|
||||
.setMerchantOrderId(walletRecharge.getId().toString()) // 业务的订单编号
|
||||
.setSubject(WALLET_RECHARGE_ORDER_SUBJECT).setBody("").setPrice(walletRecharge.getPayPrice())
|
||||
.setExpireTime(addTime(Duration.ofHours(2L))));
|
||||
.setAppId(WALLET_PAY_APP_ID).setUserIp(getClientIP()) // TODO @jason:clientIp 从 controller 传递进来噢
|
||||
.setMerchantOrderId(recharge.getId().toString()) // 业务的订单编号
|
||||
.setSubject(WALLET_RECHARGE_ORDER_SUBJECT).setBody("")
|
||||
.setPrice(recharge.getPayPrice())
|
||||
.setExpireTime(addTime(Duration.ofHours(2L)))); // TODO @芋艿:支付超时时间
|
||||
// 2.2 更新钱包充值记录中支付订单
|
||||
walletRechargeMapper.updateById(new PayWalletRechargeDO().setPayOrderId(payOrderId)
|
||||
.setId(walletRecharge.getId()));
|
||||
|
||||
walletRecharge.setPayOrderId(payOrderId);
|
||||
return walletRecharge;
|
||||
walletRechargeMapper.updateById(new PayWalletRechargeDO().setId(recharge.getId()).setPayOrderId(payOrderId));
|
||||
recharge.setPayOrderId(payOrderId);
|
||||
return recharge;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -136,9 +136,11 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
}
|
||||
// 1.2 校验钱包充值是否可以发起退款
|
||||
PayWalletDO wallet = validateWalletRechargeCanRefund(walletRecharge);
|
||||
// 2 冻结退款的余额, 暂时只处理赠送的余额也全部退回
|
||||
|
||||
// 2. 冻结退款的余额,暂时只处理赠送的余额也全部退回
|
||||
payWalletService.freezePrice(wallet.getId(), walletRecharge.getTotalPrice());
|
||||
// 3 创建退款单
|
||||
|
||||
// 3. 创建退款单
|
||||
String walletRechargeId = String.valueOf(id);
|
||||
String refundId = walletRechargeId + "-refund";
|
||||
Long payRefundId = payRefundService.createPayRefund(new PayRefundCreateReqDTO()
|
||||
@ -146,7 +148,9 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
.setMerchantOrderId(walletRechargeId)
|
||||
.setMerchantRefundId(refundId)
|
||||
.setReason("想退钱").setPrice(walletRecharge.getPayPrice()));
|
||||
// 4 更新充值记录退款单号
|
||||
|
||||
// 4. 更新充值记录退款单号
|
||||
// TODO @jaosn:一般新建这种 update 对象,建议是,第一个 set id 属性,容易知道以它为更新
|
||||
walletRechargeMapper.updateById(new PayWalletRechargeDO().setPayRefundId(payRefundId)
|
||||
.setRefundStatus(WAITING.getStatus()).setId(walletRecharge.getId()));
|
||||
}
|
||||
@ -229,6 +233,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
if (wallet.getBalance() < walletRecharge.getTotalPrice()) {
|
||||
throw exception(WALLET_RECHARGE_REFUND_BALANCE_NOT_ENOUGH);
|
||||
}
|
||||
// TODO @芋艿:需要考虑下,赠送的金额,会不会导致提现超过;
|
||||
return wallet;
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ public interface PayWalletService {
|
||||
*/
|
||||
void freezePrice(Long id, Integer price);
|
||||
|
||||
// TODO @jason:unfreeze 是单词哈,f 不用大写
|
||||
/**
|
||||
* 解冻钱包余额
|
||||
*
|
||||
|
@ -109,6 +109,7 @@ public class PayWalletServiceImpl implements PayWalletService {
|
||||
if (walletTransaction == null) {
|
||||
throw exception(WALLET_TRANSACTION_NOT_FOUND);
|
||||
}
|
||||
// 2. 校验退款是否存在
|
||||
PayWalletTransactionDO refundTransaction = walletTransactionService.getWalletTransaction(
|
||||
String.valueOf(refundId), PAYMENT_REFUND);
|
||||
if (refundTransaction != null) {
|
||||
@ -128,7 +129,7 @@ public class PayWalletServiceImpl implements PayWalletService {
|
||||
}
|
||||
|
||||
// 2.1 扣除余额
|
||||
int updateCounts = 0 ;
|
||||
int updateCounts;
|
||||
switch (bizType) {
|
||||
case PAYMENT: {
|
||||
updateCounts = walletMapper.updateWhenConsumption(payWallet.getId(), price);
|
||||
|
Loading…
Reference in New Issue
Block a user