code review:钱包、充值、转账的实现

This commit is contained in:
zhijiantianya@gmail.com 2023-10-18 18:39:05 +08:00
parent 5842a361e2
commit 00e18a480f
49 changed files with 178 additions and 112 deletions

View File

@ -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

View File

@ -51,7 +51,9 @@ public class PayTransferUnifiedReqDTO {
private String title;
/**
* 收款方信息转账类型不同收款方信息不同
* 收款方信息
*
* 转账类型 {@link #type} 不同收款方信息不同
*/
@NotEmpty(message = "收款方信息 不能为空")
private Map<String, String> payeeInfo;

View File

@ -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,16 +51,17 @@ 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){
case WX_BALANCE :
case WALLET_BALANCE : {
switch(transferType) {
case WX_BALANCE:
case WALLET_BALANCE: {
log.error("[doUnifiedTransfer],支付宝转账不支持的转账类型{}", transferType);
throw new UnsupportedOperationException(String.format("支付宝转账不支持转账类型: %s",transferType.getName()));
}
case ALIPAY_BALANCE : {
// TODO @jason是不是不用传递 transferType 参数哈因为应该已经明确是支付宝啦
case ALIPAY_BALANCE: {
// 个性化的参数
Participant payeeInfo = new Participant();
payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
@ -90,7 +93,7 @@ public class AlipayTransferClient extends AbstractAlipayPayClient {
return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
response.getOutBizNo(), response);
}
case BANK_CARD : {
case BANK_CARD: {
Participant payeeInfo = new Participant();
payeeInfo.setIdentityType("BANKCARD_ACCOUNT");
throw new UnsupportedOperationException("待实现");
@ -100,4 +103,5 @@ public class AlipayTransferClient extends AbstractAlipayPayClient {
}
}
}
}

View File

@ -70,4 +70,5 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> {
protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
throw new UnsupportedOperationException("待实现");
}
}

View File

@ -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),

View File

@ -18,10 +18,10 @@ public enum PayTransferStatusRespEnum {
/**
* TODO 转账到银行卡. 会有T+0 T+1 到账的请情况 还未实现
* TODO @jason可以看看其它开源项目针对这个场景处理策略是怎么样的例如说每天主动轮询这个状态的单子
*/
IN_PROGRESS(10, "转账进行中"),
SUCCESS(20, "转账成功"),
/**
* 转账关闭 (失败或者其它情况)

View File

@ -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());
}
}

View File

@ -18,4 +18,5 @@ public interface PayTransferApi {
* @return 转账单编号
*/
Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO);
}

View File

@ -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;
}

View File

@ -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 不能为空");

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -21,4 +21,5 @@ public class PayTransferSubmitReqVO {
@Schema(description = "转账渠道的额外参数")
private Map<String, String> channelExtras;
}

View File

@ -9,4 +9,5 @@ public class PayTransferSubmitRespVO {
@Schema(description = "转账状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 参见 PayTransferStatusEnum 枚举
private Integer status;
}

View File

@ -57,4 +57,5 @@ public class PayWalletRechargeController {
Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId());
return success(true);
}
}

View File

@ -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));
}
}

View File

@ -6,7 +6,7 @@ import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 套餐充值 Base VO提供给添加修改详细的子 VO 使用
* 充值套餐 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data

View File

@ -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)

View File

@ -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 @jasonpayPrice bonusPrice 可以去掉一般太少检索啦
@Schema(description = "支付金额", example = "16454")
private Integer payPrice;

View File

@ -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)

View File

@ -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)

View File

@ -10,4 +10,5 @@ public class PayWalletTransactionPageReqVO extends PageParam {
@Schema(description = "钱包编号", example = "1")
private Long walletId;
}

View File

@ -29,4 +29,7 @@ public class PayWalletTransactionRespVO {
@Schema(description = "交易时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
// TODO @jasonmerchantOrderId 字段需要在 PayWalletTransaction 存储下然后前端也返回下这个字段界面也展示下商户名
}

View File

@ -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 = "创建时间")

View File

@ -19,4 +19,6 @@ public class PayWalletRespVO extends PayWalletBaseVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
// TODO @jason要不把用户昵称 + avatar 也读取下
}

View File

@ -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 必须二选一
}

View File

@ -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);
}

View File

@ -20,6 +20,7 @@ import java.util.Map;
@KeySequence("pay_demo_transfer_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -9,7 +9,7 @@ import lombok.Data;
/**
* 会员钱包充值套餐 DO
*
* 通过套餐充值可以赠送一定金额
* 通过充值套餐时可以赠送一定金额
*
* @author 芋道源码
*/

View File

@ -8,7 +8,3 @@ import org.apache.ibatis.annotations.Mapper;
public interface PayDemoTransferMapper extends BaseMapperX<PayDemoTransferDO> {
}

View File

@ -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));
}
}

View File

@ -14,6 +14,7 @@ public interface PayTransferMapper extends BaseMapperX<PayTransferDO> {
return update(updateObj, new LambdaQueryWrapper<PayTransferDO>()
.eq(PayTransferDO::getId, id).in(PayTransferDO::getStatus, status));
}
}

View File

@ -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 退款金额
*/

View File

@ -19,7 +19,3 @@ public interface PayWalletRechargeMapper extends BaseMapperX<PayWalletRechargeDO
}
}

View File

@ -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));
}
}

View File

@ -180,4 +180,5 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
throw new UnsupportedOperationException("待实现");
}
}

View File

@ -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 {
}
}
}
}

View File

@ -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);
}

View File

@ -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);
@ -121,7 +127,7 @@ public class PayTransferServiceImpl implements PayTransferService {
// 2. 更新 PayTransferDO 转账成功
Boolean transferred = updateTransferSuccess(channel,transferExtension, notify);
if (transferred) {
return ;
return;
}
// 3. TODO 插入转账通知记录
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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,13 +65,14 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
@Transactional(rollbackFor = Exception.class)
public PayWalletRechargeDO createWalletRecharge(Long userId, Integer userType,
AppPayWalletRechargeCreateReqVO reqVO) {
// 1.1 校验参数
// 1.1 校验参数 TODO @jasonAppPayWalletRechargeCreateReqVO 看下校验
if (Objects.isNull(reqVO.getPayPrice()) && Objects.isNull(reqVO.getPackageId())) {
throw exception(WALLET_RECHARGE_PACKAGE_AND_PRICE_IS_EMPTY);
}
// 1.2 新增钱包充值记录
int payPrice ;
int bonusPrice = 0 ;
// 1.1 计算充值金额
int payPrice;
int bonusPrice = 0;
if (Objects.nonNull(reqVO.getPackageId())) {
PayWalletRechargePackageDO rechargePackage = payWalletRechargePackageService.validWalletRechargePackage(reqVO.getPackageId());
payPrice = rechargePackage.getPayPrice();
@ -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 @jasonclientIp 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;
}

View File

@ -90,6 +90,7 @@ public interface PayWalletService {
*/
void freezePrice(Long id, Integer price);
// TODO @jasonunfreeze 是单词哈f 不用大写
/**
* 解冻钱包余额
*

View File

@ -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);