8.5 KiB
8.5 KiB
Purpose
本规范定义登录接口返回菜单树和按钮权限的需求。
登录接口将在响应中返回三个权限相关字段:
menus: 菜单树(树形结构,用于渲染侧边栏)buttons: 按钮权限码列表(扁平数组,用于控制按钮显示)permissions: 所有权限码列表(扁平数组,保留向后兼容性)
这使得前端可以直接使用菜单树渲染侧边栏,无需二次处理,同时保持与现有系统的向后兼容性。
Requirements
Requirement: 登录响应包含菜单树和按钮权限
登录接口 SHALL 在响应中返回三个权限相关字段:
menus: 菜单树(树形结构,用于渲染侧边栏)buttons: 按钮权限码列表(扁平数组,用于控制按钮显示)permissions: 所有权限码列表(扁平数组,保留向后兼容性)
适用端点:
POST /api/admin/login(后台登录)POST /api/h5/login(H5 端登录)
Scenario: 普通用户登录成功
- WHEN 普通用户(非超级管理员)登录成功
- THEN 响应包含
menus数组(包含用户有权限的菜单树) - THEN 响应包含
buttons数组(包含用户有权限的按钮权限码) - THEN 响应包含
permissions数组(包含所有权限码) - THEN
menus数组为树形结构,每个节点包含id,perm_code,name,url,sort,children字段
Scenario: 用户无任何权限
- WHEN 用户登录成功但未分配任何角色或权限
- THEN 响应包含空的
menus数组[] - THEN 响应包含空的
buttons数组[] - THEN 响应包含空的
permissions数组[]
Requirement: 菜单权限构建树形结构
系统 SHALL 基于权限表的 perm_type 和 parent_id 字段构建菜单树:
- 只包含
perm_type = 1(菜单权限)的权限记录 - 根据
parent_id字段构建父子关系 - 根节点为
parent_id = NULL或parent_id = 0的权限 - 子节点追加到父节点的
children数组中
Scenario: 构建两级菜单树
- WHEN 用户有以下权限:
- ID=1, perm_code="user:menu", perm_type=1, parent_id=NULL(用户管理)
- ID=2, perm_code="user:list:menu", perm_type=1, parent_id=1(用户列表)
- THEN
menus数组包含 1 个根节点(用户管理) - THEN 根节点的
children数组包含 1 个子节点(用户列表)
Scenario: 孤儿节点提升为根节点
- WHEN 用户有子菜单权限(perm_code="user:list:menu", parent_id=1)
- WHEN 用户没有父菜单权限(ID=1 不在权限列表中)
- THEN 子菜单提升为根节点,出现在
menus数组的顶层 - THEN 子菜单的
children数组为空
Requirement: 按钮权限提取扁平列表
系统 SHALL 提取所有 perm_type = 2(按钮权限)的权限码作为 buttons 数组:
- 只包含
perm_code字段值 - 不构建树形结构
- 按原始顺序返回
Scenario: 提取按钮权限码
- WHEN 用户有以下权限:
- perm_code="user:create", perm_type=2
- perm_code="user:update", perm_type=2
- perm_code="user:delete", perm_type=2
- THEN
buttons数组包含["user:create", "user:update", "user:delete"]
Requirement: 平台过滤
系统 SHALL 根据登录请求的 device 参数过滤权限的 platform 字段:
platform = "all"的权限对所有端口可见platform = "web"的权限只在device = "web"时可见platform = "h5"的权限只在device = "h5"时可见- 未指定
device参数时默认为"web"
Scenario: Web 后台登录过滤 H5 菜单
- WHEN 用户登录时
device = "web" - WHEN 用户有以下权限:
- perm_code="dashboard:menu", perm_type=1, platform="all"
- perm_code="user:menu", perm_type=1, platform="web"
- perm_code="mobile:menu", perm_type=1, platform="h5"
- THEN
menus数组包含 "dashboard:menu" 和 "user:menu" - THEN
menus数组不包含 "mobile:menu"(H5 专属菜单被过滤)
Scenario: H5 端登录过滤 Web 菜单
- WHEN 用户登录时
device = "h5" - WHEN 用户有以下权限:
- perm_code="mobile:menu", perm_type=1, platform="h5"
- perm_code="user:menu", perm_type=1, platform="web"
- perm_code="common:menu", perm_type=1, platform="all"
- THEN
menus数组包含 "mobile:menu" 和 "common:menu" - THEN
menus数组不包含 "user:menu"(Web 专属菜单被过滤)
Requirement: 超级管理员获取所有权限
系统 SHALL 为超级管理员(user_type = 1)返回所有菜单和按钮权限:
- 查询数据库中所有
status = 1(启用)的权限 - 仍然应用平台过滤(根据
device参数) - 不查询角色权限关联表
Scenario: 超级管理员登录
- WHEN 超级管理员(user_type=1)登录
- WHEN 数据库包含 100 个启用的权限(50 个菜单 + 50 个按钮)
- WHEN 登录时
device = "web" - THEN
menus数组包含所有platform="all"或platform="web"的菜单权限 - THEN
buttons数组包含所有platform="all"或platform="web"的按钮权限 - THEN 不包含
platform="h5"的权限
Requirement: 菜单排序
菜单树 SHALL 根据权限表的 sort 字段排序:
- 同级菜单按
sort字段升序排列 - 子菜单在其父节点的
children数组中按sort排序 - 递归应用到所有层级
Scenario: 菜单按 sort 字段排序
- WHEN 用户有以下权限:
- perm_code="order:menu", sort=3
- perm_code="user:menu", sort=1
- perm_code="dashboard:menu", sort=2
- THEN
menus数组的顺序为["user:menu", "dashboard:menu", "order:menu"]
Scenario: 子菜单按 sort 字段排序
- WHEN 父菜单 "user:menu" 有三个子菜单:
- "user:list:menu", sort=10
- "user:role:menu", sort=5
- "user:dept:menu", sort=8
- THEN 父菜单的
children数组顺序为["user:role:menu", "user:dept:menu", "user:list:menu"]
Requirement: GetMe 接口不返回菜单
GET /api/admin/me 和 GET /api/h5/me 接口 SHALL NOT 返回 menus 和 buttons 字段:
- 只返回
user和permissions字段(现有行为保持不变) - 避免频繁查询和构建菜单树
Scenario: 调用 GetMe 接口
- WHEN 已登录用户调用
GET /api/admin/me - THEN 响应包含
user对象 - THEN 响应包含
permissions数组(权限码列表) - THEN 响应不包含
menus字段 - THEN 响应不包含
buttons字段
Requirement: MenuNode 数据结构
系统 SHALL 定义 MenuNode DTO 结构体,包含以下字段:
id(uint): 权限 IDperm_code(string): 权限码(如 "user:menu")name(string): 菜单名称(如 "用户管理")url(string): 路由路径(如 "/users")sort(int): 排序值children([]MenuNode): 子菜单数组(递归结构)
所有字段 MUST 包含 JSON 标签。
Scenario: MenuNode 结构定义
- WHEN 定义 MenuNode 结构体
- THEN 包含
id字段,类型为uint,JSON 标签为"id" - THEN 包含
perm_code字段,类型为string,JSON 标签为"perm_code" - THEN 包含
name字段,类型为string,JSON 标签为"name" - THEN 包含
url字段,类型为string,JSON 标签为"url" - THEN 包含
sort字段,类型为int,JSON 标签为"sort" - THEN 包含
children字段,类型为[]MenuNode,JSON 标签为"children"
Requirement: 响应格式向后兼容
系统 SHALL 保留原有 permissions 字段,确保向后兼容:
- 登录响应同时包含
permissions,menus,buttons三个字段 - 前端可以选择使用新字段或继续使用旧字段
permissions包含所有权限码(菜单 + 按钮)
Scenario: 向后兼容性验证
- WHEN 用户登录成功
- WHEN 用户有 3 个菜单权限和 2 个按钮权限
- THEN 响应包含
permissions数组,长度为 5 - THEN 响应包含
menus数组(树形结构) - THEN 响应包含
buttons数组,长度为 2 - THEN 旧版前端仍可使用
permissions字段正常工作
Requirement: 性能要求
菜单树构建逻辑 MUST 满足以下性能要求:
- 时间复杂度为 O(n),n 为权限数量
- 登录响应时间增加 < 50ms(在权限数量 < 100 的场景下)
- 不影响 GetMe 接口性能(未修改)
Scenario: 性能基准测试
- WHEN 用户有 50 个权限(30 个菜单 + 20 个按钮)
- WHEN 菜单最大层级为 3 级
- THEN 登录接口响应时间增加 < 50ms
- THEN 菜单树构建时间 < 10ms