Files
th-hotel-simple/docs/project/frontend-backend/backend-to-frontend-notes.md
2026-07-21 14:46:27 +07:00

554 lines
94 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# 后端提醒前端注意事项
## 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 或重试 OPERAV4 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 <access_token>` 调用401 时清理 token 并进入登录页。 |
| `POST /api/auth/logout` | 登出当前 session | 带 `Authorization: Bearer <access_token>`;成功后前端必须清理本地 token 和当前用户上下文。 |
| `GET /api/reservation/orders` | 查询订单列表 | 必须带 `Authorization: Bearer <access_token>`,需要 `RESERVATION_ORDER_READ`;默认返回全部订单状态;按后端维护的订单最近业务活动时间倒序,当前落库字段为 `workflow_reservation_order.latest_activity_at`,前端不要自行重排;`open_work_item_count` 是订单列表统一待处理展示数量,前端订单列表已用它展示待处理数,第一版等于 V4 未完成订单任务数V4 普通业务入站已停止双写旧 `workflow_reservation_task`,开发 / 测试阶段不维护 V2/V3 旧任务兼容,测试数据可重建;`open_task_count` 仍保留为旧任务表原始诊断计数,前端不要用于展示待处理数;已补齐 V4 继续处理入口字段,前端有 `next_v4_order_task_id` 时优先跳 V4 订单任务详情;`next_processable_task_id` 仅作为历史 V2/V3 诊断兼容字段,清理旧任务数据后新 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 会话接口。V4 任务详情页展示顺序固定为 Basic Information、业务卡、SourceMessage Display来源邮件卡位于页面最下方正文限定为当前触发该 order task 的 SourceMessage 正文,前端用 `source_message_summary.source_message_id` 调用 `GET /api/source-messages/{sourceMessageId}/conversation` 后定位当前邮件。Payment 卡下一阶段可返回 `payment_attachments[]` 安全摘要用于展示凭证附件,但本接口不得返回附件 URL图片缩略图 / 大图和非图片下载 URL 仍通过 SourceMessage 会话权限链路取得。CP8 起每张 V4 任务卡返回 `fields[]`,前端应以该字段白名单渲染可编辑控件;`write_target` 只返回 `confirmed_payload``review_resolution.field_overrides``none` 等前端安全语义不暴露内部列名。Room Information 卡已新增 `display_payload.room_information` 稳定展示模型,前端优先读取 `current_values` / `proposed_values` / `final_values` / `change_summary[]`,不要再从 Agent raw payload、`business_fields``target_order` 自行推导业务展示Basic Information 以及普通业务卡的 `display_payload` / `confirmed_payload` 不返回 Agent `target_order`,普通业务卡也会移除邮件 HTML、raw evidence、附件原始 URL 和 PMS 原始响应等敏感字段。 |
| `GET /api/reservation/order-tasks/{orderTaskId}/audits` | 查询 V4 订单任务审计流水 | 必须带 Bearer token需要 `RESERVATION_AUDIT_READ`,后端按订单任务实际酒店校验访问权;返回 `order_task_id``items[]``items[]` 用于展示 V4 卡片确认、复核解阻、订单归属确认轨迹和 `V4_ROOMING_LIST_AUTO_DEF` 自动 DEF 摘要只包含脱敏后的审计摘要不包含邮件正文、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 目录 | 当前 CP11 实现只支持 `hotel_id``keyword``page_num``page_size`,返回当前酒店 `ACTIVE` Rate Code下一阶段必须新增必填 `account_code``booking_type=GROUP/FIT`,只返回当前 Account + GROUP/FIT 适用的 Rate Code。`pricing_available=false` 表示后端未接真实价格,不要据此展示价格。`keyword` 匹配 Rate Code 时后端按稳定 code 大写归一化处理前端可传小写匹配显示名仍按数据库比较规则。Account 合法但无适用 Rate Code 时按空选项处理,不要当作目录不可用;`account_code` 缺失 / 无效或 `booking_type` 非法时按 400 处理。前端在 `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[]` 中可编辑字段后端以展示快照为基准合并未开放字段会被忽略Room Information 卡优先提交 `confirmed_payload.room_information.final_values`,后端写入稳定确认快照并重新派生 Nights / Breakfast / Group Booking Status 文案,不写 Agent `target_order`、Adult、邮件正文、附件 URL 或前端注入字段;确认 Rooming List 卡时前端只提交 `version` 即可,若同订单为 Group 且存在可更新 Room Information 确认快照,后端会自动把 Group Booking Status 置为 `DEF` 并写审计,刷新详情时 `display_payload` / `confirmed_payload` 均显示 DEF没有可更新投影时确认仍成功且不创建不完整 Room Information确认前会按当前酒店数据库目录校验 Account / Room Type / Rate Code下一阶段 `rate_code` 还必须属于已确认 Account + 当前业务 event `booking_type` 的适用范围Room Information 新结构错误路径形如 `room_information.final_values.room_items.0.room_type_code`,历史兼容结构可能返回如 `business_fields.after.room_items.0.room_type_code`,失败返回 `V4_FIELD_VALIDATION_FAILED`;确认后卡片 `CONFIRMED`、内部写入确认快照 / 确认人 / 确认时间并锁定,重复确认返回错误;成功返回刷新后的订单任务详情。 |
| `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` 必填,且必须是当前酒店下真实可见订单;`field_overrides[]` 只允许当前卡 `fields[]` 白名单内可编辑业务字段,问题字段可按 `fields[].validation_errors` 红字提示Room Information 复核优先使用 `/room_information/final_values/...` pointer不允许指向 `nights``target_order`、Adult、Block ID、Confirmation Number 等只读 / 派生字段;成功后卡片 `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`。V4 Payment 卡图片预览和非图片下载也走该接口,前端只能使用当前触发 SourceMessage 且被 Payment `attachment_ids[]` 引用的附件。 |
| `POST /api/system/debug/eml-superagent-runs` | Debug 页面上传 `.eml` 并调用 SuperAgent | 仅 dev/test 受控调试使用;会写入 SourceMessage Inbox。Debug 服务自身不直接创建订单和任务;如 SuperAgent 通过正式回调 / MCP 写入业务结果,必须按当前 M002 V4 契约创建 V4 order task / cards不再创建旧 `workflow_reservation_task`。 |
| `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` 保留为旧任务表原始诊断计数。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}`;开发 / 测试清理旧任务后,新 V4 订单通常不再回退旧 `/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 解析失败。测试机 V4 smoke 默认使用实时 AgentBus V4 subject历史 Debug V2/V3 profile 只能由后端显式配置,不应作为 V4 smoke 入口。 |
| `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。Rate Code 下一阶段必须带已选 / 已确认 `account_code` 和当前业务 event `booking_type`,不能再拉全酒店全量候选。`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 <access_token>`
- 当前后端已强制拦截第一批 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 <access_token>`,且当前用户需要同时拥有 `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前端类型定义需要单独处理。
- Payment 卡附件预览规则:业务卡里的 `attachment_ids[]` 是付款凭证引用,第一版只读展示并确认卡片,不允许前端增删或替换附件集合;后端下一阶段可返回 `payment_attachments[]` 安全摘要辅助展示。图片附件按 `contentType``image/` 开头判断,在卡片中展示缩略图,点击后打开大图预览;非图片附件统一展示文件名、类型、大小和下载按钮,不在 Payment 卡中内嵌 PDF / Word / Excel 预览。预览和下载必须先通过会话接口定位当前 SourceMessage再按 `externalMediaId` / `payment_attachments[].external_media_id` 或附件 ID 匹配,不能按文件名猜测。
### 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` 是旧任务表原始未关闭计数,排除 `COMPLETED``FAILED`当前保留用于历史接口兼容和问题排查V4 普通业务入站已停止双写旧任务,开发 / 测试环境应清理旧任务数据,前端展示待处理数量不要使用它。
- `next_processable_task_id` 是后端按旧 V2/V3 队列实时计算出的历史兼容字段;开发阶段不维护 V2/V3 旧任务兼容,清理旧任务数据后,新 V4 订单不应再通过它进入旧任务详情。
- 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 订单任务不计入 openS10/S99 来源通知不挂订单,不进入这些订单列表字段。
- 前端订单列表“继续处理”已按该优先级接入:有 `next_v4_order_task_id` 时跳 V4 订单任务详情;`next_v4_action_type=NONE` 且旧字段为空时展示无待处理状态,查看详情仍固定进入订单详情。开发 / 测试阶段旧任务数据可清理,清理后新 V4 订单不应再出现旧 fallback 入口。
- `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/S99V4 `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 演示数据入口,方便前端在空库或本地环境快速看到历史 V2/V3 页面效果。
```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 联调,不允许放进生产普通页面,也不要把访问口令写进前端仓库、浏览器环境变量或构建产物。
- 该接口会创建旧 `workflow_reservation_task` 演示数据,是历史 V2/V3 页面演示入口M002 V4 smoke 不应再使用该接口造数避免重新制造旧任务残留。V4 smoke 应使用 SuperAgent V4 回调或专门 V4 fixture。
返回内容:
- `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`;同卡复核中其他可编辑业务字段也可能按当前卡白名单返回 `editable=true`,前端不要只渲染 `missing_fields[]` 指向的字段,应该以 `fields[]` 中真实 `editable` 状态为准。
- `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`
- Debug 服务自身只展示 SuperAgent Open API 结果;如果 SuperAgent 后续调用正式 `task-results` / MCP 写入业务结果V4 smoke 应进入 `/api/reservation/workbench-items``/api/reservation/order-tasks/**`,不应再生成旧 `/api/reservation/tasks/**` 任务。
- 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`
- Debug 服务自身只返回 SuperAgent 结果,不直接创建订单、不直接创建任务、不调用任务结果通知接口;业务结果由 SuperAgent 后续正式回调 / MCP 写入时,按当前 V4 模型处理。
- `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` 才能拿到最新权限上下文。
- 新增菜单允许未知路由;未知路由可以保存,但正式开放可见前要确认前端页面已经存在或兜底页可接受。
- 菜单管理交互升级已提供 `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 <access_token>
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}` 展示 `basic_information_card``business_cards[]``source_message_card``card_counts``order_task` 摘要和 `adapter_contract_errors[]` 只读诊断;页面顺序固定为 Basic Information、业务卡、SourceMessage Display。
- `/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=confirmed_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[]`,前端展示非阻塞提示,但提交时仍以后端目录校验为准。
- Trace 卡 `trace_items[].department_code` 第一版先固定下拉选项 `FO``HSK``FO+HSK`,不要调用不存在的 Department lookup API也不要允许自由文本正式 Department 目录和后端目录校验后续单独扩展。
- 前端不展示 `ai_payload_json`、附件 URL、raw evidence 或 SuperAgent 原始 payloadV4 任务详情页底部 `SOURCE_MESSAGE_DISPLAY` 可展示当前触发该 order task 的 SourceMessage 正文,但必须通过 `GET /api/source-messages/{sourceMessageId}/conversation` 读取并写原文读取审计,不能要求 `GET /api/reservation/order-tasks/{orderTaskId}` 直接返回正文。Payment 卡如展示付款凭证图片,也必须通过同一会话接口获取受控附件 URL卡片内显示缩略图点击打开大图预览非图片只显示文件列表和下载。HTML 邮件优先渲染 `html_body_sanitized`,缺少原文权限或接口失败时降级为安全摘要和“查看邮件会话”入口。
- V4 来源消息卡读取 `attachments``uploaded_media``file_references` 时,只允许展示附件名称、类型和大小等安全摘要;如果后端 payload 中异常出现 `https://``oss://``s3://` 等直接 URL 字符串,前端必须替换为“未命名附件”或隐藏,不得把 URL 渲染到普通业务页面。`SOURCE_MESSAGE_DISPLAY` 的邮件正文默认做长度折叠,用户可展开全文。
- 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 文本兜底脱敏。
- Room Information 卡已由后端返回业务展示模型,前端已按该模型完成第一版业务化展示,不再自行从 Agent raw payload、`business_fields``target_order` 计算。读取路径是业务卡 `display_payload.room_information``NEW_BOOKING` 展示最终值Group 的最终订单投影字段 `group_block_name` 可编辑,默认来自 Agent `target_order.locator_value``locator_type=GROUP_CODE`Fit 的最终订单投影字段 `fit_name` 可编辑,默认来自 `guest_name ?? target_order.locator_value`Agent 原始 `target_order` 不在普通 `display_payload` / `confirmed_payload` 中暴露,用户编辑只影响本系统最终订单投影和确认快照。`UPDATE_BOOKING` 顶部展示本地当前值到 Agent 修改后值的 `change_summary[]`,日期变化时连带展示 Nights 差异,字段区展示合并后的最终值;`CANCEL_BOOKING` 从本地订单投影只读展示 current / final 模型不显示编辑控件。Nights 由后端按酒店本地日期派生前端只展示不计算Adult 不显示。
- Room Information 卡 Breakfast 前端显示为“含早”勾选框Group 固定勾选且只读Fit 由后端按最终 Rate Code 中 `RB` / `RO` 派生无法派生时作为必填勾选项。Group Booking Status 仅 Group 显示,稳定 code 为 `TEN` / `DEF` / `INQ`,展示文案为 `TEN-Tentative``DEF-Definite``INQ-Inquiry`New Group 默认 `TEN``NEW_BOOKING` / `UPDATE_BOOKING` 确认前可改选,`CANCEL_BOOKING` 只读。
- Rooming List 卡确认存在跨卡联动:同订单为 Group 时,确认 `ROOMING_LIST` 后后端已把 Group Booking Status 自动置为 `DEF`,即使此前为 `TEN``INQ`Fit 不显示也不变更该状态。该自动变更由后端写 `V4_ROOMING_LIST_AUTO_DEF` 审计,并在后续任务详情刷新时让 Room Information 的 `display_payload.room_information.final_values``confirmed_payload.room_information.final_values` 保持 DEF 口径一致;当前订单详情 `order_overview` 不返回 Group Booking Status 字段,仍只展示既有确认快照字段;如果没有可更新 Room Information 投影,确认仍成功,后端只写安全审计提示。
- Rooming List 卡第一版是轻量事项确认卡:前端展示卡片标题、状态、目标订单信息和“确认卡片”按钮即可;不要做名单 rows、附件预览、Excel 生成或 PMS 导入入口。确认仅表示该 Rooming List 事项已人工处理。
- Payment 卡下一阶段建议由后端在 `display_payload_json.payment_attachments[]` 返回安全摘要,字段只包含附件 ID、文件名、类型、大小、是否图片、是否可预览 / 下载等,不包含外链。第一版 `attachment_ids[]` 是 Agent 返回的只读业务事实,前端只展示并确认卡片,不允许用户增删、替换或重新选择附件集合,也不把 `attachment_ids[]``externalUrl` 或完整附件对象提交回确认接口。
- V4 复核态卡片仍是原业务卡,不新建单独复核任务卡;页面状态显示“需要复核”,问题字段用 `fields[].validation_errors` 红字提示,主按钮文案统一为“确认卡片”。前端内部必须根据 `card_status=REVIEW_REQUIRED` 调用 `review-resolution`,不要调用普通 `confirm`
- 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 ackM002 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 / Room Information 展示模型后,确认接口按 `fields[]` 白名单收口:前端可以只提交用户修改过的可编辑字段,不建议整包回传 `display_payload`。后端会从当前卡展示快照生成确认快照,并只合并可写叶子字段;来源邮件、路由、`target_order``order_ref``manual_review`、校验诊断字段以及前端额外注入字段不会写入内部确认快照。
- 业务卡目录校验会递归检查稳定模型或历史兼容结构。例如 Room Information 新结构的房型位于 `/room_information/final_values/room_items/0/room_type_code`,错误详情会使用 `room_information.final_values.room_items.0.room_type_code`;历史兼容 `UPDATE_BOOKING` 的房型可能仍使用 `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":"/room_information/final_values/room_items/0/room_type_code","value":"RM2"}]}``confirmed_order_id` 在订单任务归属未解决时必填;如果订单任务已经绑定订单且 `target_resolution_status=RESOLVED`,只能不传或传当前同一个订单 ID不能借该接口切换到其它订单。`field_pointer` 必须来自当前卡 `fields[]` 中可编辑的 `basic_information.*``room_information.final_values.*` 或历史兼容 `business_fields.*` 叶子字段;复核态允许编辑当前卡业务白名单内字段,不再限定只能改空值、`missing_fields[]` 或目录错误字段。前端不要提交来源邮件、路由、`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 为只读派生字段。Room Information 字段统一返回 `/room_information/final_values/...`,例如 `/room_information/final_values/arrival_date``/room_information/final_values/room_items/0/room_type_code``REVIEW_REQUIRED` 状态下只要字段仍在当前卡业务白名单内且未被前置阻塞,就会返回 `editable=true` 并允许 `review-resolution` 提交同一个 pointer即使该叶子字段原始值缺失、详情页显示 `value=null`,前端仍可按原样提交该 pointer。查询侧 editable 计算和命令侧 pointer 校验共用同一套 Room Information 字段策略。测试机如仍出现 `V4_REVIEW_POINTER_NOT_ALLOWED`,确认部署后让后端日志检索 `review_pointer_policy=m002_v4_review_pointer_runtime_fix_v1`,日志会输出两侧 pointer 白名单、validation error pointers、`display_payload_has_room_information_final_values` 和拒绝原因。前端不要自行补未返回字段。
- 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 或目录完整对象后端确认前仍会重新校验目录。Rate Code 下一阶段依赖 Account + `booking_type`:前端需在 Account 已选 / 已确认且能取得当前业务 event `booking_type` 后再请求 Rate CodeAccount 改变后清空或重新校验已选 Rate Code缺失条件时禁用或空态不硬编码 OWNER RATE Excel。`keyword` 查不到只表示当前筛选无结果,不能仅凭 `items=[]` 判断目录未初始化,应结合 `catalog_source``catalog_version``warnings[]`
- M002 V4 CP12 前端已接入上述三个 lookup APIV4 多卡详情页会按当前卡 `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 接入继续后置。