菜单管理
菜单(mdc_resource_menu)是四个权限维度的共同载体:权限码是菜单的 code,接口挂在菜单/按钮上,数据权限通过菜单表中字段控制,字段规则也挂在菜单上。本篇讲清菜单如何配置:5 种类型、关联接口、字段配置。授权与鉴权细节见各权限维度篇目。
1. 字段模型
菜单按应用隔离(appId),树形结构采用3个字段(parentId + treePath 树路径 + weight 排序)。核心字段:
| 字段 | 说明 |
|---|---|
appId | 所属应用(菜单管理页顶部下拉切换) |
code | 编码,应用内唯一——即权限码,前端按钮鉴权、@DataScope 注解都引用它 |
name / meta.title | 名称 / 路由标题(表单有「带入」按钮快捷拷贝) |
menuType | 类型:10-目录 / 20-菜单 / 30-内链 / 40-外链 / 50-按钮 |
path / component / redirect | 路由地址 / 页面组件路径或布局标识 / 重定向 |
state | 启用状态 |
dataScopeState | 数据权限开关(仅 menuType=菜单 可配),见数据权限 |
meta | 路由元数据 JSON(图标、徽标、keepAlive、hideInMenu、activePath、iframeSrc、link 等,对齐 vben 的 RouterMeta) |
注意:没有独立的 perms 字段,权限码就是 code;图标、徽标等表现层属性全部收进 meta。
2. 5 种菜单类型
| menuType | 含义 | 挂载约束(ResourceMenuServiceImpl.validateAndFill) | component 处理 |
|---|---|---|---|
10 目录 | 一级分组 | 只能挂在目录下 | 为空时自动填 LAYOUT |
20 菜单 | 页面 | 上级只能是目录或菜单;component(页面路径)必填 | 页面组件路径 |
30 内链 | iframe 内嵌页面 | 只能挂在目录下;meta.iframeSrc 必填 | 强制 IFRAME |
40 外链 | 外部链接 | 只能挂在目录下;meta.link 必填 | 强制 IFRAME |
50 按钮 | 功能按钮 | 挂在菜单下;不校验名称/路由重复 | — |
布局标识(AdminConstant):LAYOUT(控制台布局,目录默认)、OPEN_LAYOUT(开发者中心布局)、IFRAME(内嵌布局,内/外链统一改写)。后端组装用户路由树时还会动态改写:有子节点的菜单组件改为 LAYOUT;子集全部隐藏的菜单会克隆自身作为首个可见子节点。
按钮(50)不进入路由树(findUserRouter 只查 10/20/30/40),但与菜单同表同 id 空间——这是"按钮也能关联接口、参与 uri 鉴权"的前提。
3. 配置入口:菜单管理页
菜单管理页为左树右详情结构:左侧顶部应用下拉切换应用 + 菜单树(节点悬浮:移动/新增/编辑/删除);右侧详情三个 Tab:基本信息、关联接口、字段配置**;新增/编辑走弹窗表单。
表单按三个区块组织(新增时先调 GET /permission/resourceMenu/getDefMenuByParentId 拉父级默认值):
- 基本信息区:menuType、parentId、name、code、meta.title、weight、path、state、dataScopeState;
- 组件/路由区:component、redirect、meta.iframeSrc、meta.link、meta.icon、remarks;
- 元信息区:badge、activePath、activeIcon、keepAlive、hideInMenu/hideInTab/hideInBreadcrumb、ignoreAccess、ignoreLogin。
name/code/path 的唯一性分别由 GET /permission/resourceMenu/check{Name,Code,Path} 按应用维度校验。
4. 关联接口
在菜单详情「关联接口」Tab 页维护 mdc_resource_api表数据(字段:resourceId、uri、requestMethod、name、isInput),一个菜单/按钮可关联多个接口,同资源下 uri+method 幂等去重。关联的接口是接口权限的判定来源。两种录入方式:
① 扫描绑定(推荐):
「选择接口」→ 抽屉顶部选在线服务(GET /gateway/findOnlineServicePrefix 取服务名→前缀映射)
→ GET /{服务前缀}/anyone/systemApiScan 扫描该服务的全部接口
→ 按 Controller 分组的勾选树(已绑定项默认勾选且禁用)
→ 确定 → POST /permission/resourceApi/bind(过滤已存在项后批量插入)② 手动录入:「新增接口」填接口名/请求方式/URI(必须以 / 开头,支持 Ant 通配如 /user/**)→ POST /permission/resourceApi/save。
5. 字段配置
字段配置是字段权限(列级权限)的配置入口:菜单详情「字段配置」Tab 维护 mdc_resource_field 表,声明"本页面上哪些字段是敏感字段、受限时如何处理"。授权后,受限角色的用户访问该页面接口时,响应中的这些字段会被自动处理——隐藏=值置 null(前端整列不可见),脱敏=值变形(如手机号显示 138****5678)。
规则为手工录入,每条规则的含义:
| 字段 | 说明 | 示例 |
|---|---|---|
property | VO 的 Java 属性名(同菜单下唯一,格式 ^[a-zA-Z][a-zA-Z0-9_]*$) | phone |
name | 展示名 | 手机号 |
ruleType | 处理动作:10-隐藏 / 20-脱敏 | 20 |
maskRule | 脱敏规则(ruleType=20 时必填),下拉选择已注册规则 | mobile |
state | 启用状态(禁用即不生效,无需删除规则) | 启用 |
内置脱敏规则覆盖常见敏感数据:mobile(手机号留前3后4)、chinese_name(姓名)、email(保留域名)、id_card_number(身份证留前3后4)、bank_card_number(银行卡留前4后4)等 9 种;自定义算法注册后自动出现在下拉中。
两点注意:
- 后端只校验格式与脱敏规则已注册,不校验属性真实存在——VO 字段改名后需同步修改配置,否则规则静默失效;
- 字段配置只是"声明那些字段敏感",本身不影响任何用户;要到角色管理的「字段权限」Tab 把规则授权给角色后才生效,且运营管理员始终豁免。完整的授权与判定链路见字段权限。
6. 完整配置链(以「用户管理」为例)
配套说明:
- 菜单移动:
POST /permission/resourceMenu/move(递归重算子孙 treePath 并清缓存); - 菜单删除:级联删除其下所有子孙菜单及关联接口配置,前端有二次确认;
- 开启数据权限的菜单保存时,系统自动为各组织性质的权限集合角色与管理员角色授予「全部数据」档,防止管理员被锁死;
- 菜单要对用户可见:菜单启用 + 所属应用授权给角色 + 菜单本身功能授权给角色,三者缺一不可,见菜单和按钮权限。
7. 用户侧生效入口
| 接口 | 用途 |
|---|---|
GET /anyUser/findAllMenu | 应用全量菜单(免登录,公开页面用) |
GET /anyone/findUserRouter?appId= | 当前用户可用路由树(动态注册前端路由) |
GET /anyone/findUserResource?appId= | 当前用户权限码集合(前端 v-hasAnyPermission 按钮鉴权) |