refactor(tenant): 优化及修复租户相关部分代码

- 移动 TenantExtensionProperties 到 common 模块
- 修复 MenuController#tree 接口 setExcludeMenuIdList 方法判断非默认租户条件缺失
- 修复更新租户套餐菜单,没有及时更新在线用户数据权限(后面考虑重构 satoken 权限数据读取部分)
- TenantService 接口 getByDomain => getIdByDomain、getByCode => getIdByCode
- 移除 MenuService 中已废弃的方法
- LogDaoLocalImpl 还原(未测出租户用户操作,无租户 ID 问题)
- 优化 pg 数据库脚本,移除菜单表的租户相关字段
- 其他代码优化
This commit is contained in:
2025-07-20 23:13:07 +08:00
parent ada6f3ef5c
commit 84b2c39a30
24 changed files with 164 additions and 233 deletions

View File

@@ -19,7 +19,7 @@ package top.continew.admin.tenant.config;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import top.continew.admin.tenant.model.entity.TenantDO;
import top.continew.admin.common.config.TenantExtensionProperties;
import top.continew.admin.tenant.service.TenantService;
import top.continew.starter.core.util.ServletUtils;
import top.continew.starter.core.util.validation.CheckUtils;
@@ -57,9 +57,9 @@ public class DefaultTenantProvider implements TenantProvider {
if (StrUtil.isBlank(tenantCode)) {
return context;
}
TenantDO tenant = tenantService.getByCode(tenantCode);
CheckUtils.throwIfNull(tenant, "编码为 [%s] 的租户不存在".formatted(tenantCode));
tenantId = tenant.getId();
Long id = tenantService.getIdByCode(tenantCode);
CheckUtils.throwIfNull(id, "编码为 [%s] 的租户不存在".formatted(tenantCode));
tenantId = id;
} else {
// 指定租户
tenantId = Long.parseLong(tenantIdAsString);

View File

@@ -19,6 +19,7 @@ package top.continew.admin.tenant.config;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import top.continew.admin.common.config.TenantExtensionProperties;
import top.continew.admin.tenant.service.TenantService;
import top.continew.starter.extension.tenant.annotation.ConditionalOnEnabledTenant;
import top.continew.starter.extension.tenant.config.TenantProvider;

View File

@@ -1,50 +0,0 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.admin.tenant.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import top.continew.starter.core.constant.PropertiesConstants;
import java.util.List;
/**
* 租户扩展配置属性
*
* @author 小熊
* @author Charles7c
* @since 2024/11/29 12:05
*/
@Data
@ConfigurationProperties(prefix = PropertiesConstants.TENANT)
public class TenantExtensionProperties {
/**
* 请求头中租户编码键名默认X-Tenant-Code
*/
private String tenantCodeHeader = "X-Tenant-Code";
/**
* 默认租户 ID默认0
*/
private Long defaultTenantId = 0L;
/**
* 忽略菜单 ID租户不能使用的菜单
*/
private List<Long> ignoreMenus;
}

View File

@@ -25,7 +25,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import top.continew.admin.tenant.model.entity.TenantDO;
import top.continew.admin.tenant.model.query.PackageQuery;
import top.continew.admin.tenant.service.PackageService;
import top.continew.admin.tenant.service.TenantService;
@@ -60,15 +59,11 @@ public class CommonController {
return packageService.listDict(query, sortQuery);
}
@Operation(summary = "根据域名查询租户ID", description = "根据域名查询租户编码")
@SaIgnore
@TenantIgnore
@GetMapping("/id/domain")
public Long getTenantIdByUrl(@RequestParam("domain") String domain) {
TenantDO tenantDO = tenantService.getByDomain(domain);
if (tenantDO != null) {
return tenantDO.getId();
}
return null;
@Operation(summary = "根据域名查询租户 ID", description = "根据域名查询租户 ID")
@GetMapping("/id")
public Long getTenantIdByDomain(@RequestParam String domain) {
return tenantService.getIdByDomain(domain);
}
}

View File

@@ -24,7 +24,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import top.continew.admin.common.base.controller.BaseController;
import top.continew.admin.tenant.config.TenantExtensionProperties;
import top.continew.admin.common.config.TenantExtensionProperties;
import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.system.model.query.MenuQuery;
import top.continew.admin.system.service.MenuService;

View File

@@ -69,7 +69,6 @@ public class TenantDataHandlerForSystem implements TenantDataHandler {
private final PackageMenuService packageMenuService;
private final DeptMapper deptMapper;
private final MenuMapper menuMapper;
private final RoleMapper roleMapper;
private final RoleMenuService roleMenuService;
private final RoleMenuMapper roleMenuMapper;
@@ -93,11 +92,10 @@ public class TenantDataHandlerForSystem implements TenantDataHandler {
TenantUtils.execute(tenantId, () -> {
// 初始化部门
Long deptId = this.initDeptData(tenant);
// 初始化菜单
List<Long> menuIds = packageMenuService.listMenuIdsByPackageId(tenant.getPackageId());
// 初始化角色
Long roleId = this.initRoleData(tenant);
// 角色绑定菜单
List<Long> menuIds = packageMenuService.listMenuIdsByPackageId(tenant.getPackageId());
roleMenuService.add(menuIds, roleId);
// 初始化管理用户
Long userId = this.initUserData(tenant, deptId);
@@ -116,30 +114,30 @@ public class TenantDataHandlerForSystem implements TenantDataHandler {
for (UserDO user : userList) {
StpUtil.logout(user.getId());
}
Wrapper dw = Wrappers.query().eq("1", 1);
Wrapper queryWrapper = Wrappers.query().eq("1", 1);
// 部门清除
deptMapper.delete(dw);
deptMapper.delete(queryWrapper);
// 文件清除
List<Long> fileIds = CollUtils.mapToList(fileService.list(), FileDO::getId);
if (!fileIds.isEmpty()) {
fileService.delete(fileIds);
}
// 日志清除
logMapper.delete(dw);
logMapper.delete(queryWrapper);
// 消息清除
messageMapper.delete(dw);
messageUserMapper.delete(dw);
messageMapper.delete(queryWrapper);
messageUserMapper.delete(queryWrapper);
// 通知清除
noticeMapper.delete(dw);
noticeMapper.delete(queryWrapper);
// 角色相关数据清除
roleMapper.delete(dw);
roleDeptMapper.delete(dw);
roleMenuMapper.delete(dw);
roleMapper.delete(queryWrapper);
roleDeptMapper.delete(queryWrapper);
roleMenuMapper.delete(queryWrapper);
// 用户数据清除
userMapper.delete(dw);
userPasswordHistoryMapper.delete(dw);
userRoleMapper.delete(dw);
userSocialMapper.delete(dw);
userMapper.delete(queryWrapper);
userPasswordHistoryMapper.delete(queryWrapper);
userRoleMapper.delete(queryWrapper);
userSocialMapper.delete(queryWrapper);
}
/**

View File

@@ -39,17 +39,17 @@ public interface TenantService extends BaseService<TenantResp, TenantDetailResp,
* 根据绑定域名查询
*
* @param domain 绑定域名
* @return 租户信息
* @return ID
*/
TenantDO getByDomain(String domain);
Long getIdByDomain(String domain);
/**
* 根据编码查询
*
* @param code 编码
* @return 租户信息
* @return ID
*/
TenantDO getByCode(String code);
Long getIdByCode(String code);
/**
* 检查租户状态

View File

@@ -18,11 +18,12 @@ package top.continew.admin.tenant.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.alicp.jetcache.anno.Cached;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import me.ahoo.cosid.provider.IdGeneratorProvider;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.continew.admin.common.base.service.BaseServiceImpl;
import top.continew.admin.common.config.TenantExtensionProperties;
import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.common.enums.DisEnableStatusEnum;
@@ -30,7 +31,6 @@ import top.continew.admin.system.model.entity.RoleDO;
import top.continew.admin.system.model.entity.RoleMenuDO;
import top.continew.admin.system.service.RoleMenuService;
import top.continew.admin.system.service.RoleService;
import top.continew.admin.tenant.config.TenantExtensionProperties;
import top.continew.admin.tenant.constant.TenantCacheConstants;
import top.continew.admin.tenant.constant.TenantConstants;
import top.continew.admin.tenant.handler.TenantDataHandler;
@@ -44,13 +44,14 @@ import top.continew.admin.tenant.service.PackageService;
import top.continew.admin.tenant.service.TenantService;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.CollUtils;
import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.extension.tenant.util.TenantUtils;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* 租户业务实现
@@ -99,8 +100,7 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, T
@Override
public void afterUpdate(TenantReq req, TenantDO entity) {
// 更新租户缓存
RedisUtils.set(TenantCacheConstants.TENANT_KEY_PREFIX + entity.getId(), entity);
RedisUtils.deleteByPattern(TenantCacheConstants.TENANT_KEY_PREFIX + StringConstants.ASTERISK);
}
@Override
@@ -113,23 +113,29 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, T
@Override
public void afterDelete(List<Long> ids) {
ids.forEach(id -> RedisUtils.delete(TenantCacheConstants.TENANT_KEY_PREFIX + id));
RedisUtils.deleteByPattern(TenantCacheConstants.TENANT_KEY_PREFIX + StringConstants.ASTERISK);
}
@Override
@Cached(name = TenantCacheConstants.TENANT_KEY_PREFIX, key = "#id")
public TenantDO getById(Serializable id) {
return super.getById(id);
@Cached(name = TenantCacheConstants.TENANT_KEY_PREFIX, key = "#domain")
public Long getIdByDomain(String domain) {
return baseMapper.lambdaQuery()
.select(TenantDO::getId)
.eq(TenantDO::getDomain, domain)
.oneOpt()
.map(TenantDO::getId)
.orElse(null);
}
@Override
public TenantDO getByDomain(String domain) {
return baseMapper.lambdaQuery().eq(TenantDO::getDomain, domain).oneOpt().orElse(null);
}
@Override
public TenantDO getByCode(String code) {
return baseMapper.lambdaQuery().eq(TenantDO::getCode, code).oneOpt().orElse(null);
@Cached(name = TenantCacheConstants.TENANT_KEY_PREFIX, key = "#code")
public Long getIdByCode(String code) {
return baseMapper.lambdaQuery()
.select(TenantDO::getId)
.eq(TenantDO::getCode, code)
.oneOpt()
.map(TenantDO::getId)
.orElse(null);
}
@Override
@@ -147,31 +153,39 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, TenantDO, T
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateTenantMenu(List<Long> newMenuIds, Long packageId) {
List<Long> tenantIdList = this.listIdByPackageId(packageId);
if (CollUtil.isEmpty(tenantIdList)) {
return;
}
//删除旧菜单
tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> roleMenuService.remove(Wrappers
.lambdaQuery(RoleMenuDO.class)
.notIn(RoleMenuDO::getMenuId, newMenuIds))));
//新增菜单
// 删除旧菜单
tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> {
RoleDO roleDO = roleService.getByCode(SysConstants.TENANT_ADMIN_ROLE_CODE);
List<Long> oldMenuIds = roleMenuService.list(Wrappers.lambdaQuery(RoleMenuDO.class)
.eq(RoleMenuDO::getRoleId, roleDO.getId())).stream().map(RoleMenuDO::getMenuId).toList();
List<Long> addMenuIds = CollUtil.disjunction(newMenuIds, oldMenuIds).stream().toList();
if (CollUtil.isNotEmpty(addMenuIds)) {
List<RoleMenuDO> roleMenuDOList = new ArrayList<>();
for (Long addMenuId : addMenuIds) {
RoleMenuDO roleMenuDO = new RoleMenuDO(roleDO.getId(), addMenuId);
roleMenuDOList.add(roleMenuDO);
}
roleMenuService.saveBatch(roleMenuDOList, roleMenuDOList.size());
// 更新在线用户上下文
List<RoleMenuDO> roleMenuList = roleMenuService.lambdaQuery()
.select(RoleMenuDO::getRoleId)
.notIn(RoleMenuDO::getMenuId, newMenuIds)
.list();
Set<Long> roleIdSet = CollUtils.mapToSet(roleMenuList, RoleMenuDO::getRoleId);
roleIdSet.forEach(roleService::updateUserContext);
// 删除旧菜单
roleMenuService.lambdaUpdate().notIn(RoleMenuDO::getMenuId, newMenuIds).remove();
}));
// 租户管理员:新增菜单
tenantIdList.forEach(tenantId -> TenantUtils.execute(tenantId, () -> {
RoleDO role = roleService.getByCode(SysConstants.TENANT_ADMIN_ROLE_CODE);
List<Long> oldMenuIdList = roleMenuService.listMenuIdByRoleIds(List.of(role.getId()));
Collection<Long> addMenuIdList = CollUtil.disjunction(newMenuIds, oldMenuIdList);
if (CollUtil.isNotEmpty(addMenuIdList)) {
List<RoleMenuDO> roleMenuList = addMenuIdList.stream()
.map(menuId -> new RoleMenuDO(role.getId(), menuId))
.toList();
roleMenuService.saveBatch(roleMenuList, roleMenuList.size());
// 更新在线用户上下文
roleService.updateUserContext(role.getId());
}
}));
//清理角色菜单缓存
// 删除缓存
RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
}