diff --git a/ruoyi-ui/src/api/login.js b/ruoyi-ui/src/api/login.js index f65942a99..147486151 100644 --- a/ruoyi-ui/src/api/login.js +++ b/ruoyi-ui/src/api/login.js @@ -18,7 +18,7 @@ export function login(username, password, code, uuid) { // 获取用户详细信息 export function getInfo() { return request({ - url: '/getInfo', + url: '/get-info', method: 'get' }) } diff --git a/ruoyi-ui/src/api/menu.js b/ruoyi-ui/src/api/menu.js index 6e52e6e8a..d0a375553 100644 --- a/ruoyi-ui/src/api/menu.js +++ b/ruoyi-ui/src/api/menu.js @@ -3,7 +3,7 @@ import request from '@/utils/request' // 获取路由 export const getRouters = () => { return request({ - url: '/getRouters', + url: '/get-routers', method: 'get' }) -} \ No newline at end of file +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.http b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.http index 7d0b4c388..26407ff49 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.http +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.http @@ -13,6 +13,10 @@ Content-Type: application/json GET {{baseUrl}}/get-info Authorization: Bearer {{token}} +### 请求 /get-routers 接口 => 成功 +GET {{baseUrl}}/get-routers +Authorization: Bearer {{token}} + ### 请求 /druid/xxx 接口 => 失败 TODO 临时测试 GET http://127.0.0.1:8080/druid/123 Authorization: Bearer {{token}} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java index 6de13e8f6..e4a24d7e0 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/SysAuthController.java @@ -2,6 +2,7 @@ package cn.iocoder.dashboard.modules.system.controller.auth; import cn.iocoder.dashboard.common.pojo.CommonResult; import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO; +import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetRouterRespVO; import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthLoginReqVO; import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthLoginRespVO; import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService; @@ -12,6 +13,8 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.Valid; +import java.util.List; + import static cn.iocoder.dashboard.common.pojo.CommonResult.success; import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserId; import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserRoleIds; @@ -39,4 +42,11 @@ public class SysAuthController { return success(respVO); } + @ApiOperation("获得菜单 Vue 路由") + @GetMapping("get-routers") + public CommonResult> getRouters() { + List respVOList = authService.getRouters(getLoginUserId(), getLoginUserRoleIds()); + return success(respVOList); + } + } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthGetRouterRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthGetRouterRespVO.java new file mode 100644 index 000000000..54d8ee81c --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/auth/vo/SysAuthGetRouterRespVO.java @@ -0,0 +1,84 @@ +package cn.iocoder.dashboard.modules.system.controller.auth.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@Data +public class SysAuthGetRouterRespVO { + + /** + * 菜单编号 + */ + private Long menuId; + /** + * 父菜单编号 + */ + private Long parentId; + + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVO meta; + + /** + * 子路由 + */ + private List children; + + @Data + public static class MetaVO { + + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java b/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java index 5d4488cb8..d44856102 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java @@ -2,6 +2,7 @@ package cn.iocoder.dashboard.modules.system.convert.auth; import cn.iocoder.dashboard.framework.security.core.LoginUser; import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO; +import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetRouterRespVO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; @@ -27,4 +28,6 @@ public interface SysAuthConvert { return respVO; } + SysAuthGetRouterRespVO convertTreeNode(SysMenuDO menu); + } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java index b0dfad84a..c0040fba1 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/permission/SysMenuDO.java @@ -1,7 +1,6 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission; import cn.iocoder.dashboard.framework.mybatis.core.BaseDO; -import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -9,8 +8,6 @@ import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; -import java.util.ArrayList; -import java.util.List; /** * 菜单权限表 @@ -95,18 +92,4 @@ public class SysMenuDO extends BaseDO { */ private String icon; - // TODO 芋艿:非存储字段,需要提出 - - /** - * 父菜单名称 - */ - @TableField(exist = false) - private String parentName; - - /** - * 子菜单 - */ - @TableField(exist = false) - private List children = new ArrayList(); - } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/permission/MenuIdEnum.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/permission/MenuIdEnum.java new file mode 100644 index 000000000..4a1d7c28b --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/enums/permission/MenuIdEnum.java @@ -0,0 +1,20 @@ +package cn.iocoder.dashboard.modules.system.enums.permission; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Menu 编号枚举 + */ +@Getter +@AllArgsConstructor +public enum MenuIdEnum { + + /** + * 根节点 + */ + ROOT(0L); + + private final Long id; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java index b97447837..a9109c261 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java @@ -2,7 +2,9 @@ package cn.iocoder.dashboard.modules.system.service.auth; import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService; import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO; +import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetRouterRespVO; +import java.util.List; import java.util.Set; /** @@ -28,4 +30,16 @@ public interface SysAuthService extends SecurityFrameworkService { */ SysAuthGetInfoRespVO getInfo(Long userId, Set roleIds); + /** + * 获得用户的菜单 Vue 路由 + * + * 这里传输 roleIds 参数的原因,是该参数是从 LoginUser 缓存中获取到的,而我们校验权限时也是从 LoginUser 缓存中获取 roleIds + * 通过这样的方式,保持一致 + * + * @param userId 用户编号 + * @param roleIds 用户拥有的角色编号数组 + * @return 菜单 Vue 路由 + */ + List getRouters(Long userId, Set roleIds); + } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java index 0649f4ef9..868c67798 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java @@ -5,12 +5,14 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.dashboard.framework.security.config.SecurityProperties; import cn.iocoder.dashboard.framework.security.core.LoginUser; import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetInfoRespVO; +import cn.iocoder.dashboard.modules.system.controller.auth.vo.SysAuthGetRouterRespVO; +import cn.iocoder.dashboard.modules.system.convert.auth.SysAuthConvert; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; -import cn.iocoder.dashboard.modules.system.enums.user.UserStatus; -import cn.iocoder.dashboard.modules.system.convert.auth.SysAuthConvert; import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO; +import cn.iocoder.dashboard.modules.system.enums.permission.MenuIdEnum; +import cn.iocoder.dashboard.modules.system.enums.user.UserStatus; import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService; import cn.iocoder.dashboard.modules.system.service.auth.SysTokenService; import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; @@ -234,4 +236,38 @@ public class SysAuthServiceImpl implements SysAuthService { return SysAuthConvert.INSTANCE.convert(user, roleList, menuList); } + @Override + public List getRouters(Long userId, Set roleIds) { + // TODO 芋艿:去除 F 的类型,去除 禁用 的 + List menuList = permissionService.listRoleMenusFromCache(roleIds); + // 转换成 Tree 结构返回 + return buildRouterTree(menuList); + } + + private static List buildRouterTree(List menuList) { + // 排序,保证菜单的有序性 + menuList = new ArrayList<>(menuList); // 使用 ArrayList 套一下,因为 menuList 是不可修改的 List + menuList.sort(Comparator.comparing(SysMenuDO::getOrderNum)); + // 构建菜单树 + // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。 + Map treeNodeMap = new LinkedHashMap<>(); + menuList.forEach(menu -> treeNodeMap.put(menu.getMenuId(), SysAuthConvert.INSTANCE.convertTreeNode(menu))); + // 处理父子关系 + treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(MenuIdEnum.ROOT.getId())).forEach((childNode) -> { + // 获得父节点 + SysAuthGetRouterRespVO parentNode = treeNodeMap.get(childNode.getParentId()); + if (parentNode == null) { + log.error("[buildRouterTree][resource({}) 找不到父资源({})]", childNode.getMenuId(), childNode.getParentId()); + return; + } + // 将自己添加到父节点中 + if (parentNode.getChildren() == null) { + parentNode.setChildren(new ArrayList<>()); + } + parentNode.getChildren().add(childNode); + }); + // 获得到所有的根节点 + return CollectionUtils.filterList(treeNodeMap.values(), node -> MenuIdEnum.ROOT.getId().equals(node.getParentId())); + } + } diff --git a/src/main/java/cn/iocoder/dashboard/util/collection/CollectionUtils.java b/src/main/java/cn/iocoder/dashboard/util/collection/CollectionUtils.java index d15a1ff0b..433ae3a74 100644 --- a/src/main/java/cn/iocoder/dashboard/util/collection/CollectionUtils.java +++ b/src/main/java/cn/iocoder/dashboard/util/collection/CollectionUtils.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil; import java.util.*; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -17,6 +18,10 @@ public class CollectionUtils { return new HashSet<>(Arrays.asList(objs)); } + public static List filterList(Collection from, Predicate predicate) { + return from.stream().filter(predicate).collect(Collectors.toList()); + } + public static List convertList(List from, Function func) { return from.stream().map(func).collect(Collectors.toList()); }