## ADDED 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
