|
|
@@ -14,45 +14,46 @@ import cc.uncarbon.module.sys.mapper.SysMenuMapper;
|
|
|
import cc.uncarbon.module.sys.model.request.AdminInsertOrUpdateSysMenuDTO;
|
|
|
import cc.uncarbon.module.sys.model.request.AdminListSysMenuDTO;
|
|
|
import cc.uncarbon.module.sys.model.response.SysMenuBO;
|
|
|
-import cc.uncarbon.module.sys.model.response.VbenAdminMenuMetaBO;
|
|
|
+import cc.uncarbon.module.sys.model.response.VbenAdminMenuMetaVO;
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.lang.Snowflake;
|
|
|
+import cn.hutool.core.text.StrPool;
|
|
|
import cn.hutool.core.util.IdUtil;
|
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collection;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
import java.util.Set;
|
|
|
+import java.util.function.BinaryOperator;
|
|
|
import java.util.stream.Collectors;
|
|
|
-import javax.annotation.Resource;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
|
/**
|
|
|
* 后台菜单
|
|
|
+ *
|
|
|
* @author Uncarbon
|
|
|
*/
|
|
|
+@RequiredArgsConstructor
|
|
|
@Slf4j
|
|
|
@Service
|
|
|
public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuEntity> {
|
|
|
|
|
|
- @Resource
|
|
|
- private SysRoleMenuRelationService sysRoleMenuRelationService;
|
|
|
-
|
|
|
- @Resource
|
|
|
- private SysUserRoleRelationService sysUserRoleRelationService;
|
|
|
-
|
|
|
- @Resource
|
|
|
- private StringRedisTemplate stringRedisTemplate;
|
|
|
-
|
|
|
private static final Snowflake SNOWFLAKE = IdUtil.getSnowflake(0L, 0L);
|
|
|
|
|
|
+ private final SysRoleMenuRelationService sysRoleMenuRelationService;
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* 后台管理-列表
|
|
|
@@ -61,53 +62,48 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
List<SysMenuEntity> entityList = this.list(
|
|
|
new QueryWrapper<SysMenuEntity>()
|
|
|
.lambda()
|
|
|
- // 标题
|
|
|
- .like(StrUtil.isNotBlank(dto.getTitle()), SysMenuEntity::getTitle, StrUtil.cleanBlank(dto.getTitle()))
|
|
|
- // 上级ID
|
|
|
- .eq(SysMenuEntity::getParentId, ObjectUtil.isNull(dto.getParentId()) ? SysConstant.ROOT_PARENT_ID : dto.getParentId())
|
|
|
- // 菜单类型
|
|
|
- .in(CollUtil.isNotEmpty(dto.getMenuTypes()), SysMenuEntity::getType, dto.getMenuTypes())
|
|
|
// 排序
|
|
|
.orderByAsc(SysMenuEntity::getSort)
|
|
|
);
|
|
|
|
|
|
- return this.entityList2BOs(entityList, true);
|
|
|
+ return this.entityList2BOs(entityList);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 通用-详情
|
|
|
+ * 根据 ID 取详情
|
|
|
*
|
|
|
- * @deprecated 使用 getOneById(java.lang.Long, boolean) 替代
|
|
|
+ * @param id 主键ID
|
|
|
+ * @return null or BO
|
|
|
*/
|
|
|
- @Deprecated
|
|
|
- public SysMenuBO getOneById(Long entityId) throws BusinessException {
|
|
|
- return this.getOneById(entityId, true);
|
|
|
+ public SysMenuBO getOneById(Long id) {
|
|
|
+ return this.getOneById(id, false);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 通用-详情
|
|
|
+ * 根据 ID 取详情
|
|
|
*
|
|
|
- * @param entityId 实体类主键ID
|
|
|
+ * @param id 主键ID
|
|
|
* @param throwIfInvalidId 是否在 ID 无效时抛出异常
|
|
|
* @return null or BO
|
|
|
*/
|
|
|
- public SysMenuBO getOneById(Long entityId, boolean throwIfInvalidId) throws BusinessException {
|
|
|
- SysMenuEntity entity = this.getById(entityId);
|
|
|
+ public SysMenuBO getOneById(Long id, boolean throwIfInvalidId) throws BusinessException {
|
|
|
+ SysMenuEntity entity = this.getById(id);
|
|
|
if (throwIfInvalidId) {
|
|
|
SysErrorEnum.INVALID_ID.assertNotNull(entity);
|
|
|
}
|
|
|
|
|
|
- return this.entity2BO(entity, false, null);
|
|
|
+ return this.entity2BO(entity);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 后台管理-新增
|
|
|
+ *
|
|
|
* @return 主键ID
|
|
|
*/
|
|
|
@SysLog(value = "新增后台菜单")
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public Long adminInsert(AdminInsertOrUpdateSysMenuDTO dto) {
|
|
|
- log.info("[后台管理-新增后台菜单] >> DTO={}", dto);
|
|
|
+ log.info("[后台管理-新增后台菜单] >> 入参={}", dto);
|
|
|
this.checkExistence(dto);
|
|
|
|
|
|
if (ObjectUtil.isNull(dto.getParentId())) {
|
|
|
@@ -130,7 +126,7 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
@SysLog(value = "编辑后台菜单")
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void adminUpdate(AdminInsertOrUpdateSysMenuDTO dto) {
|
|
|
- log.info("[后台管理-编辑后台菜单] >> DTO={}", dto);
|
|
|
+ log.info("[后台管理-编辑后台菜单] >> 入参={}", dto);
|
|
|
this.checkExistence(dto);
|
|
|
|
|
|
if (ObjectUtil.isNull(dto.getParentId())) {
|
|
|
@@ -149,151 +145,113 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
@SysLog(value = "删除后台菜单")
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void adminDelete(Collection<Long> ids) {
|
|
|
- log.info("[后台管理-删除后台菜单] >> ids={}", ids);
|
|
|
+ log.info("[后台管理-删除后台菜单] >> 入参={}", ids);
|
|
|
this.removeByIds(ids);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 后台管理-根据角色Ids取权限串List
|
|
|
- */
|
|
|
- public List<String> adminListPermissionByRoleIds(List<Long> roleIds) {
|
|
|
- if (CollUtil.isEmpty(roleIds)) {
|
|
|
- return CollUtil.newArrayList();
|
|
|
- }
|
|
|
-
|
|
|
- // 超级管理员直接允许所有权限
|
|
|
- if (roleIds.contains(SysConstant.SUPER_ADMIN_ROLE_ID)) {
|
|
|
- return this.list().stream().map(SysMenuEntity::getPermission).filter(StrUtil::isNotEmpty).distinct().collect(Collectors.toList());
|
|
|
- }
|
|
|
-
|
|
|
- List<Long> menuIds = sysRoleMenuRelationService.listMenuIdByRoleIds(roleIds);
|
|
|
-
|
|
|
- if (CollUtil.isEmpty(menuIds)) {
|
|
|
- return CollUtil.newArrayList();
|
|
|
- }
|
|
|
-
|
|
|
- return this.list(
|
|
|
- new QueryWrapper<SysMenuEntity>()
|
|
|
- .lambda()
|
|
|
- .select(SysMenuEntity::getPermission)
|
|
|
- .in(SysMenuEntity::getId, menuIds)
|
|
|
- ).stream().map(SysMenuEntity::getPermission).filter(StrUtil::isNotEmpty).distinct().collect(Collectors.toList());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 后台管理-取当前账号可见侧边菜单
|
|
|
+ * 后台管理-取侧边菜单
|
|
|
*/
|
|
|
public List<SysMenuBO> adminListSideMenu() {
|
|
|
- List<Long> visibleMenuIds = this.listCurrentUserVisibleMenuId();
|
|
|
-
|
|
|
- // 3. 取出无上级节点菜单
|
|
|
- List<SysMenuTypeEnum> requiredMenuTypes = CollUtil.newArrayList(SysMenuTypeEnum.DIR, SysMenuTypeEnum.MENU, SysMenuTypeEnum.EXTERNAL_LINK);
|
|
|
- List<SysMenuBO> allMenus = this.listMenuByParentId(visibleMenuIds, requiredMenuTypes, SysConstant.ROOT_PARENT_ID);
|
|
|
- log.debug("[后台管理][取当前账号可见侧边菜单] 取出无上级节点菜单 >> {}", allMenus);
|
|
|
-
|
|
|
- // 4.递归查询子节点, 孙节点, 曾孙节点...
|
|
|
- this.recursiveFindChildren(visibleMenuIds, requiredMenuTypes, allMenus);
|
|
|
- log.debug("[后台管理][取当前账号可见侧边菜单] 递归查询子节点, 孙节点, 曾孙节点... >> {}", allMenus);
|
|
|
+ Set<Long> visibleMenuIds = this.listCurrentUserVisibleMenuId();
|
|
|
|
|
|
- return allMenus;
|
|
|
+ List<SysMenuTypeEnum> requiredMenuTypes = CollUtil.newArrayList(SysMenuTypeEnum.DIR, SysMenuTypeEnum.MENU,
|
|
|
+ SysMenuTypeEnum.EXTERNAL_LINK);
|
|
|
+ return this.listByIds(visibleMenuIds, requiredMenuTypes);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 后台管理-取当前账号所有可见菜单(包括按钮类型)
|
|
|
+ * 后台管理-取所有可见菜单 包括按钮类型
|
|
|
*/
|
|
|
public List<SysMenuBO> adminListVisibleMenu() {
|
|
|
- List<Long> visibleMenuIds = this.listCurrentUserVisibleMenuId();
|
|
|
+ Set<Long> visibleMenuIds = this.listCurrentUserVisibleMenuId();
|
|
|
|
|
|
- // 3. 取出无上级节点菜单
|
|
|
- List<SysMenuTypeEnum> requiredMenuTypes = CollUtil.newArrayList(SysMenuTypeEnum.DIR, SysMenuTypeEnum.MENU, SysMenuTypeEnum.EXTERNAL_LINK, SysMenuTypeEnum.BUTTON);
|
|
|
- List<SysMenuBO> allMenus = this.listMenuByParentId(visibleMenuIds, requiredMenuTypes, SysConstant.ROOT_PARENT_ID);
|
|
|
- log.debug("[后台管理][取当前账号所有可见菜单] 取出无上级节点菜单:{}", allMenus);
|
|
|
-
|
|
|
- // 4.递归查询子节点, 孙节点, 曾孙节点...
|
|
|
- this.recursiveFindChildren(visibleMenuIds, requiredMenuTypes, allMenus);
|
|
|
- log.debug("[后台管理][取当前账号所有可见菜单] 递归查询子节点, 孙节点, 曾孙节点...:{}", allMenus);
|
|
|
-
|
|
|
- return allMenus;
|
|
|
+ List<SysMenuTypeEnum> requiredMenuTypes = CollUtil.newArrayList(SysMenuTypeEnum.DIR, SysMenuTypeEnum.MENU,
|
|
|
+ SysMenuTypeEnum.EXTERNAL_LINK, SysMenuTypeEnum.BUTTON);
|
|
|
+ return this.listByIds(visibleMenuIds, requiredMenuTypes);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 内部-过滤被禁用的菜单ID
|
|
|
+ * 根据角色Ids,获取角色ID 对应的权限名 Map
|
|
|
*
|
|
|
- * @param visibleMenuIds 待过滤的可见的菜单ID列表
|
|
|
- * @return 过滤后的菜单ID列表
|
|
|
+ * @return map key=角色ID value=权限名集合
|
|
|
*/
|
|
|
- public List<Long> filterDisabledIds(List<Long> visibleMenuIds) {
|
|
|
- if (CollUtil.isEmpty(visibleMenuIds)) {
|
|
|
- return visibleMenuIds;
|
|
|
+ public Map<Long, Set<String>> getRoleIdPermissionMap(Collection<Long> roleIds) {
|
|
|
+ if (CollUtil.isEmpty(roleIds)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- 1. 找出目前被禁用的菜单ID
|
|
|
- */
|
|
|
- List<Long> disabledMenuIds = this.list(
|
|
|
- new QueryWrapper<SysMenuEntity>()
|
|
|
- .select(HelioConstant.CRUD.SQL_COLUMN_ID)
|
|
|
- .lambda()
|
|
|
- .eq(SysMenuEntity::getStatus, GenericStatusEnum.DISABLED)
|
|
|
- ).stream().map(SysMenuEntity::getId).collect(Collectors.toList());
|
|
|
-
|
|
|
+ HashMap<Long, Set<String>> ret = new HashMap<>(roleIds.size());
|
|
|
+
|
|
|
+ roleIds.forEach(
|
|
|
+ roleId -> {
|
|
|
+ Set<String> permissions;
|
|
|
+
|
|
|
+ if (SysConstant.SUPER_ADMIN_ROLE_ID.equals(roleId)) {
|
|
|
+ // 超级管理员读取所有权限,不管有没有被禁用
|
|
|
+ permissions = this.list().stream()
|
|
|
+ .map(SysMenuEntity::getPermission)
|
|
|
+ .filter(StrUtil::isNotEmpty)
|
|
|
+ .collect(Collectors.toSet())
|
|
|
+ ;
|
|
|
+ } else {
|
|
|
+ // 非超级管理员则通过角色ID,关联查询拥有的菜单,菜单上有权限名
|
|
|
+
|
|
|
+ Set<Long> menuIds = sysRoleMenuRelationService.listMenuIdByRoleId(roleId);
|
|
|
+ if (CollUtil.isEmpty(menuIds)) {
|
|
|
+ permissions = Collections.emptySet();
|
|
|
+ } else {
|
|
|
+ permissions = this.list(
|
|
|
+ new QueryWrapper<SysMenuEntity>()
|
|
|
+ .lambda()
|
|
|
+ .select(SysMenuEntity::getPermission)
|
|
|
+ .in(SysMenuEntity::getId, menuIds)
|
|
|
+ .eq(SysMenuEntity::getStatus, GenericStatusEnum.ENABLED)
|
|
|
+ )
|
|
|
+ .stream()
|
|
|
+ .map(SysMenuEntity::getPermission)
|
|
|
+ .filter(StrUtil::isNotEmpty)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- 2. 提取出符合以下条件的菜单ID:
|
|
|
- 启用状态 && 父菜单未被禁用
|
|
|
- */
|
|
|
- return this.list(
|
|
|
- new QueryWrapper<SysMenuEntity>()
|
|
|
- .select(HelioConstant.CRUD.SQL_COLUMN_ID)
|
|
|
- .lambda()
|
|
|
- .and(
|
|
|
- wrapper -> wrapper
|
|
|
- .eq(SysMenuEntity::getStatus, GenericStatusEnum.ENABLED)
|
|
|
- .notIn(CollUtil.isNotEmpty(disabledMenuIds), SysMenuEntity::getParentId, disabledMenuIds)
|
|
|
- )
|
|
|
- .in(SysMenuEntity::getId, visibleMenuIds)
|
|
|
- ).stream().map(SysMenuEntity::getId).collect(Collectors.toList());
|
|
|
- }
|
|
|
+ ret.put(roleId, permissions);
|
|
|
+ }
|
|
|
+ );
|
|
|
|
|
|
- /**
|
|
|
- * 通用-清除Redis中所有菜单缓存
|
|
|
- */
|
|
|
- public void cleanMenuCacheInRedis() {
|
|
|
- this.cleanMenuCacheInRedis(null);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 通用-根据用户ID,清除Redis中相关菜单缓存
|
|
|
- * @param userId 用户ID;为null则清除所有
|
|
|
+ * 根据菜单ID集合,取权限名集合
|
|
|
*/
|
|
|
- public void cleanMenuCacheInRedis(Long userId) {
|
|
|
- String redisKey;
|
|
|
- if (ObjectUtil.isNull(userId)) {
|
|
|
- redisKey = "*";
|
|
|
- } else {
|
|
|
- redisKey = "userId_" + StrUtil.toString(userId);
|
|
|
- }
|
|
|
-
|
|
|
- log.info("[清除Redis中相关菜单缓存] >> redisKey={}", redisKey);
|
|
|
-
|
|
|
- Set<String> keys = stringRedisTemplate.keys(SysConstant.REDIS_KEY_SIDE_MENU + redisKey);
|
|
|
- if (keys != null && !keys.isEmpty()) {
|
|
|
- stringRedisTemplate.delete(keys);
|
|
|
+ public Set<String> listPermissionByMenuIds(Collection<Long> menuIds) {
|
|
|
+ if (CollUtil.isEmpty(menuIds)) {
|
|
|
+ return Collections.emptySet();
|
|
|
}
|
|
|
|
|
|
- keys = stringRedisTemplate.keys(SysConstant.REDIS_KEY_VISIBLE_MENU + redisKey);
|
|
|
- if (keys != null && !keys.isEmpty()) {
|
|
|
- stringRedisTemplate.delete(keys);
|
|
|
- }
|
|
|
+ return this.list(
|
|
|
+ new QueryWrapper<SysMenuEntity>()
|
|
|
+ .lambda()
|
|
|
+ .select(SysMenuEntity::getPermission)
|
|
|
+ .in(SysMenuEntity::getId, menuIds)
|
|
|
+ ).stream().map(SysMenuEntity::getPermission).filter(StrUtil::isNotEmpty).collect(Collectors.toSet());
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
- 私有方法
|
|
|
- ------------------------------------------------------------------------------------------------
|
|
|
+ ----------------------------------------------------------------
|
|
|
+ 私有方法 private methods
|
|
|
+ ----------------------------------------------------------------
|
|
|
*/
|
|
|
|
|
|
- private SysMenuBO entity2BO(SysMenuEntity entity, boolean traverseChildren, List<SysMenuTypeEnum> menuTypes) {
|
|
|
+ /**
|
|
|
+ * 实体转 BO
|
|
|
+ *
|
|
|
+ * @param entity 实体
|
|
|
+ * @return BO
|
|
|
+ */
|
|
|
+ private SysMenuBO entity2BO(SysMenuEntity entity) {
|
|
|
if (entity == null) {
|
|
|
return null;
|
|
|
}
|
|
|
@@ -311,7 +269,7 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
.setIdStr(StrUtil.toString(bo.getId()))
|
|
|
.setName(snowflakeIdStr)
|
|
|
.setMeta(
|
|
|
- VbenAdminMenuMetaBO.builder()
|
|
|
+ VbenAdminMenuMetaVO.builder()
|
|
|
.title(bo.getTitle())
|
|
|
.affix(false)
|
|
|
.icon(bo.getIcon())
|
|
|
@@ -325,7 +283,7 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
bo
|
|
|
.setComponent(SysConstant.VBEN_ADMIN_BLANK_VIEW)
|
|
|
.setExternalLink(null)
|
|
|
- .setPath("/" + snowflakeIdStr)
|
|
|
+ .setPath(StrPool.SLASH + snowflakeIdStr)
|
|
|
;
|
|
|
break;
|
|
|
}
|
|
|
@@ -335,8 +293,8 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
.setPath(bo.getComponent())
|
|
|
;
|
|
|
// 防止用户忘记加了, 主动补充/
|
|
|
- if (StrUtil.isNotBlank(bo.getPath()) && !bo.getPath().startsWith("/")) {
|
|
|
- bo.setPath("/" + bo.getPath());
|
|
|
+ if (StrUtil.isNotBlank(bo.getPath()) && !bo.getPath().startsWith(StrPool.SLASH)) {
|
|
|
+ bo.setPath(StrPool.SLASH + bo.getPath());
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
@@ -349,29 +307,14 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 遍历子级菜单
|
|
|
- if (traverseChildren) {
|
|
|
- List<SysMenuBO> children = this.adminList(
|
|
|
- AdminListSysMenuDTO.builder()
|
|
|
- .parentId(bo.getId())
|
|
|
- .menuTypes(menuTypes)
|
|
|
- .build()
|
|
|
- );
|
|
|
- if (CollUtil.isEmpty(children)) {
|
|
|
- children = null;
|
|
|
- }
|
|
|
-
|
|
|
- bo.setChildren(children);
|
|
|
- }
|
|
|
-
|
|
|
return bo;
|
|
|
}
|
|
|
|
|
|
- private List<SysMenuBO> entityList2BOs(List<SysMenuEntity> entityList, Boolean traverseChildren) {
|
|
|
+ private List<SysMenuBO> entityList2BOs(List<SysMenuEntity> entityList) {
|
|
|
// 深拷贝
|
|
|
List<SysMenuBO> ret = new ArrayList<>(entityList.size());
|
|
|
entityList.forEach(
|
|
|
- entity -> ret.add(this.entity2BO(entity, traverseChildren, null))
|
|
|
+ entity -> ret.add(this.entity2BO(entity))
|
|
|
);
|
|
|
|
|
|
return ret;
|
|
|
@@ -379,43 +322,37 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
|
|
|
/**
|
|
|
* 取当前账号可见菜单Ids
|
|
|
+ *
|
|
|
* @return 菜单Ids
|
|
|
*/
|
|
|
- private List<Long> listCurrentUserVisibleMenuId() {
|
|
|
+ private Set<Long> listCurrentUserVisibleMenuId() {
|
|
|
// 1. 取当前账号拥有角色Ids
|
|
|
- List<Long> roleIds = sysUserRoleRelationService.listRoleIdByUserId(UserContextHolder.getUserId());
|
|
|
- log.debug("[后台管理][取当前账号可见菜单Ids] 当前账号拥有角色Ids >> {}", roleIds);
|
|
|
- if (CollUtil.isEmpty(roleIds)) {
|
|
|
- throw new BusinessException(SysErrorEnum.NO_ROLE_AVAILABLE_FOR_CURRENT_USER);
|
|
|
- }
|
|
|
+ Set<Long> roleIds = UserContextHolder.getUserContext().getRolesIds();
|
|
|
+ SysErrorEnum.NO_ROLE_AVAILABLE_FOR_CURRENT_USER.assertNotEmpty(roleIds);
|
|
|
|
|
|
- // 2-1. 超级管理员直接允许所有菜单,即使是禁用状态
|
|
|
- if (roleIds.contains(SysConstant.SUPER_ADMIN_ROLE_ID)) {
|
|
|
- return this.list(
|
|
|
- new QueryWrapper<SysMenuEntity>()
|
|
|
- .select(HelioConstant.CRUD.SQL_COLUMN_ID)
|
|
|
- ).stream().map(SysMenuEntity::getId).collect(Collectors.toList());
|
|
|
- }
|
|
|
+ // 2. 得到所有可用的 菜单ID-上级菜单ID map,备用
|
|
|
+ Map<Long, Long> allMenuMap = this.list(
|
|
|
+ new QueryWrapper<SysMenuEntity>()
|
|
|
+ .lambda()
|
|
|
+ .select(SysMenuEntity::getId, SysMenuEntity::getParentId)
|
|
|
+ .eq(SysMenuEntity::getStatus, GenericStatusEnum.ENABLED)
|
|
|
+ ).stream().collect(Collectors.toMap(SysMenuEntity::getId, SysMenuEntity::getParentId, ignoredThrowingMerger()));
|
|
|
|
|
|
- // 2-2. 根据角色Ids取菜单Ids
|
|
|
- List<Long> menuIds = sysRoleMenuRelationService.listMenuIdByRoleIds(roleIds);
|
|
|
- log.debug("[后台管理][取当前账号可见菜单Ids] 根据角色Ids取菜单Ids >> {}", menuIds);
|
|
|
- if (CollUtil.isEmpty(menuIds)) {
|
|
|
- throw new BusinessException(SysErrorEnum.NO_MENU_AVAILABLE_FOR_CURRENT_ROLE);
|
|
|
+ // 3. 超级管理员直接返回所有菜单
|
|
|
+ if (roleIds.contains(SysConstant.SUPER_ADMIN_ROLE_ID)) {
|
|
|
+ return new HashSet<>(allMenuMap.keySet());
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- 3. 补充上级菜单ID
|
|
|
- 这个Ant-Design的Tree组件有个吊诡的地方, 只选一部分子级的话, 父级菜单不算勾选
|
|
|
- 只能在代码里补上了
|
|
|
- */
|
|
|
- List<Long> missingParentIds = this.listMissingParentId(menuIds);
|
|
|
- menuIds.addAll(missingParentIds);
|
|
|
+ // 4. 根据现有角色,获取直接关联的菜单ID
|
|
|
+ Set<Long> directlyRelatedMenuIds = sysRoleMenuRelationService.listMenuIdByRoleIds(roleIds);
|
|
|
+ SysErrorEnum.NO_MENU_AVAILABLE_FOR_CURRENT_ROLE.assertNotEmpty(directlyRelatedMenuIds);
|
|
|
|
|
|
- return menuIds;
|
|
|
+ // 5. 因为直接关联的菜单ID,可能不包含父级菜单,使得级联关系缺失,这里得给他补上
|
|
|
+ return this.traceParentMenuIds(allMenuMap, directlyRelatedMenuIds);
|
|
|
}
|
|
|
|
|
|
- private List<SysMenuBO> listMenuByParentId(List<Long> visibleMenuIds, List<SysMenuTypeEnum> requiredMenuTypes, Long parentId) throws IllegalArgumentException {
|
|
|
+ private List<SysMenuBO> listByIds(Collection<Long> visibleMenuIds, List<SysMenuTypeEnum> requiredMenuTypes)
|
|
|
+ throws IllegalArgumentException {
|
|
|
if (CollUtil.isEmpty(visibleMenuIds)) {
|
|
|
throw new IllegalArgumentException("visibleMenuIds不能为空");
|
|
|
}
|
|
|
@@ -429,39 +366,21 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
.lambda()
|
|
|
.in(SysMenuEntity::getId, visibleMenuIds)
|
|
|
.in(SysMenuEntity::getType, requiredMenuTypes)
|
|
|
- .eq(SysMenuEntity::getParentId, parentId)
|
|
|
.orderByAsc(SysMenuEntity::getSort)
|
|
|
);
|
|
|
|
|
|
if (CollUtil.isEmpty(entityList)) {
|
|
|
- return CollUtil.newArrayList();
|
|
|
+ return Collections.emptyList();
|
|
|
}
|
|
|
|
|
|
List<SysMenuBO> ret = new ArrayList<>(entityList.size());
|
|
|
entityList.forEach(
|
|
|
- each -> ret.add(this.entity2BO(each, false, requiredMenuTypes))
|
|
|
+ each -> ret.add(this.entity2BO(each))
|
|
|
);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 递归查询子节点
|
|
|
- */
|
|
|
- private void recursiveFindChildren(List<Long> visibleMenuIds, List<SysMenuTypeEnum> requiredMenuTypes, List<SysMenuBO> menuBOs) {
|
|
|
- menuBOs.forEach(
|
|
|
- menu -> {
|
|
|
- // 查询子节点
|
|
|
- List<SysMenuBO> itsChildren = this.listMenuByParentId(visibleMenuIds, requiredMenuTypes, menu.getId());
|
|
|
- if (CollUtil.isNotEmpty(itsChildren)) {
|
|
|
- menu.setChildren(itsChildren);
|
|
|
- // 递归查询孙节点
|
|
|
- this.recursiveFindChildren(visibleMenuIds, requiredMenuTypes, menu.getChildren());
|
|
|
- }
|
|
|
- }
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 检查是否已存在相同数据
|
|
|
*
|
|
|
@@ -488,32 +407,50 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 补充遗漏的上级菜单ID
|
|
|
+ * 追溯并补充可能缺失的级联上级菜单ID
|
|
|
+ *
|
|
|
+ * @param allMenuMap 完整的 菜单ID-上级菜单ID map
|
|
|
+ * @param directlyRelatedMenuIds 当前用户直接关联的菜单ID集合
|
|
|
+ * @return Set<Long> 已经补充好的、完整的菜单ID集合
|
|
|
*/
|
|
|
- private List<Long> listMissingParentId(List<Long> menuIds) {
|
|
|
+ private Set<Long> traceParentMenuIds(Map<Long, Long> allMenuMap, Collection<Long> directlyRelatedMenuIds) {
|
|
|
// 返回值
|
|
|
- List<Long> ret = new ArrayList<>(32);
|
|
|
- // 单次循环返回值
|
|
|
- List<Long> loopRet = menuIds;
|
|
|
+ Set<Long> ret = new HashSet<>(directlyRelatedMenuIds.size() << 1);
|
|
|
+ ret.addAll(directlyRelatedMenuIds);
|
|
|
|
|
|
- for (int i = 0; i < 5; i++) {
|
|
|
- // 最多上溯5层
|
|
|
- loopRet = this.list(
|
|
|
- new QueryWrapper<SysMenuEntity>()
|
|
|
- .select(" DISTINCT parent_id ")
|
|
|
- .lambda()
|
|
|
- .ne(SysMenuEntity::getParentId, SysConstant.ROOT_PARENT_ID)
|
|
|
- .in(SysMenuEntity::getId, loopRet)
|
|
|
- .notIn(SysMenuEntity::getParentId, loopRet)
|
|
|
- ).stream().map(SysMenuEntity::getParentId).collect(Collectors.toList());
|
|
|
+ // 本次循环要遍历的菜单ID集合
|
|
|
+ HashSet<Long> thisLoop;
|
|
|
+ // 下次循环要遍历的菜单ID集合
|
|
|
+ HashSet<Long> nextLoop = new HashSet<>(directlyRelatedMenuIds);
|
|
|
|
|
|
- if (loopRet.isEmpty()) {
|
|
|
- break;
|
|
|
- } else {
|
|
|
- ret.addAll(loopRet);
|
|
|
+ do {
|
|
|
+ // 深拷贝数据,用于本次循环
|
|
|
+ thisLoop = new HashSet<>(nextLoop);
|
|
|
+ nextLoop.clear();
|
|
|
+
|
|
|
+ for (Long menuId : thisLoop) {
|
|
|
+ Long parentId = allMenuMap.get(menuId);
|
|
|
+
|
|
|
+ if (Objects.nonNull(parentId)) {
|
|
|
+ // 上级菜单可能还会有祖级菜单,需要继续上溯
|
|
|
+ nextLoop.add(parentId);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
+
|
|
|
+ ret.addAll(nextLoop);
|
|
|
+
|
|
|
+ } while (!nextLoop.isEmpty());
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 原来键冲突会报错,使用自定义函数来无视
|
|
|
+ *
|
|
|
+ * @param <T> 键类型
|
|
|
+ * @return 键
|
|
|
+ */
|
|
|
+ private static <T> BinaryOperator<T> ignoredThrowingMerger() {
|
|
|
+ return (u, v) -> u;
|
|
|
+ }
|
|
|
}
|