登录权限返回修改
This commit is contained in:
209
openspec/specs/login-menu-button-response/spec.md
Normal file
209
openspec/specs/login-menu-button-response/spec.md
Normal 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
|
||||
Reference in New Issue
Block a user