docs: 收口 V4 任务卡展示与确认口径

This commit is contained in:
andy committed 2026-07-20 23:57:01 +07:00
1 parent c5b5ce6aab
commit c1163457a9
12 files changed
+253 -98

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` 已实现。真实 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` 已实现。下一阶段已确认 Rate Code 需要按订单级 Account + `booking_type`(GROUP / FIT)过滤和校验,当前实现仍是酒店级 Rate Code 目录;Room Information 卡需要补 New / Update / Cancel 展示模型、Nights / Breakfast 派生、Group Booking Status 和 Rooming List 确认联动,但这些不扩大 SuperAgent 输入字段;Payment 卡需要补付款凭证附件安全摘要和预览 / 下载联动;真实 PMS 同步仍后置,方案见 `M002-v4-real-catalog-lookup-api-design.md`。
当前已确认开发阶段数据可以清空,因此 M002 V4 后续可以按新模型重建,不要求兼容旧任务数据、旧草稿、旧 OPERA 模拟、旧 `S000/S999`、旧 Fallback 或旧 `case_keys`。
@@ -154,7 +154,7 @@ SuperAgent、Adapter、MCP Schema 和信息系统最终必须使用完全相同
URL 由上游邮件监听或文件存储层提供。Agent 只原样转发,不生成、拼接、刷新或签发 URL。
Payment 只按 `attachment_ids[]` 引用附件,不在 Event 内复制文件名、类型、URL 或完整附件对象。
Payment 只按 `attachment_ids[]` 引用附件,不在 Event 内复制文件名、类型、URL 或完整附件对象。前端 Payment 卡的缩略图、预览和下载由本系统根据 `attachment_ids[]` 匹配当前 SourceMessage 附件后展示;这不改变 SuperAgent 输入结构。
## 7. order_contexts
@@ -199,7 +199,7 @@ Agent 给出非空 `account_code`,但信息系统运行时目录不存在该
- Market / Source:由 Account 派生,当前分别为 `LEISURE` / `TRAVEL_AGENT`。
- SuperAgent 不需要输出 `market_code`、`source_code`、`account_name`,也不要输出显示名称替代 `account_code`。
- 如果 SuperAgent 输出的 `account_code` 不在上述目录,后端会创建 `REVIEW_REQUIRED` Basic Information 卡,并在 `validation_errors_json` / 查询 `fields[].validation_errors` 中返回目录错误。
- 如果 SuperAgent 输出的业务卡 `room_items[].room_type_code` 或 `rate_code` 不在当前酒店数据库目录,后端会创建 `REVIEW_REQUIRED` 业务卡,并在 `validation_errors_json` / 查询 `fields[].validation_errors` 中返回目录错误;用户可通过 V4 复核解阻接口提交对应字段 pointer 修正。
- 如果 SuperAgent 输出的业务卡 `room_items[].room_type_code` 或 `rate_code` 不在当前酒店数据库目录,后端会创建 `REVIEW_REQUIRED` 业务卡,并在 `validation_errors_json` / 查询 `fields[].validation_errors` 中返回目录错误;下一阶段 `rate_code` 还必须属于该 `order_ref` 的 Basic Information Account + 当前 event `target_order.booking_type` 的适用关系,否则同样进入 `REVIEW_REQUIRED`。用户可通过 V4 复核解阻接口提交对应字段 pointer 修正。
## 8. message_events 公共字段
@@ -336,7 +336,7 @@ Fit 条件字段:
| --- | --- | --- | --- |
| `arrival_date` | Group / Fit | 是,可为 `null` | 入住日期,酒店本地日期 |
| `departure_date` | Group / Fit | 是,可为 `null` | 离店日期,酒店本地日期 |
| `rate_code` | Group / Fit | 是,可为 `null` | 订单级 Rate Code |
| `rate_code` | Group / Fit | 是,可为 `null` | 订单级 Rate Code;下一阶段必须属于该订单 Account + `booking_type` 的适用范围 |
| `room_items[]` | Group / Fit | 是 | 完整房型清单 |
| `room_items[].room_type_code` | Group / Fit | 是,可为 `null` | 受控 RoomType code |
| `room_items[].room_count` | Group / Fit | 是 | 房量,正整数 |
@@ -347,9 +347,10 @@ Fit 条件字段:
- Group Code 已在 `target_order.locator_value`,同时作为 Block Name。
- Fit Booking Code 已在 `target_order.locator_value`。
- Agent 不输出 `booking_name`、独立 `booking_code`、Nights、Breakfast、Group Booking Status、Adult、Block ID 或 Confirmation Number。
- Agent 不输出 `booking_name`、独立 `booking_code`、Nights、Breakfast、Group Booking Status、Adult、Block ID 或 Confirmation Number;这些属于本系统 Room Information 展示模型、系统派生字段、本地订单投影或未来 PMS 返回。New Booking 页面允许用户编辑最终订单投影字段 `group_block_name` / `fit_name`,但不改写 Agent 原始 `target_order.locator_value`。
- `proposal` 不再使用布尔字段,改为 `booking_scenario=STANDARD | PROPOSAL`。
- Adult 由信息系统按 RoomType 映射派生。
- Adult 第一版不在 Room Information 卡展示。
- New Group 的 Group Booking Status 由本系统默认 `TEN`,`booking_scenario` 只作为 Agent 场景参考,不映射 Group Booking Status。
- `room_items=[]` 只能表示已识别 New 但完整房型清单未解决,并必须 `manual_review=true`。
## 12. UPDATE_BOOKING
@@ -399,7 +400,7 @@ Fit 条件字段:
- 不使用空数组表达“无法形成完整房型清单”。
- 如果修改后整笔订单不再保留任何房间,应按整单 `CANCEL_BOOKING` 处理,而不是提交空的 Update 房型清单。
- Rate Code 不允许出现在 Update。若 Agent 仍输出,按业务契约错误处理,不能静默忽略后继续执行。
- Before、原订单和完整最新订单由信息系统查单后生成,不由 Agent 输出。
- Before、原订单和完整最新订单由信息系统查单后生成,不由 Agent 输出。Room Information 展示模型中的 `change_summary[]`、最终值、Nights 和 Breakfast 由本系统按本地订单投影 + Agent `after` 合并后派生。
## 13. CANCEL_BOOKING
@@ -423,7 +424,7 @@ Fit 条件字段:
- `CANCEL_BOOKING` 本身已经表示整单取消。
- 不输出 `cancel_scope`、`cancel_reason`、`after` 或当前订单快照。
- 减少房量、删除房型、修改日期或修改 Fit Name 属于 Update,不是 Cancel。
- 当前订单快照由信息系统查单后只读展示。
- 当前订单快照由信息系统查单后只读展示。Cancel 的 Room Information 卡只读显示本地订单投影,不由 Agent 输出当前值。
## 14. TRACE_RESERVATION_NOTES
@@ -462,7 +463,7 @@ Fit 条件字段:
| `trace_items[]` | Trace | 是 | 同一订单一张 Trace 卡,卡内多条事项 |
| `item_type` | Trace item | 是 | `GENERAL` 或 `EXTRA_BED` |
| `text` | GENERAL | 是,可为 `null` | 普通备注内容 |
| `department_code` | GENERAL / EXTRA_BED | 是 | 信息系统受控部门或组合部门 code |
| `department_code` | GENERAL / EXTRA_BED | 是 | 第一版固定为 `FO` / `HSK` / `FO+HSK`,分别表示前厅、客房和前厅 + 客房组合 |
| `target_room_type_code` | EXTRA_BED | 是,可为 `null` | 加床目标房型 |
| `extra_bed_room_count` | EXTRA_BED | 是 | 加床房间数量 |
@@ -470,7 +471,7 @@ Fit 条件字段:
- EXTRA_BED 不输出自由 `content`;信息系统固定显示 `SET EXTRA BED`。
- EXTRA_BED 不输出 `adult_after_extra_bed`;信息系统按当前 / 基础 Adult +1 计算,页面确认前允许用户纠正。
- Department code 由 Agent 必传,值来自信息系统维护的受控部门目录或组合目录。
- Department code 由 Agent 必传。第一版先固定为 `FO`、`HSK`、`FO+HSK` 三个值,不接受自由文本;正式 Department 目录和 lookup API 后续单独扩展。
- 加床目标房型不在当前订单时,Trace 卡不能确认,并提示用户核对目标房型和订单关联。
- 只有订单定位本身不可信时,才触发共享查单门槛阻断整个订单上下文。
@@ -498,7 +499,8 @@ Fit 条件字段:
- 不输出 `rows[]`、逐人名单、同住分组、18 列、Excel 或 PMS 导入参数。
- 当前也不要求 Rooming List Event 单独输出 `attachment_ids[]`。
- 原附件已经在包级 `source_message.attachments[]`,只在邮件展示卡查看。
- 页面固定展示 12 个必填字段表头的标准表格示意,当前内容不代表附件已经真实转换。
- 页面第一版只展示 Rooming List 事项卡和“确认卡片”按钮;用户确认表示已人工处理该事项。
- Rooming List 卡确认不代表名单已解析、Excel 已生成或 PMS 已导入。
- 未来取得 PMS API 后,按真实接口重新冻结住客与执行参数,不直接恢复历史草案中的 `rows[]`。
## 16. PAYMENT
@@ -528,6 +530,7 @@ Fit 条件字段:
- 一笔订单多份凭证放在同一个 Payment Event。
- `attachment_ids[]` 没有业务顺序。
- Payment 不输出 `account_code`、金额、付款日期、付款人、交易号、银行账号、付款状态、Department 或完整附件对象。
- Payment 卡展示层下一阶段可由后端补 `payment_attachments[]` 安全摘要:图片显示缩略图并支持点击大图预览,非图片显示文件列表并提供下载;附件外链不进入 V4 任务详情普通 payload,只能通过 SourceMessage 原文权限链路读取。
没有任何凭证附件时,不能创建正常的空 Payment 卡:
@@ -629,17 +632,18 @@ true
## 20. 受控 code 目录
Account、RoomType、RateCode、Department 的目录由信息系统或其主数据服务统一维护,并作为唯一事实源。
Account、RoomType、RateCode 的目录由信息系统或其主数据服务统一维护,并作为唯一事实源。Department 第一版先固定 `FO`、`HSK`、`FO+HSK` 三个 code;正式 Department 目录、系统管理维护和 lookup API 后续单独扩展。
规则:
- SuperAgent 只能输出目录中已有的稳定 code。
- SuperAgent 只能输出目录中已有的稳定 code;Trace `department_code` 在 Department 目录未正式落地前只能输出 `FO`、`HSK`、`FO+HSK`。
- SuperAgent 输出 `rate_code` 时必须结合当前 `order_ref` 的 `basic_information.account_code` 和 event `target_order.booking_type` 判断候选范围;本阶段只要求 Account + GROUP/FIT,不要求按房型、入住日期或价格计算。
- 不允许输出自由文本或自行创造 code。
- Agent 无法可靠匹配时,按对应业务字段的未解决规则处理。
- Agent 输出非空 code、但信息系统目录不存在该值时,属于目录校验或契约问题。
- Market 和 Source 由信息系统根据订单级 `account_code` 派生,不由 Agent 输出。
当前项目接受“SuperAgent 确定后把目录给本系统”的落地方式。第一阶段已用固定种子目录完成开发闭环;真实目录、系统管理维护、PMS / OPERA / OHIP 同步、前端 lookup API、缓存、权限和兜底策略已在 `M002-v4-real-catalog-lookup-api-design.md` 中设计。该设计不改变 SuperAgent V4 输入契约:SuperAgent 仍只输出稳定 code,不输出显示名或自由文本。
当前项目接受“SuperAgent 确定后把目录给本系统”的落地方式。第一阶段已用固定种子目录完成开发闭环;真实目录、系统管理维护、PMS / OPERA / OHIP 同步、前端 lookup API、缓存、权限和兜底策略已在 `M002-v4-real-catalog-lookup-api-design.md` 中设计。Account 范围 Rate Code 不改变 SuperAgent V4 输入结构:SuperAgent 仍只输出稳定 `rate_code`,不输出显示名、价格或目录完整对象;适用性由本系统后端目录服务最终校验。
## 21. 后端 V4 建模建议
@@ -691,7 +695,7 @@ AI 回调包
1. SuperAgent、Adapter、MCP Schema 和信息系统 DTO 使用同一份 V4 Schema。
2. `body_content_type` 的来源是 AgentBus / 邮件监听层还是 Adapter 派生。
3. `dispatch_run_id`、超时、错误 channel 和技术失败查询入口如何落地。
4. Account、RoomType、RateCode、Department 目录如何提供给 SuperAgent,仍需在真实目录实现后确认是离线目录包还是独立机器接口。
4. Account、RoomType、RateCode 目录如何提供给 SuperAgent,仍需在真实目录实现后确认是离线目录包还是独立机器接口;Department 暂按 `FO`、`HSK`、`FO+HSK` 固定枚举处理。
5. 如需 `event_id` 或幂等键,应作为 transport 字段设计,不作为业务页面字段。
## 24. 当前开发结论
@@ -717,6 +721,8 @@ AI 回调包
- V4 `route_code=S10/S99` 写入 `workflow_reservation_v4_source_notification` 来源通知模型;旧 `SOURCE_MESSAGE_ONLY` 只读特殊任务仅保留给 V3 S10/S99 和旧 S000/S999 兼容数据。
- 普通业务包要求 `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 导入语义。
- 能映射到现有稳定任务卡的 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,不创建订单、任务或用户可处理卡。
@@ -741,13 +747,14 @@ AI 回调包
- `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm` 确认 V4 卡片,强制 Bearer 登录、`RESERVATION_TASK_CONFIRM`、酒店访问权和 version 并发校验。
- `POST /api/reservation/source-notifications/{notificationId}/ack` 确认 V4 S10/S99 来源通知已读 / 已处理,强制 Bearer 登录、`RESERVATION_TASK_CONFIRM`、酒店访问权和 version 并发校验。
- CP7 已完成 `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution` 复核解阻和复核场景订单归属确认。
- CP8 已完成 Account / Room Type / Rate Code 目录校验和 V4 任务卡 `fields[]` 白名单;CP11 已把固定种子导入数据库目录并开放 Account / Room Type / Rate Code lookup API;CP13 已完成目录管理后台 CP1;CP14 已在 `GET /api/reservation/orders` 补齐 V4 下一步处理入口字段。真实 PMS 同步仍未实现。
- CP8 已完成 Account / Room Type / Rate Code 目录校验和 V4 任务卡 `fields[]` 白名单;CP11 已把固定种子导入数据库目录并开放 Account / Room Type / Rate Code lookup API;CP13 已完成目录管理后台 CP1;CP14 已在 `GET /api/reservation/orders` 补齐 V4 下一步处理入口字段。Account + booking type 过滤 Rate Code 已确认为下一阶段待实现;真实 PMS 同步仍未实现。
当前仍未完成:
- 普通 V4 业务包暂时仍保留旧 V3 任务状态、草稿和 OPERA 模拟骨架兼容,便于前端过渡;V4 写接口和前端页面完成后再逐步废弃旧链路。
- Account + booking type 过滤 Rate Code 的后端 lookup / 适用性校验和前端联动。
- Payment 卡付款凭证附件安全摘要、图片缩略图 / 大图预览、非图片文件列表 + 下载联动。
- 尚未接入真实 PMS / OPERA / OHIP。
- 尚未改造前端 V4 页面模型。
- SuperAgent 目录机器接口和生产目录同步方案仍后置。
## 26. 后端 CP2 设计文档状态
@@ -16,7 +16,7 @@ M002 V4 CP1 已完成 SuperAgent V4 回调包入站解析、基础校验、路
本文是 CP2 设计文档,用于把 2026-07-18 V4 字段契约落成后续可开发的数据模型和接口草案。
截至 CP14、V4 业务审计查询和停止旧任务双写补齐,后端已实现本文第 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 继续处理入口字段。真实 PMS 同步继续后置,设计见 `M002-v4-real-catalog-lookup-api-design.md`。
截至 CP14、V4 业务审计查询和停止旧任务双写补齐,后端已实现本文第 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 继续处理入口字段。下一阶段已确认 Rate Code 需要按订单级 Account + `booking_type`(GROUP / FIT)过滤和校验,当前后端 CP11 实现仍是酒店级 Rate Code 目录,是待补齐缺口;Room Information 卡需要补 New / Update / Cancel 业务展示模型、Nights / Breakfast 派生、Group Booking Status 和 Rooming List 确认联动;Payment 卡下一阶段需要展示付款凭证附件,图片为缩略图 + 点击大图预览,非图片为文件列表 + 下载,但附件 URL 仍必须走 SourceMessage 原文权限链路;真实 PMS 同步继续后置,设计见 `M002-v4-real-catalog-lookup-api-design.md`。
后续如本文与 `M002-v4-agent-callback-field-contract.md` 的字段契约冲突,以字段契约为准;如与安全边界冲突,以 `security-access-control-boundary.md` 为准。
@@ -89,10 +89,10 @@ 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` | 是 | 是 | 房间信息卡,卡内根据 event_type 展示新建、修改或整单取消字段 |
| `ROOM_INFORMATION` | `NEW_BOOKING` / `UPDATE_BOOKING` / `CANCEL_BOOKING` | 是 | 是 | 房型信息卡,卡内按 New / Update / Cancel 展示最终值、差异和系统派生字段 |
| `TRACE_RESERVATION_NOTES` | `TRACE_RESERVATION_NOTES` | 是 | 是 | Trace 卡,卡内可有普通备注和加床备注多条事项 |
| `ROOMING_LIST` | `ROOMING_LIST` | 是 | 第一版可只读或确认 | 识别到 Rooming List 任务,不在 Agent 回调里保存名单 rows |
| `PAYMENT` | `PAYMENT` | 是 | 第一版主要确认附件关联 | 付款凭证卡,只按 `attachment_ids[]` 引用包级附件 |
| `ROOMING_LIST` | `ROOMING_LIST` | 是 | 否 | 第一版只做事项确认;不在 Agent 回调里保存名单 rows,不生成 Excel,不导入 PMS |
| `PAYMENT` | `PAYMENT` | 是 | 第一版主要确认附件关联 | 付款凭证卡;业务事实仍是 `attachment_ids[]` 引用包级附件,展示层可返回匹配后的附件安全摘要 |
说明:
@@ -173,7 +173,7 @@ V4 新数据不再提供后端草稿保存。前端可以在页面本地维护
- 用户修正写入 `review_resolution_json` 和 `confirmed_payload_json`。
- CP11 起确认和复核都会校验当前酒店数据库目录字段;Basic Information 的 `account_code` 必须来自当前酒店 ACTIVE Account 目录,通过后后端派生 `market_code` / `source_code`。
- 通过校验后卡片直接进入 `CONFIRMED`,不再进入 V3 `READY` 状态。
- `field_overrides[].field_pointer` 必须是当前卡 `display_payload_json` 中允许编辑的 RFC 6901 JSON Pointer;如果当前卡展示 payload 中存在显式 `missing_fields[]`,只允许提交该清单内的 pointer;如果没有显式清单,第一版只允许 `basic_information.*` 或 `business_fields.*` 下已经存在且值为 `null` / 空字符串的未解决叶子字段,或后端 `validation_errors_json` 指向的目录错误字段,不允许替换对象或数组。
- `field_overrides[].field_pointer` 必须是当前卡 `fields[]` 白名单中允许编辑的 RFC 6901 JSON Pointer。`REVIEW_REQUIRED` 是整张原业务卡的复核状态,前端仍在原卡片内展示业务表单,问题字段用红字 / `validation_errors` 强调;用户可修改当前卡业务白名单内字段,不再限定只能改空值、`missing_fields[]` 或目录错误字段。
- 来源消息、路由、订单定位关系、诊断、缺失字段清单、`manual_review`、raw evidence 等只读字段不得提交。
- 如果订单任务归属未解决,复核请求必须提交 `confirmed_order_id`;后端按当前订单任务酒店校验该订单存在、非逻辑删除且不是系统隐藏订单。
- 如果订单任务已经有 `order_id` 且 `target_resolution_status=RESOLVED`,复核请求不能提交不同的 `confirmed_order_id`,否则返回 `V4_ORDER_REBIND_NOT_ALLOWED`;普通任务任意切换订单继续后置。
@@ -203,9 +203,9 @@ CP11 数据库初始化种子:
| Market | 由 Account 派生,当前固定为 `LEISURE` |
| Source | 由 Account 派生,当前固定为 `TRAVEL_AGENT` |
| Room Type | `TWN`、`KING`、`DBL`、`SGL`、`TRP`、`RM1`、`RM2`、`RM3` |
| Rate Code | `BAR`、`RACK`、`PACKAGE`、`GROUP`、`FIT` |
| Rate Code | `BAR`、`RACK`、`PACKAGE`、`GROUP`、`FIT`;下一阶段候选需按 Account + `booking_type` 过滤 |
说明:Room Type / Rate Code 当前只作为确认和字段控件的第一版校验 / 选项来源代码;已开放 `GET /api/reservation/lookups/accounts|room-types|rate-codes`,但尚未接真实 PMS 房型目录、Rate Code 配置中心、目录管理后台或同步 run。
说明:Room Type / Rate Code 当前只作为确认和字段控件的第一版校验 / 选项来源代码;已开放 `GET /api/reservation/lookups/accounts|room-types|rate-codes`。Rate Code 下一阶段需引入 Account + `booking_type` 适用关系,前端不再展示全酒店 Rate Code 全量候选;房型 / 日期 / 价格过滤、真实 PMS 房型目录、Rate Code 配置中心和同步 run 后置。
## 8. 订单归属和 target_order
@@ -251,20 +251,23 @@ V4 当前不做 OPERA / PMS 执行,但仍需要保留同订单处理顺序,
推荐固定顺序:
```text
10 SOURCE_MESSAGE_DISPLAY
20 BASIC_INFORMATION
10 BASIC_INFORMATION
30 ROOM_INFORMATION
40 TRACE_RESERVATION_NOTES
50 ROOMING_LIST
60 PAYMENT
90 SOURCE_MESSAGE_DISPLAY
```
规则:
- `SOURCE_MESSAGE_DISPLAY` 只读,不阻塞。
- `BASIC_INFORMATION` 未确认时,其它业务卡只能查看,不能确认。
- 除 Basic Information 必须先确认外,第一版不强制业务卡之间逐张顺序确认;业务卡可独立确认,但页面仍按固定顺序展示。
- 同类型多张业务卡按 `source_event_index` 排序。
- `SOURCE_MESSAGE_DISPLAY` 只读、不阻塞,V4 任务详情页固定放在最下方,用于查看当前触发该订单任务的 SourceMessage 正文和附件摘要。
- Trace 卡 `trace_items[].department_code` 第一版固定为 `FO`、`HSK`、`FO+HSK` 三个值;前端可先做固定下拉,后端正式 Department 目录、lookup API 和目录校验后续单独 checkpoint 扩展。
- Rooming List 卡第一版没有可编辑业务字段;页面展示为轻量事项确认卡,用户点击“确认卡片”仅表示已人工处理当前 Rooming List 事项,不代表名单已解析、Excel 已生成或 PMS 已导入。
- Room Information 卡下一阶段采用业务展示模型,不再只依赖通用 `fields[]` 扁平渲染;具体规则见“Room Information 卡展示模型”。
### 9.2 同一本地订单下多个订单任务
@@ -560,7 +563,54 @@ GET /api/reservation/order-tasks/{orderTaskId}
来源邮件摘要必须按当前查询酒店过滤。如果 V4 订单任务因脏引用指向了其它酒店的 SourceMessage,详情接口只能返回该 `source_message_id` 的空摘要占位,不得透出对方酒店的主题、外部消息 ID、发件人或会话信息。
前端 V4 来源消息卡只展示来源邮件安全摘要、邮件片段和附件名称 / 类型 / 大小等非 URL 摘要;如果 `source_message_card.display_payload` 中的 `attachments`、`uploaded_media` 或 `file_references` 异常包含直接 URL 字符串,前端必须兜底显示为未命名附件或隐藏,不能在普通业务页面渲染具体 URL。
V4 任务详情页的 `SOURCE_MESSAGE_DISPLAY` 固定展示在 Basic Information 和业务卡之后。该卡在页面上应展示“当前触发这条 V4 order task 的那封 SourceMessage 正文”,但正文不由本接口直接返回;前端应使用 `source_message_summary.source_message_id` 调用 `GET /api/source-messages/{sourceMessageId}/conversation`,并在同会话结果中定位当前 SourceMessage。HTML 邮件优先使用 `html_body_sanitized`,纯文本邮件使用 `text_body`;如果用户缺少 `SOURCE_MESSAGE_ORIGINAL_READ` 或会话接口失败,降级展示安全摘要和查看邮件会话入口。
来源邮件卡附件仍只展示名称 / 类型 / 大小等非 URL 摘要;如果 `source_message_card.display_payload`、会话返回的附件或内联媒体中包含直接 URL 字符串,前端不得在 V4 任务详情普通卡片区域直接渲染具体 URL,附件外链只能在 SourceMessage 原文权限链路中按既有规则处理。
Room Information 卡展示模型:
- `ROOM_INFORMATION` 卡只由 `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING` 三类 event 触发;`TRACE_RESERVATION_NOTES`、`ROOMING_LIST`、`PAYMENT` 不触发房型信息卡。
- SuperAgent 仍只输出字段契约中的业务字段。Nights、Breakfast、Group Booking Status、Block ID、Confirmation Number 和 Adult 不由 SuperAgent 输出;其中 Adult 第一版不在卡内展示。
- 后端下一阶段应在 V4 任务详情中为 Room Information 卡补展示模型,建议拆为 `current_values`、`proposed_values`、`final_values`、`change_summary[]`、`derived_fields` 和 `field_controls`。前端按该展示模型渲染业务 UI,`fields[]` 继续作为确认 / 复核的可编辑字段白名单。
- `NEW_BOOKING`:卡片展示创建后的最终值。Agent 提供 `target_order`、`arrival_date`、`departure_date`、`rate_code`、`booking_scenario`、`room_items[]`,Fit 可提供 `guest_name`;后端派生 `nights`、`breakfast_included` 和 Group Booking Status。
- `UPDATE_BOOKING`:后端从本地订单投影读取当前值,用 Agent `after` 合并得到最终值;页面上方展示本次实际变化的 `change_summary[]`,例如 `入住日期:2026-07-12 -> 2026-07-20`。如果日期变化导致 `nights` 变化,`nights` 也必须出现在差异区;字段区展示合并后的最终值。
- `CANCEL_BOOKING`:不使用 Agent 输出当前订单快照;后端从本地订单投影读取当前值并只读展示,用户只确认整单取消。Cancel 卡不允许编辑 Group Booking Status、Breakfast、日期、Rate Code 或房型房量。
- `nights` 由后端按酒店本地业务日期计算:`departure_date - arrival_date`,不涉及时区和 UTC;日期缺失、非法或差值小于等于 0 时,`nights` 为空并阻止确认。
- `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`;该自动变更应写入审计。Fit 不显示也不变更 Group Booking Status。
- `target_order.locator_value` 不作为前端可编辑字段;订单归属错误时通过 V4 复核选择正确订单或创建正确订单投影,不直接改写 Agent 原始 `target_order.locator_value`。但 New Booking 创建 / 确认的最终订单投影字段允许编辑:Group 显示并允许编辑 `group_block_name`,默认值来自 `target_order.locator_value` 且 `locator_type=GROUP_CODE`;Fit 显示并允许编辑 `fit_name`,默认值来自 `guest_name ?? target_order.locator_value`。用户修改这些字段只影响本系统最终订单投影和确认快照,不回写 Agent 原始定位字段。
- Block ID 和 Confirmation Number 第一版只读;存在本地投影或未来 PMS 结果时展示,否则为空。Block ID 仅 Group 显示,Confirmation Number 仅 Fit 显示。
V4 任务详情页第一版字段白名单:
| 卡片 | 可编辑字段 | 只读 / 派生字段 |
| --- | --- | --- |
| `BASIC_INFORMATION` | `basic_information.account_code` | `market_code`、`source_code`、Account 显示名等由后端按 Account 目录派生 |
| `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 |
| `TRACE_RESERVATION_NOTES` | `trace_items[].content`、`trace_items[].department_code` | Department 第一版只允许 `FO`、`HSK`、`FO+HSK`,不允许自由文本 |
| `ROOMING_LIST` | 无;用户只确认 Rooming List 事项 | 来源邮件正文和附件摘要通过底部 `SOURCE_MESSAGE_DISPLAY` 查看;确认后 Group 自动置为 `DEF` |
| `PAYMENT` | 无;用户只确认附件关联事项 | `attachment_ids[]`、`payment_attachments[]` 安全摘要、图片缩略图、文件名、类型、大小、预览 / 下载可用性 |
| `SOURCE_MESSAGE_DISPLAY` | 无 | 当前触发该 V4 order task 的 SourceMessage 正文,只读且默认长度折叠,可展开全文 |
Rooming List 卡事项确认规则:
- Rooming List 卡第一版只做事项确认,不做名单解析、附件预览、Excel 生成或 PMS 导入。
- `ROOMING_LIST` event 不输出 `rows[]`、逐人名单、同住分组、18 列、Excel 或 PMS 导入参数,也不要求单独输出 `attachment_ids[]`。
- 页面应展示卡片标题、状态、目标订单信息和“确认卡片”按钮;如需查看来源内容,仍通过本订单任务底部的 `SOURCE_MESSAGE_DISPLAY` 查看当前触发 SourceMessage 正文和附件摘要。
- 用户点击“确认卡片”表示已人工处理该 Rooming List 事项;该确认只更新 V4 卡片状态和订单任务派生状态,不代表 M010 Rooming List Excel 已生成,也不代表 PMS / OPERA / OHIP 已执行。
- 独立 Rooming List Excel 生成能力仍属于 M010 `/reservation/rooming-lists/new` 工具页面,第一版不嵌入 V4 Rooming List 卡。
Payment 卡附件展示规则:
- Payment 卡业务字段仍以 Agent 返回的 `attachment_ids[]` 为准,用于确认这些附件是否为当前订单付款凭证;这不代表已收款、已入账或付款状态已确认。第一版 `attachment_ids[]` 是只读业务事实,前端展示后只允许“确认卡片”,不允许用户增删、替换或重新选择附件集合。
- 后端下一阶段应在 Payment 卡 `display_payload` 中补 `payment_attachments[]` 安全摘要,由 `attachment_ids[]` 匹配当前触发 SourceMessage 的包级附件生成。建议字段为 `attachment_id`、`file_name`、`content_type`、`size_bytes`、`is_image`、`preview_available`、`download_available`,可选 `external_media_id`;不得包含 `externalUrl`、OSS URL、签名参数或附件原始二进制。
- 图片判断以 `content_type` 以 `image/` 开头为主;图片在 Payment 卡内展示缩略图,点击后打开大图预览。缩略图和大图实际 URL 不从 `GET /api/reservation/order-tasks/{orderTaskId}` 返回,前端必须在具备 `SOURCE_MESSAGE_READ + SOURCE_MESSAGE_ORIGINAL_READ` 时调用 `GET /api/source-messages/{sourceMessageId}/conversation`,定位当前 SourceMessage 后按 `external_media_id` / `attachment_id` 匹配对应附件。
- 非图片附件统一显示文件列表,至少展示文件名、类型和大小,并提供下载动作;第一版不在 Payment 卡内嵌 PDF、Word、Excel 或压缩包预览。
- 入站 `PAYMENT.attachment_ids[]` 无法匹配同包 `source_message.attachments[].id` 时,不创建用户可处理 Payment 卡,只写 `adapter_contract_error` transition 或按 S10 / 技术异常规则处理。只有 `attachment_ids[]` 已合法匹配、但用户缺少原文读取权限、会话接口失败、附件 URL 缺失或媒体预览链路暂不可用时,Payment 卡才展示安全摘要和“无法预览 / 无法下载”的状态,不应把附件 URL 或错误详情暴露给普通用户。
- 前端不得把附件外链写入日志、错误上报、URL query、localStorage 或确认 payload;Payment 第一版确认卡片时只提交 `version` 和必要审计说明,不提交 `attachment_ids[]`、附件 URL 或完整附件对象。后续如需支持人工选择 / 增删付款凭证附件,必须另行定义数组编辑契约和后端审计口径。
### 12.4 S10/S99 来源通知详情
@@ -603,7 +653,7 @@ GET /api/reservation/orders/{orderId}
`order_overview` 只从已确认 V4 卡片派生:
- Basic Information 已确认后,返回 `account_code`、`account_name`、`market_code`、`source_code`;
- Room Information 已确认后,返回 `arrival_date`、`departure_date`、`rate_code`、`room_items[]`;
- Room Information 已确认后,返回 `arrival_date`、`departure_date`、`rate_code`、`room_items[]`;下一阶段 Room Information 展示模型实现后,可继续从确认快照派生 `nights`、`breakfast_included` 和 Group Booking Status;
- Trace / Rooming List / Payment 返回对应最新卡片状态,方便订单详情展示待处理事项;
- 未确认 AI 建议不得进入 `order_overview`,避免把未处理内容展示成订单事实。
@@ -705,7 +755,7 @@ POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm
- 请求 JSON 必须携带 `version`;`confirmed_payload` 可选,未传时后端使用当前展示 payload 作为确认快照。
- 前端只应提交当前卡 `fields[]` 中可编辑字段。后端确认时以当前卡展示快照为基准合并 `confirmed_payload`,未出现在展示快照 / 字段白名单中的字段会被忽略,不会写入 `confirmed_payload_json`。
- Basic Information 确认时 `basic_information.account_code` 必须是当前酒店数据库 Account 目录值;后端确认前会派生 `account_name`、`market_code` 和 `source_code` 写入 `confirmed_payload_json`。
- 业务卡确认时,第一版会递归校验已有 `rate_code`、`room_items[].room_type_code` 是否在当前酒店数据库目录中;`UPDATE_BOOKING` 等嵌套结构会返回类似 `business_fields.after.room_items.0.room_type_code` 的错误路径,失败返回 `V4_FIELD_VALIDATION_FAILED`。
- 业务卡确认时,当前已递归校验已有 `rate_code`、`room_items[].room_type_code` 是否在当前酒店数据库目录中;下一阶段 `rate_code` 还必须属于当前订单 Basic Information 已确认 Account + 当前业务 event `booking_type` 的适用关系。`UPDATE_BOOKING` 等嵌套结构会返回类似 `business_fields.after.room_items.0.room_type_code` 的错误路径,失败返回 `V4_FIELD_VALIDATION_FAILED`。
- 不提交草稿。
- 必须带 `version` 做并发校验。
- 后端确认后卡片 `CONFIRMED` 并锁定。
@@ -727,8 +777,8 @@ POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution
- 可提交复核场景订单归属确认。
- 请求 JSON 必须携带卡片 `version`;可选 `reason` 写入业务审计摘要;`confirmed_order_id` 表示复核场景确认后的本地订单 ID。
- 如果订单任务当前 `order_id=null` 或 `target_resolution_status!=RESOLVED`,`confirmed_order_id` 必填;后端会按订单任务所属酒店查询并校验真实可见订单。
- `field_overrides[]` 每项包含 `field_pointer` 和 `value`;`field_pointer` 只允许指向当前卡展示 payload 中的可编辑业务字段,不允许指向 `source_message`、`route_code`、`card_type`、`target_order`、`order_ref`、`missing_fields`、`manual_review`、`raw_evidence`、`validation_errors` 等只读诊断字段。
- 第一版允许的写入容器是 `basic_information` 和 `business_fields`;如果展示 payload 中存在显式 `missing_fields[]`,只允许提交清单中的 pointer;否则只能改已存在且值为 `null` / 空字符串的叶子字段,或后端 `validation_errors_json` 指向的目录错误字段,不能修改其它已有有效值、替换整个对象 / 数组或新增未知字段。
- `field_overrides[]` 每项包含 `field_pointer` 和 `value`;`field_pointer` 只允许指向当前卡 `fields[]` 中可编辑业务字段,不允许指向 `source_message`、`route_code`、`card_type`、`target_order`、`order_ref`、`missing_fields`、`manual_review`、`raw_evidence`、`validation_errors` 等只读诊断字段。
- 第一版允许的写入容器是 `basic_information` 和 `business_fields`。复核态允许编辑当前卡业务字段白名单内的已有叶子字段;不允许替换整个对象 / 数组、新增未知字段或提交后端未返回为可编辑的字段。
- 复核提交后同样执行目录校验;Basic Information 复核成功后会在 `confirmed_payload_json.basic_information` 中写入派生的 `account_name`、`market_code`、`source_code`。
- 如果订单任务已经有 `order_id` 且 `target_resolution_status=RESOLVED`,`confirmed_order_id` 只能为空或等于当前订单 ID;提交其它订单 ID 会返回 `V4_ORDER_REBIND_NOT_ALLOWED`。
- Basic Information 必须先确认;如果 Basic Information 自身是 `REVIEW_REQUIRED`,允许通过本接口先复核并确认 Basic。
@@ -799,7 +849,7 @@ S10/S99 已确认采用来源通知模型,不继续复用隐藏技术订单或
| 确认卡片 | `FRONTEND_USER` | `RESERVATION_TASK_CONFIRM` | 写业务审计 |
| 复核解阻 | `FRONTEND_USER` | `RESERVATION_MANUAL_REVIEW_RESOLVE` | 写业务审计 |
| 确认 S10/S99 来源通知 | `FRONTEND_USER` | `RESERVATION_TASK_CONFIRM` | 写业务审计 |
| 读取邮件正文 / 附件 | `FRONTEND_USER` | `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ` | 写原文读取审计 |
| 读取邮件正文 / 附件 / Payment 凭证预览和下载 | `FRONTEND_USER` | `SOURCE_MESSAGE_READ` + `SOURCE_MESSAGE_ORIGINAL_READ` | 写原文读取审计 |
| SuperAgent V4 回调 | `THIRD_PARTY_SUPERAGENT` | HMAC 机器鉴权 | 写 AI batch / transition |
AI 原始 payload、邮件正文、附件 URL 和技术 trace 不应直接进入普通列表接口。
@@ -820,6 +870,11 @@ AI 原始 payload、邮件正文、附件 URL 和技术 trace 不应直接进入
| M002-V4-CP12 | 前端 Lookup 接入 | V4 卡片字段按 `options_source` 调用 lookup,替换固定种子硬编码选项,处理 stale / warning / 空目录 |
| M002-V4-CP13 | 目录管理后台 V1 | Account / Market / Source 管理,临时 Room Type / Rate Code 管理,目录维护权限和管理审计 |
| M002-V4-CP14 | 订单列表 V4 继续处理入口 | 已完成:`GET /api/reservation/orders` 返回 V4 下一步订单任务、卡片、动作类型、动作状态和 open 数,前端可优先跳 V4 订单任务详情 |
| M002-V4-CP14.5 | Account 范围 Rate Code Lookup | 待实现:按 Account + `booking_type` 管理和查询 Rate Code 适用关系;业务卡确认 / 复核校验 Rate Code 适用性;前端在 Account 确认后加载对应 GROUP/FIT 候选 |
| M002-V4-CP14.6 | Payment 附件预览 | 待实现:Payment 卡返回付款凭证附件安全摘要;前端图片缩略图 + 大图预览,非图片文件列表 + 下载;预览 / 下载走 SourceMessage 原文权限链路 |
| M002-V4-CP14.7 | Rooming List 事项确认卡 | 待实现前端轻量展示:Rooming List 卡第一版只展示事项和确认按钮,不解析名单、不预览附件、不生成 Excel、不导入 PMS |
| M002-V4-CP14.8 | Room Information 展示模型 | 待实现:New / Update / Cancel 按业务模型展示最终值、差异、Nights、Breakfast、Group Booking Status 和本地订单投影;Adult 不显示;Rooming List 确认时 Group 状态自动置 `DEF` |
| M002-V4-CP14.9 | 复核态卡片字段白名单和统一确认交互 | 待实现:`REVIEW_REQUIRED` 保持原业务卡内编辑,问题字段红字提示,前端按钮显示“确认卡片”但调用 `review-resolution`;后端 `fields[]` 返回当前卡业务字段白名单,复核写入不再只限空值或目录错误字段 |
| 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、最后成功快照、失败重试和同步状态管理入口 |
@@ -4,11 +4,11 @@
| 项目 | 内容 |
| --- | --- |
| 文档版本 | 0.3 |
| 日期 | 2026-07-19 |
| 状态 | CP11 已落地第一版数据库目录与 lookup API;目录管理后台 CP1 已落地后端接口;真实 PMS 同步继续后置 |
| 适用范围 | M002 V4 Account、Market、Source、Room Type、Rate Code 目录来源、数据模型、前端 lookup、缓存、酒店隔离、权限和失败兜底 |
| 不适用范围 | 真实 OPERA / OHIP 写操作、真实价格计算、前端页面实现、SuperAgent Prompt 修改、目录同步任务、SuperAgent 机器目录接口 |
| 文档版本 | 0.4 |
| 日期 | 2026-07-20 |
| 状态 | CP11 已落地第一版数据库目录与 lookup API;目录管理后台 CP1 已落地后端接口;Account + booking type 过滤 Rate Code 和 Room Information 早餐派生已确认为下一阶段待实现契约;真实 PMS 同步继续后置 |
| 适用范围 | M002 V4 Account、Market、Source、Room Type、Rate Code 目录来源、Room Information 早餐派生、数据模型、前端 lookup、缓存、酒店隔离、权限和失败兜底 |
| 不适用范围 | 真实 OPERA / OHIP 写操作、真实价格计算、房型 / 日期 / 价格级 Rate Code 适用规则、前端页面实现、SuperAgent Prompt 修改、目录同步任务、SuperAgent 机器目录接口 |
## 1. 文档定位
@@ -16,7 +16,9 @@ M002 V4 CP8 已实现第一版固定种子目录校验;M002 V4 CP11 已把该
- Basic Information 的 `account_code` 必须存在于当前酒店数据库 Account 目录。
- Market / Source 由 Account 派生,不由 SuperAgent 输出。
- Room Type / Rate Code 第一版使用当前酒店数据库目录校验和字段选项提示。
- Room Type 第一版使用当前酒店数据库目录校验和字段选项提示。
- Rate Code 当前 CP11 实现仍是酒店级目录校验和字段选项提示;下一阶段已确认需要按订单级 Account + `booking_type`(`GROUP` / `FIT`)过滤候选和校验适用性。
- Room Information 卡中 Breakfast 第一版由后端派生:Group 固定含早;Fit 按 Rate Code 中 `RB` / `RO` 判断,无法派生时要求用户在卡片中必填确认。
- 目录错误会让对应 V4 卡片进入 `REVIEW_REQUIRED`,用户通过复核解阻选择合法 code。
本文记录真实目录和 lookup API 的设计与 CP11 第一版实现。CP11 新增 `workflow_reservation_catalog_account`、`workflow_reservation_catalog_code` 两张表,并通过 Flyway 初始化 `HOTEL-TEST`、`HOTEL-DEV` 以及迁移执行时已存在的 `ACTIVE` 平台酒店的固定种子目录;同时新增 `ReservationV4CatalogBootstrapRunner`,在平台默认酒店由启动流程创建后,如果该酒店目录为空,会补一份 `FIXED_SEED_IMPORT` 初始化目录。不再在运行时代码中把固定种子作为全局目录事实。目录管理后台 CP1 已新增 Account、Room Type、Rate Code 后端查询、新增、启用 / 停用接口;`workflow_reservation_catalog_sync_run`、真实 PMS / OPERA / OHIP 同步、Market / Source 独立管理页面和 SuperAgent 机器目录供给接口继续后置。
@@ -33,7 +35,7 @@ CP11 之前固定种子实现位于后端 `ReservationV4DirectoryService` 和 `F
| Market | 由 Account 派生的订单级 Market | 当前 Account 均派生 `LEISURE` | 已进入通用代码目录,前端仍不直接编辑 |
| Source | 由 Account 派生的订单级 Source | 当前 Account 均派生 `TRAVEL_AGENT` | 已进入通用代码目录,前端仍不直接编辑 |
| Room Type | 房型 code 校验和字段选项提示 | `TWN`、`KING`、`DBL`、`SGL`、`TRP`、`RM1`、`RM2`、`RM3` | 已进入数据库初始化目录,仍不是 PMS 全量房型;后台 CP1 可新增、启用 / 停用 |
| Rate Code | Rate Code 校验和字段选项提示 | `BAR`、`RACK`、`PACKAGE`、`GROUP`、`FIT` | 已进入数据库初始化目录,不按日期、账号、房型过滤,不含价格;后台 CP1 可新增、启用 / 停用 |
| Rate Code | Rate Code 校验和字段选项提示 | `BAR`、`RACK`、`PACKAGE`、`GROUP`、`FIT` | 已进入数据库初始化目录;当前实现仍是酒店级目录,下一阶段改为 Account + booking type 适用范围过滤;不按房型、日期或价格过滤;后台 CP1 可新增、启用 / 停用 |
当前固定种子只能支撑开发和演示闭环,不能作为生产长期事实源。
@@ -47,6 +49,7 @@ CP11 之前固定种子实现位于后端 `ReservationV4DirectoryService` 和 `F
4. 目录有来源、版本、更新时间和启停状态,便于排查 SuperAgent 输出 code 与系统目录不一致的问题。
5. PMS / OPERA / OHIP 不稳定或暂未接入时,系统仍可使用最后一次成功目录快照或系统管理目录兜底。
6. 固定种子目录可以作为 dev/test 或导入初始化兜底,但生产不应默认靠代码固定值。
7. Rate Code 不再作为全酒店通用下拉;业务卡 Rate Code 候选必须先受当前订单 Basic Information 的 Account 和 event `booking_type` 限定。
## 4. 目录来源分层
@@ -55,7 +58,7 @@ CP11 之前固定种子实现位于后端 `ReservationV4DirectoryService` 和 `F
| 阶段 | 来源 | 中文说明 | 适用目录 |
| --- | --- | --- | --- |
| Phase 0 | `FIXED_SEED_IMPORT` | CP11 已将固定种子通过 Flyway 和启动补种子流程导入数据库,不再作为运行时代码全局 Map | 当前 Account、Market、Source、Room Type、Rate Code |
| Phase 1 | `SYSTEM_MANAGED` | 本系统数据库目录,由初始化脚本、管理后台或导入文件维护 | Account、Market、Source;也可临时维护 Room Type、Rate Code |
| Phase 1 | `SYSTEM_MANAGED` / `IMPORT_FILE` | 本系统数据库目录,由初始化脚本、管理后台或受控导入文件维护;`IMPORT_FILE` 用于 OWNER RATE 等人工确认过的目录 / 适用关系导入 | Account、Market、Source;也可临时维护 Room Type、Rate Code 和 Account + booking type 的 Rate Code 适用关系 |
| Phase 2 | `PMS_SYNC` | 后端从 PMS / OPERA / OHIP 同步目录到本系统本地表,业务查询只读本地快照 | Room Type、Rate Code 优先;Account、Market、Source 视 PMS 能力再接 |
原则:
@@ -72,9 +75,9 @@ CP11 之前固定种子实现位于后端 `ReservationV4DirectoryService` 和 `F
| Market | 本系统管理目录 | 可选同步 PMS market code 配置 | 否,随 Account 派生展示 | 前端不直接改 Market;修改 Account 后后端派生 Market |
| Source | 本系统管理目录 | 可选同步 PMS source code 配置 | 否,随 Account 派生展示 | 前端不直接改 Source;修改 Account 后后端派生 Source |
| Room Type | PMS / OPERA / OHIP 同步目录优先 | 同步酒店有效房型、展示名、人数、启停状态 | 是,业务卡选择房型 | 若 PMS 未接入,可临时由系统管理维护或固定种子初始化 |
| Rate Code | PMS / OPERA / OHIP 同步目录优先 | 同步有效 Rate Plan / Rate Code;价格和日期适用规则后置 | 是,New Booking 选择 Rate Code | 第一版 lookup 只选 code,不做价格计算 |
| Rate Code | PMS / OPERA / OHIP 同步目录优先;在 PMS 未接前可由系统管理或导入文件维护 Account 适用关系 | 同步有效 Rate Plan / Rate Code;Account、booking type、房型、日期和价格适用规则逐步扩展 | 是,New Booking 选择 Rate Code | 下一阶段先按 Account + GROUP/FIT 过滤候选,只选 code,不做价格计算 |
Department 目录在 V4 字段契约中也会被 Trace 使用,但当前固定种子 CP8 尚未实现。后续可沿用本文模型扩展 `DEPARTMENT`,不放入本 checkpoint。
Department 目录在 V4 字段契约中也会被 Trace 使用。当前任务详情页第一版先固定 `FO`、`HSK`、`FO+HSK` 三个 Department code,用于 Trace 卡下拉和 SuperAgent 输出约束;这不是正式数据库目录。后续可沿用本文模型扩展 `DEPARTMENT`、lookup API、系统管理维护和目录校验,不放入本 checkpoint。
## 6. 数据模型草案
@@ -135,7 +138,53 @@ Market、Source、Room Type、Rate Code 可先使用统一 code 表。
uk_reservation_catalog_code(hotel_id, catalog_type, code)
```
### 6.3 推荐表:`workflow_reservation_catalog_sync_run`
### 6.3 推荐表:`workflow_reservation_rate_code_applicability`
Rate Code 本身仍保存在 `workflow_reservation_catalog_code` 中,`catalog_type=RATE_CODE` 表示该 code 是当前酒店已知的 Rate Code;是否对某个 Account、GROUP/FIT 可用,由适用关系表表达。这样可以避免在 `metadata_json` 中写不可查询的业务规则,也避免前端硬编码 OWNER RATE Excel。
本 checkpoint 只确认 Account + booking type 粒度,不纳入房型、日期和价格规则。后续如果需要按 Room Type 或入住日期进一步过滤,应在此表或独立价格规则表上扩展,不改变前端只提交稳定 `rate_code` 的基本原则。
| 字段 | 中文说明 |
| --- | --- |
| `id` | 内部主键 ID |
| `hotel_id` | 酒店 ID,必须和 Account、Rate Code 同酒店 |
| `account_code` | Reservation Account 稳定 code,引用当前酒店 Account 目录 |
| `booking_type` | `GROUP` / `FIT`;用于区分团队价和散客价候选 |
| `rate_code` | Rate Code 稳定 code,引用当前酒店 `RATE_CODE` 目录 |
| `status` | `ACTIVE` / `DISABLED`;普通 lookup 只返回 `ACTIVE` |
| `source_system` | `SYSTEM_MANAGED` / `IMPORT_FILE` / `PMS_SYNC` / `FIXED_SEED_IMPORT` |
| `catalog_version` | 适用关系版本,用于排查和前端非阻塞提示 |
| `sort_order` | 同一 Account + booking type 下的默认展示排序 |
| `metadata_json` | 扩展说明,例如导入文件行号、业务备注;不得作为唯一可查询条件 |
| `version` | 乐观锁版本 |
| `created_at` / `updated_at` | UTC 创建和更新时间 |
| `logic_deleted_at` / `logic_deleted_reason` | 逻辑删除时间和原因 |
建议唯一约束:
```text
uk_reservation_rate_code_applicability(hotel_id, account_code, booking_type, rate_code)
```
建议外键 / 逻辑校验:
- `account_code` 必须存在于同酒店 `workflow_reservation_catalog_account` 且为 `ACTIVE`,否则不进入普通 lookup。
- `rate_code` 必须存在于同酒店 `workflow_reservation_catalog_code` 且 `catalog_type=RATE_CODE`、`status=ACTIVE`,否则不进入普通 lookup。
- 停用 Account 或 Rate Code 后,普通 Rate Code lookup 不再返回对应适用关系;已确认历史卡片不回滚。
### 6.4 Rate Code 早餐派生
Room Information 卡需要展示 `breakfast_included` 布尔字段,但该字段不由 SuperAgent 输出。
第一版派生规则:
- Group 固定含早,`breakfast_included=true`,前端显示勾选且只读。
- Fit 根据最终 Rate Code 派生:Rate Code 中包含 `RB` 时 `breakfast_included=true`,包含 `RO` 时 `breakfast_included=false`。
- Fit Rate Code 同时无法命中 `RB` / `RO` 时,后端应把 `breakfast_included` 标为未解决,前端显示必填勾选框,由用户确认是否含早。
- 该规则只用于 Room Information 卡展示和确认,不代表真实价格计算,也不替代 PMS Rate Plan 规则。
- 后续如果目录表显式维护早餐属性,可优先使用目录元数据;但普通前端仍消费布尔 `breakfast_included`,不要自行按 Rate Code 字符串猜测。
### 6.5 推荐表:`workflow_reservation_catalog_sync_run`
如果接 PMS / OPERA / OHIP 同步,建议记录每次同步运行。
@@ -168,6 +217,7 @@ workflows.reservation.service
- findAccount(hotelId, accountCode)
- isKnownRoomTypeCode(hotelId, roomTypeCode)
- isKnownRateCode(hotelId, rateCode)
- isRateCodeApplicable(hotelId, accountCode, bookingType, rateCode)
workflows.reservation.repository
ReservationV4CatalogRepository
@@ -178,6 +228,7 @@ workflows.reservation.service
- listAccounts(...)
- listRoomTypes(...)
- listRateCodes(...)
- listRateCodesByAccountAndBookingType(...)
integrations.ohip / integrations.pms
CatalogSyncAdapter
@@ -187,6 +238,7 @@ integrations.ohip / integrations.pms
规则:
- V4 入站、确认、复核只调用 `ReservationV4DirectoryService`。
- Basic Information 的 `account_code` 先确认或在同次复核中修正后,业务卡 Rate Code 才能按该 Account + `booking_type` 做适用性校验。
- 前端 lookup Controller 调用 `ReservationV4CatalogLookupService`,该服务同样只读本地目录表。
- PMS / OHIP 同步 Adapter 只能写本地目录表或同步 run,不直接参与用户确认事务。
- 如果目录服务不可用,确认接口 fail closed,不接受自由文本。
@@ -318,10 +370,12 @@ GET /api/reservation/lookups/rate-codes
| 参数 | 必需 | 中文说明 |
| --- | --- | --- |
| `hotel_id` | 否 | 当前酒店 ID |
| `account_code` | 是 | 已选择或已确认的 Reservation Account code;必须属于当前酒店 ACTIVE Account 目录 |
| `booking_type` | 是 | `GROUP` / `FIT`;取自当前业务 event 的 `target_order.booking_type` |
| `keyword` | 否 | 匹配 Rate Code 或显示名 |
| `page_num` / `page_size` | 否 | 分页 |
第一版固定只返回 `ACTIVE` Rate Code,不接 `booking_type`、`account_code`、`arrival_date` / `departure_date`,适用范围和价格过滤后置。
下一阶段 Rate Code lookup 必须按 `account_code + booking_type` 返回 `ACTIVE` 且适用的 Rate Code。`account_code` 未传、无效或不属于当前酒店时返回 400;`booking_type` 非 `GROUP` / `FIT` 时返回 400;Account 合法但当前无适用 Rate Code 时返回空 `items[]`。前端在 Account 未选时不应拉全酒店 Rate Code。`arrival_date` / `departure_date`、房型和价格过滤后置。
响应草案:
@@ -329,15 +383,17 @@ GET /api/reservation/lookups/rate-codes
{
"hotel_id": "HOTEL-TEST",
"catalog_type": "RATE_CODE",
"catalog_source": "FIXED_SEED_IMPORT",
"catalog_version": "seed-20260719-v1",
"account_code": "QBD_TRAVEL",
"booking_type": "GROUP",
"catalog_source": "IMPORT_FILE",
"catalog_version": "owner-rate-20260720-v1",
"stale": false,
"items": [
{
"code": "GROUP",
"display_name": "GROUP",
"code": "GRPA1",
"display_name": "GRPA1",
"status": "ACTIVE",
"catalog_source": "FIXED_SEED_IMPORT",
"catalog_source": "IMPORT_FILE",
"pricing_available": false
}
],
@@ -353,7 +409,8 @@ GET /api/reservation/lookups/rate-codes
前端用途:
- `fields[].options_source=reservation_v4_rate_code_catalog` 时调用。
- 第一版只选 `rate_code`,不展示或计算真实价格。
- 只能在当前订单 Basic Information 已有有效 Account,且当前业务 event 有 `booking_type` 时调用;Account 切换后必须清空或重新校验已选 Rate Code。
- 下一阶段只选 `rate_code`,不展示或计算真实价格。
- V4 契约仍禁止 `UPDATE_BOOKING` 携带 Rate Code;lookup API 不改变该规则。
### 8.4 Market / Source Lookup
@@ -370,7 +427,7 @@ Market / Source 第一版不作为用户可编辑字段,不建议给普通业
| --- | --- | --- |
| `reservation_v4_account_catalog` | `GET /api/reservation/lookups/accounts` | 渲染 Account 下拉 / 搜索选择,展示派生 Market / Source |
| `reservation_v4_room_type_catalog` | `GET /api/reservation/lookups/room-types` | 渲染房型搜索选择 |
| `reservation_v4_rate_code_catalog` | `GET /api/reservation/lookups/rate-codes` | 渲染 Rate Code 搜索选择 |
| `reservation_v4_rate_code_catalog` | `GET /api/reservation/lookups/rate-codes?account_code=...&booking_type=...` | 渲染当前 Account + GROUP/FIT 适用的 Rate Code 搜索选择 |
| `static_enum` | 使用 `fields[].enum_options` | 不调用 lookup |
| `system_case_lookup` | 后续订单 / 任务对象 lookup | 不属于本目录 checkpoint |
@@ -423,7 +480,7 @@ Market / Source 第一版不作为用户可编辑字段,不建议给普通业
| --- | --- | --- | --- | --- |
| `GET /api/reservation/lookups/accounts` | `FRONTEND_USER` | `RESERVATION_TASK_READ` | 按用户可访问酒店校验 | 只读不写业务审计 |
| `GET /api/reservation/lookups/room-types` | `FRONTEND_USER` | `RESERVATION_TASK_READ` | 按用户可访问酒店校验 | 只读不写业务审计 |
| `GET /api/reservation/lookups/rate-codes` | `FRONTEND_USER` | `RESERVATION_TASK_READ` | 按用户可访问酒店校验 | 只读不写业务审计 |
| `GET /api/reservation/lookups/rate-codes` | `FRONTEND_USER` | `RESERVATION_TASK_READ` | 按用户可访问酒店校验;`account_code` 和 `booking_type` 只能用于当前酒店目录过滤 | 只读不写业务审计 |
复用 `RESERVATION_TASK_READ` 的原因:
@@ -480,14 +537,16 @@ SuperAgent 当前不调用本 lookup API。SuperAgent 目录供给后续有两
2. 遍历每张卡 `fields[]`。
3. 只有字段 `editable=true` 且 `control_type=select/lookup` 时才加载 lookup。
4. 根据 `options_source` 选择 lookup 接口。
5. 搜索输入做 debounce,不一次性拉全量。
6. 显示 `stale=true` 或 `warnings[]` 时给用户非阻塞提醒。
7. 用户提交确认或复核时只提交 code,不提交显示名、Market / Source 派生值或目录完整对象。
8. 确认成功后以后端返回的 `confirmed_payload_json` / 刷新详情为准更新页面。
5. Rate Code 字段必须先取得当前订单 Basic Information 的 Account 和当前业务 event 的 `booking_type`;缺任一条件时禁用或显示空态,不调用全酒店 Rate Code 全量查询。
6. 搜索输入做 debounce,不一次性拉全量。
7. 显示 `stale=true` 或 `warnings[]` 时给用户非阻塞提醒。
8. 用户提交确认或复核时只提交 code,不提交显示名、Market / Source 派生值或目录完整对象。
9. 确认成功后以后端返回的 `confirmed_payload_json` / 刷新详情为准更新页面。
前端禁止:
- 硬编码 PMS 房型或 Rate Code 全集。
- 硬编码 OWNER RATE Excel 中 Account 到 Rate Code 的映射;该映射必须由后端目录 / 适用关系接口提供。
- 把目录显示名当业务 code 提交。
- 绕过 `fields[]` 自行补业务字段。
- 使用 lookup API 给 SuperAgent、AgentBus 或 Debug 链路拼接输入。
@@ -501,6 +560,7 @@ SuperAgent 当前不调用本 lookup API。SuperAgent 目录供给后续有两
| M002-V4-CP12 | 前端 Lookup 接入 | V4 卡片字段渲染按 `options_source` 调用 lookup,替换固定种子硬编码选项,处理 stale / warning / 空目录 |
| M002-V4-CP13 | 目录管理后台 V1 | CP1 已完成 Account / Room Type / Rate Code 前后端列表、新增、启用 / 停用闭环、`RESERVATION_CATALOG_MANAGE` 权限和管理审计;Market / Source 独立管理后置 |
| M002-V4-CP14 | 订单列表 V4 继续处理入口 | 已完成:订单列表返回 V4 下一步订单任务、卡片、动作类型、动作状态和 open 数,前端可优先跳 V4 订单任务详情 |
| M002-V4-CP14.5 | Account 范围 Rate Code Lookup | 待实现:新增 Account + booking type 适用关系模型 / 导入种子,Rate Code lookup 接收 `account_code`、`booking_type`,确认和复核校验 Rate Code 适用性,前端联动 Account 后展示候选 |
| M002-V4-CP15 | PMS / OPERA / OHIP 目录同步 | 同步 Adapter、同步 run 表、失败重试、最后成功快照、同步状态管理入口 |
| M002-V4-CP16 | SuperAgent 目录供给 | 明确目录版本如何给 SuperAgent,必要时新增机器目录接口或导出包 |
@@ -512,7 +572,7 @@ CP11 已作为后端第一步落地,因为它不依赖真实 PMS,也能让
2. Account code 是否继续使用本系统定义的稳定 code,例如 `QBD_TRAVEL`,还是必须对齐 PMS profile code。
3. Market / Source 是否只允许随 Account 派生,还是未来允许用户在 Basic Information 中单独改选。
4. Room Type 第一版后端已支持系统管理维护;后续是否仍要接 PMS / OHIP 同步替换为主来源待确认。
5. Rate Code 第一版是否只校验 code,还是需要按 `booking_type`、`account_code`、入住日期过滤。
5. Rate Code 已确认下一阶段按 `account_code + booking_type` 过滤;入住日期、房型和价格是否也要进入过滤仍待后续确认。
6. 生产是否允许 `FIXED_SEED` 作为兜底,还是只允许 dev/test 使用。
7. SuperAgent 是否需要读取目录;如果需要,是离线给目录包,还是新增 HMAC 机器接口。
@@ -13,6 +13,8 @@
本功能第一版只解决“来源名单到目标 Rooming List”的结构转换和分房填表问题,不依赖订单、任务、SuperAgent、AgentBus 或 OPERA。
M002 V4 `ROOMING_LIST` 任务卡与本工具是两个独立能力:V4 卡第一版只做 Rooming List 事项确认,不调用本工具、不解析名单、不生成 Excel、不导入 PMS。
## 2. 第一阶段定位
第一阶段应做到: