feat(system): 新增租户测试账户增减及奖励类型支持

- BalanceEnum 增加 BONUS 奖励类型
- ErrorCodeConstants 补充测试账户不足/操作错误码
- TenantBalanceMapper 添加乐观锁更新测试账户数方法
- TenantBalanceService 新增 reduceTestAccountNum/addTestAccountNum
- TenantBalanceServiceImpl 实现测试账户扣减与奖励发放逻辑
- TenantPointsDO/VO 增加 testAccountNum 字段
- TenantServiceImpl 创建租户时根据套餐类型自动增减测试账户
- 新增 PackageTypeEnum 枚举区分测试/代理/普通套餐
This commit is contained in:
2025-11-25 16:16:28 +08:00
parent e366af0493
commit bfc2b21872
10 changed files with 136 additions and 1 deletions

View File

@@ -55,4 +55,8 @@ public class TenantPointsPageRespVO {
@Schema(description = "租户名称", example = "租户 A")
@ExcelProperty("租户名称")
private String tenantName;
@Schema(description = "测试账号数", example = "1")
@ExcelProperty("测试账号数")
private Integer testAccountNum;
}

View File

@@ -52,4 +52,7 @@ public class TenantPointsRespVO {
@ExcelProperty("创建时间")
private LocalDateTime createdAt;
@Schema(description = "测试账号数", example = "1")
@ExcelProperty("测试账号数")
private Integer testAccountNum;
}

View File

@@ -65,4 +65,6 @@ public class TenantPointsDO {
private Long tenantId;
private String remark;
private Integer testAccountNum;
}

View File

@@ -47,4 +47,19 @@ public interface TenantBalanceMapper extends BaseMapperX<TenantBalanceDO> {
IPage<TenantBalanceRespVO> selectPageWithTenantName(IPage<TenantBalanceRespVO> iPage,@Param("req") TenantBalancePageReqVO pageReqVO);
IPage<TenantBalanceRespVO> selectPageWithSelfSubordinateTenant(@Param("iPage") IPage<TenantBalanceRespVO> iPage, @Param("pageReqVO") TenantBalancePageReqVO pageReqVO, @Param("tenantId") Long tenantId);
default Integer updateTestAccountNumWithVersion(Long currentTenantId, Integer newTestAccountNum, Integer oldVersion){
LambdaUpdateWrapper<TenantBalanceDO> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(TenantBalanceDO::getId, currentTenantId);
wrapper.eq(TenantBalanceDO::getVersion, oldVersion);
// testAccount = testAccount + -1
wrapper.setSql("test_account_num = test_account_num + " + newTestAccountNum);
// version = version + 1
wrapper.setSql("version = version + 1");
return this.update(null, wrapper);
}
}

View File

@@ -136,6 +136,8 @@ public interface ErrorCodeConstants {
ErrorCode TENANT_BALANCE_TRANSFER_ERROR_TARGET_NOT_SUBORDINATE = new ErrorCode(1_003_017_012, "转账目标租户不是当前租户的下级");
ErrorCode TENANT_BALANCE_CONSUMPTION_OPERATION_ERROR = new ErrorCode(1_003_017_013, "租户余额消费操作失败");
ErrorCode TENANT_CREATE_FAIL = new ErrorCode(1_003_017_014, "租户创建失败");
ErrorCode TENANT_BALANCE_TEST_ACCOUNT_NUM_NOT_ENOUGH = new ErrorCode(1_003_017_015, "当前账户测试账户数量不足");
ErrorCode TENANT_BALANCE_TEST_ACCOUNT_NUM_OPERATION_ERROR = new ErrorCode(1_003_017_016, "租户测试账户数量操作错误");
// ================= 租户套餐 1-003-018-000 ==================
ErrorCode TENANT_AGENCY_PACKAGE_NOT_EXISTS = new ErrorCode(1_003_018_000, "代理租户套餐不存在");

View File

@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.system.enums.agencyTenantPackage;
import lombok.Getter;
/*
* @author: ziin
* @date: 2025/11/25 15:22
*/
@Getter
public enum PackageTypeEnum {
AI(0, "AI"),
CRAWL(1, "爬虫"),
BROTHER_CRAWL(2, "兄弟爬虫"),
TEST(99, "测试账户"),
AGENCY(199, "代理");
private final Integer value;
private final String name;
PackageTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
}

View File

@@ -17,7 +17,8 @@ public enum BalanceEnum {
RECHARGE("充值"),
TRANSFER_OUT("转出"),
TRANSFER_IN("转入"),
CONSUMPTION("消费");
CONSUMPTION("消费"),
BONUS(" 奖励");
private String desc;

View File

@@ -31,6 +31,7 @@ import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import cn.iocoder.yudao.module.system.dal.mysql.tenantbalance.TenantBalanceMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.enums.agencyTenantPackage.PackageTypeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.enums.tenant.TenantEnum;
@@ -178,6 +179,15 @@ public class TenantServiceImpl implements TenantService {
if (balanceService.consumption(createReqVO.getPackageId(), tenant.getId(), createReqVO.getRemark())) {
log.info("代理: {} 开通租户:{} 成功,套餐 Id:{}", currentTenantId,tenant.getId(),createReqVO.getPackageId());
}
// 开通测试套餐扣除测试账号数
if (tenantAgencyPackageDO.getPackageType().equals(PackageTypeEnum.TEST.getValue())){
balanceService.reduceTestAccountNum(currentTenantId);
}
if (!tenantAgencyPackageDO.getPackageType().equals(PackageTypeEnum.AGENCY.getValue())
&& !tenantAgencyPackageDO.getPackageType().equals(PackageTypeEnum.TEST.getValue())) {
balanceService.addTestAccountNum(currentTenantId);
}
// 在新创建的租户上下文中执行管理员初始化操作
TenantUtils.execute(tenant.getId(), () -> {
// 创建租户的默认角色
@@ -208,6 +218,7 @@ public class TenantServiceImpl implements TenantService {
tenantBalance.setId(tenant.getId()); // 钱包ID与租户ID一致
tenantBalance.setBalance(0); // 初始余额设为0
tenantBalance.setVersion(0); // 初始版本号设为0
tenantBalance.setTestAccountNum(15);
tenantBalanceMapper.insert(tenantBalance); // 插入钱包记录
}

View File

@@ -68,4 +68,8 @@ public interface TenantBalanceService {
PageResult<TenantBalanceRespVO> getSelfSubordinateTenantBalancePage(@Valid TenantBalancePageReqVO pageReqVO);
Boolean consumption(Long PackageId, Long targetTenantId,String remark);
Boolean reduceTestAccountNum(Long currentTenantId);
Boolean addTestAccountNum(Long currentTenantId);
}

View File

@@ -358,4 +358,71 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
}
@Override
public Boolean reduceTestAccountNum(Long currentTenantId) {
TenantBalanceDO tenantBalanceDO = tenantBalanceMapper.selectById(currentTenantId);
if (tenantBalanceDO.getTestAccountNum()<=0) {
throw exception(TENANT_BALANCE_TEST_ACCOUNT_NUM_NOT_ENOUGH);
}
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
Integer oldVersion = tenantBalanceDO.getVersion();
Integer newTestAccountNum = tenantBalanceDO.getTestAccountNum() - 1;
Integer i = tenantBalanceMapper.updateTestAccountNumWithVersion(currentTenantId, -1, oldVersion);
if (i == 0) {
throw exception(TENANT_BALANCE_TEST_ACCOUNT_NUM_OPERATION_ERROR);
}
String consumption = BizNoGenerator.generate("CONSUMPTION");
// 消费积分变动记录
TenantPointsDO tenantPointsDO = new TenantPointsDO();
tenantPointsDO.setTenantId(currentTenantId); // 设置租户ID
tenantPointsDO.setPoints(-1); // 设置变动积分(负数表示减少)
tenantPointsDO.setBalance(tenantPointsDO.getBalance());
tenantPointsDO.setTestAccountNum(newTestAccountNum); // 设置变动后的余额
tenantPointsDO.setOperatorId(loginUserId); // 设置操作人ID
tenantPointsDO.setType(CONSUMPTION.getDesc()); // 设置交易类型为转账
tenantPointsDO.setDescription("开通测试账户"); // 设置交易描述
tenantPointsDO.setBizNo(consumption); // 设置业务流水号
int tenantInsert = tenantPointsMapper.insert(tenantPointsDO); // 插入记录
if (tenantInsert == 0) {
throw exception(TENANT_BALANCE_CONSUMPTION_OPERATION_ERROR);
}
return true;
}
@Override
public Boolean addTestAccountNum(Long currentTenantId) {
TenantBalanceDO tenantBalanceDO = tenantBalanceMapper.selectById(currentTenantId);
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
Integer oldVersion = tenantBalanceDO.getVersion();
Integer newTestAccountNum = tenantBalanceDO.getTestAccountNum() + 5;
Integer i = tenantBalanceMapper.updateTestAccountNumWithVersion(currentTenantId, 5, oldVersion);
if (i == 0) {
throw exception(TENANT_BALANCE_TEST_ACCOUNT_NUM_OPERATION_ERROR);
}
String bonus = BizNoGenerator.generate("BONUS");
// 消费积分变动记录
TenantPointsDO tenantPointsDO = new TenantPointsDO();
tenantPointsDO.setTenantId(currentTenantId); // 设置租户ID
tenantPointsDO.setPoints(5); // 设置变动积分(负数表示减少)
tenantPointsDO.setTestAccountNum(newTestAccountNum); // 设置变动后的余额
tenantPointsDO.setBalance(tenantPointsDO.getBalance());
tenantPointsDO.setOperatorId(loginUserId); // 设置操作人ID
tenantPointsDO.setType(BONUS.getDesc()); // 设置交易类型为转账
tenantPointsDO.setDescription("开通测试账户,增加测试账号数量"); // 设置交易描述
tenantPointsDO.setBizNo(bonus); // 设置业务流水号
int tenantInsert = tenantPointsMapper.insert(tenantPointsDO); // 插入记录
if (tenantInsert == 0) {
throw exception(TENANT_BALANCE_CONSUMPTION_OPERATION_ERROR);
}
return true;
}
}