feat(reservation): solidify booking email v0.1 intake

This commit is contained in:
鲨鱼辣椒 committed 2026-08-08 16:02:09 +08:00
1 parent 9f29b59c26
commit d91cd717df
61 files changed
+13961 -377

No files matched your search

@@ -16,7 +16,7 @@
本契约用于后续 M002 V4 主流程设计、后端领域建模、前端页面模型、Adapter / MCP Schema 对齐和 SuperAgent 联调。当前后端已按本文完成 V4 入站解析和多卡模型基线:能识别 V4 包、校验关键契约、保存 AI transition / 任务卡原始 payload,并把可映射的六类 event 写入 V4 订单任务和任务卡模型。
V4 订单任务与多卡领域模型的 CP2 设计已经单独落到 `M002-v4-order-task-card-domain-model-cp2.md`。截至 CP14 和停止旧任务双写 checkpoint,表结构、Entity、Mapper、Repository、SuperAgent V4 入站写入、V4 查询、普通卡片确认、S10/S99 ack、V4 复核解阻、数据库目录、Account / Room Type / Rate Code Lookup API、目录管理后台 CP1 后端接口、订单列表 V4 继续处理入口字段,以及 V4 普通业务不再创建旧 `workflow_reservation_task` 已实现。Room Information 卡 New / Update / Cancel 展示模型、Nights / Breakfast / Group Booking Status 派生,以及 Rooming List 确认后 Group 自动置 `DEF` 的后端联动已实现;这些均不扩大 SuperAgent 输入字段。2026-07-21 OWNER RATE `RATECODE (2)` 只读整理已确认:Room Type 第一阶段只维护 `RM2`、`RM3`、`RM4`、`SU1`、`SU2`、`SU3` 六个稳定 code,不建 Account -> Room Type 关系;Rate Code 第一阶段暂不建立 Account 适用关系,Q.B.D / LIAN TAI 的 40 个规范化 Rate Code 作为酒店级目录候选;真实 PMS 同步仍后置,方案见 `M002-v4-real-catalog-lookup-api-design.md`。
V4 订单任务与多卡领域模型的 CP2 设计已经单独落到 `M002-v4-order-task-card-domain-model-cp2.md`。截至 CP14 和停止旧任务双写 checkpoint,表结构、Entity、Mapper、Repository、SuperAgent V4 入站写入、V4 查询、普通卡片确认、S10/S99 ack、V4 复核解阻、数据库目录、Account / Room Type / Rate Code Lookup API、目录管理后台 CP1 后端接口、订单列表 V4 继续处理入口字段,以及 V4 普通业务不再创建旧 `workflow_reservation_task` 已实现。Room Information 卡 New / Update / Cancel 展示模型、Nights / Breakfast / Group Booking Status 派生,以及 Rooming List 确认无跨卡副作用的安全边界已完成;这些均不扩大 SuperAgent 输入字段。2026-07-21 OWNER RATE `RATECODE (2)` 只读整理已确认:Room Type 第一阶段只维护 `RM2`、`RM3`、`RM4`、`SU1`、`SU2`、`SU3` 六个稳定 code,不建 Account -> Room Type 关系;Rate Code 第一阶段暂不建立 Account 适用关系,Q.B.D / LIAN TAI 的 40 个规范化 Rate Code 作为酒店级目录候选;真实 PMS 同步仍后置,方案见 `M002-v4-real-catalog-lookup-api-design.md`。
2026-07-22 后,MCP `th_hotel_submit_task_results` 已与本文 V4 字段契约对齐并收口为 V4-only:只接受 `route_code + source_message + order_contexts[] + message_events[]`,旧 V2/V3 submit payload 返回 `MCP_SUBMIT_V4_REQUIRED`。
@@ -725,7 +725,7 @@ AI 回调包
- 普通业务包要求 `route_code=null`,并按 `message_events[]` 数组顺序处理。
- 第一版识别六类 `event_type`:`NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING`、`TRACE_RESERVATION_NOTES`、`ROOMING_LIST`、`PAYMENT`。
- `ROOM_INFORMATION` 卡只由 `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING` 触发;Nights、Breakfast、Group Booking Status、Block ID、Confirmation Number 和本地当前值展示均由本系统后端展示模型派生或查询,不扩大 SuperAgent 输出字段。
- `ROOMING_LIST` 第一版只表达 Rooming List 事项需要人工处理,确认卡片即表示人工已处理;不承载名单解析、附件预览、Excel 生成或 PMS 导入语义。确认后的 Group Booking Status 自动置 `DEF` 是本系统后端联动,不要求 SuperAgent 增加字段。
- `ROOMING_LIST` 第一版只表达 Rooming List 事项需要人工处理,确认卡片即表示人工已处理;不承载名单解析、附件预览、Excel 生成或 PMS 导入语义。确认不修改 Group Booking Status 或同订单其他卡片,也不要求 SuperAgent 增加字段。
- 能映射到现有稳定任务卡的 event 会创建业务任务,并在 `ai_payload_json` 中保存 `v4_source_message`、`v4_order_context`、`v4_message_event`、`route_code`、系统处理分类和 `field_contract_version=20260718-v4`。
- V4 入站校验和路由已拆分为独立 Validator / Router,主业务 service 只负责编排、幂等和落库。
- V4 包级契约错误在 SourceMessage 可定位时只写 `adapter_contract_error` transition,不创建订单、任务或用户可处理卡。
@@ -6,7 +6,7 @@
| --- | --- |
| 文档版本 | 0.9 |
| 日期 | 2026-07-21 |
| 状态 | CP2 设计已确认;CP3-CP8、CP11、CP13、CP14、V4 业务审计查询、停止 V4 普通业务双写旧任务、Room Information 后端展示模型和 Payment 附件安全摘要后端第一版已实现 |
| 状态 | CP2 设计已确认;CP3-CP8、CP11、CP13、CP14、V4 业务审计查询、停止 V4 普通业务双写旧任务、Room Information 后端展示模型、BR00 辅助订单事件 companion Room 和 Payment 附件安全摘要后端第一版已实现 |
| 适用范围 | M002 V4 入站后的订单任务、多卡、状态、查询和写操作设计 |
| 不适用范围 | 真实 PMS / OPERA / OHIP、前端页面视觉稿、生产历史数据迁移 |
@@ -16,7 +16,7 @@ M002 V4 CP1 已完成 SuperAgent V4 回调包入站解析、基础校验、路
本文是 CP2 设计文档,用于把 2026-07-18 V4 字段契约落成后续可开发的数据模型和接口草案。
截至 CP14、V4 业务审计查询、停止旧任务双写、Room Information 后端展示模型、Rooming List 确认自动 DEF 后端联动和 Payment 附件安全摘要后端第一版,后端已实现本文第 10、11、12 节中的持久化和查询基线,并已把 SuperAgent V4 入站结果写入新表:普通业务包只创建 V4 订单任务、来源邮件展示卡、Basic Information 卡和业务卡,不再创建旧 `workflow_reservation_task`;V4 S10/S99 创建来源通知。当前已开放 V4 工作台、订单任务列表 / 详情、来源通知详情查询接口、订单详情 V4 订单任务时间线、V4 卡片确认接口、S10/S99 来源通知 ack 接口、V4 `REVIEW_REQUIRED` 卡复核解阻接口、V4 订单任务 / 来源通知审计查询接口、当前酒店数据库目录校验、卡片 `fields[]` 白名单、Account / Room Type / Rate Code lookup API、目录管理后台 CP1、订单列表 V4 继续处理入口字段、Room Information New / Update / Cancel 第一版业务展示模型、Rooming List 确认触发 Group Booking Status 自动置 `DEF`,以及 Payment 卡 `payment_attachments[]` 安全摘要。2026-07-21 OWNER RATE `RATECODE (2)` 只读整理已确认:Room Type 第一阶段只维护 `RM2`、`RM3`、`RM4`、`SU1`、`SU2`、`SU3` 六个稳定 code,不建 Account -> Room Type 关系;Rate Code 第一阶段暂不建立 Account 适用关系,Q.B.D / LIAN TAI 的 40 个规范化 Rate Code 作为酒店级目录候选。真实 PMS 同步继续后置,设计见 `M002-v4-real-catalog-lookup-api-design.md`。
截至 CP14、V4 业务审计查询、停止旧任务双写、Room Information 后端展示模型、Rooming List 确认无跨卡副作用和 Payment 附件安全摘要后端第一版,后端已实现本文第 10、11、12 节中的持久化和查询基线,并已把 SuperAgent V4 入站结果写入新表:普通业务包只创建 V4 订单任务、来源邮件展示卡、Basic Information 卡和业务卡,不再创建旧 `workflow_reservation_task`;V4 S10/S99 创建来源通知。当前已开放 V4 工作台、订单任务列表 / 详情、来源通知详情查询接口、订单详情 V4 订单任务时间线、V4 卡片确认接口、S10/S99 来源通知 ack 接口、V4 `REVIEW_REQUIRED` 卡复核解阻接口、V4 订单任务 / 来源通知审计查询接口、当前酒店数据库目录校验、卡片 `fields[]` 白名单、Account / Room Type / Rate Code lookup API、目录管理后台 CP1、订单列表 V4 继续处理入口字段、Room Information New / Update / Cancel 第一版业务展示模型、Rooming List 确认不修改 Group Booking Status,以及 Payment 卡 `payment_attachments[]` 安全摘要。2026-07-21 OWNER RATE `RATECODE (2)` 只读整理已确认:Room Type 第一阶段只维护 `RM2`、`RM3`、`RM4`、`SU1`、`SU2`、`SU3` 六个稳定 code,不建 Account -> Room Type 关系;Rate Code 第一阶段暂不建立 Account 适用关系,Q.B.D / LIAN TAI 的 40 个规范化 Rate Code 作为酒店级目录候选。真实 PMS 同步继续后置,设计见 `M002-v4-real-catalog-lookup-api-design.md`。
2026-07-22 后,MCP `th_hotel_submit_task_results` 也已与本文模型对齐:MCP submit 只接受 M002 V4 包级结构,旧 V2/V3 submit payload 返回 `MCP_SUBMIT_V4_REQUIRED`,不会绕回旧 `workflow_reservation_task` 模型。
@@ -59,7 +59,8 @@ order_contexts[]
message_events[]
-> 每个 event 按 order_ref 挂到对应订单任务
-> 每个 event 创建 1 张业务任务卡
-> 每个 event 创建 1 张原生业务任务卡
-> 仅含 Trace / Rooming List / Payment 时,订单任务额外创建 1 张共享 companion Room
target_order
-> 用于订单任务绑定本地订单投影
@@ -91,7 +92,7 @@ V4 package / event contract error
| `SOURCE_MESSAGE_DISPLAY` | `source_message` | 否 | 否 | 普通业务包内固定展示当前邮件和附件入口 |
| `SOURCE_MESSAGE_NOTIFICATION` | `route_code=S10` | 是,仅确认已读 | 否 | 纯通知邮件,只需要用户确认已读 / 已处理 |
| `BASIC_INFORMATION` | `order_contexts[].basic_information` | 是 | 是 | 订单级 Account / Market / Source 卡,第一版 Agent 只给 `account_code` |
| `ROOM_INFORMATION` | `NEW_BOOKING` / `UPDATE_BOOKING` / `CANCEL_BOOKING` | 是 | 是 | 房型信息卡,卡内按 New / Update / Cancel 展示最终值、差异和系统派生字段 |
| `ROOM_INFORMATION` | `NEW_BOOKING` / `UPDATE_BOOKING` / `CANCEL_BOOKING`;或仅含辅助订单事件时的 companion Room | 是 | 是 | 房型信息卡;生命周期事件展示最终值/差异,Trace / Rooming List / Payment companion Room 展示同订单 current-only 上下文 |
| `TRACE_RESERVATION_NOTES` | `TRACE_RESERVATION_NOTES` | 是 | 是 | Trace 卡,卡内可有普通备注和加床备注多条事项 |
| `ROOMING_LIST` | `ROOMING_LIST` | 是 | 否 | 第一版只做事项确认;不在 Agent 回调里保存名单 rows,不生成 Excel,不导入 PMS |
| `PAYMENT` | `PAYMENT` | 是 | 第一版主要确认附件关联 | 付款凭证卡;业务事实仍是 `attachment_ids[]` 引用包级附件,展示层可返回匹配后的附件安全摘要 |
@@ -102,6 +103,7 @@ V4 package / event contract error
- 来源邮件展示卡不是 event,普通业务包内只读,不参与订单执行阻塞。
- S10/S99 采用来源通知模型:任务列表 / 工作台可见,点击进入纯通知详情页;只显示邮件展示卡和确认按钮,不创建订单、不进订单列表、不参与订单阻塞,也不支持编辑、复核、OPERA 或人工终止。
- 同一 `order_ref` 下如果有多个相同 `event_type`,第一版按 event 数组项分别建卡;页面排序按固定卡片顺序,再按 `source_event_index` 排序。
- 同一订单任务已有 `NEW_BOOKING`、`UPDATE_BOOKING` 或 `CANCEL_BOOKING` 的 Room 时,不再为 Trace / Rooming List / Payment 复制 Room;若订单任务只含这三类辅助事件,则按接收顺序用首个事件生成一张共享 companion Room,并保留原始 `event_type`、transition 与 `source_event_index`。
## 6. 状态设计
@@ -633,7 +635,8 @@ V4 任务详情页的 `SOURCE_MESSAGE_DISPLAY` 固定展示在 Basic Information
Room Information 卡展示模型:
- `ROOM_INFORMATION` 卡只由 `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING` 三类 event 触发;`TRACE_RESERVATION_NOTES`、`ROOMING_LIST`、`PAYMENT` 不触发房型信息卡。
- `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING` 各自生成生命周期 Room;仅含 `TRACE_RESERVATION_NOTES`、`ROOMING_LIST`、`PAYMENT` 的订单任务额外生成一张共享 companion Room,因此六类可定位订单事项都有 Basic + Room 双区块。
- auxiliary companion Room 保留原辅助 `event_type`,但不把辅助 payload 解释成房间变更:`current_values` 与 `final_values` 取同订单已确认投影,`proposed_values={}`、`change_summary=[]`。Trace / Rooming List / Payment 原生卡、确认动作和副作用保持独立。
- SuperAgent 仍只输出字段契约中的业务字段。Nights、Breakfast、Group Booking Status、Block ID、Confirmation Number 和 Adult 不由 SuperAgent 输出;其中 Adult 第一版不在卡内展示。
- 后端已在 `GET /api/reservation/order-tasks/{orderTaskId}` 的 Room Information 业务卡 `display_payload.room_information` 中补稳定展示模型,结构为 `event_type`、`booking_type`、`current_values`、`proposed_values`、`final_values`、`change_summary[]`、`group_booking_status_options[]`。前端按该展示模型渲染业务 UI,不再从 Agent raw payload / `target_order` 自行推导;如果存量或调试数据里已经持久化为稳定 `room_information.final_values` 模型,后端会按该稳定模型归一化展示和复核,不再回退到 Agent raw 推导。`fields[]` 继续作为确认 / 复核的可编辑字段白名单。`fields[].write_target` 对前端只表达请求体目标,例如 `confirmed_payload` 或 `review_resolution.field_overrides`,不暴露后端内部列名。
- `fields[]` 的 Room Information 主路径统一为 `/room_information/final_values/...`,例如 `/room_information/final_values/arrival_date`、`/room_information/final_values/room_items/0/room_type_code`。确认接口收到该结构时,后端会从展示模型派生 `confirmed_payload_json.room_information.final_values`,并重新计算 `nights`、`breakfast_included` 和 `group_booking_status_label`;只读字段、Agent `target_order`、Adult 和前端注入字段不会写入确认快照。`REVIEW_REQUIRED` 状态下,当前卡白名单内业务字段可以返回 `editable=true` 并允许同一 pointer 走 `review-resolution`,不再限定只能修空值、`missing_fields[]` 或目录错误字段。
@@ -642,8 +645,8 @@ Room Information 卡展示模型:
- `CANCEL_BOOKING`:不使用 Agent 输出当前订单快照;后端从本地订单投影读取当前值并只读展示,用户只确认整单取消。Cancel 卡不允许编辑 Group Booking Status、Breakfast、日期、Rate Code 或房型房量。
- `nights` 由后端按酒店本地业务日期计算:`departure_date - arrival_date`,不涉及时区和 UTC;日期缺失、非法或离店早于入住时,`nights` 为空。第一版确认校验要求日期必填,后续如需更严格营业日规则另开 checkpoint。
- `breakfast_included` 是卡片展示和确认使用的布尔字段。Group 固定含早,前端显示勾选且只读;Fit 按 Rate Code 派生,Rate Code 包含 `RB` 时含早,包含 `RO` 时不含早;如果 Rate Code 无法派生,前端显示必填勾选框,由用户确认是否含早。
- Group Booking Status 仅 Group 显示,稳定 code 为 `TEN`、`DEF`、`INQ`,前端显示 `TEN-Tentative`、`DEF-Definite`、`INQ-Inquiry`。New Group 默认 `TEN`;`booking_scenario=STANDARD | PROPOSAL` 仅保留为 Agent 场景参考,不映射 Group Booking Status。`NEW_BOOKING` / `UPDATE_BOOKING` 确认前可手动改选,`CANCEL_BOOKING` 只读。
- `ROOMING_LIST` 卡确认时,如果同订单为 Group,后端已把 Group Booking Status 自动置为 `DEF`,即使此前为 `TEN` 或 `INQ`;该自动变更写入 `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 字段,仍只展示既有确认快照字段。Fit 不显示也不变更 Group Booking Status。
- Group Booking Status 仅 Group 显示,稳定 code 为 `TEN`、`DEF`、`INQ`,前端显示 `TEN-Tentative`、`DEF-Definite`、`INQ-Inquiry`。New Group 默认 `TEN`;`booking_scenario=STANDARD | PROPOSAL` 仅保留为 Agent 场景参考,不映射 Group Booking Status。`NEW_BOOKING` / `UPDATE_BOOKING` 与三类 auxiliary companion Room 确认前可手动改选,`CANCEL_BOOKING` 只读。
- `ROOMING_LIST` 卡确认只更新当前事项卡和订单任务派生状态;无论 Group 或 Fit,均不修改同订单 Room Information、Group Booking Status、订单快照或其他业务卡。当前订单详情 `order_overview` 继续只展示既有确认快照字段。
- `target_order.locator_value` 不作为前端可编辑字段,也不在普通任务详情的 Basic Information 或普通业务卡 `display_payload` / `confirmed_payload` 中返回;订单归属错误时通过 V4 复核选择正确订单或创建正确订单投影,不直接改写 Agent 原始 `target_order.locator_value`。但 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 原始定位字段。普通业务卡还会移除邮件 HTML、raw evidence、附件原始 URL 和 PMS 原始响应等敏感字段。
- Block ID 和 Confirmation Number 第一版只读;存在本地投影或未来 PMS 结果时展示,否则为空。Block ID 仅 Group 显示,Confirmation Number 仅 Fit 显示。
@@ -655,8 +658,9 @@ V4 任务详情页第一版字段白名单:
| `ROOM_INFORMATION` / `NEW_BOOKING` | `group_block_name` 或 `fit_name`、`arrival_date`、`departure_date`、`rate_code`、`room_items[].room_type_code`、`room_items[].room_count`、Group 的 `group_booking_status`、无法从 Fit Rate Code 派生时的 `breakfast_included` | `nights`、Group 固定 `breakfast_included=true`、Fit 可由 Rate Code 派生的 `breakfast_included`、Adult、Block ID、Confirmation Number、Agent 原始 `target_order` |
| `ROOM_INFORMATION` / `UPDATE_BOOKING` | 修改后的 `arrival_date`、`departure_date`、`room_items[].room_type_code`、`room_items[].room_count`、Group 的 `group_booking_status`、无法从 Fit Rate Code 派生时的 `breakfast_included` | 当前值、本次变化摘要、`nights` 差异、Rate Code、Adult、Block ID、Confirmation Number、Agent 原始 `target_order` |
| `ROOM_INFORMATION` / `CANCEL_BOOKING` | 无;用户只确认取消事项 | 本地订单投影当前值、`nights`、`breakfast_included`、Group Booking Status、Rate Code、房型房量、Block ID、Confirmation Number |
| `ROOM_INFORMATION` / `TRACE_RESERVATION_NOTES`、`ROOMING_LIST`、`PAYMENT` | `names_text`、Group 的 `group_code` / `group_name`、`arrival_date`、`departure_date`、`room_items[].room_type_code`、`room_items[].room_count`、Group 的 `group_booking_status`、无法从 Fit Rate Code 派生时的 `breakfast_included` | 同订单 current-only 上下文、`proposed_values={}`、`change_summary=[]`、`nights`、Rate Code、Adult、Block ID、Confirmation Number、Agent 原始 `target_order`;Rate Code 沿用现有仅 New 可写策略 |
| `TRACE_RESERVATION_NOTES` | GENERAL:`trace_items[].text`、`trace_items[].department_code`;EXTRA_BED:`trace_items[].target_room_type_code`、`trace_items[].extra_bed_room_count`、`trace_items[].department_code` | `department_code` 第一版只允许 `FO`、`HSK`、`FO+HSK`,字段返回 `fixed_options[]` 三个固定选项,不允许自由文本;`target_room_type_code` 校验当前酒店 Room Type 目录;`extra_bed_room_count` 必须为正整数;不返回或确认 `content`、`target_order`、邮件正文、附件 URL、raw evidence 或 AI 原始 payload |
| `ROOMING_LIST` | 无;用户只确认 Rooming List 事项 | 来源邮件正文和附件摘要通过底部 `SOURCE_MESSAGE_DISPLAY` 查看;确认后 Group 自动置为 `DEF` |
| `ROOMING_LIST` | 无;用户只确认 Rooming List 事项 | 来源邮件正文和附件摘要通过底部 `SOURCE_MESSAGE_DISPLAY` 查看;确认不改变其他业务事实 |
| `PAYMENT` | 无;用户只确认附件关联事项 | `attachment_ids[]`、`payment_attachments[]` 安全摘要、图片缩略图、文件名、类型、大小、预览 / 下载可用性 |
| `SOURCE_MESSAGE_DISPLAY` | 无 | 当前触发该 V4 order task 的 SourceMessage 正文,只读且默认长度折叠,可展开全文 |
@@ -665,7 +669,7 @@ Rooming List 卡事项确认规则:
- Rooming List 卡第一版只做事项确认,不做名单解析、附件预览、Excel 生成或 PMS 导入。
- `ROOMING_LIST` event 不输出 `rows[]`、逐人名单、同住分组、18 列、Excel 或 PMS 导入参数,也不要求单独输出 `attachment_ids[]`。
- 页面应展示卡片标题、状态、目标订单信息和“确认卡片”按钮;如需查看来源内容,仍通过本订单任务底部的 `SOURCE_MESSAGE_DISPLAY` 查看当前触发 SourceMessage 正文和附件摘要。
- 用户点击“确认卡片”表示已人工处理该 Rooming List 事项;该确认会更新 V4 卡片状态和订单任务派生状态;如果同订单为 Group 且存在可更新的已确认 Room Information 快照,后端同时覆盖该快照里的 `group_booking_status=DEF` 和 `group_booking_status_label=DEF-Definite`,不改变 Agent 原始 payload,并写入可通过 V4 订单任务审计接口查询的 `V4_ROOMING_LIST_AUTO_DEF` 摘要。没有可更新投影时确认仍成功,只记录安全审计提示,不临时创建不完整 Room Information。该动作不代表 M010 Rooming List Excel 已生成,也不代表 PMS / OPERA / OHIP 已执行。
- 用户点击“确认卡片”表示已人工处理该 Rooming List 事项;该确认只更新当前 V4 卡片状态和订单任务派生状态,不改变 Agent 原始 payload、Room Information 确认快照、Group Booking Status 或其他订单事实。该动作不代表 M010 Rooming List Excel 已生成,也不代表 PMS / OPERA / OHIP 已执行。
- 独立 Rooming List Excel 生成能力仍属于 M010 `/reservation/rooming-lists/new` 工具页面,第一版不嵌入 V4 Rooming List 卡。
Payment 卡附件展示规则:
@@ -941,8 +945,9 @@ AI 原始 payload、邮件正文、附件 URL 和技术 trace 不应直接进入
| M002-V4-CP14.6 | Payment 附件预览 | 已完成前后端第一版:Payment 卡返回付款凭证附件安全摘要,不返回 URL;前端通过 SourceMessage 原文权限链路做图片缩略图 / 大图预览和非图片下载 |
| M002-V4-CP14.7 | Rooming List 事项确认卡 | 已完成前端轻量展示:Rooming List 卡第一版只展示事项和确认按钮,不解析名单、不预览附件、不生成 Excel、不导入 PMS |
| M002-V4-CP14.8 | Room Information 展示模型 | 已完成前后端第一版:后端返回 `display_payload.room_information` 稳定展示模型;前端按 New / Update / Cancel 业务表单展示最终值、差异、Nights、Breakfast、Group Booking Status 和本地订单投影;Adult 不显示 |
| M002-V4-CP14.9 | Rooming List 确认自动 DEF | 已完成后端第一版:确认 `ROOMING_LIST` 卡时,Group 同订单存在可更新 Room Information 确认快照则自动置 `DEF` 并写审计;Fit 不变更;无投影不造脏数据 |
| M002-V4-CP14.9 | Rooming List 确认无跨卡副作用 | 2026-08-08 已纠偏:确认 `ROOMING_LIST` 只更新本卡和订单任务派生状态;Group/Fit 均不改 Group Booking Status、Room Information 确认快照或其他业务卡。 |
| M002-V4-CP14.10 | 复核态卡片字段白名单和统一确认交互 | 已完成前端第一版:`REVIEW_REQUIRED` 保持原业务卡内编辑,问题字段红字提示,前端按钮显示“确认卡片”但调用 `review-resolution`;后端 `fields[]` 返回当前卡业务字段白名单,复核写入不再只限空值或目录错误字段 |
| M002-V4-CP14.11 | BR00 辅助订单事件双区块 | 已完成前后端:独立 Trace / Rooming List / Payment 订单任务各补一张共享 companion Room;同组已有生命周期 Room 时去重;详情与确认使用 current-only 模型,原生辅助卡行为不变 |
| M002-V4-CP15 | V4 业务审计查询 | 已完成:`GET /api/reservation/order-tasks/{orderTaskId}/audits` 和 `GET /api/reservation/source-notifications/{notificationId}/audits` 返回卡片确认、复核解阻和来源通知 ack 的脱敏审计流水 |
| M002-V4-CP15.1 | 订单详情 V4 化后端补齐 | 已完成:`GET /api/reservation/orders/{orderId}` 返回 `order_overview`、`next_v4_action`、`related_source_messages[]` 和 `v4_order_tasks[].cards[]`,支撑订单总览页 |
| M002-V4-CP16 | PMS / OPERA / OHIP 目录同步 | 同步 Adapter、同步 run、最后成功快照、失败重试和同步状态管理入口 |
@@ -75,7 +75,9 @@ Reservation V4 还必须满足 `docs/project/ai-nses-project-overlay.md` 的项
### 7.1 Room Information
- 只由 `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING` 触发;`TRACE_RESERVATION_NOTES`、`ROOMING_LIST`、`PAYMENT` 不触发房型信息卡。
- 2026-08-08 起以 BR00/M012 为准:New、Update、Cancel、Trace、Rooming List、Payment 六类订单事件都必须让所属 Order Task 具备 Room Information;此前“辅助三类不触发 Room”的口径已废止。
- New、Update、Cancel 继续直接生成生命周期 Room;只有 Trace、Rooming List、Payment 的 Order Task 生成一张共享 companion Room。同组已有生命周期 Room 时不重复生成,多个辅助 event 也不重复生成。
- companion Room 保留首个辅助 event 的类型和来源序号,但只显示订单 current/final 投影,`proposed_values={}`、`change_summary=[]`,不得把辅助业务字段解释为房间变更。
- 后端提供 `display_payload.room_information` 稳定展示模型,前端不从 Agent raw payload、`business_fields` 或 `target_order` 自行推导。
- `room_items[]` 支持多个房型行。每个 `room_items[].room_type_code` 必须是单个当前酒店 Room Type 目录 code;`RM2/RM3` 这类组合值必须拆成多行,不作为合法单 code。
- `NEW_BOOKING` 展示最终值;`UPDATE_BOOKING` 展示当前值、建议值、最终值和 `change_summary[]`;`CANCEL_BOOKING` 从本地订单投影只读展示。
@@ -101,9 +103,7 @@ Reservation V4 还必须满足 `docs/project/ai-nses-project-overlay.md` 的项
- Rooming List 卡第一版只做事项确认。
- 不做名单解析、附件预览、Excel 生成、PMS / OPERA / OHIP 导入。
- 用户点击“确认卡片”表示已人工处理该 Rooming List 事项。
- 如果同订单为 Group 且存在可更新的已确认 Room Information 快照,确认 Rooming List 后后端自动把 Group Booking Status 置为 `DEF`,并写 `V4_ROOMING_LIST_AUTO_DEF` 审计。
- 如果此前 Group Booking Status 是 `TEN` 或 `INQ`,确认 Rooming List 后也强制覆盖为 `DEF`。
- Fit 不显示也不变更 Group Booking Status。
- 确认 Rooming List 只确认该事项本身,不修改 Group 或 Fit 的 Group Booking Status、Room Information 确认快照或其他业务卡。
- 独立 Rooming List Excel 生成仍属于 M010 `/reservation/rooming-lists/new`,不嵌入 V4 Rooming List 卡。
### 7.4 Trace
@@ -166,10 +166,11 @@ Reservation V4 还必须满足 `docs/project/ai-nses-project-overlay.md` 的项
| --- | --- | --- | --- | --- | --- |
| Room Information 展示模型 | Done | Done | Passed | `M002-v4-order-task-card-domain-model-cp2.md`、前后端协作文档 | Implemented |
| Room Information 多 `room_items[]` 展示 | Done | Done | Passed | 本文、V4 领域模型、Lookup 文档 | Implemented |
| Trace / Rooming List / Payment 的 Basic + companion Room | Done | Done | Passed | M012 4.5、本文 7.1、V4 领域模型 | Implemented |
| Nights 后端派生 | Done | Done | Passed | V4 领域模型、Lookup 文档 | Implemented |
| Breakfast 派生:Group 固定含早,Fit 按 RB / RO | Done | Done | Partially Covered | V4 领域模型、Lookup 文档 | Implemented |
| Adult 不展示 | Done | Done | Passed | V4 领域模型、前后端协作文档 | Implemented |
| Group Booking Status:TEN / DEF / INQ 和 Rooming List 自动 DEF | Done | Done | Passed | V4 领域模型、安全边界、审计文档 | Implemented |
| Group Booking Status:TEN / DEF / INQ 与 Rooming List 无跨卡副作用 | Done | Done | Passed | V4 领域模型、安全边界、审计文档 | Implemented |
| Payment 附件安全摘要和前端预览 | Done | Done | Passed | V4 领域模型、安全边界、前后端协作文档 | Implemented |
| Payment `attachment_ids[]` 只读、确认只提交 `version` | Done | Done | Passed | V4 领域模型、前后端协作文档 | Implemented |
| Rooming List 轻量事项确认卡 | Done | Done | Passed | V4 领域模型、Agent 字段契约、前后端协作文档 | Implemented |
@@ -205,13 +206,14 @@ Reservation V4 还必须满足 `docs/project/ai-nses-project-overlay.md` 的项
- Given Room Information 存在多个 `room_items[]`,When 打开 V4 任务详情,Then API 和页面都应展示多行房型,不把组合 code 当成单个合法房型。
- Given Payment 卡引用图片和非图片附件,When 打开 V4 任务详情,Then 任务详情 API 只返回附件安全摘要,前端通过 SourceMessage 原文权限链路展示图片预览和非图片下载。
- Given 用户触发 Payment 图片预览或非图片下载,When 浏览器渲染预览或下载入口,Then DOM `src/href` 可以临时使用 conversation 接口返回的受权限附件 URL,但页面可见文本、V4 task detail API、确认 payload、日志和本地存储仍不得暴露该 URL。
- Given Rooming List 卡被确认且同订单 Group 有可更新 Room Information 快照,When 刷新详情,Then Group Booking Status 显示 `DEF-Definite` 并可查到自动 DEF 审计。
- Given Rooming List 卡被确认,When 刷新详情,Then 仅该卡变为确认态;同订单 Group Booking Status、Room Information 快照和其他业务卡保持原值。
- Given Trace 卡确认或复核成功,When 刷新详情,Then `fields[].value` 显示已确认值,旧 validation errors 清空。
- Given 一个 Order Task 只包含 Trace、Rooming List 或 Payment,When V4 intake 完成并查询详情,Then 返回一张 Basic、一张 current-only companion Room 和对应专属卡;同组已有 New/Update/Cancel 时 Room 不重复。
## 11. 测试范围
- 单元测试:本文不新增代码测试;后续代码变更仍按对应前后端模块测试要求执行。
- 集成测试:本文不改变接口;已有 smoke 已覆盖主要 V4 卡片链路。
- 单元测试:Room current-only 分类、辅助事件字段白名单与 lifecycle Room 去重按对应服务测试覆盖。
- 集成测试:新增独立 Trace、Rooming List、Payment 的建卡数量、卡片顺序、Room 展示模型和敏感字段回归。
- 手工验证:本次文档 checkpoint 使用 `git diff --check` 验证 Markdown 格式。
- 后续 smoke:单卡可操作态数据集已回填;后续如需要可继续用 fresh runId 造演示数据,避免复用旧 SourceMessage 时间线造成阻塞误判。
@@ -0,0 +1,384 @@
# M012 酒店预订邮件识别到人工确认端到端 V0.1
| 项目 | 内容 |
| --- | --- |
| 文档状态 | V0.1 已实现并验收;2026-08-08 补充 BR00 订单卡双区块后端收口 |
| 业务基线 | `BR00-BASELINE-1`,高于本仓库旧样例、旧 prompt 和旧字段语义 |
| 适用范围 | 员工上传真实 `.eml` 或 AgentBus 投递邮件后,到任务卡关键参数被用户确认为止 |
| 非适用范围 | 确认后的 Opera / PMS API 调用、客户回复、Invoice、独立 Manual RateCode、Rooming List 名单解析 |
| Catalog 版本 | `booking-catalog-v20260808` |
| Parser 版本 | `booking-email-parser-v0.1` |
本次“已实现并验收”指三封指定真实邮件覆盖的固定渠道纵向切片:普通员工手工导入、LIANTAI/QBD workbook 确定性解析、V4 建卡、字段复核与人工确认。第 4 节仍完整保存 BR00 业务语义,但未被这三封样本覆盖的 Allotment、独立 Rooming List、独立 Payment、图片语义和统一 Booking Agent fallback,不因本次切片通过而宣称已完成;其现状和边界见第 15 节。
## 1. 背景与架构位置
AgentBus 同时承担邮件消息入口和 Agent 平台。本项目不能把“附件解析”孤立成一次文件读取:邮件正文、当前回复、历史往来、附件版本和本次附件中的更新行共同决定业务动作。
本期沿用七层架构,并把邮件入口耦合进去:
1. **信息系统接入与编排**:接收 AgentBus 邮件或员工上传的 `.eml`,建立 SourceMessage、附件摘要、幂等键和处理批次。
2. **材料预处理**:拆分 current 与 quoted history,识别附件版本,对固定渠道 Excel 只选择本次候选业务行,并保留 sheet/行号/底色等证据。
3. **确定性 Parser + Catalog**:用版本化业务目录把渠道字段、动作别名、房型写法和数值编码归一为标准事实;不能可靠解析的字段保持未解决,不猜测。
4. **当前态与邮件上下文**:按会话读取历史 SourceMessage 和既有业务任务,向业务识别提供“当前邮件事实 + 必要历史事实”,历史内容不重复触发动作。
5. **业务识别**:程序先处理固定渠道可确定部分;失败或存在语义歧义时,把隐私最小化的材料交给 Booking Agent。输出业务事件、关联事件和通知,不直接调用 PMS。
6. **确定性校验与人工确认**:同一 Catalog 校验程序或 Agent 输出,生成可编辑任务卡、缺失字段、告警和证据;即使全部字段符合目录,也必须由用户确认。
7. **执行适配器**:确认后调用 PMS/Opera,明确不在 M012 V0.1 范围。
Catalog 不单独构成业务层。它是第 3 层的版本化确定性知识,同时被 Parser、第 5 层 Agent reference 和第 6 层校验器消费。三者必须记录同一个 Catalog 版本,避免同一来源词在不同环节被翻译成不同业务代码。
## 2. 目标
- 提供普通员工可用、受现有权限保护的 `.eml` 导入 API 和可点击页面,不依赖 Debug Key。
- 保存一封实际邮件一次;相同实际邮件重复投递命中幂等,分别发送的邮件分别处理。
- 对固定渠道 Excel 按“业务范围内整行均有非白色底色”选择候选行,而不是任一单元格有颜色。
- 将候选行确定性解析成统一事实,生成 New / Update / Cancel 及 Trace、Rooming List、Payment 或 General/Risk 通知。
- 复用 SourceMessage 与 Reservation V4 任务卡、目录 lookup、人工复核、确认和审计主线。
- 在 UI 展示邮件证据、解析证据、告警、关键字段和确认阻断;用户可修正允许字段并逐卡确认。
- 用三封 2026-08-08 指定真实邮件做只读验收输入,并用去隐私合成 fixture 建立自动化回归。
- 提供 PostgreSQL 项目专属 Schema `th_hotel_booking` 的版本化迁移与验证脚本;远程执行前必须只读确认无同名冲突及权限边界。
## 3. 非目标
- 不在本期执行 PMS/Opera 操作,也不伪造执行成功。
- 不自动回复邮件、发送通知或修改 AgentBus 外部状态。
- 不以 Tour Code 代替 Group Code,也不根据样例猜 Account / Market / Source / Rate Code 映射。
- 不把价格作为前端结构化确认字段;价格只参加未来 Rate Code 映射并留在来源证据中。
- 不解析 Rooming List 旅客名单、不导入旅客信息、不做名单差异比对。
- 不将真实邮件、真实附件、旅客信息、邮箱地址或数据库密码提交到 Git。
## 4. 统一业务语义
### 4.1 生命周期与任务
订单生命周期事件:
- `NEW_BOOKING`
- `UPDATE_BOOKING`
- `CANCEL_BOOKING`
同订单可附带的独立任务卡:
- `TRACE_RESERVATION_NOTES`
- `ROOMING_LIST`
- `PAYMENT`
来源邮件级通知:
- `S10` General:整封当前邮件没有任何支持的业务任务时最多一条。
- `S99` Risk:当前邮件存在无法分类或高风险歧义时最多一条;已识别的清晰任务仍继续创建。
已知类型但缺字段时保留原类型卡并进入 `REVIEW_REQUIRED`,不得降格成 Risk。
### 4.2 名称和订单字段
- `Tour Code` 是 `Name` 的一个可能来源值。
- `Name of Group`、`Group Name` 在本期统一为 `Group Name = Block Name`。
- `Group Code` 是独立字段,不能由 Tour Code 推导。
- 一个清晰预订分组生成一个订单任务;多个 Name、多个房型、多个日期段可以属于同一订单,不按 Name 拆卡。
- New Booking 的实际总房数 `< 5` 为 `FIT`,`>= 5` 为 `GROUP`。
- 所有订单相关任务均有 Basic Information 与 Room Information。
- Basic Information 只允许由已确认发件人映射生成;映射未配置时 Account/Market/Source 留空并阻断确认。
### 4.3 New Booking 确认条件
通用必填:
- Booking Type
- 一个或多个 Name
- Arrival Date、Departure Date;Nights 由日期派生
- 至少一个 Room Type + Quantity
- 一个订单级 Rate Code
Group 额外必填:
- Group Code
- Group Name / Block Name
Rate Code 根据公司、房型、价格、早餐信息映射,但当前映射仍待配置。零候选或多候选都保留 New Booking,让用户选择并阻断确认;系统不得自动选择。
### 4.4 Update、Cancel 与关联任务
- `AMEND`、`AMD`、`AMEND TO` 等归一为 Update;旧 Group Code → 新 Group Code 仍视为同一订单链。
- 每封实际 Update 邮件、每个订单生成一张新的 Update 卡,不按历史内容做业务去重。
- Cancel 只在未来 PMS 执行成功后结束订单;本期只确认 Cancel 动作参数。
- Allotment:N 个实际团生成 N 张 New 卡,并生成一个共享来源扣减/取消动作;来源动作失败不阻断实际团。
- Extra Bed 是 Trace,不是 Room Type;同一 Group Code、同一当前邮件的多个 Trace item 合并一张卡。
- Trace 的 Department 必须由用户选择 FO、HSK 或 FO+HSK 才能确认。
- Rooming List 与 Payment 的原生事项卡仍是通知型卡,不解析名单、不核验付款;其 companion Room 只补订单上下文,不改变该业务边界。
### 4.5 订单任务双区块与 companion Room
- 每个可定位订单的 Order Task 固定包含一张 `BASIC_INFORMATION`;New、Update、Cancel、Trace、Rooming List、Payment 都必须同时具备 `ROOM_INFORMATION`,General/Risk 来源通知不适用。
- New、Update、Cancel 继续由生命周期 event 直接生成 Room Information。若同一 `source_message + order_ref` 已有任一生命周期 event,不再为同组 Trace、Rooming List、Payment 重复生成 Room。
- 若一个 Order Task 只有 Trace、Rooming List、Payment,则以后端接收顺序中的首个关联 event 生成一张共享 companion Room,并保留该 event 的类型、transition 和 `source_event_index` 以便追溯;不能伪装成新的 `UPDATE_BOOKING` event。
- companion Room 只展示同订单已确认完成态或前置 New 计划完成态:`current_values` 与 `final_values` 使用该投影,`proposed_values` 为空,`change_summary` 为空;辅助 event 自身字段不得被当成房间变更。
- 同一 Order Task 的多张业务卡仍分别确认;Basic Information 保持前置门禁。Trace、Rooming List、Payment 专属卡、附件安全边界和确认副作用不因 companion Room 改变。
- 本增量只改变新建订单任务的 intake 行为;已持久化且已有卡片的历史 V4 Order Task 受现有幂等门禁保护,不在重放时隐式补卡。若存量也要补齐,必须另做可审计、可回滚的受控回填。
## 5. 邮件与上下文边界
### 5.1 current/history
- 当前邮件的正文、当前附件和明确的当前指令可以触发业务。
- quoted history 只用于解释当前语义、继承订单身份和检测重复,不可再次触发旧动作。
- 没有附件时不经过 Excel 预处理,直接进入上下文组装和业务识别;并非跳过 SourceMessage 接入。
- 当前正文仅有标题/签名、但附有明确更新附件时,附件是本次动作核心证据。
### 5.2 附件版本
- 当前邮件明确写 `REV.n`、`use this file` 等版本指令时,优先当前附件。
- 当前版本没有严格候选行或只有部分底色时,可以和同会话前版本做差异,差异只作为复核证据,不直接成为自动执行事实。
- 同一事实再次出现时仍展示重复/陈旧告警,由用户判断,不静默吞掉新邮件。
## 6. 固定渠道预处理
### 6.1 共同底色口径
- 只认单元格底色,不认字体颜色、批注、筛选或条件格式推测。
- 白色、默认色、无填充不算。
- 只检查渠道 Catalog 定义的业务列范围;该范围内每个业务列都必须有非白色底色,才是 `STRICT_CURRENT_CANDIDATE`。
- 一条渠道业务记录可以由 anchor row 与后续 continuation rows 组成;Tour Code/酒店明细在 anchor,当前动作在后续行时,以该 logical record block 的业务列底色并集判断完整性,并同时记录 anchor/action row。不得要求所有字段出现在同一物理行。
- 只有部分业务列有底色时记为 `PARTIAL_FILL_REVIEW_EVIDENCE`,不自动生成事实。
- 表头颜色不触发业务行。
### 6.2 渠道 Profile
| Profile | 表头 | 业务列 | 动作列 | 主要来源列 |
| --- | ---: | --- | --- | --- |
| `LIANTAI_FIT` | 3 | A:F | F | Tour Code=B;酒店明细=D/E |
| `QBD_MONTHLY` | 3 | B:H | H | Tour Code=C;酒店明细=E |
| `LIANTAI_UPDATE` | 2 | A:I | G | Tour Code=A;酒店明细=D;状态=H;备注=I |
动作别名必须由 Catalog 版本管理,至少包含:
- New:`NEW BOOKING`
- Update:`UPDATE`、`AMEND`、`AMD BOOKING`、`AMEND TO`
- Cancel:`CANCEL`、`CXL`、`ยกเลิก`
不得为了“程序识别”无限穷举整句。Parser 先分离动作核心 token、动作日期和自由文本,再用有限别名字典归一;不能可靠归一时降级 Agent 或人工复核。
### 6.3 当前日期与异常年份
- 候选行的动作 marker 优先按日/月与邮件接收或发送日期匹配。
- FIT 文件若因 Excel 自动填充出现 2026、2027……2041 的异常连续年份,日/月匹配的行仍保留为候选,并加 `ACTION_DATE_YEAR_ANOMALY`。
- 异常年份不能作为真实业务年;入住/离店年份以邮件业务日期和 sheet 月份解析,并在不唯一时留待复核。
- QBD 中严格底色但动作日期早于当前邮件日期的行保留证据并加 `STALE_STRICT_CANDIDATE`,不得静默当成本次唯一事实。
## 7. Parser 与 Agent 分工
### 7.1 确定性 Parser 输出
Parser 输出事实,不输出 PMS 命令:
```json
{
"catalog_version": "booking-catalog-v20260808",
"parser_version": "booking-email-parser-v0.1",
"source": {
"attachment_name": "safe-name.xlsx",
"profile": "LIANTAI_UPDATE",
"sheet": "safe-sheet",
"row_number": 57,
"selection_status": "STRICT_CURRENT_CANDIDATE"
},
"action": "NEW_BOOKING",
"names": ["TOUR-CODE-VALUE"],
"group_code": null,
"group_name": null,
"arrival_date": "2026-08-12",
"departure_date": "2026-08-15",
"room_items": [
{
"source_label": "U-TWN8.5",
"room_type_code": "RM3",
"room_count": 12
}
],
"rate_code": null,
"warnings": ["RATE_CODE_UNRESOLVED"]
}
```
固定渠道优先走程序:
1. Profile、严格候选行、动作和字段均可确定时,由 Parser 生成标准事实。
2. 某个字段失败时保留已确定字段,标注具体 warning;不丢弃整行。
3. 需要自然语言语义、图片、复杂历史关系或未知格式时,将最小必要材料交给 Booking Agent。
4. Agent 输出仍必须经过相同 Catalog 版本的确定性校验和人工确认。
### 7.2 初始房型映射
当前可确定映射:
- `U-DBL`、`Sup DBL` → `RM2`
- `U-TWN`、`Sup TWN` → `RM3`
- `U-TRP`、`Sup TRP`、`TRP` → `RM2`
- `DBL SUITE` → `SU1`
- 明确 `TWN SUITE` → `SU2`
- `FAM 6+4` → `RM4`
- Family 3/4 → `SU3`
`ST:1卧双标 TWN` 等未被当前目录唯一覆盖的变体必须留空并复核。即使不同原始房型映射到同一 PMS code,也必须分别保留 raw label 与数量,不能按 PMS code 合并。
## 8. 后端接口
### 8.1 普通员工 EML 导入
`POST /api/reservation/booking-email-intakes`
- `multipart/form-data`
- `file`:必填,只接受 `.eml`
- `hotel_id`:可选,仍以当前登录用户酒店上下文校验
- 权限:`RESERVATION_TASK_EDIT`
- 文件大小遵循 Spring multipart 和本模块安全上限
- 不接收 Debug Key,不直接接受外部附件 URL
成功返回 HTTP 201:
```json
{
"source_message_id": "123",
"duplicate": false,
"status": "TASKS_CREATED",
"catalog_version": "booking-catalog-v20260808",
"parser_version": "booking-email-parser-v0.1",
"order_task_ids": ["501"],
"source_notification_ids": [],
"warnings": [
{
"code": "RATE_CODE_UNRESOLVED",
"message": "Rate Code 需要人工选择"
}
]
}
```
重复投递返回同一 SourceMessage 和已有任务/通知,不重复创建。
### 8.2 内部 V4 契约增量
- `names[]` 是 Agent/Parser 的标准数组;V0.1 前端投影为换行分隔 `names_text` 便于编辑。
- `group_code` 与 `group_name` 分别保存;兼容读取旧 `group_block_name` / `fit_name`,新入口不再错误默认。
- `booking_type`、`group_code`、`group_name`、`names_text`、日期、房型数量、Rate Code 进入 Room Information 的最终值。
- `nights` 为派生只读字段。
- `block_id` 不展示。
- Confirmation Number 仅当当前邮件明确提及时展示。
- `recognition` 安全块保留 profile、sheet、anchor row、action row、版本、选择状态和 warning code,不包含完整原始行、附件 URL 或旅客隐私。
- 新订单在确认前允许 `order_id` / target binding 未解决;人工复核不得强制用户输入已存在本地订单 ID。Update/Cancel 等既有订单动作仍要求可靠归属。
- V4 intake 必须按 4.5 的规则保证每个订单任务具有 Room Information;这是同一 Order Task 的展示/确认上下文补齐,不要求 Parser 或 Agent 在 Trace、Rooming List、Payment event 中复制完整房间字段。
- `GET /api/reservation/order-tasks/{orderTaskId}` 对 companion Room 返回与生命周期 Room 相同的安全 `display_payload.room_information` 和 `fields[]` 结构;辅助 event 采用 current-only 模型,不返回 Agent `target_order`、邮件正文、附件 URL 或原始 evidence。
## 9. 前端验收
新增路由:`/reservation/email-intake`,权限 `RESERVATION_TASK_EDIT`。
页面必须是真实可操作页面并连接后端:
- 点击或拖放选择一封 `.eml`;显示文件名、大小和移除/重新选择。
- 导入按钮有 idle、uploading、success、partial warning、duplicate 和 error 状态。
- 成功后展示 SourceMessage ID、解析版本、任务数、通知数和 warning。
- 每个创建结果可点击进入现有订单任务详情或来源通知详情;可返回任务队列。
- 任务列表提供“导入邮件”入口。
- 任务详情展示识别证据和 warning;Basic Information 必须先确认,随后业务卡可编辑/确认。
- New Booking 未绑定既有订单时隐藏或明确标注“无需现有订单 ID”,不得用不可理解的强制输入阻塞用户。
- 具备键盘焦点、可见 label、错误关联、响应式布局及 loading/empty/error 状态。
## 10. 安全、隐私与审计
- 原始 EML 与附件内容仅在受控 SourceMessage 原文能力或临时解析内使用;普通任务 API 只返回安全摘要。
- 日志、错误、前端结果和 migration 不输出原始正文、真实邮箱、完整原始行、OSS 签名 URL、数据库密码或其他 Secret。
- 真实样本不得复制到 `server/src/test/resources`;测试 fixture 必须合成并去隐私。
- 用户对 Basic、Room、Trace 等卡的复核/确认继续写现有审计日志。
- 业务操作记录保留要求为 3 个月;V0.1 先保存 `retention_until`/可清理时间语义,清理调度不在本期强制范围。
## 11. PostgreSQL 项目专属 Schema
- Schema 固定为 `th_hotel_booking`。
- migration 必须显式 `CREATE SCHEMA IF NOT EXISTS th_hotel_booking` 并限定 `search_path` 或使用全限定名。
- 不修改 `public`,不复用旧脚本中的 `booking` schema,不删除或改名其他项目对象。
- 远程执行顺序:只读读取 `current_database/current_user` → 列出同名 schema/对象 → 检查 CREATE/USAGE 权限 → 创建本 schema → 只在本 schema 运行 migration → 对比其他 schema 对象计数/更新时间。
- 密码只通过进程环境或交互输入,绝不写入仓库、planning 文件、命令回显或交付文本。
- 当前 Spring 应用仍以 MySQL/H2 为运行基线;PostgreSQL migration 是项目专属目标模型,数据库方言切换不在本期暗中完成。
## 12. 测试与完成定义
### 12.1 自动化
- EML:Message-ID、Conversation-ID、current/history、无附件和多个附件。
- Excel:三种 Profile、整行业务列有非白底色、部分底色、白色、表头颜色、异常年份、陈旧严格行、多个日期段。
- Parser:动作别名、房型/价格/数量、New 房量阈值、Update、Cancel、Extra Bed Trace、未解决字段。
- 业务:一单多 Name/房型/日期段不误拆;三封样本中的生命周期/linked Trace;无确定事实时单封通知;重复投递幂等。Allotment、独立 Rooming List/Payment 与图片语义不属于本次固定渠道切片的完成证据。
- API:权限、文件类型/大小、201、重复结果、错误安全、任务创建。
- V4:新订单不要求既有 order ID;Basic 先确认;关键字段阻断;确认审计。
- V4 双区块:独立 Trace、Rooming List、Payment 各自创建 Basic + companion Room + 专属卡;同组已有 New/Update/Cancel 时不重复 Room;详情返回 current-only Room 模型。
- 前端:上传成功/失败/重复、结果链接、字段渲染、warning、复核与确认请求。
### 12.2 三封真实邮件验收
真实样本只从用户指定绝对路径读取:
1. FIT LIANTAI:动作日/月候选可被选中;异常连续年份产生 warning,不被当成 2041 年业务日期。
2. QBD REV.2:当前严格行与陈旧严格行被区分;同一 Tour Code 的多个日期段和房型保持一个订单任务。
3. Update Booking ครั้งที่2:严格底色行生成 9 New/1 Update;Honeymoon 和 Extra Bed 分别生成同订单 Trace;未确认映射保持待复核。
### 12.3 完成定义
只有以下条件同时满足才可宣布 V0.1 完成:
- 普通员工可在可点击页面上传三封真实邮件。
- 后端真实创建 SourceMessage、订单任务/通知和任务卡,重复上传不重复建任务。
- 页面可查看证据、warning、关键参数,并能完成允许范围内的修正与确认。
- 没有执行 PMS/Opera。
- 后端和前端自动化通过;本地全栈 smoke 通过。
- PostgreSQL migration 通过本地语法/作用域验证;若远程测试库凭据可安全注入且只读检查无冲突,则项目 schema 已创建并证明未触碰其他 schema。
- 自主推断规则、应用代码位置、未解决业务映射和测试证据已记录。
## 13. 与 M011 的关系
M011 仍描述“给 SuperAgent 的通用 Excel 高亮证据增强”,其 V1 规则是任一业务单元格有底色即可抽取。M012 在固定渠道、本期业务入口中采用更严格且已获业务确认的“业务范围整行均有非白底色”规则,并直接形成标准事实候选。
两者不应静默混用:
- M011 通用 extraction 继续供 Debug/AgentBus 证据预览。
- M012 deterministic parser 使用独立的版本化 Channel Profile 和严格行选择器。
- 后续若把 M012 规则推广到 M011,需单独 Change Request 和回归验证。
## 14. 待配置而非待猜测
- 发件人/渠道 → Account Code / Account Name / Market Code / Source Code 映射。
- 公司 + 房型 + 价格 + 早餐 → 唯一 Rate Code 映射。
- `TWN SUITE` 的正式房型代码。
- Allotment 来源扣减动作在未来 PMS 适配器中的命令结构。
- AgentBus 生产投递的认证/重试配置和 Booking Agent profile 版本。
这些项不阻止识别和建卡,但相关卡必须保持 `REVIEW_REQUIRED` 或明确 warning,不能用样本值代填。
## 15. 实施与验收结果
2026-08-08 已完成以下纵向闭环:
- 普通员工页面 `/reservation/email-intake` 可选择或拖放 `.eml`,调用真实 `POST /api/reservation/booking-email-intakes`,显示解析结果、warning,并跳转到真实 V4 订单任务或来源通知。
- 三封外部真实 EML 的 API 验收结果依次为 20、1、10 张订单任务;业务构成为 19 Update + 1 Cancel、1 Update、9 New + 1 Update,并在第三封中生成 3 张 linked Trace;重复导入不重复建任务。
- 浏览器全栈 smoke 已从任务队列进入导入页,上传第三封真实邮件,打开首张任务,选择 Account 并成功确认 Basic Information;桌面和 390px 宽度无横向溢出,fresh session 无 console warning/error。
- 后端全量测试 441 项通过,0 failure、0 error、1 skipped;真实样本 opt-in 验收 1 项通过。
- 前端 29 个测试文件共 255 项通过,TypeScript typecheck 和生产 build 通过;lint 0 error,本轮文件无新增 warning。
- 测试库已仅创建 `th_hotel_booking`:9 张表、8 个 identity sequence、30 个索引/约束索引;迁移前后其他项目 schema 对象计数不变,无跨 schema 外键;回滚型 smoke 后无残留测试行。
- 本期没有调用 PMS/Opera、发送外部邮件或修改 AgentBus 外部状态。
V0.1 的已知运行边界:
- 普通员工手工导入路径在请求内解析附件,SourceMessage 和任务保存附件元数据与安全识别证据,但不长期保存可下载的原始附件字节;如需原件归档/下载,应接入既有 AgentBus/OSS 原文链路后另做 checkpoint。
- 固定渠道确定性 Parser 无法形成事实时,当前手工导入路径生成 S10/S99 来源通知供人工处理;不会在该请求内自动调用外部 Booking Agent。既有 AgentBus → SuperAgent 路径继续独立存在,生产启用和统一 fallback 编排需后续配置与联调。
- Account/Market/Source、Rate Code 和未唯一覆盖的房型映射仍按本章第 14 节由用户确认,不以真实样本值自动猜测。
- 确认后的 PMS/Opera 执行仍明确不在 V0.1。
## 16. 2026-08-08 BR00 双区块增量追踪
| 需求项 | 后端状态 | 前端状态 | 测试状态 | 文档位置 | 当前状态 |
| --- | --- | --- | --- | --- | --- |
| 独立 Trace 补 Basic + companion Room | Done | Done | Passed | 本文 4.5、8.2、12.1 | Implemented |
| 独立 Rooming List 补 Basic + companion Room | Done | Done | Passed | 本文 4.5、8.2、12.1 | Implemented |
| 独立 Payment 补 Basic + companion Room | Done | Done | Passed | 本文 4.5、8.2、12.1 | Implemented |
| 同组生命周期 Room 去重 | Done | N/A | Passed | 本文 4.5 | Implemented |
| General/Risk、权限、酒店隔离与敏感数据边界不变 | N/A | N/A | Passed(既有回归) | 本文 4.5、10 | Verified |