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

@@ -24,6 +24,7 @@ import jakarta.servlet.http.HttpServletRequest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import top.continew.admin.auth.model.req.LoginReq;
import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.common.context.RoleContext;
import top.continew.admin.common.context.UserContext;
import top.continew.admin.common.context.UserContextHolder;
@@ -88,9 +89,9 @@ public abstract class AbstractLoginHandler<T extends LoginReq> implements LoginH
*
* @param user 用户信息
* @param client 客户端信息
* @return token 令牌信息
* @return 登录响应参数
*/
protected String authenticate(UserDO user, ClientResp client) {
protected LoginResp authenticate(UserDO user, ClientResp client) {
// 获取权限、角色、密码过期天数
Long userId = user.getId();
Long tenantId = TenantContextHolder.getTenantId();
@@ -127,7 +128,10 @@ public abstract class AbstractLoginHandler<T extends LoginReq> implements LoginH
StpUtil.login(userContext.getId(), loginParameter.setExtraData(BeanUtil
.beanToMap(new UserExtraContext(ServletUtils.getRequest()))));
UserContextHolder.setContext(userContext);
return StpUtil.getTokenValue();
return LoginResp.builder()
.token(StpUtil.getTokenValue())
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
}
/**

View File

@@ -39,7 +39,6 @@ import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.util.ExceptionUtils;
import top.continew.starter.core.util.validation.CheckUtils;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
import java.time.Duration;
@@ -71,11 +70,7 @@ public class AccountLoginHandler extends AbstractLoginHandler<AccountLoginReq> {
// 检查用户状态
super.checkUserStatus(user);
// 执行认证
String token = this.authenticate(user, client);
return LoginResp.builder()
.token(token)
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
return super.authenticate(user, client);
}
@Override

View File

@@ -27,7 +27,6 @@ import top.continew.admin.system.model.entity.user.UserDO;
import top.continew.admin.system.model.resp.ClientResp;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
/**
* 邮箱登录处理器
@@ -47,11 +46,7 @@ public class EmailLoginHandler extends AbstractLoginHandler<EmailLoginReq> {
// 检查用户状态
super.checkUserStatus(user);
// 执行认证
String token = super.authenticate(user, client);
return LoginResp.builder()
.token(token)
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
return super.authenticate(user, client);
}
@Override

View File

@@ -27,7 +27,6 @@ import top.continew.admin.system.model.entity.user.UserDO;
import top.continew.admin.system.model.resp.ClientResp;
import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
/**
* 手机号登录处理器
@@ -47,11 +46,7 @@ public class PhoneLoginHandler extends AbstractLoginHandler<PhoneLoginReq> {
// 检查用户状态
super.checkUserStatus(user);
// 执行认证
String token = super.authenticate(user, client);
return LoginResp.builder()
.token(token)
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
return super.authenticate(user, client);
}
@Override

View File

@@ -53,7 +53,6 @@ import top.continew.admin.system.service.UserSocialService;
import top.continew.starter.core.autoconfigure.application.ApplicationProperties;
import top.continew.starter.core.exception.BadRequestException;
import top.continew.starter.core.util.validation.ValidationUtils;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
import java.time.LocalDateTime;
import java.util.Collections;
@@ -127,11 +126,7 @@ public class SocialLoginHandler extends AbstractLoginHandler<SocialLoginReq> {
userSocial.setLastLoginTime(LocalDateTime.now());
userSocialService.saveOrUpdate(userSocial);
// 执行认证
String token = super.authenticate(user, client);
return LoginResp.builder()
.token(token)
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
.build();
return super.authenticate(user, client);
}
@Override

View File

@@ -43,6 +43,9 @@ public class LoginResp implements Serializable {
@Schema(description = "令牌", example = "eyJ0eXAiOiJlV1QiLCJhbGciqiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb29pbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiSjd4SUljYnU5cmNwU09vQ3Uyc1ND1BYYTYycFRjcjAifQ.KUPOYm-2wfuLUSfEEAbpGE527fzmkAJG7sMNcQ0pUZ8")
private String token;
@Schema(description = "租户ID", example = "0")
/**
* 租户 ID
*/
@Schema(description = "租户 ID", example = "0")
private Long tenantId;
}

View File

@@ -23,11 +23,12 @@ import cn.hutool.core.util.StrUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RestController;
import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.common.base.controller.BaseController;
import top.continew.admin.common.config.TenantExtensionProperties;
import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.system.model.query.MenuQuery;
import top.continew.admin.system.model.req.MenuReq;
import top.continew.admin.system.model.resp.MenuResp;
@@ -40,6 +41,7 @@ import top.continew.starter.extension.crud.annotation.CrudApi;
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
import top.continew.starter.extension.crud.enums.Api;
import top.continew.starter.extension.crud.model.query.SortQuery;
import top.continew.starter.extension.tenant.context.TenantContextHolder;
import java.lang.reflect.Method;
import java.util.List;
@@ -52,11 +54,12 @@ import java.util.List;
*/
@Tag(name = "菜单管理 API")
@RestController
@AllArgsConstructor
@RequiredArgsConstructor
@CrudRequestMapping(value = "/system/menu", api = {Api.TREE, Api.GET, Api.CREATE, Api.UPDATE, Api.BATCH_DELETE})
public class MenuController extends BaseController<MenuService, MenuResp, MenuResp, MenuQuery, MenuReq> {
private final MenuService menuService;
private final TenantExtensionProperties tenantExtensionProperties;
@Operation(summary = "清除缓存", description = "清除缓存")
@SaCheckPermission("system:menu:clearCache")
@@ -88,7 +91,9 @@ public class MenuController extends BaseController<MenuService, MenuResp, MenuRe
@Override
public List<Tree<Long>> tree(@Valid MenuQuery query, @Valid SortQuery sortQuery) {
query.setExcludeMenuIdList(menuService.listExcludeTenantMenu());
if (TenantContextHolder.isTenantEnabled() && !tenantExtensionProperties.isDefaultTenant()) {
query.setExcludeMenuIdList(menuService.listExcludeTenantMenu());
}
return super.tree(query, sortQuery);
}

View File

@@ -51,22 +51,9 @@ public interface MenuService extends BaseService<MenuResp, MenuResp, MenuQuery,
List<MenuResp> listByRoleId(Long roleId);
/**
* 删除租户菜单
* 查询租户排除的菜单 ID 列表
*
* @param menuS 菜单列表
*/
void deleteTenantMenus(List<MenuDO> menuS);
/**
* 新增租户菜单
*
* @param menu 新增的菜单
* @param parentMenu 父菜单
*/
void addTenantMenu(MenuDO menu, MenuDO parentMenu);
/**
* 查询租户排除的菜单
* @return 租户排除的菜单 ID 列表
*/
List<Long> listExcludeTenantMenu();
}

View File

@@ -39,14 +39,14 @@ public interface RoleMenuService extends IService<RoleMenuDO> {
boolean add(List<Long> menuIds, Long roleId);
/**
* 根据角色 ID 删除
* 根据角色 ID 列表删除
*
* @param roleIds 角色 ID 列表
*/
void deleteByRoleIds(List<Long> roleIds);
/**
* 根据角色 ID 查询
* 根据角色 ID 列表查询
*
* @param roleIds 角色 ID 列表
* @return 菜单 ID 列表

View File

@@ -53,6 +53,13 @@ public interface RoleService extends BaseService<RoleResp, RoleDetailResp, RoleQ
*/
void assignToUsers(Long id, List<Long> userIds);
/**
* 更新用户上下文
*
* @param roleId 角色 ID
*/
void updateUserContext(Long roleId);
/**
* 根据用户 ID 查询权限码
*

View File

@@ -18,11 +18,11 @@ package top.continew.admin.system.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.alicp.jetcache.anno.Cached;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.continew.admin.common.base.service.BaseServiceImpl;
@@ -31,20 +31,18 @@ import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.system.enums.MenuTypeEnum;
import top.continew.admin.system.mapper.MenuMapper;
import top.continew.admin.system.mapper.RoleMapper;
import top.continew.admin.system.model.entity.MenuDO;
import top.continew.admin.system.model.entity.RoleDO;
import top.continew.admin.system.model.entity.RoleMenuDO;
import top.continew.admin.system.model.query.MenuQuery;
import top.continew.admin.system.model.req.MenuReq;
import top.continew.admin.system.model.resp.MenuResp;
import top.continew.admin.system.service.MenuService;
import top.continew.admin.system.service.RoleMenuService;
import top.continew.admin.system.service.RoleService;
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 java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -58,8 +56,9 @@ import java.util.Set;
@RequiredArgsConstructor
public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuResp, MenuResp, MenuQuery, MenuReq> implements MenuService {
private final RoleMenuService roleMenuService;
private final RoleMapper roleMapper;
@Lazy
@Resource
private RoleService roleService;
@Override
public Long create(MenuReq req) {
@@ -114,66 +113,12 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
return list;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteTenantMenus(List<MenuDO> menuList) {
if (CollUtil.isEmpty(menuList)) {
return;
}
List<Long> delIds = new ArrayList<>();
for (MenuDO menu : menuList) {
MenuDO parentMenu = baseMapper.query()
.eq(menu.getType().equals(MenuTypeEnum.BUTTON), "CONCAT(title,permission)", menu.getTitle() + menu
.getPermission())
.eq(!menu.getType().equals(MenuTypeEnum.BUTTON), "name", menu.getName())
.one();
if (parentMenu != null) {
delIds.add(parentMenu.getId());
}
}
if (!delIds.isEmpty()) {
// 菜单删除
this.delete(delIds);
// 删除绑定关系
roleMenuService.remove(Wrappers.lambdaQuery(RoleMenuDO.class).in(RoleMenuDO::getMenuId, delIds));
}
// 删除缓存
RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
}
@Override
public void addTenantMenu(MenuDO menu, MenuDO parentMenu) {
Long parentId = SysConstants.SUPER_PARENT_ID;
if (parentMenu != null) {
MenuDO parent = baseMapper.query()
.eq(parentMenu.getType().equals(MenuTypeEnum.BUTTON), "CONCAT(title,permission)", parentMenu
.getTitle() + parentMenu.getPermission())
.eq(!parentMenu.getType().equals(MenuTypeEnum.BUTTON), "name", parentMenu.getName())
.one();
parentId = parent.getId();
}
menu.setId(null);
menu.setParentId(parentId);
// 菜单新增
baseMapper.insert(menu);
// 管理员绑定菜单
RoleDO role = roleMapper.selectOne(Wrappers.lambdaQuery(RoleDO.class)
.eq(RoleDO::getCode, SysConstants.TENANT_ADMIN_ROLE_CODE));
roleMenuService.save(new RoleMenuDO(role.getId(), menu.getId()));
// 删除缓存
RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
}
@Override
public List<Long> listExcludeTenantMenu() {
RoleDO role = roleMapper.selectOne(Wrappers.lambdaQuery(RoleDO.class)
.eq(RoleDO::getCode, SysConstants.TENANT_ADMIN_ROLE_CODE));
if (role == null) {
return ListUtil.of();
}
List<Long> allMenuList = list().stream().map(MenuDO::getId).toList();
List<Long> menuList = baseMapper.selectListByRoleId(role.getId()).stream().map(MenuDO::getId).toList();
return CollUtil.disjunction(allMenuList, menuList).stream().toList();
RoleDO role = roleService.getByCode(SysConstants.TENANT_ADMIN_ROLE_CODE);
List<Long> allMenuIdList = CollUtils.mapToList(super.list(), MenuDO::getId);
List<Long> menuIdList = CollUtils.mapToList(baseMapper.selectListByRoleId(role.getId()), MenuDO::getId);
return CollUtil.disjunction(allMenuIdList, menuIdList).stream().toList();
}
/**

View File

@@ -20,6 +20,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alicp.jetcache.anno.CacheInvalidate;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -57,7 +58,8 @@ import java.util.Set;
@RequiredArgsConstructor
public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleResp, RoleDetailResp, RoleQuery, RoleReq> implements RoleService {
private final MenuService menuService;
@Resource
private MenuService menuService;
private final RoleMenuService roleMenuService;
private final RoleDeptService roleDeptService;
private final UserRoleService userRoleService;
@@ -116,6 +118,17 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
roleDeptService.deleteByRoleIds(ids);
}
@Override
public void fill(Object obj) {
super.fill(obj);
if (obj instanceof RoleDetailResp detail) {
Long roleId = detail.getId();
List<MenuResp> list = menuService.listByRoleId(roleId);
List<Long> menuIds = CollUtils.mapToList(list, MenuResp::getId);
detail.setMenuIds(menuIds);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
@CacheInvalidate(key = "#id", name = CacheConstants.ROLE_MENU_KEY_PREFIX)
@@ -143,14 +156,16 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
}
@Override
public void fill(Object obj) {
super.fill(obj);
if (obj instanceof RoleDetailResp detail) {
Long roleId = detail.getId();
List<MenuResp> list = menuService.listByRoleId(roleId);
List<Long> menuIds = CollUtils.mapToList(list, MenuResp::getId);
detail.setMenuIds(menuIds);
}
public void updateUserContext(Long roleId) {
List<Long> userIdList = userRoleService.listUserIdByRoleId(roleId);
userIdList.forEach(userId -> {
UserContext userContext = UserContextHolder.getContext(userId);
if (userContext != null) {
userContext.setRoles(this.listByUserId(userId));
userContext.setPermissions(this.listPermissionByUserId(userId));
UserContextHolder.setContext(userContext);
}
});
}
@Override
@@ -232,21 +247,4 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
.ne(id != null, RoleDO::getId, id)
.exists(), "编码为 [{}] 的角色已存在", code);
}
/**
* 更新用户上下文
*
* @param roleId 角色 ID
*/
private void updateUserContext(Long roleId) {
List<Long> userIdList = userRoleService.listUserIdByRoleId(roleId);
userIdList.parallelStream().forEach(userId -> {
UserContext userContext = UserContextHolder.getContext(userId);
if (userContext != null) {
userContext.setRoles(this.listByUserId(userId));
userContext.setPermissions(this.listPermissionByUserId(userId));
UserContextHolder.setContext(userContext);
}
});
}
}