fix(system/role): 修复角色菜单权限缓存未清理错误,优化角色菜单缓存逻辑

Closes #IBNENK
This commit is contained in:
2025-02-27 20:02:53 +08:00
parent 65941c1ee4
commit 0a62f81ad7
9 changed files with 50 additions and 47 deletions

View File

@@ -42,9 +42,9 @@ public class CacheConstants {
public static final String USER_KEY_PREFIX = "USER" + DELIMITER; public static final String USER_KEY_PREFIX = "USER" + DELIMITER;
/** /**
* 菜单缓存键前缀 * 角色菜单缓存键前缀
*/ */
public static final String MENU_KEY_PREFIX = "MENU" + DELIMITER; public static final String ROLE_MENU_KEY_PREFIX = "ROLE_MENU" + DELIMITER;
/** /**
* 字典缓存键前缀 * 字典缓存键前缀

View File

@@ -17,6 +17,7 @@
package top.continew.admin.common.context; package top.continew.admin.common.context;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import top.continew.admin.common.enums.DataScopeEnum; import top.continew.admin.common.enums.DataScopeEnum;
import java.io.Serial; import java.io.Serial;
@@ -29,6 +30,7 @@ import java.io.Serializable;
* @since 2023/3/7 22:08 * @since 2023/3/7 22:08
*/ */
@Data @Data
@NoArgsConstructor
public class RoleContext implements Serializable { public class RoleContext implements Serializable {
@Serial @Serial
@@ -48,4 +50,10 @@ public class RoleContext implements Serializable {
* 数据权限 * 数据权限
*/ */
private DataScopeEnum dataScope; private DataScopeEnum dataScope;
public RoleContext(Long id, String code, DataScopeEnum dataScope) {
this.id = id;
this.code = code;
this.dataScope = dataScope;
}
} }

View File

@@ -32,6 +32,7 @@ import top.continew.admin.auth.model.resp.LoginResp;
import top.continew.admin.auth.model.resp.RouteResp; import top.continew.admin.auth.model.resp.RouteResp;
import top.continew.admin.auth.service.AuthService; import top.continew.admin.auth.service.AuthService;
import top.continew.admin.common.constant.SysConstants; import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.common.context.RoleContext;
import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.system.enums.MenuTypeEnum; import top.continew.admin.system.enums.MenuTypeEnum;
import top.continew.admin.system.model.resp.ClientResp; import top.continew.admin.system.model.resp.ClientResp;
@@ -86,16 +87,16 @@ public class AuthServiceImpl implements AuthService {
@Override @Override
public List<RouteResp> buildRouteTree(Long userId) { public List<RouteResp> buildRouteTree(Long userId) {
Set<String> roleCodeSet = roleService.listCodeByUserId(userId); Set<RoleContext> roleSet = roleService.listByUserId(userId);
if (CollUtil.isEmpty(roleCodeSet)) { if (CollUtil.isEmpty(roleSet)) {
return new ArrayList<>(0); return new ArrayList<>(0);
} }
// 查询菜单列表 // 查询菜单列表
Set<MenuResp> menuSet = new LinkedHashSet<>(); Set<MenuResp> menuSet = new LinkedHashSet<>();
if (roleCodeSet.contains(SysConstants.SUPER_ROLE_CODE)) { if (roleSet.stream().anyMatch(r -> SysConstants.SUPER_ROLE_ID.equals(r.getId()))) {
menuSet.addAll(menuService.listAll()); menuSet.addAll(menuService.listByRoleId(SysConstants.SUPER_ROLE_ID));
} else { } else {
roleCodeSet.forEach(roleCode -> menuSet.addAll(menuService.listByRoleCode(roleCode))); roleSet.forEach(r -> menuSet.addAll(menuService.listByRoleId(r.getId())));
} }
List<MenuResp> menuList = menuSet.stream().filter(m -> !MenuTypeEnum.BUTTON.equals(m.getType())).toList(); List<MenuResp> menuList = menuSet.stream().filter(m -> !MenuTypeEnum.BUTTON.equals(m.getType())).toList();
if (CollUtil.isEmpty(menuList)) { if (CollUtil.isEmpty(menuList)) {

View File

@@ -40,10 +40,10 @@ public interface MenuMapper extends BaseMapper<MenuDO> {
Set<String> selectPermissionByUserId(@Param("userId") Long userId); Set<String> selectPermissionByUserId(@Param("userId") Long userId);
/** /**
* 根据角色编码查询 * 根据角色 ID 查询
* *
* @param roleCode 角色编码 * @param roleId 角色 ID
* @return 菜单列表 * @return 菜单列表
*/ */
List<MenuDO> selectListByRoleCode(@Param("roleCode") String roleCode); List<MenuDO> selectListByRoleId(@Param("roleId") Long roleId);
} }

View File

@@ -34,13 +34,6 @@ import java.util.Set;
*/ */
public interface MenuService extends BaseService<MenuResp, MenuResp, MenuQuery, MenuReq>, IService<MenuDO> { public interface MenuService extends BaseService<MenuResp, MenuResp, MenuQuery, MenuReq>, IService<MenuDO> {
/**
* 查询全部菜单
*
* @return 菜单列表
*/
List<MenuResp> listAll();
/** /**
* 根据用户 ID 查询 * 根据用户 ID 查询
* *
@@ -50,10 +43,10 @@ public interface MenuService extends BaseService<MenuResp, MenuResp, MenuQuery,
Set<String> listPermissionByUserId(Long userId); Set<String> listPermissionByUserId(Long userId);
/** /**
* 根据角色编码查询 * 根据角色 ID 查询
* *
* @param roleCode 角色编码 * @param roleId 角色 ID
* @return 菜单列表 * @return 菜单列表
*/ */
List<MenuResp> listByRoleCode(String roleCode); List<MenuResp> listByRoleId(Long roleId);
} }

View File

@@ -23,6 +23,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import top.continew.admin.common.constant.CacheConstants; import top.continew.admin.common.constant.CacheConstants;
import top.continew.admin.common.constant.SysConstants;
import top.continew.admin.common.enums.DisEnableStatusEnum; import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.system.enums.MenuTypeEnum; import top.continew.admin.system.enums.MenuTypeEnum;
import top.continew.admin.system.mapper.MenuMapper; import top.continew.admin.system.mapper.MenuMapper;
@@ -62,7 +63,7 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
if (MenuTypeEnum.DIR.equals(req.getType())) { if (MenuTypeEnum.DIR.equals(req.getType())) {
req.setComponent(StrUtil.blankToDefault(req.getComponent(), "Layout")); req.setComponent(StrUtil.blankToDefault(req.getComponent(), "Layout"));
} }
RedisUtils.deleteByPattern(CacheConstants.MENU_KEY_PREFIX + StringConstants.ASTERISK); RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
return super.add(req); return super.add(req);
} }
@@ -78,7 +79,7 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
MenuDO oldMenu = super.getById(id); MenuDO oldMenu = super.getById(id);
CheckUtils.throwIfNotEqual(req.getType(), oldMenu.getType(), "不允许修改菜单类型"); CheckUtils.throwIfNotEqual(req.getType(), oldMenu.getType(), "不允许修改菜单类型");
super.update(req, id); super.update(req, id);
RedisUtils.deleteByPattern(CacheConstants.MENU_KEY_PREFIX + StringConstants.ASTERISK); RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
} }
@Override @Override
@@ -86,13 +87,7 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
public void delete(List<Long> ids) { public void delete(List<Long> ids) {
baseMapper.lambdaUpdate().in(MenuDO::getParentId, ids).remove(); baseMapper.lambdaUpdate().in(MenuDO::getParentId, ids).remove();
super.delete(ids); super.delete(ids);
RedisUtils.deleteByPattern(CacheConstants.MENU_KEY_PREFIX + StringConstants.ASTERISK); RedisUtils.deleteByPattern(CacheConstants.ROLE_MENU_KEY_PREFIX + StringConstants.ASTERISK);
}
@Override
@Cached(key = "'ALL'", name = CacheConstants.MENU_KEY_PREFIX)
public List<MenuResp> listAll() {
return super.list(new MenuQuery(DisEnableStatusEnum.ENABLE), null);
} }
@Override @Override
@@ -101,9 +96,12 @@ public class MenuServiceImpl extends BaseServiceImpl<MenuMapper, MenuDO, MenuRes
} }
@Override @Override
@Cached(key = "#roleCode", name = CacheConstants.MENU_KEY_PREFIX) @Cached(key = "#roleId", name = CacheConstants.ROLE_MENU_KEY_PREFIX)
public List<MenuResp> listByRoleCode(String roleCode) { public List<MenuResp> listByRoleId(Long roleId) {
List<MenuDO> menuList = baseMapper.selectListByRoleCode(roleCode); if (SysConstants.SUPER_ROLE_ID.equals(roleId)) {
return super.list(new MenuQuery(DisEnableStatusEnum.ENABLE), null);
}
List<MenuDO> menuList = baseMapper.selectListByRoleId(roleId);
List<MenuResp> list = BeanUtil.copyToList(menuList, MenuResp.class); List<MenuResp> list = BeanUtil.copyToList(menuList, MenuResp.class);
list.forEach(super::fill); list.forEach(super::fill);
return list; return list;

View File

@@ -18,7 +18,6 @@ package top.continew.admin.system.service.impl;
import cn.crane4j.annotation.ContainerMethod; import cn.crane4j.annotation.ContainerMethod;
import cn.crane4j.annotation.MappingType; import cn.crane4j.annotation.MappingType;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.alicp.jetcache.anno.CacheInvalidate; import com.alicp.jetcache.anno.CacheInvalidate;
@@ -45,7 +44,10 @@ import top.continew.admin.system.service.*;
import top.continew.starter.core.validation.CheckUtils; import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.extension.crud.service.BaseServiceImpl; import top.continew.starter.extension.crud.service.BaseServiceImpl;
import java.util.*; import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -79,7 +81,6 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@CacheInvalidate(key = "#req.code == 'admin' ? 'ALL' : #req.code", name = CacheConstants.MENU_KEY_PREFIX)
public void update(RoleReq req, Long id) { public void update(RoleReq req, Long id) {
String name = req.getName(); String name = req.getName();
CheckUtils.throwIf(this.isNameExists(name, id), "修改失败,[{}] 已存在", name); CheckUtils.throwIf(this.isNameExists(name, id), "修改失败,[{}] 已存在", name);
@@ -120,6 +121,7 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@CacheInvalidate(key = "#id", name = CacheConstants.ROLE_MENU_KEY_PREFIX)
public void updatePermission(Long id, RoleUpdatePermissionReq req) { public void updatePermission(Long id, RoleUpdatePermissionReq req) {
super.getById(id); super.getById(id);
// 保存角色和菜单关联 // 保存角色和菜单关联
@@ -148,13 +150,9 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
super.fill(obj); super.fill(obj);
if (obj instanceof RoleDetailResp detail) { if (obj instanceof RoleDetailResp detail) {
Long roleId = detail.getId(); Long roleId = detail.getId();
if (SysConstants.SUPER_ROLE_CODE.equals(detail.getCode())) { List<MenuResp> list = menuService.listByRoleId(roleId);
List<MenuResp> list = menuService.listAll(); List<Long> menuIds = list.stream().map(MenuResp::getId).toList();
List<Long> menuIds = list.stream().map(MenuResp::getId).toList(); detail.setMenuIds(menuIds);
detail.setMenuIds(menuIds);
} else {
detail.setMenuIds(roleMenuService.listMenuIdByRoleIds(CollUtil.newArrayList(roleId)));
}
} }
} }
@@ -194,8 +192,13 @@ public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO, RoleRes
if (CollUtil.isEmpty(roleIdList)) { if (CollUtil.isEmpty(roleIdList)) {
return Collections.emptySet(); return Collections.emptySet();
} }
List<RoleDO> roleList = baseMapper.lambdaQuery().in(RoleDO::getId, roleIdList).list(); List<RoleDO> roleList = baseMapper.lambdaQuery()
return new HashSet<>(BeanUtil.copyToList(roleList, RoleContext.class)); .select(RoleDO::getId, RoleDO::getCode, RoleDO::getDataScope)
.in(RoleDO::getId, roleIdList)
.list();
return roleList.stream()
.map(r -> new RoleContext(r.getId(), r.getCode(), r.getDataScope()))
.collect(Collectors.toSet());
} }
@Override @Override

View File

@@ -13,12 +13,12 @@
AND t1.permission IS NOT NULL AND t1.permission IS NOT NULL
</select> </select>
<select id="selectListByRoleCode" resultType="top.continew.admin.system.model.entity.MenuDO"> <select id="selectListByRoleId" resultType="top.continew.admin.system.model.entity.MenuDO">
SELECT t1.* SELECT t1.*
FROM sys_menu AS t1 FROM sys_menu AS t1
LEFT JOIN sys_role_menu AS t2 ON t2.menu_id = t1.id LEFT JOIN sys_role_menu AS t2 ON t2.menu_id = t1.id
LEFT JOIN sys_role AS t3 ON t3.id = t2.role_id LEFT JOIN sys_role AS t3 ON t3.id = t2.role_id
WHERE t3.code = #{roleCode} WHERE t3.id = #{roleId}
AND t1.status = 1 AND t1.status = 1
</select> </select>
</mapper> </mapper>

View File

@@ -131,7 +131,7 @@ public class DemoEnvironmentJob {
roleMenuMapper.lambdaUpdate().notIn(RoleMenuDO::getRoleId, ROLE_FLAG).remove(); roleMenuMapper.lambdaUpdate().notIn(RoleMenuDO::getRoleId, ROLE_FLAG).remove();
return roleMapper.lambdaUpdate().notIn(RoleDO::getId, ROLE_FLAG).remove(); return roleMapper.lambdaUpdate().notIn(RoleDO::getId, ROLE_FLAG).remove();
}); });
this.clean(menuCount, "菜单", CacheConstants.MENU_KEY_PREFIX, () -> menuMapper.lambdaUpdate() this.clean(menuCount, "菜单", CacheConstants.ROLE_MENU_KEY_PREFIX, () -> menuMapper.lambdaUpdate()
.gt(MenuDO::getId, DELETE_FLAG) .gt(MenuDO::getId, DELETE_FLAG)
.remove()); .remove());
this.clean(deptCount, "部门", null, () -> deptMapper.lambdaUpdate().gt(DeptDO::getId, DEPT_FLAG).remove()); this.clean(deptCount, "部门", null, () -> deptMapper.lambdaUpdate().gt(DeptDO::getId, DEPT_FLAG).remove());