# 后端提醒前端注意事项 ## 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 长整型精度问题;如果当前接口仍返回数字,前端不要自行做数学运算。 - 接口字段发生变化前,需要先更新本目录沟通文档或对应需求文档。 - 前端接口新增或字段变更时,后端需同步更新 `docs/project/security-access-control-boundary.md`,前端也应按该文档区分普通业务、系统管理、Debug 和第三方接口。 - 前端页面不得把 Debug、Demo、Replay、Probe 等系统调试接口当成普通用户能力;这类入口需要环境开关和专门权限。 ## 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/20260712/前端字段控件修改说明_给信息系统小伙伴Codex_2026-07-12.md` 是前端字段控件、人工复核编辑和只读证据的外部输入资料;本项目开发以 `docs/project/requirements/M002-task-field-control-contract-v1.md` 的落地口径为准。 - `docs/project/requirements/M002-task-field-control-contract-v1.md` 是后端已扩展 `fields[]` 和前端后续控件渲染的字段控件契约 V1。 - `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` 继续按历史数据兼容展示。 - V3 S10/S99 会创建旧 `SOURCE_MESSAGE_ONLY` 只读源邮件通知卡,任务列表可见,订单列表不可见;V4 S10/S99 已改为独立来源通知模型,并通过 V4 工作台 / 来源通知详情接口展示。 - 旧 V3 源邮件只读通知卡不允许编辑、确认、人工转换订单、执行 OPERA 或重试 OPERA;V4 S10/S99 来源通知支持确认已读 / 已处理 ack,但不支持编辑、复核、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 ` 调用;401 时清理 token 并进入登录页。 | | `POST /api/auth/logout` | 登出当前 session | 带 `Authorization: Bearer `;成功后前端必须清理本地 token 和当前用户上下文。 | | `GET /api/reservation/orders` | 查询订单列表 | 必须带 `Authorization: Bearer `,需要 `RESERVATION_ORDER_READ`;默认返回全部订单状态;按后端维护的订单最近业务活动时间倒序,当前落库字段为 `workflow_reservation_order.latest_activity_at`,前端不要自行重排;`open_work_item_count` 是订单列表统一待处理展示数量,前端订单列表已用它展示待处理数,第一版等于 V4 未完成订单任务数;`open_task_count` 仍保留为旧 V2/V3 诊断计数,排除 `COMPLETED` 和 `FAILED`;已补齐 V4 继续处理入口字段,前端有 `next_v4_order_task_id` 时优先跳 V4 订单任务详情;隐藏技术订单不返回,因此 S10/S99 和旧 S000/S999 不会在订单列表形成订单。 | | `GET /api/reservation/tasks` | 查询任务列表 / 工作台 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`;未传 `order_id` 时按来源消息接收时间倒序,传 `order_id` 时按同订单队列顺序正序;用 `can_process` 和 `readonly_reason_code` 控制入口按钮;列表不返回 AI 原始 payload、邮件正文或附件 URL;已返回来源邮件会话摘要字段,并支持 `order_status` 按任务所属订单状态筛选;旧 S000/S999 和 V3 S10/S99 以 `task_type=SOURCE_MESSAGE_ONLY` 只读任务返回,列表已透出 `result_type`、`ai_task_type`、`route_code`、`system_process_category`。V4 S10/S99 不再进入该旧任务表,应从 V4 工作台来源通知接口展示。 | | `GET /api/reservation/workbench-items` | 查询 V4 工作台统一列表 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`;返回 V4 业务订单任务和 S10/S99 来源通知混排摘要;支持 `hotel_id`、`item_type`、`keyword`、`page_num`、`page_size`;默认按 `source_received_at` 倒序,同一来源时间下按 `updated_at`、`created_at`、数字 `target_id` 倒序;列表不返回邮件正文、附件 URL、`ai_payload_json` 或来源通知原始 payload。 | | `GET /api/reservation/order-tasks` | 查询 V4 业务订单任务列表 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`;只返回 V4 业务订单任务,不包含 S10/S99 来源通知;支持 `hotel_id`、`order_id`、`order_task_status`、`card_status`、`keyword`、`page_num`、`page_size`;`order_task_status` 非 `OPEN` / `COMPLETED` 返回 400,`card_status` 非 V4 卡状态返回 400;`card_status` 只筛业务 / 可处理卡,固定来源邮件展示卡不参与筛选。 | | `GET /api/reservation/order-tasks/{orderTaskId}` | 查询 V4 订单任务详情 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`,后端按订单任务实际酒店校验访问权;返回 `order_task`、`source_message_summary`、`source_message_card`、`basic_information_card`、`business_cards[]`、`card_counts`、`adapter_contract_errors[]` 和 `availability`;来源摘要按酒店过滤,邮件正文和附件仍走 SourceMessage 会话接口。CP8 起每张 V4 任务卡返回 `fields[]`,前端应以该字段白名单渲染可编辑控件。 | | `GET /api/reservation/order-tasks/{orderTaskId}/audits` | 查询 V4 订单任务审计流水 | 必须带 Bearer token,需要 `RESERVATION_AUDIT_READ`,后端按订单任务实际酒店校验访问权;返回 `order_task_id` 和 `items[]`。`items[]` 用于展示 V4 卡片确认、复核解阻和订单归属确认轨迹,只包含脱敏后的审计摘要,不包含邮件正文、HTML、附件 URL、AI 原始 payload、token 或 secret。 | | `GET /api/reservation/source-notifications/{notificationId}` | 查询 V4 S10/S99 来源通知详情 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`,后端按来源通知实际酒店校验访问权;只返回通知摘要、来源邮件通知卡、会话摘要和 `availability`;不返回订单任务、业务卡、邮件正文、附件 URL 或原始 AI payload。 | | `GET /api/reservation/source-notifications/{notificationId}/audits` | 查询 V4 S10/S99 来源通知审计流水 | 必须带 Bearer token,需要 `RESERVATION_AUDIT_READ`,后端按来源通知实际酒店校验访问权;返回 `notification_id` 和 `items[]`。`items[]` 第一版用于展示来源通知 ack 记录,只包含脱敏后的审计摘要。 | | `GET /api/reservation/lookups/accounts` | 查询 V4 Account 目录 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`,支持 `hotel_id`、`keyword`、`page_num`、`page_size`;返回统一 wrapper:`hotel_id`、`catalog_type=ACCOUNT`、`catalog_source`、`catalog_version`、`stale`、`items[]`、`page`、`warnings[]`。`keyword` 匹配目录 code 时后端按稳定 code 大写归一化处理,前端可传小写;匹配显示名仍按数据库比较规则。`keyword` 无匹配时 `items=[]` / `page.total=0`,但只要酒店未过滤目录存在,`catalog_source/catalog_version` 仍保持真实目录元数据,不代表目录未初始化。前端在 `options_source=reservation_v4_account_catalog` 时调用,只提交 `items[].code`,Market / Source 以后端确认派生结果为准。 | | `GET /api/reservation/lookups/room-types` | 查询 V4 Room Type 目录 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`,支持 `hotel_id`、`keyword`、`page_num`、`page_size`;第一版只返回当前酒店 `ACTIVE` 房型目录,不接日期过滤,不代表 PMS 全量房型。`keyword` 匹配房型 code 时后端按稳定 code 大写归一化处理,前端可传小写;匹配显示名仍按数据库比较规则。`keyword` 无匹配时按空选项处理,不要当作目录不可用。前端在 `options_source=reservation_v4_room_type_catalog` 时调用。 | | `GET /api/reservation/lookups/rate-codes` | 查询 V4 Rate Code 目录 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`,支持 `hotel_id`、`keyword`、`page_num`、`page_size`;第一版只返回当前酒店 `ACTIVE` Rate Code,`pricing_available=false` 表示后端未接真实价格,不要据此展示价格。`keyword` 匹配 Rate Code 时后端按稳定 code 大写归一化处理,前端可传小写;匹配显示名仍按数据库比较规则。`keyword` 无匹配时按空选项处理,不要当作目录不可用。前端在 `options_source=reservation_v4_rate_code_catalog` 时调用。 | | `GET /api/admin/reservation/catalogs/accounts` | 管理后台 Account 目录列表 | 必须带 Bearer token,需要 `RESERVATION_CATALOG_MANAGE` 和目标酒店访问权;支持 `hotel_id`、`keyword`、`status=ACTIVE/DISABLED`、`page_num`、`page_size`;`keyword` 搜索 code 时大小写不敏感,显示名仍按数据库比较规则;返回 `items[] + page`,包含 `id`、`account_code`、`account_name`、`market_code`、`source_code`、`status`、`catalog_source`、`external_account_id`、`catalog_version`、`metadata_json`、`version`、`created_at`、`updated_at`。 | | `POST /api/admin/reservation/catalogs/accounts` | 管理后台新增 Account 目录 | 必须带 `RESERVATION_CATALOG_MANAGE`;请求 `hotel_id`、`account_code`、`account_name`、`market_code`、`source_code`,可选 `external_account_id`、`catalog_version`、`metadata_json`;后端校验 Market / Source 当前酒店 ACTIVE,新增后默认 `ACTIVE`、`catalog_source=SYSTEM_MANAGED`,写管理审计。 | | `PUT /api/admin/reservation/catalogs/accounts/{accountId}/status` | 管理后台启用 / 停用 Account | 必须带 `RESERVATION_CATALOG_MANAGE`;请求 `{"status":"ACTIVE"}` 或 `{"status":"DISABLED"}`;按记录所属酒店校验访问权;停用后普通 Account lookup 不再返回;如果提交的状态和当前状态一致,后端幂等返回当前记录,不新增管理审计。 | | `GET /api/admin/reservation/catalogs/room-types` / `GET /api/admin/reservation/catalogs/rate-codes` | 管理后台 Room Type / Rate Code 目录列表 | 必须带 `RESERVATION_CATALOG_MANAGE`;支持 `hotel_id`、`keyword`、`status`、`page_num`、`page_size`;`keyword` 搜索 code 时大小写不敏感,显示名仍按数据库比较规则;返回 `items[] + page`,包含 `id`、`catalog_type`、`code`、`display_name`、`status`、`catalog_source`、`external_id`、`sort_order`、`catalog_version`、`metadata_json`、`version`、`created_at`、`updated_at`。 | | `POST /api/admin/reservation/catalogs/room-types` / `POST /api/admin/reservation/catalogs/rate-codes` | 管理后台新增 Room Type / Rate Code | 必须带 `RESERVATION_CATALOG_MANAGE`;请求 `hotel_id`、`code`、`display_name`,可选 `external_id`、`sort_order`、`catalog_version`、`metadata_json`;新增后默认 `ACTIVE`、`catalog_source=SYSTEM_MANAGED`,写管理审计。 | | `PUT /api/admin/reservation/catalogs/room-types/{catalogId}/status` / `PUT /api/admin/reservation/catalogs/rate-codes/{catalogId}/status` | 管理后台启用 / 停用 Room Type / Rate Code | 必须带 `RESERVATION_CATALOG_MANAGE`;请求 `{"status":"ACTIVE"}` 或 `{"status":"DISABLED"}`;按记录所属酒店校验;停用后对应普通 lookup 不再返回;如果提交的状态和当前状态一致,后端幂等返回当前记录,不新增管理审计。 | 前端已在系统设置下新增 `/system/reservation-catalogs` 消费上述目录管理接口。页面入口要求 `RESERVATION_CATALOG_MANAGE`,列表过滤直接传 `hotel_id`、`keyword`、`status`、`page_num`、`page_size`;Account 新增第一版固定提交 `market_code=LEISURE`、`source_code=TRAVEL_AGENT`;状态重复提交按成功提示处理,不额外弹失败。 | `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm` | 确认 V4 订单任务卡 | 必须带 Bearer token,需要 `RESERVATION_TASK_CONFIRM`,请求 JSON 带 `version`,可选 `confirmed_payload`;Basic Information 必须先确认,业务卡第一版不强制逐张顺序确认;前端只提交当前卡 `fields[]` 中可编辑字段,后端以展示快照为基准合并,未开放字段会被忽略;确认前会按当前酒店数据库目录校验 Account / Room Type / Rate Code,嵌套字段错误会返回如 `business_fields.after.room_items.0.room_type_code` 的路径,失败返回 `V4_FIELD_VALIDATION_FAILED`;确认后卡片 `CONFIRMED`、写 `confirmed_payload_json/confirmed_at/confirmed_by` 并锁定,重复确认返回错误;成功返回刷新后的订单任务详情。 | | `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution` | V4 复核解阻并确认卡片 | 必须带 Bearer token,需要 `RESERVATION_MANUAL_REVIEW_RESOLVE`,仅用于 `card_status=REVIEW_REQUIRED`;请求 JSON 带 `version`,可选 `field_overrides[]` 和 `reason`;订单任务归属未解决时 `confirmed_order_id` 必填,且必须是当前酒店下真实可见订单;目录错误字段可按 `validation_errors_json` / `fields[].validation_errors` 指向的 pointer 修正;成功后卡片 `CONFIRMED`、`review_status=RESOLVED`,写 `review_resolution_json/confirmed_payload_json/confirmed_at/confirmed_by` 并返回刷新后的订单任务详情。 | | `POST /api/reservation/source-notifications/{notificationId}/ack` | 确认 V4 S10/S99 来源通知已读 / 已处理 | 必须带 Bearer token,需要 `RESERVATION_TASK_CONFIRM`,请求 JSON 带 `version`;仅允许 `route_code=S10/S99`;确认后 `notification_status=ACKED`,写 `ack_by/ack_at`,成功返回刷新后的来源通知详情;重复 ack 返回当前已确认状态且不新增审计;该动作不创建订单、不参与订单阻塞。 | | `GET /api/reservation/order-tasks/{orderTaskId}/audits` / `GET /api/reservation/source-notifications/{notificationId}/audits` | 查询 V4 业务审计展示数据 | 必须带 Bearer token,需要 `RESERVATION_AUDIT_READ`;前端可在 V4 订单任务详情和 S10/S99 来源通知详情的“审计时间线”中调用。响应沿用旧审计行结构:`audit_id`、`actor_type`、`actor_id`、`action`、`reason`、`before_snapshot`、`after_snapshot`、`occurred_at`。快照已由后端脱敏,前端仍不要把未知 URL-like 字符串当附件或正文直渲。 | | `GET /api/reservation/orders/{orderId}` | 查询订单详情与任务时间线 | 必须带 Bearer token,需要 `RESERVATION_ORDER_READ`,后端按订单所属酒店做访问校验;`include_tasks=false` 可只取轻量摘要,此时旧 `tasks[]`、V4 `v4_order_tasks[]` 和 `related_source_messages[]` 都为空数组,`order_overview` 为空快照,`next_v4_action.action_type=NONE`;旧 `tasks[]` 按后端队列顺序返回,前端不要自行按创建时间重排;V4 `v4_order_tasks[]` 按同订单 V4 订单任务来源时间正序返回;隐藏技术订单详情不可作为普通订单页打开。 | | `GET /api/reservation/tasks/{taskId}` | 查询任务详情 | 必须带 Bearer token,需要 `RESERVATION_TASK_READ`,后端按任务所属酒店做访问校验;以返回的可处理状态和只读原因控制按钮,不只看任务状态;`fields[]` 已包含 P0 字段元数据;旧源邮件只读通知卡字段列表和 OPERA 操作列表为空;V3 结构化 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` | 查询任务审计流水 | 必须带 Bearer token,需要 `RESERVATION_AUDIT_READ`,后端按任务所属酒店做访问校验;用于展示人工确认、转换、模拟操作等轨迹。 | | `GET /api/source-messages` | 查询来源消息安全摘要 | 必须带 Bearer token,需要 `SOURCE_MESSAGE_READ`;列表不返回邮件正文、HTML、附件 URL 或原始 payload;查询参数以 `hotel_id`、`external_message_id`、`external_conversation_id`、`page_num`、`page_size` 为准,后端暂兼容早期 camelCase 参数。 | | `GET /api/source-messages/{id}` | 查询来源消息安全详情 | 必须带 Bearer token,需要 `SOURCE_MESSAGE_READ`,后端按消息所属酒店做访问校验;只用于安全摘要详情。 | | `GET /api/source-messages/{id}/original` | 读取来源消息原文 | 必须带 Bearer token,需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;后端按消息所属酒店做访问校验;返回 HTML 时前端展示前必须 sanitize。 | | `GET /api/source-messages/{sourceMessageId}/conversation` | 读取邮件会话详情 | 必须带 Bearer token,需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;返回同一外部会话全部邮件的完整 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。 | | `POST /api/reservation/rooming-lists/generations` | 生成 Rooming List Excel | 必须带 Bearer token,需要 `RESERVATION_ROOMING_LIST_GENERATE`;请求为 multipart/form-data,成功后直接返回 `.xlsx` 文件流,前端按 Blob 下载处理。 | ### 5.1 本轮新增 / 修改接口说明 本轮后端新增或补齐了以下前端 P0 查询能力。前端后续开发时,应优先以本节作为接入口径。 | 接口 | 本轮变化 | 前端接入注意 | | --- | --- | --- | | `GET /api/reservation/orders` | 补齐订单列表 V4 继续处理入口字段,并新增统一 open count 字段;前端展示已接入。 | `order_status` 不传时默认查询全部订单状态;`page_num` 从 1 开始;`page_size` 后端有最大值保护;`open_work_item_count` 是订单列表展示用统一待处理数量,开发阶段不考虑旧数据,第一版直接等于 `v4_open_order_task_count`;前端展示待处理数量时只读该字段,不自行计算旧任务数和 V4 数,也不使用旧 `open_task_count` 作为展示数量;旧 `open_task_count` 和 `next_processable_task_id` 继续保留用于 V2/V3 兼容与排查。V4 新增 `next_v4_order_task_id`、`next_v4_action_card_id`、`next_v4_action_type`、`next_v4_action_status`、`v4_open_order_task_count`;前端“继续处理”已按优先级实现:存在 `next_v4_order_task_id` 时跳 `/reservation/order-tasks/{next_v4_order_task_id}`,否则回退旧 `/reservation/tasks/{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[]` 来源邮件会话摘要字段,并新增 V4 总览和 `v4_order_tasks[]` 订单任务时间线;前端订单详情总览页已接入。 | `include_tasks=false` 可只取轻量摘要,此时 `tasks[]`、`v4_order_tasks[]` 和 `related_source_messages[]` 都为空,`order_overview` 为空快照,`next_v4_action.action_type=NONE`;旧 `tasks[]` 顺序由后端按订单队列返回,V4 `v4_order_tasks[]` 按 `source_received_at`、`source_message_id`、`order_context_index`、`created_at`、数字 ID 正序返回;前端不要自行重排。订单详情页只展示 V4 确认快照、下一步入口、关联来源消息和任务卡安全摘要,不在该页确认、复核或编辑任务卡,也不展示 payload、邮件正文或附件 URL。`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 解析失败。 | | `GET /api/reservation/workbench-items` / `/api/reservation/order-tasks/**` / `/api/reservation/source-notifications/{notificationId}` | 新增 M002 V4 CP5 查询接口,并在 CP6 打开卡片确认 / 来源通知 ack availability。 | 这是 V4 新模型前端主入口;前端应按每张卡或通知返回的 `availability.confirmable`、`availability.ackable`、`readonly_reason_code` 控制按钮。工作台条目已返回 `created_at` / `updated_at` 作为排序兜底和调试字段;前端不要继续从旧 `/api/reservation/tasks/**` 推断 V4 多卡详情。 | | `GET /api/reservation/lookups/accounts` / `/room-types` / `/rate-codes` | 新增 M002 V4 CP11 数据库目录 lookup。 | 前端从任务详情 `fields[].options_source` 选择调用哪个 lookup;只提交返回项的 `code`,不要提交显示名、派生 Market / Source、目录完整对象或前端自造 code。`warnings[]` 非空时可做非阻塞提示。 | | `GET/POST/PUT /api/admin/reservation/catalogs/...` | 新增 M002 V4 目录管理后台 CP1 后端接口。 | 仅供系统设置 / 管理后台页面使用,必须带 `RESERVATION_CATALOG_MANAGE`;支持 Account、Room Type、Rate Code 列表、新增、启用 / 停用;停用后普通 lookup 不再返回该目录项。 | V4 CP5 分页注意:`page_num` 从 1 开始,后端第一版安全上限为 100;`page_size` 最大 100。超出上限时后端按上限处理并在 `page.page_num` / `page.page_size` 中返回实际使用值。`order_task_status` 和 `card_status` 是稳定枚举查询参数,前端不要传中文文案或自造状态码。 酒店上下文注意:Reservation 列表、订单详情、任务列表和 Debug EML 上传的 `hotel_id` 第一版都是可选参数。对已收口的 Reservation / SourceMessage 只读接口,前端必须先登录并带 Bearer token;不传 `hotel_id` 时后端按当前登录用户默认酒店或对象所属酒店校验,传了当前选中酒店时后端会校验该酒店是否可访问。Debug EML 仍按调试入口规则受控,不属于本轮登录权限收口范围。 ### 5.2 登录权限接入注意 后端已提供 M003 登录和权限底座第一版接口: ```text POST /api/auth/login GET /api/auth/me POST /api/auth/logout ``` 前端注意: - 登录成功后只把 `access_token` 保存到 `sessionStorage`;刷新同一浏览器会话可恢复,关闭浏览器后需要重新登录。 - 所有需要登录态的后端请求使用 `Authorization: Bearer `。 - 当前后端已强制拦截第一批 Reservation / SourceMessage 只读接口:任务列表、订单列表、订单详情、任务详情、任务审计列表、SourceMessage 摘要列表和摘要详情。调用这些接口必须带 Bearer token。 - 第一批只读接口权限码分别是:`RESERVATION_TASK_READ`、`RESERVATION_ORDER_READ`、`RESERVATION_AUDIT_READ`、`SOURCE_MESSAGE_READ`。前端菜单、按钮和路由守卫应使用 `/api/auth/me` 返回的 `permissions[]` 与 `menus[]`。 - 后端会按当前登录用户的可访问酒店集合做隔离;显式传 `hotel_id` 时会校验该酒店是否可访问,按 `orderId`、`taskId`、`sourceMessageId` 定位的详情接口会反查对象实际所属酒店并校验访问权。 - 邮件原文 / conversation 完整正文接口已完成权限收口,必须带 Bearer token 且同时需要 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;Reservation 写操作、Debug / Demo / Replay / Probe 等接口仍按 `../security-access-control-boundary.md` 的分阶段计划继续收口。 - `/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、回登录页的逻辑。 - 403 的 `FRONTEND_PERMISSION_DENIED` 表示当前用户没有对应业务权限;`HOTEL_ACCESS_DENIED` 表示用户无权访问目标酒店或对象所属酒店,前端应展示无权限状态,不要重试或静默降级为 404。 ### 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` | 同一外部会话下的邮件数量。 | 用于提示用户打开的是整段会话,不是单封邮件。 | 订单详情已经补齐并完成前端接入 V4 订单页总览字段:`order_overview`、`next_v4_action`、`related_source_messages[]` 和增强后的 `v4_order_tasks[].cards[]`。订单详情页定位为订单视角总览,不要在订单详情页直接编辑、确认或复核任务卡;点击 `next_v4_action.order_task_id` 或时间线 `order_task_id` 后进入 `/reservation/order-tasks/{orderTaskId}` 对应的 V4 订单任务详情页处理。 `order_overview` 只从已确认 V4 卡片派生:Basic Information 未确认时 Account / Market / Source 为空;Room Information 未确认时日期、Rate Code、房型房量为空。前端不要把未确认卡片 display payload 反推成订单事实。 订单详情新增的 V4 `v4_order_tasks[]` 每项只返回订单任务安全摘要: | 字段 | 说明 | 前端使用方式 | | --- | --- | --- | | `order_task_id` | V4 订单任务 ID,字符串。 | 点击 V4 时间线项时跳转前端路由 `/reservation/order-tasks/{orderTaskId}`,该页面再调用 `GET /api/reservation/order-tasks/{orderTaskId}`。 | | `order_ref` | SuperAgent V4 回调包内订单引用。 | 用于区分同一邮件里的多个订单上下文,不等同 PMS 永久订单号。 | | `order_task_status` | V4 订单任务状态,当前为 `OPEN` / `COMPLETED`。 | 展示处理状态,不要用于替代卡片级 `availability`。 | | `card_counts` | V4 卡片数量摘要。 | 展示待确认、待复核和已确认规模。 | | `cards[]` | V4 任务卡安全摘要,只包含卡片 ID、类型、状态、复核状态、确认人、确认时间和最近活动时间。 | 用于订单页时间线和状态 chips;业务字段仍去 V4 订单任务详情读取。 | | `source_message_summary` | 来源邮件安全摘要。 | 不包含正文、HTML、附件 URL 或 AI 原始 payload;需要看原文时继续调用邮件会话详情接口。 | | `source_received_at` / `created_at` / `updated_at` / `latest_activity_at` | UTC 时间点。 | `latest_activity_at` 是 V4 订单任务及其卡片更新时间的最大值,可用于展示最近动作时间。 | ### 5.4 邮件会话详情接入注意 - `GET /api/source-messages/{sourceMessageId}/conversation` 只接收路径参数 `sourceMessageId`;第一版不接收 `hotelId`、`includeBody`、`includeRelated`;请求必须带 `Authorization: Bearer `,且当前用户需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`。 - 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。 - 会话详情接口由后端内部写原文读取审计,actor 使用当前登录用户稳定 ID;前端不传 `X-TH-Hotel-Source-Original-Read-Key`、`X-TH-Hotel-Actor` 或 `X-TH-Hotel-Access-Scene`。 - 会话详情外层字段主要是 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_work_item_count` 是订单列表统一展示数量,第一版按 V4 未完成订单任务计算,即等于 `v4_open_order_task_count`;前端展示待处理数量时只使用它,不回退旧 `open_task_count`,也不要自行把旧任务数和 V4 数相加。 - `open_task_count` 只统计旧 V2/V3 未关闭任务,排除 `COMPLETED` 和 `FAILED`,当前保留用于兼容和问题排查。 - `next_processable_task_id` 是后端按同订单队列实时计算出的下一条可处理任务;前端可以用它做“继续处理”入口。 - V4 订单任务入口字段由后端实时派生:`v4_open_order_task_count` 统计当前订单下 `order_task_status!=COMPLETED` 的 V4 订单任务;`next_v4_order_task_id` 是同订单第一条仍需用户处理的 V4 订单任务;`next_v4_action_card_id` 是该订单任务下第一张待处理卡;`next_v4_action_type` 取 `CONFIRM` / `REVIEW` / `NONE`;`next_v4_action_status` 取 `PENDING_CONFIRM` / `REVIEW_REQUIRED` 或空。 - V4 派生规则:Basic Information 必须优先于业务卡;Basic 已确认后,业务卡中 `REVIEW_REQUIRED` 优先于普通 `PENDING_CONFIRM`;`COMPLETED` 的 V4 订单任务不计入 open;S10/S99 来源通知不挂订单,不进入这些订单列表字段。 - 前端订单列表“继续处理”已按该优先级接入:有 `next_v4_order_task_id` 时跳 V4 订单任务详情;否则有旧 `next_processable_task_id` 时跳旧任务详情;`next_v4_action_type=NONE` 且旧字段为空时展示无待处理状态,查看详情仍固定进入订单详情。 - `display_order_key` 是前端优先展示的订单业务号或临时订单号;`group_code` 和 `confirmation_number` 只有在当前订单业务号类型匹配时返回。 - 订单 ID、任务 ID、SourceMessage ID 在这些前端接口中按字符串返回,前端不要转换成 JavaScript number。 - 当前 V3 / 过渡实现中,源邮件只读通知卡背后有隐藏技术订单用于满足后端任务外键,但订单列表不会返回该订单;任务列表中该类任务的 `display_order_key`、`temporary_order_no`、`group_code`、`confirmation_number` 可能为空,前端不要因此隐藏整条任务。V4 S10/S99 目标模型已改为独立来源通知,不再挂隐藏技术订单。 - 当前前端已按 `SOURCE_MESSAGE_ONLY` 展示旧 S000/S999 和 V3 S10/S99;V4 `route_code=S10/S99` 已写入独立来源通知模型,不再通过旧任务列表和旧任务详情展示;`INFORMATIONAL_MESSAGE` 仅作为历史 Message Notification 兼容路径保留。 - 任务列表里旧 `task_type=SOURCE_MESSAGE_ONLY`、`task_subtype=S000/S999` 或 V3 `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,页面会显示候选空态。 - `Cancel Allotment / cancel_allotment_control_block` 第一版复用旧 `Cancel Booking` 字段矩阵。后端在确认和复核解阻时会派生 `extracted_fields.cancel_object_type=allotment_control_block`,并接受 `extracted_fields.cancel_scope=entire_allotment_control_block`;前端不需要为了这两个 P0.1 系统字段额外阻塞人工复核提交。 - 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`。 - Parent / Allotment 场景中,SuperAgent 可能在 `manual_review.missing_fields[]` 同时返回 `/case_keys/group_code` 和 `/case_keys/block_code`。本系统第一版任务卡只暴露 `case_keys.group_code`,后端复核解阻会把 `/case_keys/block_code` 视为同一业务字段的输入侧别名;前端按 `fields[]` 渲染并提交 `/case_keys/group_code` 即可,不需要额外造 `block_code` 输入框。 - 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 任务详情字段元数据接入注意 - M002 V3 CP9 后,`GET /api/reservation/tasks/{taskId}` 的 `fields[]` 是后端已经按 `visible`、`result_type`、`task_type`、`task_subtype` 和 `display_condition` 过滤后的当前任务生效字段集合。前端必须直接以 `fields[]` 为权威列表渲染、校验和提交,不再按完整字段矩阵自行补齐后端未返回的字段。 - 后端未返回的字段应视为“当前任务不展示 / 不可提交”,不是接口错误。例如 `new_group_block` 不再返回 FIT 专属 `case_keys.confirmation_number`,无附件时也可能不返回 `attachments` 字段;前端不得为了旧矩阵完整性临时合成这些字段。 - 保存草稿、最终确认和同卡人工复核解阻只允许从当前 `fields[]` 中选择字段提交。`manual_review.missing_fields[]` 如果指向的字段没有出现在当前 `fields[]`,前端只展示“当前字段后端未开放编辑”,不要自行构造 `field_pointer` 或 `field_path`。 - `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.7.1 字段控件契约 V1 接入注意 后端已按 `docs/project/requirements/M002-task-field-control-contract-v1.md` 返回字段控件契约 V1。前端接入时注意: - 任务详情 `fields[]` 已新增 `control_type`、`edit_scope`、`write_target`、`options_source`、`raw_readonly`、`control_hint`。 - 任务详情 `fields[]` 是后端按当前任务生效规则过滤后的字段集合,不是完整字段矩阵;前端不得自行补齐后端未返回的字段,也不要依赖固定 `fields.length`。 - 未出现在 `fields[]` 的字段表示当前任务不展示、不校验、不提交。例如 `new_group_block` 不返回 FIT 专属 `case_keys.confirmation_number`,也不返回 Allotment 专属 `extracted_fields.child_room_items[]`;无附件时可以不返回 `attachments`。 - 前端应优先按 `control_type` 渲染字段;旧 `input_editable`、`select_editable`、`date_picker`、`number_input`、`file_display`、`table_editable` 只作为兼容兜底。 - `raw_readonly=true`、`edit_scope=never/system_only` 或 `write_target=none` 的字段不能展示普通编辑控件。 - `source_message`、邮件正文、附件引用、raw evidence、`event_type`、`source_event_index`、关系索引、`route_code`、`result_type`、`task_type`、`task_subtype`、`manual_review.reason_code` 等字段必须只读。 - `extracted_fields.room_items.0.room_type_raw` 是房型原文证据,第一版返回 `control_type=readonly`、`edit_scope=never`、`raw_readonly=true`;用户应确认或修改 `pms_room_type_code`,不要覆盖 raw 原文。 - `extracted_fields.room_items.0.room_quantity` 返回 `control_type=number`;`extracted_fields.room_items.0.pms_room_type_code` 返回 `control_type=select`、`options_source=active_pms_room_type_catalog`。 - type-known manual review 的 `manual_review.missing_fields[]` 应按 JSON Pointer 匹配 `fields[].field_pointer`,并复用对应字段控件提交 `field_overrides[]`;匹配不到的 pointer 不要临时生成任意输入框。 - 缺失字段会返回 `edit_scope=manual_review_only` 和 `write_target=review_resolution.field_overrides`;同卡复核中其他可编辑业务字段可能返回 `normal_and_manual_review`,前端第一版仍优先只渲染 `missing_fields[]` 指向的字段。 - `field_overrides[]` 新页面优先提交 `field_pointer`,可同时提交 `fields[]` 中的主 `field_path`;不要提交旧扁平 key 作为新逻辑首选。 - `options_source=active_pms_room_type_catalog`、`rate_code_catalog`、`system_case_lookup` 第一版仅代表选项来源,真实目录 / lookup 未接入前,前端不得硬编码 PMS 房型、Rate Code 或系统对象全集。 - 后端可能返回 `control_hint=catalog_backend_pending`、`lookup_backend_pending`、`structured_table_editor_pending`,用于提示前端目录、lookup 或表格编辑后端能力仍未接入。 - `control_type=structured_table` 第一版如未实现编辑控件,可以只读展示或按后端 `edit_scope/options_source` 给出待接入提示;不要把对象数组压成单行自由文本再提交。 - `control_type=workflow_state` 表示流程状态或动作入口,例如复核解阻状态;不要把它作为普通 `field_values` 保存。 ### 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 由 M007 单独建设;这个接口是人工 Debug 上传链路,不代表实时生产链路。 - `external_message_id` 是后端生成的 Debug 独立 ID,格式类似 `debug-eml-run-{debugRunId}-{sha256前缀}`;原始邮件 `Message-ID` 不再放入 `agentbus_like_payload`,需要排查时看原始 EML OSS 文件和后端 Debug run / SuperAgent metadata。 - 邮件会话解析支持 `References`、`In-Reply-To` 和 `Thread-Index`,但 Debug EML 的 `external_message_id` 不使用原始 `Message-ID` 做幂等。 - `agentbus_like_payload` 是后端发送给 SuperAgent 的 AgentBus Outlook-like 主输入,前端只做只读展示;该对象会包含普通 `reply_policy.mode=manual`、`reply_policy.final_only=true`,但不再包含 `schema_version`、`source.provider=DEBUG_EML_UPLOAD`、`debug_context` 或旧的 Debug 专属 `reply_policy.mode=debug_only`。 - M011 Debug EML 开关启用后,后端会在 `agentbus_like_payload.attachment_extractions[]` 和 Debug 响应中返回 Booking Excel 附件预处理结果;AgentBus dispatch 开关启用后,后端会在调用 SuperAgent 前把同结构结果追加到后台分发 payload。前端只做只读调试展示,不允许编辑后重新提交,也不得把其中的附件 URL、客户敏感字段或解析 JSON 写入普通日志、埋点、localStorage 或 URL query。 - Debug 来源版本仍由后端 SourceMessage payload 表 `schemaVersion=debug-eml-upload-v1` 记录,前端页面不要再依赖 `agentbus_like_payload.schema_version`。 - 第一版只返回 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 `,无 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` 才能拿到最新权限上下文。 - 新增菜单允许未知路由;未知路由可以保存,但正式开放可见前要确认前端页面已经存在或兜底页可接受。 - 菜单管理交互升级已提供 `GET /api/admin/menus/tree` 和 `PUT /api/admin/menus/tree-order`:前者返回完整菜单树,后者批量保存 `parent_id` 和 `sort_order`。这两个接口仍属于 `/api/admin/menus/**`,必须带 Bearer token,并需要 `SYSTEM_MENU_MANAGE`。 - `PUT /api/admin/menus/tree-order` 只能修改菜单父级和排序,不能顺带修改菜单名称、路由、权限码、可见性或状态;后端会校验父级存在、自引用和循环树,并写入 `platform_admin_audit_log`。`sort_order` 可为空;为空时后端按请求 `items[]` 顺序生成 `100`、`200`、`300`... 的稳定排序号。 - 新增酒店默认 `DISABLED`,`hotel_id` 新增后不能修改。 - 单酒店阶段只允许一家 `ACTIVE` 酒店,后端会拒绝启用第二家 `ACTIVE`,也会拒绝禁用最后一家 `ACTIVE`。 - 系统管理写操作会写入 `platform_admin_audit_log`;审计接口 `GET /api/admin/audits` 可按 `target_type`、`target_id`、`action` 查询。 ### 5.10 Excel 转 PDF 手动上传接口接入注意 后端已提供 M008 CP2 Excel 转 PDF 手动上传接口: ```text POST /api/system/document-conversions/excel-to-pdf Header: X-TH-Hotel-Document-Conversion-Key: <文件转换访问口令> Content-Type: multipart/form-data file: .xls / .xlsx 文件 hotel_id: 可选;用于 OSS 对象路径分组 ``` 成功响应: ```json { "conversion_status": "SUCCEEDED", "source_file_name": "booking-request.xlsx", "source_size_bytes": 12345, "pdf_file_name": "booking-request.pdf", "pdf_url": "https://oss.example.test/document-conversions/excel-to-pdf/HOTEL-TEST/2026-07-16/.../booking-request.pdf", "object_key": "document-conversions/excel-to-pdf/HOTEL-TEST/2026-07-16/.../booking-request.pdf", "content_type": "application/pdf", "pdf_size_bytes": 67890, "duration_millis": 1200 } ``` 前端注意: - 该接口当前属于受控调试 / 后台工具能力,不是普通公开上传接口。 - `X-TH-Hotel-Document-Conversion-Key` 不能写入 `VITE_*`、源码、构建产物、URL query、localStorage、错误上报或普通日志。 - 只允许上传 `.xls` / `.xlsx`;`.xlsm` 第一版不支持。后端会做扩展名和文件头轻量校验,改后缀的非 Excel 文件会返回 `DOCUMENT_CONVERSION_FILE_CONTENT_INVALID`。 - 后端默认大小限制是 20 MB,测试机可以通过环境变量调整。 - `pdf_url` 来自本系统 OSS,可用于预览或下载,但不要写入普通日志、埋点、错误上报或 URL query。 - `DOCUMENT_CONVERSION_DISABLED` 表示后端未开启文件转换能力,页面应提示联系管理员或切换到已开启环境。 - `DOCUMENT_CONVERSION_BUSY` 表示后端 LibreOffice 并发已满,页面可以提示稍后重试。 - `DOCUMENT_CONVERSION_TIMEOUT` / `DOCUMENT_CONVERSION_FAILED` 通常需要后端排查 LibreOffice、字体、文件格式或临时目录权限。 - CP2 不会创建转换任务记录,也不会自动处理邮件附件;邮件附件自动派生 PDF 是 M008 后续 checkpoint。 ### 5.11 Manual Invoice 手工开票页面接入方向 M009 后端 CP2 已实现:页面可不依赖订单或任务,用户手工填写 / 选择字段后由后端填充 Excel 模板、转换 PDF 并上传 OSS。 前端注意: - 前端 V1 已按 `invoice.html` 的业务排版和字段关系实现为项目内页面,但不是直接上线原始 HTML。 - 前端 V1 已新增 `/reservation/invoices/new` 页面,按 `RESERVATION_INVOICE_GENERATE` 路由权限保护,使用登录态 Bearer token 调用正式业务接口。 - 页面标题、关键输入占位符和主要操作按钮已接入中 / 英 / 泰 i18n;第一版仍保留原型中的部分英文业务字段标签,后续若 Manual Invoice 全量纳入三语言范围,再统一清理。 - 页面已支持无订单 / 无任务独立生成,固定提交 `source_type=MANUAL`,`order_id=null`,`task_id=null`;暂不做订单 / 任务自动预填。 - 侧边栏入口不由前端硬编码公开,仍依赖后端登录态 `menus[]`。如需菜单中展示,建议菜单管理配置 `menu_code=RESERVATION_MANUAL_INVOICE`、`route_path=/reservation/invoices/new`、`permission_code=RESERVATION_INVOICE_GENERATE`。 - 第一阶段入口建议是独立页面,例如 `/reservation/invoices/new` 或 `/invoices/new`;不要求必须从任务详情或订单详情进入。 - 无订单 / 无任务时,页面按 `source_type=MANUAL` 提交,`task_id` 和 `order_id` 可以为空。 - 从任务进入时,后续可以使用 `GET /api/reservation/tasks/{taskId}` 的 `fields[]`、草稿或确认 payload 预填;从订单进入时,需要明确选择具体任务或提示仅使用订单摘要,避免一个订单多任务时字段来源不清。 - Company、Attention、Address、Tel、Email、Booking Date 第一阶段可以按“选择 + Manual 手填”控件处理。 - Company、Attention、Address、Tel、Email 是一组收件方联系人档案,不是五个互相独立字段;选择 Company 后应刷新 Attention 候选,选择 Attention 后应带出 Address、Tel、Email。Booking Date 展示在同一区域,但不属于联系人档案,应作为 `document.booking_date` 独立提交。 - 第一阶段已确认三组客户 / 旅行社种子数据:`LIAN_TAI` / `LIAN TAI TRAVEL (THAILAND) CO., LTD.` + `Khun Ann`;`QBD` / `Q.B.D. TRAVEL GROUP CO., LTD` + `Jitdanun Panaphuchong`;`HANATOUR` / `HANATOUR TD CO., LTD.` + 7 个联系人。完整数据以 `docs/project/requirements/M009-manual-invoice-generation-v1.md` 为准。 - Room Type、Room Rate、Extra Bed 建议也预留“选择 + 可手填 / 可覆盖”能力,后续接 PMS 房型、Rate Code 或价格配置。 - Amount、Sub-Total、VAT、Total 是只读计算字段;前端可展示预览,但最终金额以后端计算和模板公式为准。 - 酒店名称、Tax ID、法人主体、银行账户、Logo 和固定付款文案不应作为每张 Invoice 的手工输入;第一阶段可由模板或酒店发票配置提供。 - 正式业务生成接口为 `POST /api/reservation/invoices/manual-generations`,需要 Bearer token、酒店访问权和 `RESERVATION_INVOICE_GENERATE` 权限。 - 第一版后端只支持 `source_type=MANUAL`,`task_id` / `order_id` 可以为空;传入时后端会反查对象所属酒店;如果两者同时传入,任务必须属于该订单,否则返回 `RESERVATION_INVOICE_CONTEXT_MISMATCH`。 - 第一版后端最多支持 10 条费用明细;超过 10 条会返回 `RESERVATION_INVOICE_VALIDATION_FAILED`。 - `pdf_url` 前端会优先用 `fetch + Blob` 下载;如果 OSS CORS 不允许浏览器读取文件,则只能退回打开 PDF 页面。若测试环境或正式环境需要稳定下载按钮,建议后端后续提供带 `Content-Disposition` 的下载代理或签名下载 URL。 - 错误响应里的 `error_code` 用于普通用户主提示;`message` / `details[]` 前端仅折叠为技术详情,避免把字段路径、模板异常等内部信息直接铺到主提示区。 - 第一版暂未提供 Invoice 历史列表、详情查询、任务 / 订单预填接口和客户联系人目录查询接口;前端客户 / 联系人候选可先按 M009 文档中的种子数据实现。 - 前端不得直接调用 `POST /api/system/document-conversions/excel-to-pdf` 来完成业务开票;该接口是 M008 调试 / 后台工具能力,受 access key 控制,不具备业务开票审计和权限边界。 ### 5.12 Rooming List Excel 生成页面接入方向 M010 后端 CP1 已实现。第一版生成结果直接下载 `.xlsx`,不落库、不上传 OSS,不依赖订单或任务。 接口: ```text POST /api/reservation/rooming-lists/generations Authorization: Bearer Content-Type: multipart/form-data ``` 权限: ```text RESERVATION_ROOMING_LIST_GENERATE ``` 前端注意: - 前端 V1 已新增 `/reservation/rooming-lists/new` 页面,按 `RESERVATION_ROOMING_LIST_GENERATE` 路由权限保护,并使用登录态 Bearer token 调用正式业务接口。 - 侧边栏入口不由前端硬编码公开,仍依赖后端登录态 `menus[]`。如需菜单中展示,建议菜单管理配置 `menu_code=RESERVATION_ROOMING_LIST`、`route_path=/reservation/rooming-lists/new`、`permission_code=RESERVATION_ROOMING_LIST_GENERATE`。 - 该页面上传来源名单 Excel,后端默认识别 `护照全名` 列。 - 姓名支持 `LI/CHUNHONG` 和 `LI CHUNHONG` 两类格式;后端会把第一段写入目标 `Name`,剩余部分写入目标 `First Name`。 - 前端必须让用户输入 `people_per_room`,后端按名单顺序分组,并用 `ceil(total_people / people_per_room)` 生成房间行。 - 每组第一位旅客写入 `Name` / `First Name`,同组剩余旅客写入 `Accompanying Guests`,多人用英文逗号分隔。 - 除 `Line`、`Name`、`First Name`、`Number of Rooms`、`Accompanying Guests` 外,目标 Excel 其他字段第一版都由前端输入或选择,例如 `Title`、`Arrival`、`Departure`、`Room Type`、`Rate Code`、`Payment Type`、`Nationality`。 - 第一版接口成功后直接返回 Excel 文件流,前端应按 Blob 下载处理,不要期待 JSON 里的 URL。 - 失败时返回统一 JSON 错误,例如 `ROOMING_LIST_VALIDATION_FAILED`、`ROOMING_LIST_SOURCE_FILE_INVALID`、`HOTEL_ACCESS_DENIED` 或 `FRONTEND_PERMISSION_DENIED`。 - 该上传接口会在 multipart 参数绑定前先校验 Bearer token 和 `RESERVATION_ROOMING_LIST_GENERATE` 权限;未登录时优先返回 401,不会因为缺少业务字段先返回 400。 - `ROOMING_LIST_VALIDATION_FAILED` 已覆盖 multipart 必填字段缺失、日期格式错误和数字格式错误,`details[]` 会返回字段级提示。 - 页面不要把上传文件内容、客人名单、生成文件内容写入浏览器日志、埋点、错误上报、URL 或 localStorage。 - 第一版没有 preview 接口、生成记录接口、OSS URL、历史下载和订单 / 任务预填;前端不要在页面上承诺这些能力。 ### 5.13 M002 V4 前端接入约束 后端已提供以下 V4 查询和写接口,前端接入时按这些约束实现;在前端页面正式集成并完成联调前,不把 V4 前端页面视为完成: - `/reservation/tasks` 应使用 `GET /api/reservation/workbench-items` 作为默认工作台列表入口,展示 V4 订单任务和 S10/S99 来源通知混排摘要;当筛选工作项类型为订单任务时,改用 `GET /api/reservation/order-tasks`,仅发送后端当前支持的 `keyword`、`order_task_status`、`card_status`、`page_num`、`page_size`。 - `/reservation/order-tasks/{orderTaskId}` 应使用 `GET /api/reservation/order-tasks/{orderTaskId}` 展示 `source_message_card`、`basic_information_card`、`business_cards[]`、`card_counts`、`order_task` 摘要和 `adapter_contract_errors[]` 只读诊断。 - `/reservation/source-notifications/{notificationId}` 应使用 `GET /api/reservation/source-notifications/{notificationId}` 展示 S10/S99 来源通知详情,并通过 `POST /api/reservation/source-notifications/{notificationId}/ack` 确认已读 / 已处理。 - V4 审计时间线应分别调用 `GET /api/reservation/order-tasks/{orderTaskId}/audits` 和 `GET /api/reservation/source-notifications/{notificationId}/audits`;按钮权限使用 `/api/auth/me.permissions[]` 中的 `RESERVATION_AUDIT_READ`。 - V4 卡片确认应使用 `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm`,请求带 `version`;前端只从当前卡 `fields[]` 中挑选 `editable=true`、`raw_readonly!=true`、`write_target` 指向确认 payload 的字段构造 `confirmed_payload`。 - V4 复核解阻应使用 `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution`,请求带 `version`;`field_overrides[]` 只使用当前卡 `fields[].field_pointer`,订单任务归属未解决时允许用户填写 `confirmed_order_id`。 - 所有按钮应按后端 `availability.confirmable`、`availability.reviewable`、`availability.ackable` 和前端权限共同控制;不可操作原因优先展示 `readonly_reason_message`,否则按 `readonly_reason_code` 做友好映射。 - `fields[].validation_errors` 应展示在对应字段旁边;接口返回 `V4_FIELD_VALIDATION_FAILED` 且 `details[]` 带嵌套路径时,前端应尝试定位到对应 field,定位不到则在当前卡片动作错误区展示。 - `options_source=reservation_v4_account_catalog`、`reservation_v4_room_type_catalog`、`reservation_v4_rate_code_catalog` 时,前端应调用对应 lookup API,不再硬编码固定种子;如果 lookup 返回空列表或 `warnings[]`,前端展示非阻塞提示,但提交时仍以后端目录校验为准。 - 前端不展示 `ai_payload_json`、邮件完整正文、附件 URL、raw evidence 或 SuperAgent 原始 payload;来源邮件详情仍从邮件会话页面查看,卡片内仅展示后端普通接口返回的安全摘要、邮件片段和附件名称。 - V4 来源消息卡读取 `attachments`、`uploaded_media`、`file_references` 时,只允许展示附件名称、类型和大小等安全摘要;如果后端 payload 中异常出现 `https://`、`oss://`、`s3://` 等直接 URL 字符串,前端必须替换为“未命名附件”或隐藏,不得把 URL 渲染到普通业务页面。 - V4 主流程不调用旧 V2/V3 草稿、旧任务确认、旧同卡复核接口,也不展示 OPERA 模拟操作入口。 - 2026-07-20 测试机 smoke 注意:订单详情页依赖 `GET /api/reservation/orders/{orderId}` 返回 `order_overview`、`next_v4_action`、`related_source_messages[]` 和 `v4_order_tasks[].cards[]`。如果测试机响应仍只有旧 `order`、`tasks[]`、基础 `v4_order_tasks[]` 和 `warnings`,应先确认测试机后端是否部署了包含 M002 V4 CP15.1 的最新包;前端不要为了该旧响应重新做兼容逻辑,避免把部署问题固化成页面分支。 ## 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/integrations/superagent/task-results` 已支持 M002 V4 入站解析基线;这是第三方回调能力,不是前端页面接口,前端只通过任务列表 / 任务详情观察后端派生后的结果。 - `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 V4 入站解析与数据模型基线已完成第一版:后端可接收 `source_message + order_contexts[] + message_events[]`,识别 `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING`、`TRACE_RESERVATION_NOTES`、`ROOMING_LIST`、`PAYMENT`,并保存 V4 原始 payload、`route_code`、系统处理分类和 `field_contract_version=20260718-v4`。前端暂不需要直接调用 V4 回调接口。 - M002 V4 CP5 已完成查询接口:普通 V4 业务包可通过 `/api/reservation/workbench-items`、`/api/reservation/order-tasks`、`/api/reservation/order-tasks/{orderTaskId}` 查看;V4 S10/S99 来源通知可通过 `/api/reservation/source-notifications/{notificationId}` 查看。M002 V4 CP6 已开放普通卡片确认和 S10/S99 ack 写接口;M002 V4 CP7 已开放 `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution` 复核解阻接口;M002 V4 CP8 已开放 V4 卡片 `fields[]` 白名单和目录校验;M002 V4 CP11 已把固定种子迁移到数据库目录,并开放 Account / Room Type / Rate Code lookup API;目录管理后台 CP1 已开放 Account / Room Type / Rate Code 后端列表、新增、启用 / 停用接口;V4 业务审计查询已补齐订单任务审计和来源通知 ack 审计两个只读接口。 - V4 任务卡的 `display_payload_json` 只保留后端白名单展示字段;`ai_payload_json` 才包含完整 SuperAgent 原始 event。后续 V4 查询接口不得把 `ai_payload_json`、附件 URL 或 raw evidence 直接给普通页面渲染;前端对来源消息卡附件字段仍做 URL-like 文本兜底脱敏。 - V4 可映射 event 现阶段仍保留现有任务详情结构作为过渡兼容;任务详情中若出现 `field_contract_version=20260718-v4` 或 AI payload 内的 `v4_source_message`、`v4_order_context`、`v4_message_event`,前端第一版只读展示即可,不要据此假定完整 V4 多卡页面已经完成。 - V4 `PAYMENT.attachment_ids[]` 不匹配、`UPDATE_BOOKING` 携带 `rate_code` 等问题会出现在任务详情同批次的 `adapter_contract_errors[]` 只读诊断块中,不展示保存、确认、执行或重试按钮。该字段只返回白名单诊断字段,不返回完整 AI payload、邮件正文、附件 URL 或 raw evidence。 - V4 包级契约错误只会保存在 AI transition 中,不会出现在普通任务列表;V4 event 级契约错误如果同批次存在其它业务任务,前端仍按任务详情里的 `adapter_contract_errors[]` 只读展示诊断信息。 - M002 V4 CP2 订单任务与多卡领域模型设计已落到 `docs/project/requirements/M002-v4-order-task-card-domain-model-cp2.md`:后续前端 V4 页面应围绕 `order_task + source_message_card + basic_information_card + business_cards[]` 设计;V4 工作台统一列表、业务订单任务列表 / 详情和 S10/S99 来源通知详情已实现。 - M002 V4 CP3 已新增 V4 订单任务、任务卡、S10/S99 来源通知三张表和 Repository 基线;M002 V4 CP4 已把正式 V4 回调写入这些表;M002 V4 CP5 已开放查询;M002 V4 CP6 已开放普通卡片确认和 S10/S99 ack;M002 V4 CP7 已开放复核解阻与复核场景订单归属确认;M002 V4 CP8 已开放目录校验和 V4 任务卡 `fields[]` 字段白名单;M002 V4 CP11 已新增目录表、数据库种子和 lookup API;目录管理后台 CP1 已新增 `RESERVATION_CATALOG_MANAGE` 后端接口;V4 审计查询已开放 `RESERVATION_AUDIT_READ` 下的订单任务审计和来源通知审计。 - V4 订单任务和卡片 `availability` 已新增 `reviewable`。当前语义:`REVIEW_REQUIRED` 卡如果未被 Basic Information 或前置订单任务阻塞,会返回 `read_only=false`、`editable=true`、`confirmable=false`、`reviewable=true`、`readonly_reason_code=PROCESSABLE`;前端应调用 `review-resolution`,不要调用普通 `confirm`。 - CP11 起 V4 入站阶段按当前酒店数据库目录做校验:Account 缺失或不存在时 Basic Information 卡直接 `REVIEW_REQUIRED`;业务卡已有 `room_items[].room_type_code` 或 `rate_code` 但不在当前酒店目录时,业务卡也会直接 `REVIEW_REQUIRED`,错误会回显在 `fields[].validation_errors`。 - CP8 确认接口也按 `fields[]` 白名单收口:前端可以只提交用户修改过的可编辑字段,不建议整包回传 `display_payload`。后端会从当前卡展示快照生成确认快照,并只合并可写叶子字段;来源邮件、路由、`target_order`、`order_ref`、`manual_review`、校验诊断字段以及前端额外注入字段不会写入 `confirmed_payload_json`。 - 业务卡目录校验会递归检查 `business_fields` 下的嵌套结构。例如 `UPDATE_BOOKING` 的房型可能位于 `/business_fields/after/room_items/0/room_type_code`,错误详情会使用 `business_fields.after.room_items.0.room_type_code`;前端展示错误时优先用 `fields[].validation_errors`,接口 400 时可直接展示 `details[]`。 - `review-resolution` 请求示例:`{"version":0,"reason":"确认房型映射","confirmed_order_id":"123456","field_overrides":[{"field_pointer":"/business_fields/room_items/0/pms_room_type_code","value":"RM2"}]}`。`confirmed_order_id` 在订单任务归属未解决时必填;如果订单任务已经绑定订单且 `target_resolution_status=RESOLVED`,只能不传或传当前同一个订单 ID,不能借该接口切换到其它订单。`field_pointer` 必须来自当前卡允许编辑的 `basic_information.*` 或 `business_fields.*` 叶子字段;展示 payload 有 `missing_fields[]` 时只提交清单里的 pointer,没有显式清单时只提交当前值为 `null` / 空字符串的未解决叶子字段;如果是目录校验错误,也可以提交后端 `fields[].validation_errors` 对应的字段 pointer。前端不要提交来源邮件、路由、`target_order`、`order_ref`、缺失字段清单、`manual_review`、raw evidence、校验诊断字段,也不能替换整个对象 / 数组。 - V4 `fields[]` 第一版字段说明:Basic Information 固定返回 `/basic_information/account_code`、`/basic_information/market_code`、`/basic_information/source_code`;其中 Account `control_type=select`、`options_source=reservation_v4_account_catalog`,Market / Source 为只读派生字段。业务卡会按展示 payload 里的业务叶子字段返回字段白名单,例如 `/room_items/0/room_type_code` 或 `/business_fields/room_items/0/pms_room_type_code`;前端不要自行补未返回字段。 - V4 CP11 已开放独立目录 lookup API。前端应使用 `GET /api/reservation/lookups/accounts`、`GET /api/reservation/lookups/room-types`、`GET /api/reservation/lookups/rate-codes` 渲染 Account / Room Type / Rate Code 选项;用户提交确认或复核时只提交稳定 `code`,不要提交显示名、派生 Market / Source 或目录完整对象;后端确认前仍会重新校验目录。`keyword` 查不到只表示当前筛选无结果,不能仅凭 `items=[]` 判断目录未初始化,应结合 `catalog_source`、`catalog_version` 和 `warnings[]`。 - M002 V4 CP12 前端已接入上述三个 lookup API:V4 多卡详情页会按当前卡 `fields[].options_source` 拉取目录选项,空 `items[]`、`stale=true` 和 `warnings[]` 作为非阻塞提示展示;Account 选择后只展示目录返回的 `market_code` / `source_code` 辅助确认,确认 / 复核请求仍只提交用户选择的 code。 - V4 新模型确认口径是不保存后端草稿、卡片最终确认后锁定、技术异常不进入用户可处理卡、当前不生成 OPERA 模拟操作。Basic Information 必须先确认;其它业务卡第一版不强制逐张顺序确认。现有 V3 `draft`、`confirm`、`manual-review-resolutions` 和 OPERA 模拟接口仍只代表旧链路能力,不能直接等同 V4 多卡最终接口。 - V4 S10/S99 已采用来源通知模型入库:新 V4 `route_code=S10/S99` 不再挂隐藏技术订单,也不再创建旧 `SOURCE_MESSAGE_ONLY` 任务;对应工作台 / 来源通知详情查询接口和 ack 写接口已开放。旧 `SOURCE_MESSAGE_ONLY` 只读任务仅代表 V3 S10/S99 和旧 S000/S999 兼容数据。 - 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 接入继续后置。