# 后端提醒前端注意事项

## 1. 文档定位

本文记录后端侧提醒前端开发时必须注意的项目规范、业务规则、接口边界和安全要求。当前内容服务于 Reservation 任务详情、订单详情、任务列表、S10/S99 源邮件只读通知卡、旧 S000/S999 兼容展示、历史 Message Notification、系统管理后台等第一版页面。

## 2. 项目开发注意事项

- 前端只调用本项目后端接口，不直接调用 SuperAgent、AgentBus、OPERA、OHIP 或数据库。
- API 调用应统一放在前端 `src/services`，页面组件不要直接拼接后端 URL。
- 业务判断必须使用后端返回的稳定 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 长整型精度问题；如果当前接口仍返回数字，前端不要自行做数学运算。
- 接口字段发生变化前，需要先更新本目录沟通文档或对应需求文档。

## 3. 字段来源注意事项

- `docs/import/20260711/开发交付_P0冻结基线_2026-07-11/01_Adapter_Frontend/任务卡前端字段变更与路由说明_3.0_to_current.md` 是 0711 P0 前端 / Adapter 路由说明，覆盖 S10/S99、type-known manual review 和 fail-closed 口径；其中 Parent split / 42 路由口径已被 0712 P0.1 覆盖。
- `docs/project/requirements/M002-v3-p0.1-parent-group-routing-update.md` 是当前 Parent Group / Allotment 路由修订说明：前端应按 40 路由口径处理 Parent split。
- `docs/import/20260711/开发交付_P0冻结基线_2026-07-11/01_Adapter_Frontend/任务卡前端字段变更说明_3.0_to_当前版_2026-07-10.xlsx` 是当前前端展示 / 编辑白名单和三元组路由表。
- `docs/import/20260708/任务卡前端展示字段表 3.0.xlsx` 是历史前端展示 / 编辑白名单，已被 0711 P0 冻结基线承接。
- `docs/import/20260706/任务卡展示编辑矩阵.xlsx` 是后端校验、最终确认写入、OPERA 映射和展示条件的完整规则来源。
- 前端不要直接把整个旧 `ai_task_results[]` 或 V3 `message_events[]` 渲染成表单，只展示白名单允许的字段。
- 如果 3.0 白名单与旧矩阵冲突，应记录为前后端待确认问题，不由前端单方面放宽必填、枚举或校验规则。

## 4. 业务规则注意事项

- 任务详情页里，保存草稿和最终确认是两个独立动作，不能合并。
- 用户可以修改任务字段内容；最终确认后，后端使用 `confirmed_payload_json` 作为 OPERA 模拟输入来源。
- 同一订单下，前置任务未结束时，后续任务只能查看，不能编辑、确认或执行 OPERA 模拟操作。
- 任务状态 `FAILED` 第一版视为结束状态，不阻塞后续任务；但失败的 OPERA 操作不能跳过，必须展示失败原因并允许重试。
- M002 V3 新入口采用结构化 `S10/S99`：`S10` 表示未匹配当前支持的业务事件，`S99` 表示输入不足或无法形成业务素材包；旧 `S000/S999` 继续按历史数据兼容展示。
- `S10/S99` 后端会创建只读源邮件通知卡，任务列表可见，订单列表不可见；当前代码中的旧 `SOURCE_MESSAGE_ONLY` 任务仍按同一只读语义展示。
- 源邮件只读通知卡不允许编辑、确认、人工转换订单、执行 OPERA 或重试 OPERA；不参与订单任务执行队列，不阻塞其他任务，也不被其他任务阻塞。
- type-known manual review 已支持同卡复核解阻第一版：应展示为原业务任务卡的复核模式，不应统一展示成 Fallback。只有业务类型或 subtype 本身未知时才进入 Fallback。
- 复核场景下允许用户确认订单归属；当前第一版只允许确认当前任务所属订单，不等于开放普通任务任意切换订单。
- 历史 Message Notification 挂临时订单，只读展示，不参与订单任务执行队列，不阻塞其他任务，也不被其他任务阻塞。
- Fallback / manual_review 转为 New / Update / Cancel 时需要展示审计轨迹；登录权限底座已提供，具体业务审计 actor 迁移仍后置。
- 普通任务切换订单接口已确认后置，前端第一版不要把普通任务拖拽或切换订单做成可提交能力。

## 5. 当前前端可用接口注意事项

| 接口 | 用途 | 前端注意 |
| --- | --- | --- |
| `POST /api/auth/login` | 用户名密码登录 | 成功后返回 `access_token`、当前用户、可访问酒店、权限码和可见菜单；token 只放 `sessionStorage`，不要放 `localStorage`、URL、日志或错误上报。 |
| `GET /api/auth/me` | 恢复当前登录态 | 前端启动后带 `Authorization: Bearer <access_token>` 调用；401 时清理 token 并进入登录页。 |
| `POST /api/auth/logout` | 登出当前 session | 带 `Authorization: Bearer <access_token>`；成功后前端必须清理本地 token 和当前用户上下文。 |
| `GET /api/reservation/orders` | 查询订单列表 | 默认返回全部订单状态；`open_task_count` 排除 `COMPLETED` 和 `FAILED`；隐藏技术订单不返回，因此 S10/S99 和旧 S000/S999 不会在订单列表形成订单。 |
| `GET /api/reservation/tasks` | 查询任务列表 / 工作台 | 用 `can_process` 和 `readonly_reason_code` 控制入口按钮；列表不返回 AI 原始 payload、邮件正文或附件 URL；已返回来源邮件会话摘要字段，并支持 `order_status` 按任务所属订单状态筛选；旧 S000/S999 和新 S10/S99 都以 `task_type=SOURCE_MESSAGE_ONLY` 只读任务返回，列表已透出 `result_type`、`ai_task_type`、`route_code`、`system_process_category`。 |
| `GET /api/reservation/orders/{orderId}` | 查询订单详情与任务时间线 | `include_tasks=false` 可只取订单摘要；时间线按后端队列顺序返回，前端不要自行按创建时间重排；`tasks[]` 已返回来源邮件会话摘要字段和 V3 路由字段；隐藏技术订单详情不可作为普通订单页打开。 |
| `GET /api/reservation/tasks/{taskId}` | 查询任务详情 | 以返回的可处理状态和只读原因控制按钮，不只看任务状态；`fields[]` 已包含 P0 字段元数据；源邮件只读通知卡字段列表和 OPERA 操作列表为空；结构化 S10/S99 通过 `source_message_only_result.agent_assessment`、`notification`、`manual_review` 展示；普通业务任务可通过 `adapter_contract_errors[]` 和 `unhandled_intents[]` 查看同批次未建任务的诊断信息；type-known manual review 会返回顶层 `review_status`、`review_resolution` 和 `manual_review`。 |
| `PUT /api/reservation/tasks/{taskId}/draft` | 保存任务草稿 | 只保存草稿，不代表用户最终确认。 |
| `POST /api/reservation/tasks/{taskId}/confirm` | 最终确认任务 | 后端会做第一版字段校验，通过后进入 `READY`。 |
| `POST /api/reservation/tasks/{taskId}/manual-review-conversions` | Fallback 人工转换 | 只用于 manual_review / fallback，不用于普通任务切换订单。 |
| `POST /api/reservation/tasks/{taskId}/manual-review-resolutions` | type-known manual review 同卡复核解阻 | 只用于已知业务类型的 `result_type=manual_review` 任务；提交 `field_overrides[]` 和当前订单归属确认，通过后进入 `READY` 并生成两条 OPERA 模拟操作。 |
| `POST /api/reservation/tasks/{taskId}/opera-operations/{operationId}/execute` | 执行 OPERA 模拟操作 | 当前是模拟，不调用真实 OPERA。 |
| `POST /api/reservation/tasks/{taskId}/opera-operations/{operationId}/retry` | 重试失败 OPERA 模拟操作 | 重试会追加 attempt 历史，前端不要覆盖旧失败记录。 |
| `GET /api/reservation/tasks/{taskId}/audits` | 查询任务审计流水 | 用于展示人工确认、转换、模拟操作等轨迹。 |
| `GET /api/source-messages` | 查询来源消息安全摘要 | 列表不返回邮件正文、HTML、附件 URL 或原始 payload。 |
| `GET /api/source-messages/{id}` | 查询来源消息安全详情 | 只用于安全摘要详情。 |
| `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 本轮新增 / 修改接口说明

本轮后端新增或补齐了以下前端 P0 查询能力。前端后续开发时，应优先以本节作为接入口径。

| 接口 | 本轮变化 | 前端接入注意 |
| --- | --- | --- |
| `GET /api/reservation/orders` | 新增订单列表接口。 | `order_status` 不传时默认查询全部订单状态；`page_num` 从 1 开始；`page_size` 后端有最大值保护；`open_task_count` 排除 `COMPLETED` 和 `FAILED`；`next_processable_task_id` 为空表示当前没有可继续处理的任务。 |
| `GET /api/reservation/tasks` | 补齐来源邮件会话摘要字段，并新增 `order_status` 查询参数。 | `order_status` 按任务所属订单状态过滤，支持 `TEMPORARY`、`ACTIVE`、`ENDED`、`LOGIC_DELETED`；列表仍然只返回安全摘要，不返回正文、HTML、附件 URL 或 AI 原始 payload；点击邮件入口时使用 `source_message_id` 调会话详情。 |
| `GET /api/reservation/orders/{orderId}` | 补齐 `tasks[]` 每条任务的来源邮件会话摘要字段。 | `include_tasks=false` 可只取订单摘要；时间线顺序由后端按订单队列返回，前端不要自行按创建时间重排。`include_source_summary` 第一版不作为前端裁剪字段的强约束，前端暂不要依赖它减少返回字段。 |
| `GET /api/reservation/tasks/{taskId}` | 补齐顶层来源邮件字段，并扩展 `fields[]` 元数据。 | 顶层来源字段用于打开邮件会话；`fields[]` 中的 `result_type`、`task_type`、`task_subtype`、`default_value_source` 用于前端字段分组、调试和白名单对齐。 |
| `GET /api/source-messages/{sourceMessageId}/conversation` | 新增邮件会话详情接口，并补齐 `html_body_sanitized` / `html_render_mode`。 | 当前唯一推荐路径是这个接口；前端渲染邮件 HTML 时优先使用 `html_body_sanitized`；不要调用历史讨论过的 `/api/source-message-conversations/{externalConversationId}`。 |
| `POST /api/system/debug/eml-superagent-runs` | 新增 Debug EML 上传到 SuperAgent 调试接口，并补齐独立 Debug 外部消息 ID、原始 Message-ID 保留、安全 HTML 字段和入口通知识别。 | 只用于调试页面；请求为 multipart/form-data；必须传 `X-TH-Hotel-Debug-Upload-Key`，但该 key 不能写进前端源码、构建产物、URL、localStorage 或错误上报；SuperAgent 返回旧 S000/S999 或新 S10/S99 入口通知时都不应被前端视为 JSON 解析失败。 |

酒店上下文注意：Reservation 列表、订单详情、任务列表和 Debug EML 上传的 `hotel_id` 第一版都是可选参数。前端默认可以不传；后端会按当前登录用户酒店上下文或平台酒店表唯一 `ACTIVE` 酒店解析。如果前端传了当前选中酒店，后端会校验该酒店是否可访问。

### 5.2 登录权限接入注意

后端已提供 M003 登录和权限底座第一版接口：

```text
POST /api/auth/login
GET /api/auth/me
POST /api/auth/logout
```

前端注意：

- 登录成功后只把 `access_token` 保存到 `sessionStorage`；刷新同一浏览器会话可恢复，关闭浏览器后需要重新登录。
- 所有需要登录态的后端请求使用 `Authorization: Bearer <access_token>`。
- 当前后端第一版不强制拦截既有 Reservation / SourceMessage 业务接口；但是前端接入登录后应统一带上 Bearer token，方便后续审计 actor 和权限收口。
- `/api/auth/me` 返回 `user`、`default_hotel_id`、`hotels[]`、`permissions[]`、`menus[]`；菜单入口应优先使用 `menus[]`，不要继续硬编码订单列表、任务队列、Debug EML。
- `menus[]` 只包含可见菜单；订单详情、任务详情和邮件会话详情是隐藏详情路由，不会作为菜单项返回。
- `DEBUG_EML_SUPERAGENT` 菜单第一版只授予 `SYSTEM_ADMIN`；这只表示页面入口是否可见，不代表后端会把 `X-TH-Hotel-Debug-Upload-Key` 下发给前端。
- `user.id` 是字符串；前端不要把任何后端 ID 转成 JavaScript number。
- 登录失败统一显示用户名或密码错误，不要根据错误文案推断账号是否存在或是否禁用。
- 401 的 `AUTH_TOKEN_REQUIRED` / `AUTH_SESSION_INVALID` 应统一走清理 token、回登录页的逻辑。

### 5.3 来源邮件会话字段说明

任务列表、订单详情任务时间线、任务详情顶层会返回以下来源邮件字段：

| 字段 | 说明 | 前端使用方式 |
| --- | --- | --- |
| `source_message_id` | 本系统内部 SourceMessage Inbox ID。 | 打开邮件会话详情时作为路径参数传入 `/api/source-messages/{sourceMessageId}/conversation`。 |
| `source_subject` | 来源邮件主题安全摘要。 | 用于列表或任务详情标题旁展示，不代表完整邮件主题一定无敏感信息。 |
| `source_sender_summary` | 来源发件人展示值，当前不打码。 | 用于辅助用户判断邮件来源。 |
| `source_received_at` | 邮件来源接收时间，UTC；优先取 AgentBus payload `received_at`，缺失时使用本系统接收时间。 | 前端展示时按用户或酒店时区格式化。 |
| `external_conversation_id` | 外部邮件会话 ID。 | 仅用于展示或调试，不作为当前会话详情接口路径参数。 |
| `conversation_message_count` | 同一外部会话下的邮件数量。 | 用于提示用户打开的是整段会话，不是单封邮件。 |

### 5.4 邮件会话详情接入注意

- `GET /api/source-messages/{sourceMessageId}/conversation` 只接收路径参数 `sourceMessageId`；第一版不接收 `hotelId`、`includeBody`、`includeRelated`。
- Reservation 列表、任务列表和订单详情默认不需要前端传 `hotel_id`；如果前端已经接入酒店选择器，可以把当前选中酒店作为可选 `hotel_id` 传给后端。任务详情、任务写操作和邮件会话详情当前仍按对象 ID 定位，不接收该参数。
- 后端会根据 `sourceMessageId` 定位 `external_conversation_id`，并返回同一会话下全部邮件；如果来源消息没有外部会话 ID，会降级返回当前单封邮件。
- `messages[]` 按邮件来源接收时间正序返回，前端不要重新按创建时间或任务时间排序。
- 返回内容包含完整 `text_body`、`html_body`、`inline_images[]`、`attachments[]`、`related_orders[]`、`related_tasks[]`。
- `html_body` 是原始 HTML 兼容字段；`html_body_sanitized` 是后端第一版清洗结果，已移除脚本标签、事件属性和危险协议链接。前端生产展示必须优先使用 `html_body_sanitized`，并可用 `html_render_mode=SANITIZED_HTML` 判断渲染模式。
- 第一版仅处理 HTML 内容安全；`inline_images[]` 和 `attachments[]` 的 `externalUrl` 来自本系统 OSS 服务，暂不做额外拦截，但前端仍不得写入普通日志、错误上报、localStorage 或 URL query。
- 会话详情接口由后端内部写原文读取审计，前端不传 `X-TH-Hotel-Source-Original-Read-Key`。
- 会话详情外层字段主要是 snake_case，但媒体对象沿用原文读取接口字段，当前是 `mediaType`、`fileName`、`contentType`、`sizeBytes`、`externalUrl`、`externalMediaId` 这种 camelCase，前端类型定义需要单独处理。

### 5.5 订单列表接入注意

- `GET /api/reservation/orders` 默认返回全部订单状态，包括 `TEMPORARY`、`ACTIVE`、`ENDED`、`LOGIC_DELETED`。
- `keyword` 会匹配订单业务号、临时订单号、展示名、订单状态，也会匹配来源消息安全摘要命中的 SourceMessage ID；前端可以用邮件主题、外部消息 ID 或会话 ID 辅助查订单。
- `open_task_count` 只统计未关闭任务，排除 `COMPLETED` 和 `FAILED`。
- `next_processable_task_id` 是后端按同订单队列实时计算出的下一条可处理任务；前端可以用它做“继续处理”入口。
- `display_order_key` 是前端优先展示的订单业务号或临时订单号；`group_code` 和 `confirmation_number` 只有在当前订单业务号类型匹配时返回。
- 订单 ID、任务 ID、SourceMessage ID 在这些前端接口中按字符串返回，前端不要转换成 JavaScript number。
- 源邮件只读通知卡背后有隐藏技术订单用于满足后端任务外键，但订单列表不会返回该订单；任务列表中该类任务的 `display_order_key`、`temporary_order_no`、`group_code`、`confirmation_number` 可能为空，前端不要因此隐藏整条任务。
- 当前前端已按 `SOURCE_MESSAGE_ONLY` 展示旧 S000/S999；后端回调已支持结构化 `route_code=S10/S99`、`result_type=source_message_review_notification` 的新入口通知，并继续只在任务列表和任务详情提供只读查看入口；`INFORMATIONAL_MESSAGE` 仅作为历史 Message Notification 兼容路径保留。
- 任务列表里旧 `task_type=SOURCE_MESSAGE_ONLY`、`task_subtype=S000/S999` 或新 `task_subtype=S10/S99` 的记录只展示邮件来源和 SuperAgent 入口结果，不展示处理按钮。
- 任务详情里 `source_message_only_result` 仅对 `SOURCE_MESSAGE_ONLY` 返回，包含 `entry_result_code`、`entry_result_meaning`、`entry_result_description`、`entry_result_source_message_id`、`result_type`、`route_code`、`agent_assessment`、`notification`、`manual_review` 和 `raw_answer`；普通业务任务该字段为空。
- 任务列表、订单任务时间线和任务详情顶层已透出 `result_type`、`ai_task_type`、`route_code`、`system_process_category`。前端展示任务卡标题和标签时优先用这些稳定 code，不要只靠旧 `task_type` 判断。
- P0.1 后，Parent split 父事件不再是独立 Parent Cancel Booking 卡；前端应展示为 `Parent Group / Cancel Allotment / cancel_allotment_control_block`。`route_code=R08_CANCEL_ALLOTMENT_CONTROL_BLOCK_NORMAL` 是普通业务卡，`route_code=R08_CANCEL_ALLOTMENT_CONTROL_BLOCK_REVIEW` 是同卡人工复核业务卡，不应展示成 `adapter_contract_error`。`linked_parent_release_after_child_split` 只作为关系字段或详情信息，不作为任务 subtype 筛选项。
- `manual_review.reason_code=target_object_unclear` 时，前端需要在任务详情展示 `manual_review.visible_reason`、`missing_fields`、`blocking_points`、`conflicting_points`、`suggested_human_actions`、`evidence_to_check`，并展示 `context_used.parent_identity_candidates[]` 辅助确认 Parent Group identity。当前前端已兼容顶层 `context_used.parent_identity_candidates[]` 或 `manual_review.context_used.parent_identity_candidates[]`；若后端 DTO 不透出 candidates，页面会显示候选空态。
- P0.1 的“40 条路由”表示当前合法 route definition 数量；`route_code` 保持历史稳定且不连续重编号，因此 `R41_FALLBACK_BUSINESS_EVENT_REVIEW` 和 `R42_UNHANDLED_CURRENT_INTENT` 仍是合法展示 code。
- `adapter_contract_errors[]` 和 `unhandled_intents[]` 只在任务详情返回，表示同一 SuperAgent 入站批次中没有生成业务任务的诊断块；前端只读展示并提供来源邮件入口，不显示保存、确认、执行或重试按钮。

### 5.5.1 Type-known manual review 同卡复核解阻接入注意

- `result_type=manual_review` 且 `system_task_type` 不是 `MANUAL_REVIEW` 时，前端应在原业务任务卡上展示复核模式，不要跳到 Fallback 转换页面。
- 任务详情顶层返回 `review_status`。`PENDING` 表示等待用户补字段或确认订单归属；`RESOLVED` 表示同卡复核已解阻。
- 任务详情顶层 `manual_review` 返回 SuperAgent 原始复核原因、缺失字段、阻塞点和建议动作，前端只读展示；不要把它当成可编辑表单直接提交。
- 解阻接口使用 `POST /api/reservation/tasks/{taskId}/manual-review-resolutions`。请求体：

```json
{
  "confirmed_order_id": "20001",
  "reason": "确认 PMS 房型代码后解阻。",
  "field_overrides": [
    {
      "field_pointer": "/extracted_fields/room_items/0/pms_room_type_code",
      "value": "RM3"
    }
  ]
}
```

- `field_pointer` 必须是 RFC 6901 JSON Pointer，并且只能指向当前任务卡可编辑字段；后端会映射到矩阵 `field_path`。非法或只读字段会返回 `TASK_REVIEW_POINTER_INVALID`。
- 复核解阻也可以提交 `field_path`，支持 P0 主路径和旧扁平路径；如果同时提交 `field_pointer` 和 `field_path`，两者必须指向同一个字段。前端新页面优先用任务详情 `fields[].field_pointer`，无法方便处理 JSON Pointer 时可用 `fields[].field_path`。
- 0711 P0 的房型字段主路径已迁移到 `room_items[0]`。任务详情 `fields[]` 中，房量、房型原文、PMS 房型代码分别返回：
  - `field_path=extracted_fields.room_items.0.room_quantity`，`field_pointer=/extracted_fields/room_items/0/room_quantity`
  - `field_path=extracted_fields.room_items.0.room_type_raw`，`field_pointer=/extracted_fields/room_items/0/room_type_raw`
  - `field_path=extracted_fields.room_items.0.pms_room_type_code`，`field_pointer=/extracted_fields/room_items/0/pms_room_type_code`
- `legacy_field_path` 仅用于前端过渡显示旧扁平字段；新页面保存草稿、最终确认和复核解阻应优先提交 `field_pointer` 或 P0 主 `field_path`。
- 后端仍兼容旧提交 key：`extracted_fields.room_quantity`、`extracted_fields.room_type`、`extracted_fields.pms_room_type_code`；同卡复核也兼容旧 `field_path` / 旧 pointer。响应会归一化到 P0 主 `field_path`；当只提交 `field_path` 时，响应里的 `field_pointer` 使用 P0 主 JSON Pointer。`confirmed_payload.legacy_field_values` / `draft_payload.legacy_field_values` 只供旧前端回显，不作为新逻辑判断依据。
- 当前第一版只支持 `room_items[0]`；`/extracted_fields/room_items/1/...` 或更大下标不会自动落到 0。
- 同一次请求不能重复提交同一字段；重复 `field_pointer` 或重复映射到同一 `field_path` 会返回 `TASK_REVIEW_POINTER_DUPLICATE`。
- `confirmed_order_id` 第一版必须等于当前任务的 `order_id`；如果前端需要选择其他订单，仍属于后续“复核场景订单归属选择”细化，不要复用普通任务切换订单能力。
- 解阻成功后返回 `task_status=READY`、`review_status=RESOLVED`、`review_resolution.field_overrides[]`、`confirmed_payload` 和两条 `opera_operations[]`。前端应刷新任务详情并显示 OPERA 模拟操作入口。
- 解阻过程不改写 `ai_payload_json`；用户修正值保存在 `review_resolution`、`confirmed_payload.field_values` 和 `confirmed_payload.effective_payload` 中。`effective_payload` 是后端第一版嵌套结构，后续真实 OPERA 参数仍会在 OPERA 层重新组装。
- 历史 `field_contract_version=code-v1` 的任务卡如果已经有 `draft_payload_json` 或 `confirmed_payload_json`，后端迁移不会强行改成 `20260711-p0`。前端读取历史任务时，如果看到旧版本，应优先使用 `legacy_field_path` / `legacy_field_values` 做过渡回显；新保存或新确认后再以 P0 主路径为准。
- `review_resolution.resolved_at` 是 UTC `Z` 时间点。
- type-known manual review 不允许调用通用 `POST /api/reservation/tasks/{taskId}/confirm`；前端必须使用本节解阻接口，否则后端返回 `TASK_REVIEW_RESOLUTION_REQUIRED`。

### 5.6 前端联调演示数据 seed 接口

后端提供一个受控的 dev/test 演示数据入口，方便前端在空库或本地环境快速看到页面效果。

```text
POST /api/system/reservation/demo-data
Header: X-TH-Hotel-Demo-Data-Key: <本地演示数据访问口令>
Content-Type: application/json

{
  "run_label": "frontend-smoke"
}
```

启用方式：

- dev profile 默认开启；test 默认关闭，需要后端环境显式设置 `reservation.demo-data.enabled=true` 或环境变量 `RESERVATION_TEST_DEMO_DATA_ENABLED=true`。
- 必须配置 `reservation.demo-data.access-key`；dev 优先使用 `RESERVATION_DEV_DEMO_DATA_ACCESS_KEY`，test 优先使用 `RESERVATION_TEST_DEMO_DATA_ACCESS_KEY`，旧通用变量 `RESERVATION_DEMO_DATA_ACCESS_KEY` 仅作为兼容兜底。
- 该接口只用于 dev/test 联调，不允许放进生产普通页面，也不要把访问口令写进前端仓库、浏览器环境变量或构建产物。

返回内容：

- `demo_run_id`：本次 seed 的唯一关键词，可用于任务列表 / 订单列表搜索。
- `source_messages[]`：本次生成的 SourceMessage ID、外部消息 ID 和会话 ID。
- `orders[]`：本次生成的订单 ID、订单状态和展示键。
- `tasks[]`：本次生成的任务 ID、任务类型、任务 subtype 和任务状态。
- `entrypoints`：可直接访问的后端查询入口，包括任务列表、订单列表、队列订单详情、失败任务详情和邮件会话详情。

当前 seed 覆盖的页面效果：

- 同订单前置任务未完成，后续任务只读不可处理。
- 已完成 New Booking 任务和两条 OPERA 模拟成功记录。
- OPERA 模拟失败任务，可在任务详情看到失败 attempt 和重试入口。
- Fallback / manual_review 任务。
- 历史 Message Notification 只读任务；旧 S000/S999 和新 S10/S99 特殊只读任务可通过 SuperAgent 回调补充，前端 fixture 已补 S10 和同卡人工复核最小样例。
- 同一邮件会话下多封邮件、完整 HTML、附件外链和内联图片外链。

### 5.7 任务详情字段元数据接入注意

- `fields[]` 第一版服务于任务详情动态展示，字段来源与白名单规则后续以 `docs/import/20260711/开发交付_P0冻结基线_2026-07-11/01_Adapter_Frontend/任务卡前端字段变更说明_3.0_to_当前版_2026-07-10.xlsx` 和同目录路由说明为准；当前代码中仍有 20260708 白名单兼容口径。
- `result_type`、`task_type`、`task_subtype`、`default_value_source` 已透出给前端，用于和最新前端白名单对齐。
- 后端校验、最终确认写入、OPERA 映射和展示条件仍以 `docs/import/20260706/任务卡展示编辑矩阵.xlsx` 为完整规则来源。
- 前端保存草稿时不要自行按 `write_path` 重组 OPERA 参数；第一版按任务详情返回的字段和值提交即可，真实 OPERA 参数组装后续由后端 adapter / 转换层处理。
- 任务详情页控制按钮时以 `availability.editable`、`availability.confirmable`、`availability.executable`、`availability.read_only` 和 `availability.blocked` 为准；`can_process` 和 `readonly_reason_code` 只出现在任务列表 / 订单时间线摘要里。

### 5.8 Debug EML 上传接口接入注意

后端已提供 Debug 页面专用的 `.eml` 上传和 SuperAgent 调试入口：

```text
POST /api/system/debug/eml-superagent-runs
Header: X-TH-Hotel-Debug-Upload-Key: <调试访问口令>
Content-Type: multipart/form-data

file: .eml 文件
hotel_id: 可选；缺省使用后端系统酒店，显式传值时必须是当前可访问酒店
run_label: 可选调试标签
```

页面级对接细节请优先阅读 `docs/project/frontend-backend/debug-eml-page-integration-guide.md`。

前端注意：

- 该接口只用于 dev/test 调试页面，不是生产普通业务页面接口。
- `hotel_id` 第一版可不传；单酒店阶段后端按平台酒店表唯一 `ACTIVE` 酒店解析。只有在调试人员明确要覆盖当前酒店时，前端才传当前选中酒店。
- 接口会解析 `.eml`，上传原始邮件、内联图片和附件到本系统阿里云 OSS，替换 HTML 内 `cid:` 图片，再写入 SourceMessage Inbox。
- SourceMessage 来源 provider 固定为 `DEBUG_EML_UPLOAD`，用于和 AgentBus 入库邮件区分。
- 当前 AgentBus 实时收到邮件后自动推 SuperAgent 还没有做；这个接口是人工 Debug 上传链路，不代表实时生产链路。
- `external_message_id` 是后端生成的 Debug 独立 ID，格式类似 `debug-eml-run-{debugRunId}-{sha256前缀}`；原始邮件 `Message-ID` 保存在 `agentbus_like_payload.source.original_message_id`。
- 邮件会话解析支持 `References`、`In-Reply-To` 和 `Thread-Index`，但 Debug EML 的 `external_message_id` 不使用原始 `Message-ID` 做幂等。
- `agentbus_like_payload.schema_version` 固定为 `debug-eml-upload-v1`，前端可用于调试展示和版本判断。
- 第一版只返回 SuperAgent 结果，不创建订单、不创建任务、不调用任务结果通知接口。
- `X-TH-Hotel-Debug-Upload-Key` 只能由调试人员在受控环境手动提供，不能放入 `VITE_*`、源码、构建产物、URL query、localStorage、错误上报或普通日志。
- 返回的 `html_body_sanitized` 复用邮件会话详情的安全策略，前端展示 HTML 时优先使用；`html_body_with_oss_urls` 只作为调试原始处理结果，不建议直接渲染。
- 返回的 `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；旧 S000/S999 和新 S10/S99 都属于可解释入口结果，不是普通解析失败。

### 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 联调造数，不是生产业务页面接口；访问口令不能进入前端代码。
- `POST /api/system/debug/eml-superagent-runs` 只用于 dev/test Debug 页面，不是生产普通业务页面接口；访问口令不能进入前端代码或构建产物。
- `POST /api/integrations/superagent/task-results` 是 SuperAgent 到后端的服务到服务入站接口。
- `POST /api/ai-query/v1/case-context` 和 `POST /api/ai-query/v1/object-detail` 是 SuperAgent 查询上下文接口，不是前端页面接口。
- `GET /api/source-message-conversations/{externalConversationId}` 是历史讨论过的候选路径，当前后端不提供，前端不要接入。
- AgentBus probe、fixture、replay、system 类接口不应放到普通业务前端页面。

## 7. 需要持续提醒的后置事项

- 普通任务切换订单接口继续后置。
- M002 V3 的结构化 `S10/S99` 入站、40 条 P0.1 路由枚举 / 稳定配置、`UNHANDLED_CURRENT_INTENT`、`adapter_contract_error` transition 最小落库、任务列表 / 订单时间线 / 任务详情 V3 路由字段和只读诊断块透出、type-known manual review 同卡解阻第一版、typed infrastructure error、P0 fixtures 回归基线和 Parent Group / Cancel Allotment 路由修订均已完成。
- 系统管理后台 V1 已完成；后续若要做用户搜索更多筛选、批量操作、密码策略增强、MFA、登录设备管理，应单独开需求。
- 现有 Reservation / SourceMessage 业务接口的强制登录、强制权限和业务审计 actor 全量迁移仍后置。
- 真实 OPERA / OHIP 接入继续后置。
