feat(system): 增加租户代理层级控制与级别校验

This commit is contained in:
2025-11-21 19:50:04 +08:00
parent 5a15f2208b
commit 431c943019
7 changed files with 114 additions and 30 deletions

View File

@@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantLevelRespVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
@@ -136,4 +138,13 @@ public class TenantController {
return success(BeanUtils.toBean(pageResult, TenantRespVO.class));
}
@GetMapping("/getSelfTenantLevel")
@Operation(summary = "获取自身代理级别")
@PreAuthorize("@ss.hasPermission('system:tenant:query-self-Level')")
public CommonResult<TenantLevelRespVO> getSelfTenantPage() {
Long tenantId = TenantContextHolder.getTenantId();
TenantDO tenant = tenantService.getTenant(tenantId);
return success(BeanUtils.toBean(tenant, TenantLevelRespVO.class));
}
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 租户 Response VO")
@Data
@ExcelIgnoreUnannotated
public class TenantLevelRespVO {
@Schema(description = "租户等级",example = "1")
private String tenantLevel;
}

View File

@@ -67,6 +67,9 @@ public class TenantRespVO {
@Schema(description = "上级租户 Id", example = "1024")
private Long parentId;
@Schema(description = "租户类型", example = "代理/客户")
private String tenantType;
@Schema(description = "租户类型", example = "代理/客户")
private String tenantType;
@Schema(description = "租户等级",example = "1")
private String tenantLevel;
}

View File

@@ -106,4 +106,9 @@ public class TenantDO extends BaseDO {
* 租户类型
*/
private String tenantType;
/**
* 代理级别
*/
private Integer tenantLevel;
}

View File

@@ -109,6 +109,7 @@ public interface ErrorCodeConstants {
ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!");
ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在");
ErrorCode TENANT_WEBSITE_DUPLICATE = new ErrorCode(1_002_015_005, "域名为【{}】的租户已存在");
ErrorCode TENANT_LEVEL_CANT_CREATE_AGENCY = new ErrorCode(1_002_015_006, "租户级别不能创建代理租户");
// ========== 租户套餐 1-002-016-000 ==========
ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在");

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.system.enums.tenant;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
/*
* @author: ziin
* @date: 2025/11/21 18:55
*/
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum TenantEnum {
AGENCY("代理"),
USER("用户");
private String tenantType;
}

View File

@@ -26,6 +26,7 @@ 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.enums.permission.RoleCodeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.enums.tenant.TenantEnum;
import cn.iocoder.yudao.module.system.service.permission.MenuService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.permission.RoleService;
@@ -110,37 +111,62 @@ public class TenantServiceImpl implements TenantService {
@DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
@DataPermission(enable = false) // 参见 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1154 说明
public Long createTenant(TenantSaveReqVO createReqVO) {
// 校验租户名称是否重复
// 校验租户名称是否重复null表示新增场景无需排除当前ID
validTenantNameDuplicate(createReqVO.getName(), null);
// 校验租户域名是否重复
// 校验租户域名是否重复null表示新增场景无需排除当前ID
validTenantWebsiteDuplicate(createReqVO.getWebsite(), null);
// 校验套餐被禁用
// 校验并获取租户套餐信息(确保套餐存在且未被禁用)
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId());
// 创建租户
TenantDO tenant = BeanUtils.toBean(createReqVO, TenantDO.class);
Long tenantId = TenantContextHolder.getTenantId();
tenant.setParentId(tenantId);
tenantMapper.insert(tenant);
// 创建租户的管理员
TenantUtils.execute(tenant.getId(), () -> {
// 创建角色
Long roleId = createRole(tenantPackage);
// 创建用户,并分配角色
Long userId = createUser(roleId, createReqVO);
// 修改租户的管理员
tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId));
});
// 初始化代理钱包
if (tenant.getTenantType().equals("代理")) {
// 创建租户的钱包
TenantBalanceDO tenantBalance = new TenantBalanceDO();
tenantBalance.setId(tenant.getId());
tenantBalance.setBalance(0);
tenantBalance.setVersion(0);
tenantBalanceMapper.insert(tenantBalance);
// 获取当前操作租户的ID即创建者的租户ID
Long currentTenantId = TenantContextHolder.getTenantId();
// 查询当前租户的详细信息
TenantDO currentTenant = tenantMapper.selectById(currentTenantId);
// 校验租户类型和层级权限不允许创建代理类型租户或当前租户级别为2的租户不允许创建
if (createReqVO.getTenantType().equals(TenantEnum.AGENCY.getTenantType()) && currentTenant.getTenantLevel() == 2) {
throw exception(TENANT_LEVEL_CANT_CREATE_AGENCY);
}
// 将请求参数转换为租户数据对象
TenantDO tenant = BeanUtils.toBean(createReqVO, TenantDO.class);
// 设置新租户的级别为当前租户级别+1层级关系
if (createReqVO.getTenantType().equals(TenantEnum.AGENCY.getTenantType())){
tenant.setTenantLevel(currentTenant.getTenantLevel() + 1);
}
// 设置新租户的父租户ID为当前租户ID建立层级关系
tenant.setParentId(currentTenantId);
// 将新租户信息插入数据库
tenantMapper.insert(tenant);
// 在新创建的租户上下文中执行管理员初始化操作
TenantUtils.execute(tenant.getId(), () -> {
// 创建租户的默认角色
Long roleId = createRole(tenantPackage);
// 创建租户的管理员用户,并分配角色
Long userId = createUser(roleId, createReqVO);
// 将创建的用户设置为租户的联系人(管理员)
tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId));
});
// 如果创建的是代理类型租户,则初始化其钱包
if (tenant.getTenantType().equals("代理")) {
// 创建租户钱包对象
TenantBalanceDO tenantBalance = new TenantBalanceDO();
tenantBalance.setId(tenant.getId()); // 钱包ID与租户ID一致
tenantBalance.setBalance(0); // 初始余额设为0
tenantBalance.setVersion(0); // 初始版本号设为0
tenantBalanceMapper.insert(tenantBalance); // 插入钱包记录
}
// 返回新创建的租户ID
return tenant.getId();
}
@@ -333,4 +359,4 @@ public class TenantServiceImpl implements TenantService {
return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable());
}
}
}