外观
菜单管理
菜单用于描述客户端导航,并以 code 对接认证模块名称。先按快速开始引入管理端 Controller,再维护菜单和角色授权。
接口约定
本模块 HTTP 接口返回 ResultModel<T>,业务数据位于 data;默认成功业务码为 00000,外层另有 success、message、traceId、t。状态修改、排序和绑定操作使用无参数的 R.success(),虽然方法泛型为 Boolean,成功时也没有 true 业务值。删除接口显式返回 data=true。
菜单 ID、角色 ID、关系 ID、用户 ID 的 Java 类型均为 Long。Base 默认 MVC JSON 将包装类型 Long 输出为字符串,应用覆盖转换器时以实际协议为准。前端应保留完整精度,示例以十进制字符串提交 ID,不应转换成可能丢精度的 JavaScript Number。
公共响应与失败标识的边界见 Base 响应约定,不能仅根据 HTTP 成功状态或 success=true 判断业务成功。
分页使用 page、rows:默认第 1 页、每页 10 条,rows 最大 100。结果包含 data.page、rows、total、list、isLastPage。keywords 虽继承自基础 Query,本模块没有配置对应搜索字段,不产生关键字过滤;公共行为见 Base Business 查询与分页。
完整 VO 的创建时间以及菜单、角色的修改时间使用 LocalDateTime,格式为 yyyy-MM-dd HH:mm:ss,注解标注 GMT+8;它们不是 Unix 时间戳。数据库列为 datetime,应用需统一数据库与 JVM 的时间口径。创建与修改人字段取决于应用审计填充,不能仅凭调用 CRUD 保证这些字段非空。MapVo 是轻量业务视图,不含创建、修改审计字段。
管理端请求不会自动将 clientId 覆盖为会话客户端;缺少客户端条件时可跨客户端查询,按 ID 操作也不校验所属客户端。需要客户端隔离的后台应在应用入口补齐授权范围校验。
查询与维护接口
以下路径统一加前缀 /api/admin/system/menu,权限模块编码为 system-menu。表中同一格的多项操作权限必须同时满足。
| 方法与路径 | 请求体 | 返回业务数据 | 操作权限 |
|---|---|---|---|
POST /page | SystemMenuQuery | PageModel<SystemMenuVo> | 查询 |
POST /tree | SystemMenuQuery | TreeNode<SystemMenuMapVo>[] | 查询 |
GET /{menuId} | 无 | SystemMenuVo | 查看详情、查询 |
POST / | SystemMenuDto | SystemMenuVo | 新增、查询 |
PUT /{menuId} | SystemMenuDto | SystemMenuVo | 修改、查询 |
DELETE /{menuId} | 无 | true,需要二次确认 | 删除、查询 |
PUT /disable/{menuId}/{disable} | 无 | 无 | 修改、查询 |
PUT /show/{menuId}/{show} | 无 | 无 | 修改、查询 |
PUT /advanced/sort | SystemMenuAdvancedSortForm | 无 | 高级排序、修改、查询 |
菜单管理没有独立的 /all、/map、批量删除接口;App 的 /all 见当前用户查询。
查询条件与树结构
json
{
"page": 1,
"rows": 10,
"clientId": "web",
"title": "报表",
"isDisable": 0
}| 条件 | 行为 |
|---|---|
code、title | 非空白时模糊匹配 |
clientId | 非空白时精确匹配 |
parentId | 非空时精确匹配父级 ID;传 null 表示不限制,并非只取根节点 |
isDisable、isShow | 非空时精确匹配整数状态 |
管理端 Query 查询按 createTime、id 倒序,树接口先查询再组树,不分页,也不自动补齐被条件过滤掉的祖先。节点为 {"item":{...菜单字段...},"children":[...]};父节点不在结果集合中的节点会成为根节点。children 可为空值。
菜单表单和字段
SystemMenuDto 用于新增和修改。菜单 VO 包含下表业务字段以及审计字段、version;MapVo 包含下表除 memo 外的业务字段。
| 字段 | 含义与当前校验 |
|---|---|
code | 菜单编码,必填,最多 50 字符;认证按此值定位模块 |
title | 标题,必填,最多 8 字符 |
clientId | 客户端标识,必填,最多 32 字符 |
parentId | 父菜单 ID,根节点可为 null |
path、icon | 路由和图标元数据,由前端解释 |
isDisable | 0 可用、1 禁用;执行 DTO 校验时缺省为 0 |
isShow | 0 隐藏、1 显示;执行 DTO 校验时缺省为 1,App 查询不据此过滤 |
permission | 菜单可配置的操作字符串,如 查询,新增;不会自动写入角色授权 |
sortOrder | 排序值,执行 DTO 校验时缺省为 0 |
chainName、chainIds | 逗号分隔的祖先链名称与 ID,由调用方维护 |
memo | 备注;当前 DTO 新增转换时不会复制该值,修改已有实体时才复制 |
isDisable、isShow 会对输入做 % 2,不是严格的 0/1 校验,调用方应只提交 0、1。完整修改也会应用缺省状态与排序值,不应把 PUT 当成仅更新已提交字段的 PATCH。parentId、path、icon、permission、chainName、chainIds 允许以空值覆盖已有值。
菜单编码没有数据库唯一约束,服务也未检查同客户端编码重复。为使认证按模块定位唯一菜单,应用应保证同一 clientId 下的 code 唯一。
层级校验
修改时只有 chainIds 非空白才检查父节点不是自身、链中不含自身,以及名称链和 ID 链长度一致。chainIds 为空时会跳过这些检查;新增只做基础字段校验。
服务不会自动计算链字段,也未检查祖先链真实性、跨客户端父节点或完整环路。应用应先验证父节点及整条祖先链,再同步写入层级字段;移动父节点时还需自行维护后代链信息。
修改状态与排序
HTTP 状态路径必须带 disable 或 show,只使用 0、1。Service 的对应方法支持传 null 切换已有状态,但 HTTP 没有省略状态值的路由,不能通过空路径触发切换。
高级排序请求示例,替换为实际菜单 ID:
json
{
"itemList": [
{ "id": "10001", "sort": 10 },
{ "id": "10002", "sort": 20 }
]
}表单要求非空列表,每项 id、sort 不能为 null。请求字段是 sort,最终写入菜单的 sortOrder;不会修改父节点,也不重算连续序号。服务逐项更新,未声明整批事务或检查各次更新行数。
目前管理端查询按创建时间和 ID 倒序,App 菜单查询没有显式排序,均未按 sortOrder 排序。前端如需按排序值展示,应在平铺列表或每一层树节点中自行排序。排序写入本身不刷新权限缓存;菜单新增、修改、删除和显隐/禁用操作会刷新全局权限缓存时间戳。
删除及二次确认
管理端菜单、角色和角色权限删除均标记二次确认。其装配要求 Servlet/AOP 相关类、StoreClient 与 StoreLockManager,且确认开关启用。满足默认配置时,首次 DELETE 返回业务码 C0001 和确认提示;客户端取 data.key、data.token,用户确认后在有效期内以相同身份、方法、地址、参数重发,并添加该请求头。默认有效期为 30 秒,应以响应的头名与令牌为准。
菜单删除使用逻辑删除,只删除该菜单记录,不会级联删除子菜单和角色权限记录。原有子菜单可能因父节点不再出现在结果中而成为根节点。确认提示中的“所有资料会被删除”不能视为级联清理承诺。
接口不以更新或删除影响行数决定成功,data=true 不能证明目标原先存在。角色删除的关系清理与缓存边界见角色与授权。