Files
th-hotel-simple/docs/project/requirements/M006-system-admin-management-console-v1.md
2026-07-17 13:13:47 +07:00

920 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# M006 System Admin Management Console 系统管理后台 V1
## 文档信息
| 项目 | 内容 |
| --- | --- |
| 文档版本 | 0.6 |
| 日期 | 2026-07-16 |
| 状态 | V1 已按当前代码实现更新CP4-4a 菜单树增强接口已完成 |
| 适用范围 | 用户、角色、权限、菜单、酒店和用户酒店授权的后台维护 |
| 依赖前置 | M003 登录权限与酒店菜单底座、M005 酒店上下文统一收口 |
| 主要读者 | 产品、后端、前端、测试、后续协作 agent |
## 1. 文档定位
本文记录系统管理后台的整体建设方案。当前系统已经具备登录、session token、权限码、角色权限矩阵、可见菜单和可访问酒店等运行时底座但这些数据主要由后端启动初始化和数据库表承载尚不能通过前端页面进行日常维护。
本方案目标是把用户、角色、权限、菜单和酒店这些平台基础数据,从“只能依赖初始化或手工改库”推进到“通过受控后端接口和前端管理页面维护”。
该能力属于平台管理能力,不属于 `workflows.reservation`。业务模块只能消费当前用户、权限、菜单和酒店上下文,不应反向依赖管理后台实现。
## 2. 当前现状
### 2.1 已有后端能力
当前后端已有以下表结构:
| 表 | 用途 |
| --- | --- |
| `platform_user` | 用户账号、密码哈希、状态和超级管理员标记 |
| `platform_user_session` | 数据库 session token库里只保存 token hash |
| `platform_role` | 角色定义 |
| `platform_permission` | 权限码定义 |
| `platform_user_role` | 用户角色关系 |
| `platform_role_permission` | 角色权限关系 |
| `platform_hotel` | 酒店基础资料 |
| `platform_user_hotel` | 用户酒店授权和默认酒店 |
| `platform_menu` | 菜单定义、路由、图标、权限码和可见性 |
当前后端已有登录接口:
```text
POST /api/auth/login
GET /api/auth/me
POST /api/auth/logout
```
当前后端已有平台服务能力:
- 登录成功返回 `access_token`、当前用户、权限码、酒店列表和可见菜单。
- 可选解析 Bearer token并将当前用户上下文放入请求线程。
- 可判断用户是否拥有权限码。
- 可判断用户是否可访问指定酒店。
- 可按权限码查询可见菜单。
- 启动时同步内置权限、内置角色、角色权限矩阵、内置菜单、默认酒店和首个超级管理员。
### 2.2 已有前端能力
当前前端已有以下能力:
- 登录页。
- token 存入 `sessionStorage`
- 启动时通过 `/api/auth/me` 恢复登录态。
- 请求自动携带 `Authorization: Bearer <access_token>`
- 根据 `menus[]` 渲染侧边栏菜单。
- 根据路由 `meta.permission` 做页面级权限拦截。
- 根据 `hotels[]``HOTEL_SWITCH` 权限展示酒店切换。
- 任务详情部分按钮已做权限控制例如编辑、确认、OPERA 模拟、审计查看。
### 2.3 当前缺口
当前缺口不是“没有权限体系”,而是“没有管理维护能力”:
- 没有用户管理接口和页面。
- 没有角色管理接口和页面。
- 没有权限分配接口和页面。
- 没有菜单管理接口和页面。
- 没有酒店管理接口和页面。
- 没有用户酒店授权管理接口和页面。
- 管理类接口还没有强制登录和权限拦截。
- 管理操作审计还没有落地。
- 内置角色权限矩阵由代码启动同步,页面修改内置角色的边界尚未确认。
## 3. 核心目标
系统管理后台 V1 要解决以下问题:
- 系统管理员可以在前端查看平台用户、角色、权限、菜单和酒店数据。
- 系统管理员可以创建和维护普通用户。
- 系统管理员可以给用户分配角色。
- 系统管理员可以给普通用户授权酒店,并设置默认酒店。
- 系统管理员可以查看角色拥有的权限,并在确认规则后维护角色权限。
- 系统管理员可以维护菜单入口的显示、隐藏、排序、图标和权限绑定。
- 系统管理员可以维护酒店基础资料和启停状态。
- 所有管理接口必须强制登录,并按管理权限码拦截。
- 所有写操作必须可审计、可回溯,不允许普通用户通过前端绕过权限。
### 3.1 补充决策和约束
以下内容用于避免开发时把 M003 / M005 的运行时底座误用成完整管理后台:
- 管理后台接口第一版统一使用 `/api/admin` 前缀,和 `/api/system` 调试类接口区分。
- 系统管理页面第一版使用子路由:`/system/users``/system/roles``/system/menus``/system/hotels``/system/audits`
- 管理接口必须显式强制登录和权限校验,不能依赖当前 `OptionalAuthTokenFilter` 的可选解析行为。
- 后端应新增统一的管理接口鉴权辅助能力,例如 `requireLogin()``requirePermission(permissionCode)``AdminAuthorizationService`,避免每个 Controller 手写不同的 401 / 403 逻辑。
- 单酒店阶段仍以 M005 为准:`platform_hotel` 只能有一家 `ACTIVE` 酒店。酒店管理第一版可以查看和维护酒店资料,但启用第二家 `ACTIVE` 酒店必须被后端拒绝。
- 内置角色权限矩阵仍由代码启动同步。第一版管理后台不允许运营修改内置角色权限,否则会被启动同步覆盖,且容易造成权限预期不一致。
- 系统管理入口新增 `SYSTEM_ADMIN_CONSOLE_ACCESS` 权限码。拥有任一系统管理能力的角色应同时持有该入口权限;子页面和接口仍按各自管理权限码拦截。
- 菜单可见性只控制入口,不替代后端接口权限。前端隐藏菜单或按钮不能作为安全边界。
- 第一版允许新增菜单配置,包括当前前端尚未注册的菜单;未知路由如果直接设为可见启用,前端需要有安全兜底,不应导致页面崩溃。
## 4. 非目标范围
第一版不做以下能力:
- 不做外部 SSO、OIDC、LDAP、企业微信或短信登录。
- 不做 MFA、多设备管理、登录设备踢出、密码找回。
- 不让前端直接操作数据库。
- 不让前端保存或传递任何后端 Secret。
- 不把菜单隐藏当成后端权限校验。
- 不允许运营随意新增后端没有实现的权限码。
- 允许新增当前前端尚未注册的菜单配置,但未知路由不能被视为已可用页面;前端需要安全兜底,后续页面开发完成后再正式开放入口。
- 不一次性完成所有 Reservation / SourceMessage 业务接口强制鉴权,该事项仍属于 M003 CP3 或后续安全收口。
## 5. 权限边界
系统管理后台至少使用以下权限码:
| 权限码 | 管理范围 |
| --- | --- |
| `SYSTEM_USER_MANAGE` | 用户管理、用户角色、用户酒店授权 |
| `SYSTEM_ROLE_MANAGE` | 角色管理、角色权限分配 |
| `SYSTEM_MENU_MANAGE` | 菜单管理 |
| `HOTEL_MANAGE` | 酒店管理 |
| `SYSTEM_AUTH_READ` | 读取当前登录上下文 |
| `SYSTEM_ADMIN_CONSOLE_ACCESS` | 进入系统管理入口 |
建议规则:
- 进入系统管理入口需要 `SYSTEM_ADMIN_CONSOLE_ACCESS`
- 用户管理页面需要 `SYSTEM_USER_MANAGE`
- 角色权限页面需要 `SYSTEM_ROLE_MANAGE`
- 菜单管理页面需要 `SYSTEM_MENU_MANAGE`
- 酒店管理页面需要 `HOTEL_MANAGE`
- 后端管理接口必须逐个校验权限,不依赖前端隐藏按钮。
- 当前 `platform_menu.permission_code` 只支持单个权限码。系统管理入口已确认通过新增入口权限解决“任意管理权限可见”的建模问题。
入口权限建模决策:
| 方案 | 说明 | 影响 |
| --- | --- | --- |
| 新增 `SYSTEM_ADMIN_CONSOLE_ACCESS` | 系统管理入口绑定该权限,拥有任一管理能力的角色都额外授予该入口权限 | 已确认采用 |
| 拆分多个系统管理菜单 | 用户、角色、菜单、酒店分别作为菜单入口,各自绑定对应权限 | 菜单更多,但不需要新增入口权限 |
| 菜单不绑定权限,前端子路由拦截 | `/system` 入口所有登录用户可见,进入后再按子页面权限拦截 | 不推荐,普通用户会看到无效入口 |
## 6. 后端改动范围
### 6.1 新增管理接口模块
建议在现有平台模块内补管理接口,不单独创建业务工作流模块:
```text
platform.identity.control
// 用户管理 Controller
platform.access.control
// 角色和权限管理 Controller
platform.navigation.control
// 菜单管理 Controller
platform.hotel.control
// 酒店和用户酒店授权管理 Controller
```
中文说明:
- 用户账号仍归 `platform.identity`
- 角色、权限和授权关系仍归 `platform.access`
- 菜单仍归 `platform.navigation`
- 酒店和用户酒店授权仍归 `platform.hotel`
- 管理接口第一版建议统一使用 `/api/admin/...` 前缀,避免和 `/api/system/...` 调试、运维、fixture 接口混在一起。
### 6.1.1 管理接口统一鉴权和错误响应
管理接口不能复用“可选登录态”的兼容策略,必须强制登录和权限校验。
建议统一规则:
| 场景 | HTTP 状态 | 建议错误码 | 说明 |
| --- | --- | --- | --- |
| 未传 Bearer token | 401 | `ADMIN_AUTH_REQUIRED` | 管理后台必须先登录 |
| token 无效、过期或 session 已撤销 | 401 | `AUTH_SESSION_INVALID` | 可复用登录态失效语义 |
| 已登录但缺少权限码 | 403 | `ADMIN_PERMISSION_DENIED` | 返回缺少的权限码摘要,不返回敏感信息 |
| 请求参数错误 | 400 | `ADMIN_INVALID_REQUEST` | 字段校验失败 |
| 目标对象不存在 | 404 | `ADMIN_TARGET_NOT_FOUND` | 用户、角色、菜单、酒店不存在 |
| 状态冲突或唯一约束冲突 | 409 | `ADMIN_CONFLICT` | 用户名重复、启用第二家 ACTIVE 酒店等 |
实现建议:
-`platform.security` 或对应管理模块下补统一鉴权服务,不在 Controller 里散落 token 解析逻辑。
- Controller 只负责 HTTP 参数转换Service 负责业务规则和事务。
- ControllerAdvice 统一处理管理接口异常错误响应不得包含密码、token、Secret 或原始敏感数据。
### 6.2 用户管理后端能力
第一版建议支持:
| 能力 | 说明 |
| --- | --- |
| 用户分页列表 | 按用户名、展示名、状态、角色、酒店筛选 |
| 用户详情 | 返回基础信息、角色、授权酒店、默认酒店 |
| 新增用户 | 创建用户名、初始密码、展示名、联系方式、状态 |
| 编辑用户 | 修改展示名、邮箱、手机号、状态 |
| 禁用 / 启用用户 | 禁用后不能登录,并撤销该用户全部 ACTIVE session |
| 重置密码 | 管理员重置临时密码;密码不得出现在日志 |
| 分配角色 | 覆盖用户角色关系 |
| 分配酒店 | 覆盖普通用户可访问酒店,并设置默认酒店 |
注意:
- `username` 全局唯一。
- 密码只保存单向哈希。
- 超级管理员账号的禁用、降权和删除需要额外保护,避免系统锁死。
- 用户 ID 返回前端时必须是字符串,前端不要转成 JavaScript number。
### 6.3 角色权限管理后端能力
第一版建议支持:
| 能力 | 说明 |
| --- | --- |
| 角色列表 | 返回角色代码、名称、状态、是否内置、权限数量、用户数量 |
| 角色详情 | 返回角色基础信息和权限列表 |
| 权限列表 | 只读返回权限码、名称、分组、状态、是否内置 |
| 新增自定义角色 | 创建业务角色,角色代码稳定唯一 |
| 编辑角色 | 修改角色名称、状态 |
| 分配权限 | 覆盖角色权限关系 |
内置角色边界已确认:
- 当前 `SYSTEM_ADMIN``RESERVATION_OPERATOR``RESERVATION_VIEWER` 是内置角色。
- 当前代码启动时会同步内置角色权限矩阵,并清理矩阵之外的旧关系。
- 第一版内置角色只读,不允许通过页面修改内置角色权限。
- 自定义角色允许新增、编辑和配置权限。
### 6.4 菜单管理后端能力
第一版建议支持:
| 能力 | 说明 |
| --- | --- |
| 菜单列表 | 返回菜单代码、名称、路由、图标、权限、排序、可见性、状态 |
| 菜单详情 | 查看单个菜单完整配置 |
| 编辑菜单 | 修改名称、图标、排序、可见性、状态、权限码 |
| 新增菜单 | 第一版允许新增菜单配置,包括当前前端尚未注册的菜单 |
| 菜单排序 | 支持保存排序号 |
| 菜单树查询 | 返回完整菜单树,用于前端左侧树形菜单管理 |
| 批量调整父级和排序 | 支持一次性保存菜单父子关系和同级排序 |
建议限制:
- `permission_code` 必须来自已启用权限码。
- 第一版允许新增未知菜单,但未知 `route_path` 不能被视为已可用页面;前端路由不存在时需要展示安全兜底或跳转到无权限 / 未找到页面。
- 新增菜单如果配置了未知路由,建议默认 `visible=false``menu_status=DISABLED`,由管理员在前端支持到位后再开放。
- 菜单只决定入口可见性,不替代后端接口权限。
- 批量调整菜单树时只允许修改 `parent_id``sort_order`,不能顺带修改路由、权限、可见性或状态。
- 后端必须校验父级存在、禁止把自己设为父级、禁止形成循环树;失败时返回受控业务错误。
### 6.5 酒店管理后端能力
第一版建议支持:
| 能力 | 说明 |
| --- | --- |
| 酒店列表 | 返回酒店 ID、名称、状态、时区、排序 |
| 新增酒店 | 创建业务酒店 ID、名称、时区 |
| 编辑酒店 | 修改名称、时区、排序 |
| 启用 / 禁用酒店 | 禁用后普通用户不可选择 |
注意:
- `hotel_id` 是业务数据隔离关键字段,不能随意修改。第一版建议新增后不允许修改 `hotel_id`
- 禁用酒店前需要确认是否已有 SourceMessage、Reservation、Task 等业务数据。
- 超级管理员默认可访问全部启用酒店。
- 普通用户只能访问 `platform_user_hotel` 授权酒店。
- 单酒店阶段数据库已通过 V12 约束最多只有一家 `ACTIVE` 酒店。启用酒店接口必须先判断当前是否已有其他 `ACTIVE` 酒店;如果已有,应返回 409而不是依赖数据库异常直接冒出。
- 第一版允许新增酒店,但新增后默认创建为 `DISABLED`,避免新增即触发单酒店约束。
- 禁用当前唯一 `ACTIVE` 酒店会影响系统默认酒店上下文解析。第一版禁止禁用最后一家 `ACTIVE` 酒店。
### 6.6 管理操作审计
建议新增或复用平台审计能力,至少记录:
| 字段 | 说明 |
| --- | --- |
| actor_user_id | 操作用户 ID |
| actor_username | 操作用户名摘要 |
| target_type | 操作对象类型,例如 USER、ROLE、MENU、HOTEL |
| target_id | 操作对象 ID |
| action | 操作类型 |
| before_snapshot_json | 变更前摘要不保存密码、token、secret |
| after_snapshot_json | 变更后摘要不保存密码、token、secret |
| occurred_at | 操作 UTC 时间 |
审计可以作为写操作正式开放前的必要前置。
## 7. 前端改动范围
### 7.1 系统管理入口
第一版新增一个系统管理入口:
```text
/system
```
第一版已确认采用子路由:
```text
/system/users
/system/roles
/system/menus
/system/hotels
/system/audits
```
第一版使用子路由,便于后续做页面权限、刷新保持位置和独立测试。
### 7.2 用户管理页面
页面建议包含:
- 用户列表表格。
- 状态、关键词、角色、酒店筛选。
- 新增用户弹窗或独立页面。
- 用户详情抽屉。
- 角色分配区域。
- 酒店授权区域。
- 设置默认酒店操作。
- 重置密码操作。
- 启用 / 禁用操作。
用户表格建议字段:
| 字段 | 说明 |
| --- | --- |
| 用户名 | 登录用户名 |
| 展示名 | 业务人员可读名称 |
| 状态 | ACTIVE / DISABLED |
| 超级管理员 | 是否超级管理员 |
| 角色 | 当前角色摘要 |
| 默认酒店 | 默认酒店 |
| 最近登录 | UTC 转本地展示 |
| 更新时间 | UTC 转本地展示 |
### 7.3 角色权限页面
页面建议包含:
- 角色列表。
- 角色详情。
- 权限分组勾选。
- 内置角色只读提示。
- 自定义角色新增和编辑。
权限建议按分组展示:
| 分组 | 示例权限 |
| --- | --- |
| SYSTEM | 系统用户、角色、菜单、Debug |
| HOTEL | 酒店管理、酒店切换 |
| RESERVATION | 订单、任务、确认、OPERA、审计 |
| SOURCE_MESSAGE | 邮件摘要、邮件原文 |
### 7.4 菜单管理页面
页面建议包含:
- 菜单树或菜单表格。
- 菜单名称、路由、图标、权限码、排序、状态、是否可见。
- 路由输入、已知路由提示和未知路由安全兜底。
- 权限码下拉选择。
- 预览当前菜单可见效果。
### 7.5 酒店管理页面
页面建议包含:
- 酒店列表。
- 新增酒店。
- 编辑酒店名称、时区、排序。
- 启用 / 禁用酒店。
- 查看授权用户数量。
### 7.6 前端服务和类型
建议新增:
```text
client/src/services/systemAdminService.ts
client/src/types/systemAdmin.ts
```
前端请求仍统一复用当前 `httpClient`,由它自动携带 Bearer token。
### 7.7 当前前端交互问题
当前 `/system` 页面已经接通 V1 后端接口,但页面交互仍偏“开发调试式 CRUD”主要问题如下
- 系统设置入口只有横向子路由 tab缺少设置中心的信息分组用户不容易理解“账号权限、菜单导航、酒店配置、审计日志”之间的关系。
- 用户、角色、菜单、酒店页面把筛选、新建、列表、详情和编辑堆在同一页,管理员容易迷失当前操作上下文。
- 角色、酒店、权限分配使用原生多选框,数据量稍大时难以搜索、比对和确认变更。
- 菜单天然是树形导航配置,目前以表格平铺展示,父子关系、排序、可见性和未知路由风险不够直观。
- 高风险操作缺少清晰确认,例如重置密码、禁用用户、启用 / 禁用酒店、修改角色权限和开放菜单。
- 保存中、保存成功、保存失败、未保存离开、只读原因等状态反馈不够明显。
### 7.8 系统设置整体低保真结构
系统设置建议从“每页堆表单”调整为“设置中心 + 模块列表 + 详情抽屉 / 专用编辑区”。除菜单树查询和批量树排序这两个已确认增强点外,其他第一阶段交互调整尽量复用当前 `/api/admin/**` 能力。
```text
┌──────────────────────────────────────────────────────────────┐
│ 系统设置 │
│ 管理账号、权限、菜单导航、酒店配置和后台审计。 │
├──────────────────────────────────────────────────────────────┤
│ 分组导航 │
│ [账号与权限] [菜单与导航] [酒店配置] [审计日志] │
├──────────────────────────────────────────────────────────────┤
│ 当前模块标题 [主要操作按钮] │
│ 简短说明 / 权限提示 / 当前酒店或系统状态 │
├──────────────────────────────────────────────────────────────┤
│ 筛选栏:关键词、状态、业务分组、刷新 │
├──────────────────────────────┬───────────────────────────────┤
│ 列表 / 树 / 审计时间线 │ 详情抽屉 / 编辑面板 │
│ - 行选中态 │ - 基础信息 │
│ - 状态标签 │ - 授权配置 │
│ - 操作入口 │ - 变更摘要 │
│ │ - 保存 / 取消 / 高风险确认 │
└──────────────────────────────┴───────────────────────────────┘
```
交互规则:
- 默认进入 `/system` 时仍按当前权限跳转到第一个可访问子页面;后续可以增加 `/system/overview`,但不是 V1 必需项。
- 新建动作优先使用抽屉或弹窗,不再常驻占用页面首屏。
- 列表行点击后在右侧抽屉展示详情;窄屏下抽屉改为全屏面板。
- 编辑和详情放在同一个抽屉里,通过“查看 / 编辑”状态切换,避免页面底部出现第二个编辑表单。
- 写操作提交前展示变更摘要;涉及权限、酒店、菜单可见性和密码重置时展示二次确认。
- 接口 401 / 403 / 409 / 5xx 使用模块内错误态,不静默失败。
### 7.9 各模块低保真结构
#### 用户管理
```text
┌ 用户管理 ─────────────────────────────────────────────────────┐
│ [关键词] [状态] [角色] [酒店] [新建用户] │
├──────────────────────────────┬───────────────────────────────┤
│ 用户列表 │ 用户详情抽屉 │
│ 用户名 / 展示名 / 状态 │ 基础信息 │
│ 角色摘要 / 默认酒店 / 更新时间 │ 角色授权 │
│ 操作:查看 │ 酒店授权与默认酒店 │
│ │ 安全操作:重置密码 / 禁用 │
└──────────────────────────────┴───────────────────────────────┘
```
建议:
- 角色和酒店授权改为可搜索多选列表,已选项以标签展示。
- 默认酒店只能从已授权酒店中选择;未满足时禁用保存并给出明确提示。
- 重置密码只在详情抽屉的“安全操作”区域展示,结果只展示一次,不写入普通日志或 URL。
#### 角色权限
```text
┌ 角色权限 ─────────────────────────────────────────────────────┐
│ [关键词] [状态] [新建角色] │
├──────────────────────────────┬───────────────────────────────┤
│ 角色列表 │ 角色详情抽屉 │
│ 角色名 / 角色代码 / 内置标记 │ 基础信息 │
│ 权限数量 / 使用人数 │ 权限矩阵 │
│ │ 变更摘要:新增 / 移除权限 │
└──────────────────────────────┴───────────────────────────────┘
```
权限矩阵建议:
```text
[搜索权限码或名称]
SYSTEM
[ ] 用户管理 SYSTEM_USER_MANAGE
[ ] 角色管理 SYSTEM_ROLE_MANAGE
[ ] 菜单管理 SYSTEM_MENU_MANAGE
RESERVATION
[ ] 订单读取 RESERVATION_ORDER_READ
[ ] 任务确认 RESERVATION_TASK_CONFIRM
```
建议:
- 使用 `permission_group` 分组展示,权限码作为次要信息,不让管理员只面对代码列表。
- 内置角色只读时,整块权限矩阵禁用,并展示“内置角色由系统同步,不允许页面修改”。
- 保存前展示“将新增 N 个权限、移除 M 个权限”,降低误操作风险。
#### 菜单管理
菜单管理建议优先改成树形结构,因为它直接影响侧边栏和子菜单的真实体验。
```text
┌ 菜单管理 ─────────────────────────────────────────────────────┐
│ [搜索菜单 / 路由] [状态] [只看可见] [新建菜单] │
├──────────────────────────────┬───────────────────────────────┤
│ 菜单树 │ 菜单配置面板 │
│ ▾ 系统设置 │ 基础配置 │
│ ├─ 用户管理 可见 ACTIVE │ 路由与组件 │
│ ├─ 角色权限 可见 ACTIVE │ 权限与可见性 │
│ ├─ 菜单管理 可见 ACTIVE │ 排序与状态 │
│ ▾ 订单处理 │ 影响预览 / 未知路由提示 │
└──────────────────────────────┴───────────────────────────────┘
```
菜单配置面板建议字段:
| 区域 | 字段 | 交互建议 |
| --- | --- | --- |
| 基础配置 | 菜单名称、菜单代码、父级菜单、图标 | 菜单代码新增后不建议修改;图标使用可搜索选择器 |
| 路由与组件 | `route_path``component_key``known_route` | 未知路由展示风险提示,默认不建议设为可见启用 |
| 权限与可见性 | `permission_code``visible``menu_status` | 明确提示“菜单可见不等于接口授权” |
| 排序与预览 | `sort_order`、侧边栏预览 | 保存前预览菜单在侧边栏中的位置 |
当前 `AdminMenuResult` 已包含 `parent_id``sort_order``visible``known_route`。为避免分页列表导致前端树不完整,菜单管理交互升级时建议后端补 `GET /api/admin/menus/tree`;前端树形管理页优先使用后端完整树接口,不再依赖分页列表自行拼完整树。
#### 酒店管理
```text
┌ 酒店管理 ─────────────────────────────────────────────────────┐
│ [关键词] [状态] [新建酒店] │
├──────────────────────────────┬───────────────────────────────┤
│ 酒店列表 │ 酒店详情抽屉 │
│ 酒店名 / 状态 / 时区 │ 基础信息 │
│ 授权用户数 / 更新时间 │ 启用 / 禁用确认 │
└──────────────────────────────┴───────────────────────────────┘
```
建议:
- 单酒店阶段把“只能有一家 ACTIVE 酒店”作为常驻提示,不放在表单底部。
- 启用第二家 ACTIVE 酒店、禁用最后一家 ACTIVE 酒店会由后端返回 409前端需要把错误展示成可理解的业务提示。
#### 审计日志
```text
┌ 审计日志 ─────────────────────────────────────────────────────┐
│ [对象类型] [对象 ID] [操作类型] [时间范围] [刷新] │
├──────────────────────────────┬───────────────────────────────┤
│ 审计列表 / 时间线 │ 审计详情抽屉 │
│ 操作 / 对象 / 操作人 / 时间 │ 操作前快照 │
│ │ 操作后快照 │
└──────────────────────────────┴───────────────────────────────┘
```
建议:
- 审计页保持只读,不出现编辑控件。
- `before_snapshot_json` / `after_snapshot_json` 可用折叠 JSON 或差异视图展示但不要展示密码、token、secret。
- 如果后端暂不支持时间范围筛选,前端不伪造筛选,只保留当前接口已支持的对象类型、对象 ID 和操作类型。
### 7.10 动效和状态反馈建议
动效只服务可用性,建议控制在 160ms - 220ms
- 子导航 active indicator 平移动效,帮助用户识别当前模块。
- 列表行 hover / selected 使用轻微背景和边框变化。
- 详情抽屉进入、关闭、切换编辑状态使用短过渡。
- 保存按钮展示 loading成功后出现短暂 success 状态;失败时在表单顶部固定展示错误。
- 权限勾选、菜单可见性开关、酒店状态切换提供明确即时反馈。
- 支持 `prefers-reduced-motion`,用户关闭系统动效时前端应降低或取消动画。
## 8. 接口草案
正式开发前需要再细化字段、分页格式和错误码。第一版接口前缀建议统一使用 `/api/admin`,先按以下方向设计。
通用要求:
- 所有 `/api/admin/**` 接口必须携带 `Authorization: Bearer <access_token>`
- 无 token 或 token 无效返回 401。
- 已登录但缺少对应管理权限返回 403。
- 所有 `BIGINT` ID 返回前端时使用字符串,前端不得转换为 JavaScript number。
- 时间点字段继续使用带 `Z` 的 ISO 8601 UTC 时间。
- 分页请求和响应统一当前 Reservation 风格:请求参数使用 `page_num``page_size`;响应使用 `{ "items": [...], "page": { "page_num": 1, "page_size": 20, "total": 0 } }`
### 8.1 用户管理
```text
GET /api/admin/users
GET /api/admin/users/{userId}
POST /api/admin/users
PUT /api/admin/users/{userId}
POST /api/admin/users/{userId}/password-reset
PUT /api/admin/users/{userId}/roles
PUT /api/admin/users/{userId}/hotels
```
说明:用户启用 / 禁用第一版通过 `PUT /api/admin/users/{userId}``user_status` 字段完成;禁用用户会撤销该用户全部 ACTIVE session。
### 8.2 角色权限管理
```text
GET /api/admin/roles
GET /api/admin/roles/{roleId}
POST /api/admin/roles
PUT /api/admin/roles/{roleId}
PUT /api/admin/roles/{roleId}/permissions
GET /api/admin/permissions
```
说明:角色启用 / 禁用第一版通过 `PUT /api/admin/roles/{roleId}``role_status` 字段完成;内置角色只读,编辑和权限分配会返回冲突错误。
### 8.3 菜单管理
```text
GET /api/admin/menus
GET /api/admin/menus/{menuId}
GET /api/admin/menus/tree
POST /api/admin/menus
PUT /api/admin/menus/{menuId}
PUT /api/admin/menus/tree-order
```
说明:菜单排序第一版可以继续通过单条菜单的 `sort_order` 字段保存;树形菜单管理升级后,前端优先使用 `GET /api/admin/menus/tree` 获取完整树,使用 `PUT /api/admin/menus/tree-order` 批量保存父级和排序。路由选项接口未做,前端允许手工输入未知路由并通过兜底页保护。
菜单树查询建议:
```text
GET /api/admin/menus/tree
```
返回完整菜单树,不分页;默认包含 `ACTIVE` / `DISABLED``visible=true` / `false` 的全部菜单。同级排序按 `sort_order` 升序,其次按 `menu_name``id` 稳定排序。
建议节点结构沿用现有菜单结果并增加 `children[]`
```json
{
"items": [
{
"id": "10001",
"parent_id": null,
"menu_code": "SYSTEM_SETTINGS",
"menu_name": "系统设置",
"route_path": "/system",
"permission_code": "SYSTEM_ADMIN_CONSOLE_ACCESS",
"sort_order": 900,
"visible": true,
"menu_status": "ACTIVE",
"known_route": true,
"children": []
}
],
"warnings": []
}
```
批量调整父级和排序建议:
```text
PUT /api/admin/menus/tree-order
```
请求体建议:
```json
{
"items": [
{
"menu_id": "10002",
"parent_id": "10001",
"sort_order": 100
}
]
}
```
要求:
- 必须登录并拥有 `SYSTEM_MENU_MANAGE`
- 只允许修改 `parent_id``sort_order`
- 使用事务保存。
- 校验 `menu_id` 存在。
- 校验 `parent_id` 为空或存在。
- 禁止把自己设为自己的父级。
- 禁止形成循环菜单树。
- `sort_order` 可为空;为空时后端按请求 `items[]` 顺序生成稳定排序号 `100``200``300`...
- 成功后返回更新后的完整菜单树,方便前端立即刷新。
- 写操作必须写 `platform_admin_audit_log`,审计中记录调整前后的 `parent_id` / `sort_order`
### 8.4 酒店管理
```text
GET /api/admin/hotels
GET /api/admin/hotels/{hotelId}
POST /api/admin/hotels
PUT /api/admin/hotels/{hotelId}
PUT /api/admin/hotels/{hotelId}/status
```
说明:新增酒店默认 `DISABLED`;状态切换统一走 `/status`,启用第二家 ACTIVE 酒店和禁用最后一家 ACTIVE 酒店都会返回冲突错误。
### 8.5 管理审计
```text
GET /api/admin/audits
```
说明:写操作会记录 `platform_admin_audit_log`,审计查询支持 `target_type``target_id``action``page_num``page_size`
## 9. 推荐分期
### CP4-1系统管理只读页
目标:先让管理员能通过前端看见当前用户、角色、权限、菜单和酒店数据。
范围:
- 后端新增只读查询接口。
- 前端新增系统管理入口和只读页面。
- 系统设置菜单从隐藏调整为有权限可见,入口权限使用 `SYSTEM_ADMIN_CONSOLE_ACCESS`
- 管理接口强制登录和权限校验。
- 不做新增、编辑、禁用、分配。
验收标准:
- 系统管理员登录后能看到系统管理入口。
- 非系统管理权限用户看不到入口,直接访问路由也会被拦截。
- 用户、角色、权限、菜单、酒店列表能正常展示。
- 后端接口无 token 返回 401无权限返回 403。
当前实现状态:已完成。
### CP4-2用户管理写操作
目标:支持日常账号维护。
范围:
- 新增用户。
- 编辑用户基础信息。
- 启用 / 禁用用户。
- 重置密码。
- 分配角色。
- 分配酒店和默认酒店。
- 写操作审计。
验收标准:
- 新用户可用初始密码登录。
- 禁用用户不能登录。
- 普通用户只能看到授权酒店。
- 默认酒店最多只有一个。
当前实现状态:已完成。新增用户、编辑状态、重置密码、覆盖角色、覆盖酒店授权已接入前后端;禁用用户会撤销 ACTIVE session。
### CP4-3角色权限管理
目标:支持自定义业务角色。
范围:
- 新增自定义角色。
- 编辑自定义角色。
- 启用 / 禁用自定义角色。
- 分配权限。
- 内置角色只读或按确认后的规则开放。
验收标准:
- 自定义角色权限变更后,用户重新登录或刷新上下文后生效。
- 无权限码的页面入口和按钮不可见,后端接口仍能拦截。
当前实现状态:已完成。自定义角色可新增、编辑和分配权限;内置角色只读。
### CP4-4菜单和酒店管理
目标:支持菜单入口和酒店基础资料维护。
范围:
- 编辑菜单名称、图标、排序、可见性、状态和权限码。
- 酒店新增、编辑、启用、禁用。
- 路由配置、未知路由兜底和权限码选择。
验收标准:
- 菜单配置变更后,用户刷新 `/api/auth/me` 可以拿到新菜单。
- 禁用酒店后普通用户不可再选择该酒店。
- 允许配置当前前端尚不存在的菜单路由,但前端必须安全兜底,不能因为未知路由导致页面崩溃。
当前实现状态:已完成。菜单支持新增和编辑;酒店支持新增、编辑和状态切换;前端提供未知路由兜底页。
### CP4-4a菜单树增强接口
目标:支撑前端菜单管理从表格交互升级为“左侧菜单树 + 右侧配置面板”。
范围:
- 后端新增 `GET /api/admin/menus/tree`,返回完整菜单树,不分页。
- 后端新增 `PUT /api/admin/menus/tree-order`,批量保存菜单父级和排序。
- 批量保存时校验父级存在、禁止自引用、禁止循环树。
- 批量保存只修改 `parent_id``sort_order`
- 批量保存写入 `platform_admin_audit_log`
验收标准:
- 前端可以不依赖分页菜单列表,直接渲染完整菜单树。
- 拖拽排序或批量调整层级后,前端可以一次性保存并刷新完整树。
- 无 token 返回 401`SYSTEM_MENU_MANAGE` 返回 403。
- 循环树、自引用、不存在的菜单或父级返回受控业务错误。
当前实现状态:已完成。后端已提供完整菜单树查询和批量树排序保存;批量保存 `sort_order` 允许为空,后端按请求 `items[]` 顺序生成 `100``200``300`... 的稳定排序号。
### CP4-4b管理操作审计页
目标:系统管理员可以查看管理后台写操作审计。
范围:
- 后端新增 `GET /api/admin/audits`
- 前端新增 `/system/audits`
- 审计页按对象类型、对象 ID 和操作类型筛选。
当前实现状态:已完成。
### CP4-5业务接口强制权限收口
目标:把管理后台和业务接口的权限模型闭环。
范围:
- Reservation 查询接口强制登录和酒店权限。
- Reservation 写接口强制具体操作权限。
- SourceMessage 原文读取迁移到 `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ`。已通过接口权限与酒店隔离收口 CP2 完成。
- 业务审计 actor 全面迁移到当前用户。
该阶段和 M003 CP3、M005 酒店上下文统一收口有关,建议单独拆文档和任务。
## 10. 风险和约束
| 风险 | 说明 | 建议 |
| --- | --- | --- |
| 内置角色被启动同步覆盖 | 当前代码会按矩阵同步内置角色权限 | 第一版内置角色只读,自定义角色可编辑 |
| 菜单配置了不存在路由 | 前端无法渲染或跳转失败 | 允许保存未知路由,但前端必须安全兜底;未知路由正式开放前建议保持隐藏或禁用 |
| 前端隐藏按钮被绕过 | 用户可直接调用接口 | 后端所有管理接口必须强制鉴权和权限校验 |
| 可选登录过滤器被误认为强制鉴权 | 当前过滤器只解析 token不拦截业务接口 | `/api/admin/**` 必须显式 require login / permission |
| 禁用超级管理员导致锁死 | 系统可能没有可登录管理员 | 禁止禁用最后一个启用超级管理员 |
| 用户默认酒店冲突 | 普通用户可能有多个默认酒店 | 后端服务和数据库唯一索引双重保证 |
| 单酒店阶段启用第二家酒店 | V12 会阻止多家 ACTIVE 酒店,业务层若不拦截会暴露数据库异常 | 酒店启用前主动检查,返回 409 |
| 系统设置菜单只有单权限码 | `platform_menu.permission_code` 当前只支持一个权限码 | 新增 `SYSTEM_ADMIN_CONSOLE_ACCESS` 作为统一入口权限 |
| 酒店禁用影响历史业务数据 | 历史订单、邮件仍属于该酒店 | 禁用只影响新选择和访问,不删除历史数据 |
| 密码或 token 泄漏 | 管理后台涉及重置密码 | 密码只返回一次,不进日志、审计和错误响应 |
## 11. 已确认决策
| 问题 | 建议 | 状态 |
| --- | --- | --- |
| 管理接口前缀用 `/api/admin` 还是 `/api/system` | 使用 `/api/admin`,语义更清楚 | 已确认 |
| 第一版系统管理页面用 Tab 还是子路由? | 使用子路由 | 已确认 |
| 系统管理入口权限如何建模? | 新增 `SYSTEM_ADMIN_CONSOLE_ACCESS`,由具备任一管理能力的角色持有 | 已确认 |
| 内置角色是否允许页面修改权限? | 第一版只读 | 已确认 |
| 是否允许新增菜单? | 允许新增菜单配置,包括未知菜单;未知路由需要前端安全兜底 | 已确认 |
| 是否允许修改 `hotel_id` | 新增后不可修改 | 已确认 |
| 单酒店阶段是否允许新增酒店? | 允许新增但默认 `DISABLED`,不允许启用第二家 `ACTIVE` | 已确认 |
| 是否允许禁用最后一家 `ACTIVE` 酒店? | 禁止,避免系统酒店上下文无法解析 | 已确认 |
| 禁用用户时是否撤销已有 session | 撤销该用户全部 ACTIVE session | 已确认 |
| 重置密码是否要求用户下次登录修改? | 第一版先不强制,后续安全增强 | 已确认 |
| 管理操作审计用新平台审计表还是复用现有审计表? | 新增平台管理审计表 | 已确认 |
| 管理接口分页格式是否统一为当前 Reservation 风格? | 统一使用 `page_num``page_size``items``page` | 已确认 |
## 12. 建议第一个最小 checkpoint
建议第一个最小 checkpoint 为:
```text
checkpoint-M006-CP4-1-system-admin-readonly-console
```
范围只做只读:
- 后端新增用户、角色、权限、菜单、酒店只读接口。
- 管理接口强制登录和权限校验。
- 前端新增系统管理入口和只读页面。
- 不做新增、编辑、禁用、分配、重置密码。
这样可以先验证:
- 当前数据模型是否足够支撑页面。
- 菜单和权限入口是否顺畅。
- 管理后台的页面结构是否符合实际使用。
- 后续写操作是否需要补表或调整字段。
## 13. 目标模式提示词建议
如果要进入目标模式开发只读版,可以这样说:
```text
目标:实现 M006 CP4-1 系统管理只读页。
要求:
1. 只做用户、角色、权限、菜单、酒店的只读查询,不做新增、编辑、删除、禁用、分配。
2. 后端新增管理查询接口,接口必须强制登录,并按管理权限码校验。
3. 前端新增系统管理入口和只读页面,按当前用户权限显示入口和页面。
4. 新增 `SYSTEM_ADMIN_CONSOLE_ACCESS` 入口权限,系统设置菜单从隐藏调整为绑定该权限后可见。
5. 不改变现有 Reservation / SourceMessage 业务接口鉴权策略。
6. 不开放内置角色页面编辑;只允许为系统管理入口补必要的内置权限和菜单初始化。
7. 不提交真实密码、token、业务数据或构建产物。
8. 补充必要测试。
9. 完成后运行相关后端和前端测试,不提交,先给我看改动。
```