feat(system): 新增租户余额转账功能
This commit is contained in:
@@ -109,4 +109,12 @@ public class TenantBalanceController {
|
|||||||
tenantBalanceService.addTenantBalance(addReqVO);
|
tenantBalanceService.addTenantBalance(addReqVO);
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/transfer")
|
||||||
|
@Operation(summary = "租户余额转账")
|
||||||
|
@PreAuthorize("@ss.hasPermission('system:tenant-balance:transfer')")
|
||||||
|
public CommonResult<Boolean> transferToTenant(@Valid @RequestBody TenantBalanceTransferReqVO transferReqVO) {
|
||||||
|
tenantBalanceService.transferToTenant(transferReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package cn.iocoder.yudao.module.system.controller.admin.tenantbalance.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author: ziin
|
||||||
|
* @date: 2025/11/19 21:05
|
||||||
|
*/
|
||||||
|
@Schema(description = "管理后台 - 租户余额添加VO")
|
||||||
|
@Data
|
||||||
|
public class TenantBalanceTransferReqVO {
|
||||||
|
@Schema(description = "租户 Id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
@NotNull(message = "租户 Id 不能为空")
|
||||||
|
private Long targetTenantId;
|
||||||
|
|
||||||
|
|
||||||
|
@Schema(description = "转账的金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
|
||||||
|
@NotNull(message = "增加的余额不能为空")
|
||||||
|
private Integer transferAmount;
|
||||||
|
|
||||||
|
@Schema(description = "变动描述", example = "充值")
|
||||||
|
@NotNull(message = "变动描述不能为空")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "登录密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
|
||||||
|
@NotNull(message = "登录密码不能为空")
|
||||||
|
private String password;
|
||||||
|
}
|
||||||
@@ -125,6 +125,14 @@ public interface ErrorCodeConstants {
|
|||||||
ErrorCode TENANT_BALANCE_BALANCE_ERROR = new ErrorCode(1_003_017_002, "租户余额余额错误");
|
ErrorCode TENANT_BALANCE_BALANCE_ERROR = new ErrorCode(1_003_017_002, "租户余额余额错误");
|
||||||
ErrorCode TENANT_BALANCE_BALANCE_NOT_ENOUGH = new ErrorCode(1_003_017_003, "租户余额余额不足");
|
ErrorCode TENANT_BALANCE_BALANCE_NOT_ENOUGH = new ErrorCode(1_003_017_003, "租户余额余额不足");
|
||||||
ErrorCode TENANT_BALANCE_RECHARGE_ERROR = new ErrorCode(1_003_017_004, "租户余额充值错误");
|
ErrorCode TENANT_BALANCE_RECHARGE_ERROR = new ErrorCode(1_003_017_004, "租户余额充值错误");
|
||||||
|
ErrorCode TENANT_WALLET_NOT_EXISTS = new ErrorCode(1_003_017_005, "租户钱包不存在");
|
||||||
|
ErrorCode TENANT_BALANCE_NOT_ENOUGH = new ErrorCode(1_003_017_006, "当前余额余额不足");
|
||||||
|
ErrorCode TENANT_BALANCE_TRANSFER_ERROR = new ErrorCode(1_003_017_007, "转账余额不能小于等与 0");
|
||||||
|
ErrorCode TENANT_BALANCE_TRANSFER_TARGET_NOT_EXISTS = new ErrorCode(1_003_017_008, "转账目标租户钱包不存在");
|
||||||
|
ErrorCode TENANT_BALANCE_TRANSFER_OPERATION_ERROR = new ErrorCode(1_003_017_009, "转账操作失败");
|
||||||
|
ErrorCode TENANT_BALANCE_TRANSFER_PASSWORD_ERROR = new ErrorCode(1_003_017_010, "转账密码错误");
|
||||||
|
ErrorCode TENANT_BALANCE_TRANSFER_PASSWORD_ERROR_IS_NULL = new ErrorCode(1_003_017_011, "转账密码不能为空");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ========== 社交用户 1-002-018-000 ==========
|
// ========== 社交用户 1-002-018-000 ==========
|
||||||
|
|||||||
@@ -60,4 +60,6 @@ public interface TenantBalanceService {
|
|||||||
PageResult<TenantBalanceRespVO> getTenantBalancePage(TenantBalancePageReqVO pageReqVO);
|
PageResult<TenantBalanceRespVO> getTenantBalancePage(TenantBalancePageReqVO pageReqVO);
|
||||||
|
|
||||||
void addTenantBalance(@Valid TenantBalanceAddReqVO updateReqVO);
|
void addTenantBalance(@Valid TenantBalanceAddReqVO updateReqVO);
|
||||||
|
|
||||||
|
void transferToTenant(@Valid TenantBalanceTransferReqVO transferReqVO);
|
||||||
}
|
}
|
||||||
@@ -5,14 +5,15 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.tenantbalance.vo.TenantBalanceAddReqVO;
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.tenantbalance.vo.TenantBalancePageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.tenantbalance.vo.*;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.tenantbalance.vo.TenantBalanceRespVO;
|
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.tenantbalance.vo.TenantBalanceSaveReqVO;
|
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenantbalance.TenantBalanceDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.tenantbalance.TenantBalanceDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenantpoints.TenantPointsDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.tenantpoints.TenantPointsDO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.tenantbalance.TenantBalanceMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.tenantbalance.TenantBalanceMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.tenantpoints.TenantPointsMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.tenantpoints.TenantPointsMapper;
|
||||||
|
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
|
||||||
|
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||||
import cn.iocoder.yudao.module.system.util.BizNoGenerator;
|
import cn.iocoder.yudao.module.system.util.BizNoGenerator;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
@@ -26,6 +27,7 @@ import java.util.List;
|
|||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
import static cn.iocoder.yudao.module.system.enums.balance.BalanceEnum.RECHARGE;
|
import static cn.iocoder.yudao.module.system.enums.balance.BalanceEnum.RECHARGE;
|
||||||
|
import static cn.iocoder.yudao.module.system.enums.balance.BalanceEnum.TRANSFER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户余额 Service 实现类
|
* 租户余额 Service 实现类
|
||||||
@@ -42,6 +44,9 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
|||||||
@Resource
|
@Resource
|
||||||
private TenantPointsMapper tenantPointsMapper;
|
private TenantPointsMapper tenantPointsMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AdminUserService userService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createTenantBalance(TenantBalanceSaveReqVO createReqVO) {
|
public Long createTenantBalance(TenantBalanceSaveReqVO createReqVO) {
|
||||||
// 插入
|
// 插入
|
||||||
@@ -121,10 +126,6 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
|||||||
Integer newBalance = tenantBalance.getBalance()+addReqVO.getAmount();
|
Integer newBalance = tenantBalance.getBalance()+addReqVO.getAmount();
|
||||||
Integer oldVersion = tenantBalance.getVersion();
|
Integer oldVersion = tenantBalance.getVersion();
|
||||||
|
|
||||||
TenantBalanceDO updateObj = BeanUtils.toBean(addReqVO, TenantBalanceDO.class);
|
|
||||||
|
|
||||||
updateObj.setVersion(oldVersion);
|
|
||||||
updateObj.setBalance(newBalance);
|
|
||||||
Integer updateCount = tenantBalanceMapper.updateBalanceWithVersion(addReqVO.getId(), addReqVO.getAmount(), oldVersion);
|
Integer updateCount = tenantBalanceMapper.updateBalanceWithVersion(addReqVO.getId(), addReqVO.getAmount(), oldVersion);
|
||||||
if (updateCount == 0) {
|
if (updateCount == 0) {
|
||||||
throw exception(TENANT_BALANCE_RECHARGE_ERROR);
|
throw exception(TENANT_BALANCE_RECHARGE_ERROR);
|
||||||
@@ -145,4 +146,117 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
|||||||
tenantPointsMapper.insert(tenantPointsDO);
|
tenantPointsMapper.insert(tenantPointsDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void transferToTenant(TenantBalanceTransferReqVO transferReqVO) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行租户余额转账操作
|
||||||
|
* 注:这部分代码应该位于某个转账相关的方法中
|
||||||
|
*/
|
||||||
|
// 获取当前登录用户ID,用于操作记录和权限验证
|
||||||
|
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||||
|
|
||||||
|
// 生成转账业务流水号,用于唯一标识这笔转账交易
|
||||||
|
String transfer = BizNoGenerator.generate("TRANSFER");
|
||||||
|
|
||||||
|
// 验证转账密码是否为空
|
||||||
|
if (transferReqVO.getPassword().isEmpty()) {
|
||||||
|
throw exception(TENANT_BALANCE_TRANSFER_PASSWORD_ERROR_IS_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前登录用户信息
|
||||||
|
AdminUserDO user = userService.getUser(loginUserId);
|
||||||
|
|
||||||
|
// 验证转账密码是否正确
|
||||||
|
if (!userService.isPasswordMatch(transferReqVO.getPassword(), user.getPassword())) {
|
||||||
|
throw exception(TENANT_BALANCE_TRANSFER_PASSWORD_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前租户ID(转出方)
|
||||||
|
Long tenantId = TenantContextHolder.getTenantId();
|
||||||
|
|
||||||
|
// 校验当前租户钱包是否存在
|
||||||
|
TenantBalanceDO tenantBalanceDO = tenantBalanceMapper.selectById(tenantId);
|
||||||
|
if (tenantBalanceDO == null) {
|
||||||
|
throw exception(TENANT_WALLET_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取目标租户(转入方)的余额信息
|
||||||
|
TenantBalanceDO targetTenantBalance = tenantBalanceMapper.selectById(transferReqVO.getTargetTenantId());
|
||||||
|
|
||||||
|
// 校验目标租户钱包是否存在
|
||||||
|
if (targetTenantBalance == null) {
|
||||||
|
throw exception(TENANT_BALANCE_TRANSFER_TARGET_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验当前租户余额是否足够支付转账金额
|
||||||
|
if (tenantBalanceDO.getBalance() < transferReqVO.getTransferAmount()) {
|
||||||
|
throw exception(TENANT_BALANCE_NOT_ENOUGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验转账金额是否有效(必须大于0)
|
||||||
|
if (transferReqVO.getTransferAmount() <= 0) {
|
||||||
|
throw exception(TENANT_BALANCE_TRANSFER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算转账后转出方的新余额
|
||||||
|
Integer tenantNewBalance = tenantBalanceDO.getBalance() - transferReqVO.getTransferAmount();
|
||||||
|
|
||||||
|
// 计算转账后转入方的新余额
|
||||||
|
Integer targetTenantNewBalance = targetTenantBalance.getBalance() + transferReqVO.getTransferAmount();
|
||||||
|
|
||||||
|
// 获取转出方当前的版本号(用于乐观锁控制)
|
||||||
|
Integer tenantBalanceDOVersion = tenantBalanceDO.getVersion();
|
||||||
|
|
||||||
|
// 获取转入方当前的版本号(用于乐观锁控制)
|
||||||
|
Integer targetTenantBalanceVersion = targetTenantBalance.getVersion();
|
||||||
|
|
||||||
|
// 先更新转入方的余额(使用乐观锁)
|
||||||
|
Integer updateTargetBalanceCount = tenantBalanceMapper
|
||||||
|
.updateBalanceWithVersion(transferReqVO.getTargetTenantId(), transferReqVO.getTransferAmount(), targetTenantBalanceVersion);
|
||||||
|
|
||||||
|
// 如果更新转入方失败,抛出异常
|
||||||
|
if (updateTargetBalanceCount == 0) {
|
||||||
|
throw exception(TENANT_BALANCE_TRANSFER_OPERATION_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再更新转出方的余额(使用乐观锁,金额为负数表示减少)
|
||||||
|
Integer updateCount = tenantBalanceMapper
|
||||||
|
.updateBalanceWithVersion(tenantId, -transferReqVO.getTransferAmount(), tenantBalanceDOVersion);
|
||||||
|
|
||||||
|
// 如果更新转出方失败,抛出异常
|
||||||
|
if (updateCount == 0) {
|
||||||
|
throw exception(TENANT_BALANCE_TRANSFER_OPERATION_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建转出方的积分变动记录
|
||||||
|
TenantPointsDO tenantPointsDO = new TenantPointsDO();
|
||||||
|
tenantPointsDO.setTenantId(tenantId); // 设置租户ID
|
||||||
|
tenantPointsDO.setPoints(-transferReqVO.getTransferAmount()); // 设置变动积分(负数表示减少)
|
||||||
|
tenantPointsDO.setBalance(tenantNewBalance); // 设置变动后的余额
|
||||||
|
tenantPointsDO.setOperatorId(loginUserId); // 设置操作人ID
|
||||||
|
tenantPointsDO.setType(TRANSFER.getDesc()); // 设置交易类型为转账
|
||||||
|
tenantPointsDO.setDescription(transferReqVO.getDescription()); // 设置交易描述
|
||||||
|
tenantPointsDO.setBizNo(transfer); // 设置业务流水号
|
||||||
|
int tenantInsert = tenantPointsMapper.insert(tenantPointsDO); // 插入记录
|
||||||
|
|
||||||
|
// 创建转入方的积分变动记录
|
||||||
|
TenantPointsDO targetPointsDO = new TenantPointsDO();
|
||||||
|
targetPointsDO.setTenantId(transferReqVO.getTargetTenantId()); // 设置目标租户ID
|
||||||
|
targetPointsDO.setPoints(transferReqVO.getTransferAmount()); // 设置变动积分(正数表示增加)
|
||||||
|
targetPointsDO.setBalance(targetTenantNewBalance); // 设置变动后的余额
|
||||||
|
targetPointsDO.setOperatorId(loginUserId); // 设置操作人ID
|
||||||
|
targetPointsDO.setType(TRANSFER.getDesc()); // 设置交易类型为转账
|
||||||
|
targetPointsDO.setDescription(transferReqVO.getDescription()); // 设置交易描述
|
||||||
|
targetPointsDO.setBizNo(transfer); // 设置业务流水号(与转出方使用同一个流水号)
|
||||||
|
int targetInsert = tenantPointsMapper.insert(targetPointsDO); // 插入记录
|
||||||
|
|
||||||
|
// 验证交易记录是否都插入成功
|
||||||
|
if (tenantInsert == 0 || targetInsert == 0) {
|
||||||
|
throw exception(TENANT_BALANCE_TRANSFER_OPERATION_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user