package com.iailab.module.system.service.tenant;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.StrUtil;
|
import com.iailab.framework.common.enums.CommonStatusEnum;
|
import com.iailab.framework.common.pojo.PageResult;
|
import com.iailab.framework.common.util.collection.CollectionUtils;
|
import com.iailab.framework.common.util.date.DateUtils;
|
import com.iailab.framework.common.util.object.BeanUtils;
|
import com.iailab.framework.tenant.config.TenantProperties;
|
import com.iailab.framework.tenant.core.context.TenantContextHolder;
|
import com.iailab.framework.tenant.core.util.TenantUtils;
|
import com.iailab.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
|
import com.iailab.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
|
import com.iailab.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
|
import com.iailab.module.system.convert.tenant.TenantConvert;
|
import com.iailab.module.system.dal.dataobject.permission.MenuDO;
|
import com.iailab.module.system.dal.dataobject.permission.RoleDO;
|
import com.iailab.module.system.dal.dataobject.tenant.TenantDO;
|
import com.iailab.module.system.dal.dataobject.tenant.TenantPackageDO;
|
import com.iailab.module.system.dal.mysql.tenant.TenantMapper;
|
import com.iailab.module.system.enums.permission.RoleCodeEnum;
|
import com.iailab.module.system.enums.permission.RoleTypeEnum;
|
import com.iailab.module.system.service.permission.MenuService;
|
import com.iailab.module.system.service.permission.PermissionService;
|
import com.iailab.module.system.service.permission.RoleService;
|
import com.iailab.module.system.service.tenant.handler.TenantInfoHandler;
|
import com.iailab.module.system.service.tenant.handler.TenantMenuHandler;
|
import com.iailab.module.system.service.user.AdminUserService;
|
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.stereotype.Service;
|
import org.springframework.validation.annotation.Validated;
|
|
import javax.annotation.Resource;
|
import java.util.Collections;
|
import java.util.List;
|
import java.util.Objects;
|
import java.util.Set;
|
|
import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static com.iailab.module.system.enums.ErrorCodeConstants.*;
|
import static java.util.Collections.singleton;
|
|
/**
|
* 租户 Service 实现类
|
*
|
* @author iailab
|
*/
|
@Service
|
@Validated
|
@Slf4j
|
public class TenantServiceImpl implements TenantService {
|
|
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
|
@Autowired(required = false) // 由于 iailab.tenant.enable 配置项,可以关闭多租户的功能,所以这里只能不强制注入
|
private TenantProperties tenantProperties;
|
|
@Resource
|
private TenantMapper tenantMapper;
|
|
@Resource
|
private TenantPackageService tenantPackageService;
|
@Resource
|
@Lazy // 延迟,避免循环依赖报错
|
private AdminUserService userService;
|
@Resource
|
private RoleService roleService;
|
@Resource
|
private MenuService menuService;
|
@Resource
|
private PermissionService permissionService;
|
|
@Override
|
public List<Long> getTenantIdList() {
|
List<TenantDO> tenants = tenantMapper.selectList();
|
return CollectionUtils.convertList(tenants, TenantDO::getId);
|
}
|
|
@Override
|
public void validTenant(Long id) {
|
TenantDO tenant = getTenant(id);
|
if (tenant == null) {
|
throw exception(TENANT_NOT_EXISTS);
|
}
|
if (tenant.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) {
|
throw exception(TENANT_DISABLE, tenant.getName());
|
}
|
if (DateUtils.isExpired(tenant.getExpireTime())) {
|
throw exception(TENANT_EXPIRE, tenant.getName());
|
}
|
}
|
|
@Override
|
@DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
|
public Long createTenant(TenantSaveReqVO createReqVO) {
|
// 校验租户名称是否重复
|
validTenantNameDuplicate(createReqVO.getName(), null);
|
// 校验租户域名是否重复
|
validTenantWebsiteDuplicate(createReqVO.getWebsite(), null);
|
// 校验套餐被禁用
|
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId());
|
|
// 创建租户
|
TenantDO tenant = BeanUtils.toBean(createReqVO, TenantDO.class);
|
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));
|
});
|
return tenant.getId();
|
}
|
|
private Long createUser(Long roleId, TenantSaveReqVO createReqVO) {
|
// 创建用户
|
Long userId = userService.createUser(TenantConvert.INSTANCE.convert02(createReqVO));
|
// 分配角色
|
permissionService.assignUserRole(userId, singleton(roleId));
|
return userId;
|
}
|
|
private Long createRole(TenantPackageDO tenantPackage) {
|
// 创建角色
|
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, tenantPackage.getMenuIds());
|
return roleId;
|
}
|
|
@Override
|
@DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
|
public void updateTenant(TenantSaveReqVO updateReqVO) {
|
// 校验存在
|
TenantDO tenant = validateUpdateTenant(updateReqVO.getId());
|
// 校验租户名称是否重复
|
validTenantNameDuplicate(updateReqVO.getName(), updateReqVO.getId());
|
// 校验租户域名是否重复
|
validTenantWebsiteDuplicate(updateReqVO.getWebsite(), updateReqVO.getId());
|
// 校验套餐被禁用
|
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId());
|
|
// 更新租户
|
TenantDO updateObj = BeanUtils.toBean(updateReqVO, TenantDO.class);
|
tenantMapper.updateById(updateObj);
|
// 如果套餐发生变化,则修改其角色的权限
|
if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) {
|
updateTenantRoleMenu(tenant.getId(), tenantPackage.getMenuIds());
|
}
|
}
|
|
private void validTenantNameDuplicate(String name, Long id) {
|
TenantDO tenant = tenantMapper.selectByName(name);
|
if (tenant == null) {
|
return;
|
}
|
// 如果 id 为空,说明不用比较是否为相同名字的租户
|
if (id == null) {
|
throw exception(TENANT_NAME_DUPLICATE, name);
|
}
|
if (!tenant.getId().equals(id)) {
|
throw exception(TENANT_NAME_DUPLICATE, name);
|
}
|
}
|
|
private void validTenantWebsiteDuplicate(String website, Long id) {
|
if (StrUtil.isEmpty(website)) {
|
return;
|
}
|
TenantDO tenant = tenantMapper.selectByWebsite(website);
|
if (tenant == null) {
|
return;
|
}
|
// 如果 id 为空,说明不用比较是否为相同名字的租户
|
if (id == null) {
|
throw exception(TENANT_WEBSITE_DUPLICATE, website);
|
}
|
if (!tenant.getId().equals(id)) {
|
throw exception(TENANT_WEBSITE_DUPLICATE, website);
|
}
|
}
|
|
@Override
|
@DSTransactional
|
public void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds) {
|
TenantUtils.execute(tenantId, () -> {
|
// 获得所有角色
|
List<RoleDO> roles = roleService.getRoleList();
|
roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配",
|
role.getId(), role.getTenantId(), tenantId)); // 兜底校验
|
// 重新分配每个角色的权限
|
roles.forEach(role -> {
|
// 如果是租户管理员,重新分配其权限为租户套餐的权限
|
if (Objects.equals(role.getCode(), RoleCodeEnum.TENANT_ADMIN.getCode())) {
|
permissionService.assignRoleMenu(role.getId(), menuIds);
|
log.info("[updateTenantRoleMenu][租户管理员({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), menuIds);
|
return;
|
}
|
// 如果是其他角色,则去掉超过套餐的权限
|
Set<Long> roleMenuIds = permissionService.getRoleMenuListByRoleId(role.getId());
|
roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds);
|
permissionService.assignRoleMenu(role.getId(), roleMenuIds);
|
log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds);
|
});
|
});
|
}
|
|
@Override
|
public void deleteTenant(Long id) {
|
// 校验存在
|
validateUpdateTenant(id);
|
// 删除
|
tenantMapper.deleteById(id);
|
}
|
|
private TenantDO validateUpdateTenant(Long id) {
|
TenantDO tenant = tenantMapper.selectById(id);
|
if (tenant == null) {
|
throw exception(TENANT_NOT_EXISTS);
|
}
|
// 内置租户,不允许删除
|
if (isSystemTenant(tenant)) {
|
throw exception(TENANT_CAN_NOT_UPDATE_SYSTEM);
|
}
|
return tenant;
|
}
|
|
@Override
|
public TenantDO getTenant(Long id) {
|
return tenantMapper.selectById(id);
|
}
|
|
@Override
|
public PageResult<TenantDO> getTenantPage(TenantPageReqVO pageReqVO) {
|
return tenantMapper.selectPage(pageReqVO);
|
}
|
|
@Override
|
public List<TenantDO> getSimpleTenant() {
|
return tenantMapper.selectList();
|
}
|
|
@Override
|
public TenantDO getTenantByName(String name) {
|
return tenantMapper.selectByName(name);
|
}
|
|
@Override
|
public TenantDO getTenantByWebsite(String website) {
|
return tenantMapper.selectByWebsite(website);
|
}
|
|
@Override
|
public Long getTenantCountByPackageId(Long packageId) {
|
return tenantMapper.selectCountByPackageId(packageId);
|
}
|
|
@Override
|
public List<TenantDO> getTenantListByPackageId(Long packageId) {
|
return tenantMapper.selectListByPackageId(packageId);
|
}
|
|
@Override
|
public void handleTenantInfo(TenantInfoHandler handler) {
|
// 如果禁用,则不执行逻辑
|
if (isTenantDisable()) {
|
return;
|
}
|
// 获得租户
|
TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId());
|
// 执行处理器
|
handler.handle(tenant);
|
}
|
|
@Override
|
public void handleTenantMenu(TenantMenuHandler handler) {
|
// 如果禁用,则不执行逻辑
|
if (isTenantDisable()) {
|
return;
|
}
|
// 获得租户,然后获得菜单
|
TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId());
|
Set<Long> menuIds;
|
if (isSystemTenant(tenant)) { // 系统租户,菜单是全量的
|
menuIds = CollectionUtils.convertSet(menuService.getMenuList(), MenuDO::getId);
|
} else {
|
menuIds = tenantPackageService.getTenantPackage(tenant.getPackageId()).getMenuIds();
|
}
|
// 执行处理器
|
handler.handle(menuIds);
|
}
|
|
private static boolean isSystemTenant(TenantDO tenant) {
|
return Objects.equals(tenant.getPackageId(), TenantDO.PACKAGE_ID_SYSTEM);
|
}
|
|
private boolean isTenantDisable() {
|
return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable());
|
}
|
|
}
|