# 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 `。 - 根据 `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 `。 - 无 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_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. 完成后运行相关后端和前端测试,不提交,先给我看改动。 ```