feat(system): 增加代理租户创建失败错误码并重构套餐逻辑
This commit is contained in:
@@ -100,4 +100,5 @@ public class TenantAgencyPackageController {
|
||||
BeanUtils.toBean(list, TenantAgencyPackageRespVO.class));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public class TenantAgencyPackageSaveReqVO {
|
||||
|
||||
@Schema(description = "关联的菜单编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "关联的菜单编号不能为空")
|
||||
private String menuIds;
|
||||
private Set<Long> menuIds;
|
||||
|
||||
@Schema(description = "套餐天数", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "套餐天数不能为空")
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.*;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenantagencypackage.TenantAgencyPackageDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenantagencypackage.vo.*;
|
||||
@@ -33,4 +34,8 @@ public interface TenantAgencyPackageMapper extends BaseMapperX<TenantAgencyPacka
|
||||
.orderByDesc(TenantAgencyPackageDO::getId));
|
||||
}
|
||||
|
||||
default List<TenantAgencyPackageDO> selectListByStatus(Integer status) {
|
||||
return selectList(TenantAgencyPackageDO::getStatus, status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -135,6 +135,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode TENANT_BALANCE_TRANSFER_PASSWORD_ERROR_IS_NULL = new ErrorCode(1_003_017_011, "转账密码不能为空");
|
||||
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, "租户创建失败");
|
||||
|
||||
// ================= 租户套餐 1-003-018-000 ==================
|
||||
ErrorCode TENANT_AGENCY_PACKAGE_NOT_EXISTS = new ErrorCode(1_003_018_000, "代理租户套餐不存在");
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
package cn.iocoder.yudao.module.system.service.tenant;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSaveReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSimpleRespVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenantagencypackage.TenantAgencyPackageDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.tenantagencypackage.TenantAgencyPackageMapper;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
@@ -38,6 +43,9 @@ public class TenantPackageServiceImpl implements TenantPackageService {
|
||||
@Lazy // 避免循环依赖的报错
|
||||
private TenantService tenantService;
|
||||
|
||||
@Resource
|
||||
private TenantAgencyPackageMapper tenantAgencyPackageMapper;
|
||||
|
||||
@Override
|
||||
public Long createTenantPackage(TenantPackageSaveReqVO createReqVO) {
|
||||
// 校验套餐名是否重复
|
||||
@@ -114,6 +122,11 @@ public class TenantPackageServiceImpl implements TenantPackageService {
|
||||
|
||||
@Override
|
||||
public List<TenantPackageDO> getTenantPackageListByStatus(Integer status) {
|
||||
Long tenantId = TenantContextHolder.getTenantId();
|
||||
if (tenantId != 1) {
|
||||
List<TenantAgencyPackageDO> tenantAgencyPackageDOS = tenantAgencyPackageMapper.selectListByStatus(status);
|
||||
return BeanUtils.toBean(tenantAgencyPackageDOS, TenantPackageDO.class);
|
||||
}
|
||||
return tenantPackageMapper.selectListByStatus(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package cn.iocoder.yudao.module.system.service.tenant;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -21,9 +24,13 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenantagencypackage.TenantAgencyPackageDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenantbalance.TenantBalanceDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
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.permission.RoleCodeEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.enums.tenant.TenantEnum;
|
||||
@@ -32,6 +39,8 @@ import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.RoleService;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler;
|
||||
import cn.iocoder.yudao.module.system.service.tenantagencypackage.TenantAgencyPackageService;
|
||||
import cn.iocoder.yudao.module.system.service.tenantbalance.TenantBalanceService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -41,6 +50,8 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@@ -79,7 +90,12 @@ public class TenantServiceImpl implements TenantService {
|
||||
private PermissionService permissionService;
|
||||
@Resource
|
||||
private TenantBalanceMapper tenantBalanceMapper;
|
||||
|
||||
@Resource
|
||||
private TenantAgencyPackageService tenantAgencyPackageService;
|
||||
@Resource
|
||||
private AdminUserMapper userMapper;
|
||||
@Resource
|
||||
private TenantBalanceService balanceService;
|
||||
@Override
|
||||
public List<Long> getTenantIdList() {
|
||||
List<TenantDO> tenants = tenantMapper.selectList();
|
||||
@@ -111,61 +127,141 @@ 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());
|
||||
|
||||
|
||||
// 获取当前操作租户的ID(即创建者的租户ID)
|
||||
Long currentTenantId = TenantContextHolder.getTenantId();
|
||||
|
||||
if (currentTenantId != 1){
|
||||
// 校验租户名称是否重复(null表示新增场景,无需排除当前ID)
|
||||
validTenantNameDuplicate(createReqVO.getName(), null);
|
||||
|
||||
// 校验租户域名是否重复(null表示新增场景,无需排除当前ID)
|
||||
validTenantWebsiteDuplicate(createReqVO.getWebsite(), null);
|
||||
|
||||
// 校验并获取租户套餐信息(确保套餐存在且未被禁用)
|
||||
TenantAgencyPackageDO tenantAgencyPackageDO = tenantAgencyPackageService.validTenantPackage(createReqVO.getPackageId());
|
||||
|
||||
// 查询当前租户的详细信息
|
||||
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);
|
||||
|
||||
String now = DateUtil.now();
|
||||
DateTime dt = DateUtil.parse(now);
|
||||
LocalDateTime localDateTime = LocalDateTimeUtil.of(dt);
|
||||
LocalDateTime offset = LocalDateTimeUtil.offset(localDateTime, tenantAgencyPackageDO.getDays(), ChronoUnit.DAYS);
|
||||
tenant.setExpireTime(offset);
|
||||
if (tenantAgencyPackageDO.getBrotherClient() == 1){
|
||||
tenant.setBrotherExpireTime(offset);
|
||||
}
|
||||
if (tenantAgencyPackageDO.getAiClient() == 1){
|
||||
tenant.setAiExpireTime(offset);
|
||||
}
|
||||
// 设置新租户的级别为当前租户级别+1(层级关系)
|
||||
if (createReqVO.getTenantType().equals(TenantEnum.AGENCY.getTenantType())){
|
||||
tenant.setTenantLevel(currentTenant.getTenantLevel() + 1);
|
||||
}
|
||||
// 设置新租户的父租户ID为当前租户ID(建立层级关系)
|
||||
tenant.setParentId(currentTenantId);
|
||||
|
||||
tenantMapper.insert(tenant);
|
||||
// 扣除开通费用,
|
||||
if (balanceService.consumption(createReqVO.getPackageId(), tenant.getId(), createReqVO.getRemark())) {
|
||||
log.info("代理: {} 开通租户:{} 成功,套餐 Id:{}", currentTenantId,tenant.getId(),createReqVO.getPackageId());
|
||||
}
|
||||
// 在新创建的租户上下文中执行管理员初始化操作
|
||||
TenantUtils.execute(tenant.getId(), () -> {
|
||||
// 创建租户的默认角色
|
||||
Long roleId = AgencyCreateRole(tenantAgencyPackageDO);
|
||||
|
||||
// 创建租户的管理员用户,并分配角色
|
||||
Long userId = createUser(roleId, createReqVO);
|
||||
AdminUserDO user = userService.getUser(userId);
|
||||
if (tenantAgencyPackageDO.getHostslClient() == 1){
|
||||
user.setCrawl((byte) 1);
|
||||
}
|
||||
if (tenantAgencyPackageDO.getHostslClient() == 1){
|
||||
user.setBigBrother((byte) 1);
|
||||
}
|
||||
if (tenantAgencyPackageDO.getAiClient() == 1){
|
||||
user.setAiChat((byte) 1);
|
||||
user.setAiReplay((byte) 1);
|
||||
}
|
||||
userMapper.updateById(user);
|
||||
// 将创建的用户设置为租户的联系人(管理员)
|
||||
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();
|
||||
}
|
||||
// 校验租户名称是否重复(null表示新增场景,无需排除当前ID)
|
||||
validTenantNameDuplicate(createReqVO.getName(), null);
|
||||
|
||||
// 校验租户域名是否重复(null表示新增场景,无需排除当前ID)
|
||||
validTenantWebsiteDuplicate(createReqVO.getWebsite(), null);
|
||||
|
||||
// 校验并获取租户套餐信息(确保套餐存在且未被禁用)
|
||||
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId());
|
||||
|
||||
// 查询当前租户的详细信息
|
||||
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));
|
||||
// 创建租户的默认角色
|
||||
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); // 插入钱包记录
|
||||
// 创建租户钱包对象
|
||||
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();
|
||||
}
|
||||
@@ -189,6 +285,17 @@ public class TenantServiceImpl implements TenantService {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
private Long AgencyCreateRole(TenantAgencyPackageDO agencyPackageDO) {
|
||||
// 创建角色
|
||||
RoleSaveReqVO reqVO = new RoleSaveReqVO();
|
||||
reqVO.setName(RoleCodeEnum.TENANT_ADMIN.getName()).setCode(RoleCodeEnum.TENANT_ADMIN.getCode())
|
||||
.setSort(0).setRemark("系统自动生成");
|
||||
Long roleId = roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType());
|
||||
// 分配权限
|
||||
permissionService.assignRoleMenu(roleId, agencyPackageDO.getMenuIds());
|
||||
return roleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
|
||||
public void updateTenant(TenantSaveReqVO updateReqVO) {
|
||||
|
||||
@@ -326,6 +326,8 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
||||
Integer tenantBalanceDOVersion = tenantBalance.getVersion();
|
||||
|
||||
Integer oldBalance = tenantBalance.getBalance();
|
||||
|
||||
Integer newBalance = oldBalance - tenantAgencyPackageDO.getPrice();
|
||||
// 更新租户余额
|
||||
Integer updateCount = tenantBalanceMapper.updateBalanceWithVersion(tenantId, -tenantAgencyPackageDO.getPrice(), tenantBalanceDOVersion);
|
||||
|
||||
@@ -337,7 +339,7 @@ public class TenantBalanceServiceImpl implements TenantBalanceService {
|
||||
TenantPointsDO tenantPointsDO = new TenantPointsDO();
|
||||
tenantPointsDO.setTenantId(tenantId); // 设置租户ID
|
||||
tenantPointsDO.setPoints(-tenantAgencyPackageDO.getPrice()); // 设置变动积分(负数表示减少)
|
||||
tenantPointsDO.setBalance(oldBalance - tenantAgencyPackageDO.getPrice()); // 设置变动后的余额
|
||||
tenantPointsDO.setBalance(newBalance); // 设置变动后的余额
|
||||
tenantPointsDO.setOperatorId(loginUserId); // 设置操作人ID
|
||||
tenantPointsDO.setType(CONSUMPTION.getDesc()); // 设置交易类型为转账
|
||||
tenantPointsDO.setDescription("给租户" + targetTenantId + "开通套餐" + tenantAgencyPackageDO.getName() + ",金额:" + tenantAgencyPackageDO.getPrice()); // 设置交易描述
|
||||
|
||||
Reference in New Issue
Block a user