登录权限返回修改

This commit is contained in:
2026-01-30 17:22:38 +08:00
parent 4856a88d41
commit ffeb0417c0
12 changed files with 1884 additions and 7 deletions

View File

@@ -0,0 +1,209 @@
# 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): 权限 ID
- `perm_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