实现系统管理后台 V1

This commit is contained in:
andy
2026-07-10 18:32:34 +08:00
parent e76cb80f28
commit aa5783ba61
92 changed files with 9383 additions and 21 deletions

View File

@@ -2,7 +2,7 @@
## 1. 文档定位
本文记录后端侧提醒前端开发时必须注意的项目规范、业务规则、接口边界和安全要求。当前内容服务于 Reservation 任务详情、订单详情、任务列表、S000/S999 特殊只读任务、历史 Message Notification 等第一版页面。
本文记录后端侧提醒前端开发时必须注意的项目规范、业务规则、接口边界和安全要求。当前内容服务于 Reservation 任务详情、订单详情、任务列表、S000/S999 特殊只读任务、历史 Message Notification、系统管理后台等第一版页面。
## 2. 项目开发注意事项
@@ -11,6 +11,7 @@
- 业务判断必须使用后端返回的稳定 code不使用中文或英文展示文案做判断。
- 后端返回的时间点字段统一是带 `Z` 的 ISO 8601 UTC 时间,例如 `created_at``updated_at``received_at``last_updated_at`;前端展示时再按用户或酒店时区格式化。
- 入住日期、离店日期、酒店营业日属于酒店本地业务日期,不要按 UTC 时间点自动换算日期。
- 详细时间设计参考 `docs/project/backend-time-design.md`,不要把数据库 UTC 时间直接当酒店当地时间展示。
- 前端不得保存或传递后端 Secret、replay access key、Provider API Key、Oracle 凭证、AgentBus Token。
- 后端数据库 ID 未来应尽量以字符串形式给前端,避免 JavaScript 长整型精度问题;如果当前接口仍返回数字,前端不要自行做数学运算。
- 接口字段发生变化前,需要先更新本目录沟通文档或对应需求文档。
@@ -57,6 +58,12 @@
| `GET /api/source-messages/{id}/original` | 读取来源消息原文 | 需要受控访问头,返回 HTML 时前端展示前必须 sanitize。 |
| `GET /api/source-messages/{sourceMessageId}/conversation` | 读取邮件会话详情 | 返回同一外部会话全部邮件的完整 text/html、`html_body_sanitized`、附件外链、内联图片和关联订单 / 任务摘要;前端不传原文读取 key展示 HTML 时优先使用 `html_body_sanitized`。 |
| `POST /api/system/debug/eml-superagent-runs` | Debug 页面上传 `.eml` 并调用 SuperAgent | 仅 dev/test 受控调试使用;会写入 SourceMessage Inbox但不创建订单和任务。 |
| `GET/POST/PUT /api/admin/users...` | 系统管理用户维护 | 需要 Bearer token 和 `SYSTEM_USER_MANAGE`;用户 ID 返回字符串;禁用用户会撤销其 ACTIVE session。 |
| `GET/POST/PUT /api/admin/roles...` | 系统管理角色权限维护 | 需要 `SYSTEM_ROLE_MANAGE`;内置角色只读,自定义角色可新增、编辑和分配权限。 |
| `GET /api/admin/permissions` | 权限码只读列表 | 需要 `SYSTEM_ROLE_MANAGE`;前端只展示和选择已有权限码,不自行造权限码。 |
| `GET/POST/PUT /api/admin/menus...` | 系统管理菜单维护 | 需要 `SYSTEM_MENU_MANAGE`;允许保存未知路由,前端必须有未知路由兜底页。 |
| `GET/POST/PUT /api/admin/hotels...` | 系统管理酒店维护 | 需要 `HOTEL_MANAGE`;新增酒店默认 `DISABLED`,单酒店阶段不能启用第二家 `ACTIVE`。 |
| `GET /api/admin/audits` | 系统管理操作审计 | 需要 `SYSTEM_ADMIN_CONSOLE_ACCESS`用于查看管理后台写操作审计不包含密码、token、secret。 |
### 5.1 本轮新增 / 修改接口说明
@@ -209,6 +216,41 @@ run_label: 可选调试标签
- 返回的 `uploaded_media[]``original_eml_oss_url``html_body_with_oss_urls``html_body_sanitized` 可能包含 OSS URL前端不要写入普通日志、埋点、错误上报或 URL query。
- `superagent_parsed_json` 为空时,前端展示 `superagent_raw_answer``warnings[]`,不要假定 SuperAgent 总能返回 JSON。
### 5.9 系统管理后台接口接入注意
系统管理后台 V1 已提供 `/system` 前端入口和 `/api/admin/**` 后端接口。所有管理接口都必须带 `Authorization: Bearer <access_token>`,无 token 返回 401已登录但缺少权限返回 403。
前端路由和按钮注意:
- `/system` 入口需要 `SYSTEM_ADMIN_CONSOLE_ACCESS`;子页面按 `SYSTEM_USER_MANAGE``SYSTEM_ROLE_MANAGE``SYSTEM_MENU_MANAGE``HOTEL_MANAGE` 展示。
- 如果用户只有酒店管理权限,进入 `/system` 时应跳到 `/system/hotels`,不要固定跳 `/system/users`
- 系统管理入口只代表可进入后台,不代表拥有所有子页面操作权限;按钮仍需按具体权限控制。
- 直接访问未知菜单路由时前端必须展示安全兜底页,不要让页面白屏。
接口分页和字段注意:
- 分页统一使用 `page_num``page_size`,响应统一是 `{ items, page: { page_num, page_size, total } }`
- 后端 `BIGINT` ID 返回字符串,前端不要转成 JavaScript number。
- 时间点字段是带 `Z` 的 UTC 时间,展示时按用户或酒店时区格式化。
- 写操作失败时前端应展示后端 `message``error_code`,尤其是启用第二家 `ACTIVE` 酒店、禁用最后一家 `ACTIVE` 酒店、修改内置角色、用户名重复等 409 场景。
用户管理注意:
- 新增用户必须传初始密码,后端只保存哈希。
- 用户启用 / 禁用通过 `PUT /api/admin/users/{userId}``user_status` 完成,没有单独 enable / disable 路径。
- 禁用用户会撤销该用户全部 ACTIVE session前端若正用该用户 token会在下一次 `/api/auth/me` 或业务请求时收到 401。
- 重置密码接口 `POST /api/admin/users/{userId}/password-reset` 只在本次响应返回 `temporary_password`前端不能写入日志、埋点、URL、localStorage 或错误上报。
- 用户授权酒店必须全部是 `ACTIVE` 酒店,默认酒店必须在授权酒店列表内。
角色、菜单、酒店注意:
- 内置角色 `system_builtin=true` 时只读,前端应禁用编辑和权限分配按钮;后端仍会返回 409 兜底。
- 新增自定义角色后,用户需要重新登录或刷新 `/api/auth/me` 才能拿到最新权限上下文。
- 新增菜单允许未知路由;未知路由可以保存,但正式开放可见前要确认前端页面已经存在或兜底页可接受。
- 新增酒店默认 `DISABLED``hotel_id` 新增后不能修改。
- 单酒店阶段只允许一家 `ACTIVE` 酒店,后端会拒绝启用第二家 `ACTIVE`,也会拒绝禁用最后一家 `ACTIVE`
- 系统管理写操作会写入 `platform_admin_audit_log`;审计接口 `GET /api/admin/audits` 可按 `target_type``target_id``action` 查询。
## 6. 不给前端直接调用的接口
- `POST /api/system/reservation/demo-data` 只用于 dev/test 联调造数,不是生产业务页面接口;访问口令不能进入前端代码。
@@ -221,5 +263,6 @@ run_label: 可选调试标签
## 7. 需要持续提醒的后置事项
- 普通任务切换订单接口继续后置。
- 用户 / 权限底座后端 CP1 已完成;前端登录页、动态菜单和管理后台仍后置
- 系统管理后台 V1 已完成后续若要做用户搜索更多筛选、批量操作、密码策略增强、MFA、登录设备管理应单独开需求
- 现有 Reservation / SourceMessage 业务接口的强制登录、强制权限和业务审计 actor 全量迁移仍后置。
- 真实 OPERA / OHIP 接入继续后置。

View File

@@ -19,6 +19,7 @@
- SuperAgent 查询上下文接口 1、2支持 HMAC 鉴权的订单上下文查询和对象详情查询。
- Debug EML 上传到 SuperAgent 调试链路:受控上传 `.eml`、转存阿里云 OSS、写入 SourceMessage Inbox、调用 SuperAgent Open API 并返回调试结果。
- 登录权限底座:支持用户名密码登录、登出、当前用户上下文、数据库 session token、可访问酒店、权限码和可见菜单。
- 系统管理后台 V1支持用户、角色权限、菜单、酒店和管理操作审计的受控维护接口与前端页面。
当前不要把以下能力当作已上线:
@@ -28,10 +29,9 @@
- 业务前端页面展示邮件原文。
- OHIP / OPERA 或其他业务系统真实写操作。
- 普通任务切换订单接口。
- 用户、角色、权限、菜单和酒店管理后台 CRUD。
- 现有业务接口强制登录和强制权限拦截。
- 业务审计 actor 全量迁移到当前登录用户。
- Debug EML 上传链路不属于生产普通业务页面能力,生产默认关闭;未接入正式用户权限前不要开放给普通用户。
- Debug EML 上传链路不属于生产普通业务页面能力,生产默认关闭;即使已有登录权限,也不要开放给普通用户。
## 2. 上线前必须确认
@@ -44,6 +44,9 @@
- 生产默认不保存 AgentBus raw frame 样本。
- AgentBus 实时链路开启前,已经确认 WebSocket URL、Token、Bot Address、外部消息幂等键和断线重连语义。
- S000/S999 特殊入口结果上线前,必须确认 `platform_hotel` 中存在且只存在一家 `ACTIVE` 酒店,并且已有 SourceMessage Inbox 数据的 `hotel_id` 与该酒店一致。
- 系统管理后台上线前,必须确认至少存在一个 `ACTIVE` 超级管理员账号,且该账号拥有 `SYSTEM_ADMIN_CONSOLE_ACCESS` 和各系统管理权限。
- 单酒店阶段上线前,必须确认 `platform_hotel` 中只有一家 `ACTIVE` 酒店;新增酒店可以存在但应保持 `DISABLED`
- 管理后台启用后,不要继续把手工改库作为常规运营方式;用户、角色、菜单和酒店变更应通过 `/api/admin/**` 并写入管理审计。
- 原文读取接口开启前,已经确认谁可以使用、在哪些场景使用、如何轮换访问 key。
- 日志采集、错误响应和监控面板都不会展示邮件正文、HTML、附件 URL、Token、Cookie、客户姓名、邮箱、电话或支付信息。
@@ -78,10 +81,10 @@
- 当前第一版只做可选 Bearer token 解析,现有 Reservation / SourceMessage 业务接口仍不强制登录。
- `/api/auth/me``/api/auth/logout` 需要 `Authorization: Bearer <access_token>`
- 初始管理员 bootstrap 只以“启用状态超级管理员”为阻断条件;如果测试库或生产库只剩禁用超级管理员,应通过环境变量恢复一个可登录超级管理员后再排查账号运营问题。
- 内置角色权限矩阵在启动时按代码同步,矩阵移除的旧权限关系会被清理;管理后台上线前不要手工给内置角色追加临时权限作为长期方案
- 内置角色权限矩阵在启动时按代码同步,矩阵移除的旧权限关系会被清理;管理后台 V1 也不允许修改内置角色权限,临时权限应通过自定义角色承载
- 普通用户默认酒店由后端写入逻辑和数据库唯一索引共同保持单默认V10 migration 会在建约束前把历史重复默认清理为每个用户保留 id 最大的一条。
- 单酒店阶段系统酒店由 `platform_hotel` 唯一 `ACTIVE` 酒店决定V12 migration 会通过唯一索引阻止第二家 `ACTIVE` 酒店。上线前如果已有多家 `ACTIVE` 酒店,必须先调整数据,否则迁移或运行时解析会失败。
- 管理后台还未上线时,不要把数据库手工改用户、角色、权限作为常规运营手段
- 管理后台 V1 写操作会记录 `platform_admin_audit_log`;重置密码只允许临时密码出现在本次响应中,不得进入日志、审计快照或前端持久化存储
### 3.3 SourceMessage
@@ -201,6 +204,11 @@
- `server/src/main/resources/db/migration/V9__create_identity_access_hotel_menu.sql`
- `server/src/main/resources/db/migration/V10__enforce_single_default_user_hotel.sql`
- `server/src/main/resources/db/migration/V12__enforce_single_active_platform_hotel.sql`
当前 M006 系统管理相关 migration
- `server/src/main/resources/db/migration/V15__create_platform_admin_audit_log.sql`
上线前确认:
@@ -213,6 +221,7 @@
- MySQL JDBC URL 建议明确 `serverTimezone=UTC`;部署容器和 JVM 也应使用 UTC或至少确认应用代码所有入库时间均通过 UTC 时钟生成。
- AgentBus 邮件来源时间、SuperAgent HMAC timestamp、本系统 `created_at` / `updated_at` 等时间点统一按 UTC 理解SourceMessage `received_at` 优先保存 AgentBus payload `received_at`,缺失时回退本系统接收时间,前端展示时再按用户或酒店时区格式化。
- 入住日期、离店日期、酒店营业日属于酒店本地业务日期,不应因为 UTC 换算而自动前后偏移。
- 详细时间设计、页面展示和按酒店本地日期筛选规则见 `docs/project/backend-time-design.md`
- 执行 V4 前,如果目标库已有 M002 试运行数据,必须先检查 ACTIVE 订单业务号重复和同订单任务队列序号重复。
- 执行 V5 / V6 前,如果目标库已有 M002 试运行数据,必须确认任务草稿、确认 payload 和 OPERA 模拟操作表允许从空数据开始补齐;不要手工伪造已确认 payload 或 attempt 历史。
- 执行 V11 前,如果目标库已有手工造数或历史隐藏订单方案,必须确认是否需要回填 `order_visibility`;默认值 `VISIBLE` 会让历史订单继续出现在订单列表。

View File

@@ -36,9 +36,9 @@
- `AUTH_BOOTSTRAP_ADMIN_USERNAME`
- `AUTH_BOOTSTRAP_ADMIN_PASSWORD`
- `AUTH_BOOTSTRAP_ADMIN_DISPLAY_NAME`
- 系统中已存在超级管理员后,不再使用环境变量覆盖管理员账号或密码。
- 系统中已存在启用状态的超级管理员后,不再使用环境变量覆盖管理员账号或密码;如果只存在禁用的超级管理员,环境变量仍可初始化一个可登录超级管理员,避免系统锁死
- 第一期开启登录和权限底座,但不强制拦截现有业务接口。
- 管理后台还没有做,必须明确后置
- 系统管理后台已由 M006 承接M003 仍只描述登录、权限、酒店和菜单运行时底座
## 3. 核心目标
@@ -225,6 +225,7 @@ platform.security
- 普通用户至少应有一个可访问酒店。
- 普通用户最多只能有一个默认酒店。
- 后端写入用户酒店授权时,如果将某个酒店设为默认酒店,会同时清理该用户其他酒店默认标记;数据库通过生成列和唯一索引兜底约束同一用户最多一条 `default_hotel=1`
- 超级管理员不需要为每个酒店插授权关系,默认可访问全部启用酒店。
- 业务接口收到 `hotel_id` 时,后续强制鉴权阶段必须校验当前用户是否可访问该酒店。
@@ -291,6 +292,7 @@ platform.security
- `RESERVATION_OPERATOR` 需要查看邮件原文和附件外链来处理任务,因此第一版包含 `SOURCE_MESSAGE_ORIGINAL_READ`
- `RESERVATION_VIEWER` 只读查看订单、任务、审计和来源消息安全摘要;不允许保存、确认、执行 OPERA 模拟,也不允许访问 Debug EML。
- `SYSTEM_DEBUG_EML_RUN` 第一版只授予 `SYSTEM_ADMIN`,避免普通业务用户触发 SuperAgent 调试链路。
- 启动初始化会按上表同步内置角色权限矩阵:矩阵中新增的权限会补齐,矩阵中移除的旧关系会清理。后续如果管理后台允许人工改内置角色,需要先重新确认“代码矩阵”和“后台配置”的优先级。
第一期可内置菜单,需和当前前端路由保持一致:
@@ -557,7 +559,7 @@ AUTH_TEST_SESSION_TTL_MINUTES=720
说明:
- 启动初始化发现系统中已经存在超级管理员后,不会继续用环境变量覆盖管理员用户名或密码。
- 启动初始化发现系统中已经存在启用状态的超级管理员后,不会继续用环境变量覆盖管理员用户名或密码;禁用状态超级管理员不阻止首次可登录管理员恢复初始化
- 数据库 `platform_user_session` 只保存 `token_hash`,不保存明文 token。
- 当前后端提供可选 Bearer token 解析,现有 Reservation / SourceMessage 业务接口第一版仍不强制登录。

View File

@@ -0,0 +1,653 @@
# M006 System Admin Management Console 系统管理后台 V1
## 文档信息
| 项目 | 内容 |
| --- | --- |
| 文档版本 | 0.4 |
| 日期 | 2026-07-10 |
| 状态 | V1 已按当前代码实现更新 |
| 适用范围 | 用户、角色、权限、菜单、酒店和用户酒店授权的后台维护 |
| 依赖前置 | 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`,由管理员在前端支持到位后再开放。
- 菜单只决定入口可见性,不替代后端接口权限。
### 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。
## 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}
POST /api/admin/menus
PUT /api/admin/menus/{menuId}
```
说明:菜单排序第一版通过单条菜单的 `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-4b管理操作审计页
目标:系统管理员可以查看管理后台写操作审计。
范围:
- 后端新增 `GET /api/admin/audits`
- 前端新增 `/system/audits`
- 审计页按对象类型、对象 ID 和操作类型筛选。
当前实现状态:已完成。
### CP4-5业务接口强制权限收口
目标:把管理后台和业务接口的权限模型闭环。
范围:
- Reservation 查询接口强制登录和酒店权限。
- Reservation 写接口强制具体操作权限。
- SourceMessage 原文读取迁移到 `SOURCE_MESSAGE_ORIGINAL_READ`
- 业务审计 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. 完成后运行相关后端和前端测试,不提交,先给我看改动。
```