Files
th-hotel-simple/docs/project/requirements/M002-v4-order-task-card-domain-model-cp2.md
2026-07-20 01:54:38 +07:00

796 lines
44 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.

# M002 V4 CP2 订单任务与多卡领域模型设计
## 文档信息
| 项目 | 内容 |
| --- | --- |
| 文档版本 | 0.6 |
| 日期 | 2026-07-19 |
| 状态 | CP2 设计已确认CP3-CP8 已实现CP11 DB 目录与 Lookup API V1 已实现 |
| 适用范围 | M002 V4 入站后的订单任务、多卡、状态、查询和写操作设计 |
| 不适用范围 | V4 复核接口、真实 PMS / OPERA / OHIP、前端页面视觉稿、历史数据迁移 |
## 1. 文档定位
M002 V4 CP1 已完成 SuperAgent V4 回调包入站解析、基础校验、路由适配和 AI transition 最小落库。CP1 仍然把可映射的 V4 event 临时接入 M002 V3 的订单 / 任务 / 任务卡链路。
本文是 CP2 设计文档,用于把 2026-07-18 V4 字段契约落成后续可开发的数据模型和接口草案。
截至 CP11后端已实现本文第 10、11 节中的持久化基线,并已把 SuperAgent V4 入站结果写入新表:普通业务包创建 V4 订单任务、来源邮件展示卡、Basic Information 卡和业务卡V4 S10/S99 创建来源通知。当前已开放 V4 工作台、订单任务列表 / 详情、来源通知详情查询接口、订单详情 V4 订单任务时间线、V4 卡片确认接口、S10/S99 来源通知 ack 接口、V4 `REVIEW_REQUIRED` 卡复核解阻接口、当前酒店数据库目录校验、卡片 `fields[]` 白名单,以及 Account / Room Type / Rate Code lookup API。真实 PMS 同步和目录管理后台继续后置,设计见 `M002-v4-real-catalog-lookup-api-design.md`
后续如本文与 `M002-v4-agent-callback-field-contract.md` 的字段契约冲突,以字段契约为准;如与安全边界冲突,以 `security-access-control-boundary.md` 为准。
## 2. CP1 已完成和 CP2 差距
| 主题 | CP1 当前实现 | V4 目标模型差距 |
| --- | --- | --- |
| 入站识别 | 已识别 `route_code``source_message``order_contexts[]``message_events[]` | CP4 已把有合法 event 的 `order_ref` 建成订单任务聚合CP5 已开放 V4 安全查询接口 |
| SourceMessage | 已按 `source_message.source_message_id` 反查 SourceMessage Inbox | CP4 已固定生成普通业务包内邮件展示卡;邮件正文完整读取仍走 SourceMessage 会话接口 |
| Basic Information | 已写入 V4 Basic Information 独立卡 | CP6 已支持确认并锁定CP7 已支持复核解阻CP8 已支持目录校验和 `fields[]` 白名单CP11 已改为按当前酒店数据库 Account 目录校验并派生 Market / Source |
| 业务 Event | 可映射 event 临时创建旧 `workflow_reservation_task`,并已额外创建 V4 业务卡 | 旧任务链路仍作前端过渡兼容,后续 V4 查询和写接口完成后再逐步废弃 |
| 技术错误 | 已落 `adapter_contract_error` transition | 已符合目标方向:不创建用户可处理卡 |
| 草稿 / READY / OPERA | 仍复用 V3 草稿、READY 和 OPERA 模拟骨架 | V4 新数据确认口径是不保存草稿、确认后锁定、当前不生成 OPERA |
| 前端查询 | 复用旧任务列表和任务详情 | CP5 已开放 V4 工作台、订单任务列表 / 详情、来源通知详情和订单详情 V4 时间线 |
## 3. 领域词汇
| 术语 | 英文建议 | 中文含义 |
| --- | --- | --- |
| AI 回调包 | AI Callback Package | SuperAgent 对本系统提交的一次 V4 JSON 包,对应一个 SourceMessage 和一个 AI batch |
| 来源邮件展示卡 | Source Message Display Card | 由包级 `source_message` 固定生成的只读展示卡,用于查看当前邮件正文、附件和会话入口 |
| 订单任务 | Reservation Order Task | 同一 `source_message_id + order_ref` 在本系统内形成的业务处理聚合,不等同 PMS 订单,也不等同旧 `workflow_reservation_task` 单任务 |
| 本地订单投影 | Reservation Order Projection | 本系统保存的订单归属和业务号投影,当前主要复用 `workflow_reservation_order` |
| 任务卡 | Reservation Task Card | 订单任务下可独立确认、独立锁定的卡片,例如 Basic Information、房间信息、Trace、Rooming List、Payment |
| 技术失败记录 | Adapter / Technical Error | Agent / Adapter / MCP 契约或技术异常,只进入 AI transition 或技术运行记录,不进入用户可处理任务卡 |
## 4. 核心关系
普通业务包:
```text
source_message
-> 1 个来源邮件展示卡
order_contexts[]
-> 每个 order_ref 创建 1 个订单任务
-> 每个订单任务创建 1 张 Basic Information 卡
message_events[]
-> 每个 event 按 order_ref 挂到对应订单任务
-> 每个 event 创建 1 张业务任务卡
target_order
-> 用于订单任务绑定本地订单投影
```
纯通知包 `route_code=S10`
```text
source_message
-> 只显示来源邮件通知卡
-> 不创建订单任务
-> 不创建 Basic Information 或业务卡
```
技术异常:
```text
V4 package / event contract error
-> workflow_reservation_ai_transition
-> system_process_category=ADAPTER_CONTRACT_ERROR
-> 不创建订单任务
-> 不创建任务卡
```
## 5. 卡片类型第一版
| 卡片类型 | 来源 | 是否用户可确认 | 是否可编辑 | 中文说明 |
| --- | --- | --- | --- | --- |
| `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 展示新建、修改或整单取消字段 |
| `TRACE_RESERVATION_NOTES` | `TRACE_RESERVATION_NOTES` | 是 | 是 | Trace 卡,卡内可有普通备注和加床备注多条事项 |
| `ROOMING_LIST` | `ROOMING_LIST` | 是 | 第一版可只读或确认 | 识别到 Rooming List 任务,不在 Agent 回调里保存名单 rows |
| `PAYMENT` | `PAYMENT` | 是 | 第一版主要确认附件关联 | 付款凭证卡,只按 `attachment_ids[]` 引用包级附件 |
说明:
- Basic Information 不是 event但必须是独立任务卡。
- 来源邮件展示卡不是 event普通业务包内只读不参与订单执行阻塞。
- S10/S99 采用来源通知模型:任务列表 / 工作台可见点击进入纯通知详情页只显示邮件展示卡和确认按钮不创建订单、不进订单列表、不参与订单阻塞也不支持编辑、复核、OPERA 或人工终止。
- 同一 `order_ref` 下如果有多个相同 `event_type`,第一版按 event 数组项分别建卡;页面排序按固定卡片顺序,再按 `source_event_index` 排序。
## 6. 状态设计
### 6.1 订单任务状态
订单任务状态用于列表和页面入口,第一版按卡片状态派生,不允许用户直接编辑。为了便于列表查询,`order_task_status` 可以作为落库快照字段保存当前订单任务自身完成情况;同订单前置任务造成的阻塞不落入 `order_task_status`,而是在查询响应的 `availability` 中实时派生。
| 状态 | 中文含义 | 派生规则 |
| --- | --- | --- |
| `OPEN` | 仍有待处理卡 | 存在 `PENDING_CONFIRM``REVIEW_REQUIRED` 的可确认卡 |
| `COMPLETED` | 当前订单任务已完成 | 所有用户可确认卡均已 `CONFIRMED` |
查询响应可以额外返回 `display_status=BLOCKED`表示当前订单任务因同订单前置订单任务未完成、Basic Information 未确认或权限不足而只能查看;该值由查询服务实时计算。
技术错误不派生订单任务状态,因为技术错误不创建订单任务。
### 6.2 卡片状态
| 状态 | 中文含义 | 允许动作 |
| --- | --- | --- |
| `READONLY` | 只读卡 | 只能查看,例如普通业务包的来源邮件展示卡 |
| `PENDING_CONFIRM` | 待确认 | 若没有前置阻塞,允许提交最终确认 |
| `REVIEW_REQUIRED` | 待人工复核 | 若没有前置阻塞,允许提交复核修正并确认 |
| `CONFIRMED` | 已确认锁定 | 只能查看,不允许再次编辑或覆盖 |
| `ACK_REQUIRED` | 通知待确认 | 仅用于 S10/S99 来源通知,落在 `workflow_reservation_v4_source_notification.notification_status` |
| `ACKED` | 通知已确认 | 仅用于 S10/S99 来源通知,落在 `workflow_reservation_v4_source_notification.notification_status` |
### 6.3 可操作性派生
接口响应应返回 `availability` 或等价字段,避免前端自己猜。
| 派生状态 | 中文含义 |
| --- | --- |
| `available=true` | 当前用户在权限、酒店、状态和阻塞规则下可以处理 |
| `blocked=true` | 当前卡只能查看 |
| `readonly_reason_code=PRIOR_CARD_NOT_CONFIRMED` | 同一订单任务内前置卡未确认 |
| `readonly_reason_code=PRIOR_ORDER_TASK_NOT_COMPLETED` | 同一本地订单下更早订单任务未完成 |
| `readonly_reason_code=CARD_LOCKED` | 当前卡已确认锁定 |
| `readonly_reason_code=SOURCE_MESSAGE_DISPLAY_READONLY` | 来源邮件展示卡只读 |
## 7. 确认、复核和锁定规则
### 7.1 不保存草稿
V4 新数据不再提供后端草稿保存。前端可以在页面本地维护未提交表单状态,但后端只接受最终确认或复核解阻提交。
原因:
- 2026-07-18 V4 契约已确认“不保存草稿”。
- V4 当前没有真实 OPERA / PMS 执行,后端不需要为半成品 payload 生成后续操作。
- 可以避免 V3 `draft_payload_json``READY` 和 OPERA 模拟骨架继续扩大。
### 7.2 最终确认
用户确认卡片时,后端保存:
- `confirmed_payload_json`:用户确认后的结构化字段。
- `confirmed_by`:当前登录用户 ID。
- `confirmed_at`UTC 时间点。
- `card_status=CONFIRMED`
确认后永久锁定。若后续发现错误,不覆盖原确认记录,应该通过审计记录和未来纠错流程表达。
### 7.3 人工复核
`manual_review=true` 或目录校验失败时,卡片进入 `REVIEW_REQUIRED`
复核提交后:
- 不改写 AI 原始 payload。
- 用户修正写入 `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` 指向的目录错误字段,不允许替换对象或数组。
- 来源消息、路由、订单定位关系、诊断、缺失字段清单、`manual_review`、raw evidence 等只读字段不得提交。
- 如果订单任务归属未解决,复核请求必须提交 `confirmed_order_id`;后端按当前订单任务酒店校验该订单存在、非逻辑删除且不是系统隐藏订单。
- 如果订单任务已经有 `order_id``target_resolution_status=RESOLVED`,复核请求不能提交不同的 `confirmed_order_id`,否则返回 `V4_ORDER_REBIND_NOT_ALLOWED`;普通任务任意切换订单继续后置。
- `availability.reviewable=true``card_status=REVIEW_REQUIRED` 时,前端可以展示复核提交入口;普通确认接口仍拒绝 `REVIEW_REQUIRED` 卡。
### 7.4 Basic Information 目录规则
第一版 Basic Information 至少包含:
- Agent 原始 `account_code`
- 信息系统目录校验状态。
- 后端派生的 `market_code``source_code` 快照,来源是 Account 目录。
如果 `account_code=null` 或目录不存在:
- 卡片进入 `REVIEW_REQUIRED`
- 用户只能从信息系统已有 Account 目录中选择。
- 后端不得反向篡改 Agent 原始 `basic_information.manual_review`
CP11 起 Account / Market / Source 目录使用本系统数据库目录,不依赖 SuperAgent 动态提供目录文件。当前初始目录来自固定种子导入,`source_system=FIXED_SEED_IMPORT`V24 会覆盖 `HOTEL-TEST``HOTEL-DEV` 和迁移执行时已有的 `ACTIVE` 酒店。后续 Account / Market / Source 优先由系统管理维护Room Type / Rate Code 未来优先来自 PMS / OPERA / OHIP 同步,本地目录表和 lookup API 设计见 `M002-v4-real-catalog-lookup-api-design.md`
CP11 数据库初始化种子:
| 目录 | 第一版代码 |
| --- | --- |
| Account | `QBD_TRAVEL``LIAN_TAI``HANATOUR_TD` |
| Market | 由 Account 派生,当前固定为 `LEISURE` |
| Source | 由 Account 派生,当前固定为 `TRAVEL_AGENT` |
| Room Type | `TWN``KING``DBL``SGL``TRP``RM1``RM2``RM3` |
| Rate Code | `BAR``RACK``PACKAGE``GROUP``FIT` |
说明Room Type / Rate Code 当前只作为确认和字段控件的第一版校验 / 选项来源代码;已开放 `GET /api/reservation/lookups/accounts|room-types|rate-codes`,但尚未接真实 PMS 房型目录、Rate Code 配置中心、目录管理后台或同步 run。
## 8. 订单归属和 target_order
### 8.1 order_ref 和 target_order 分工
| 字段 | 用途 |
| --- | --- |
| `order_ref` | 当前回调包内归组,同一 `order_ref` 形成一个订单任务 |
| `target_order` | 本系统查找或创建本地订单投影,用于后续同订单阻塞和页面关联 |
不得用相同 `target_order=null` 合并订单任务Agent 如果无法判断是否同单,应输出不同 `order_ref`
### 8.2 本地订单绑定规则
| `booking_type` | `locator_type` | 第一版绑定规则 |
| --- | --- | --- |
| `GROUP` | `GROUP_CODE` | 按 `hotel_id + GROUP_CODE` 查找 ACTIVE 本地订单;不存在则创建或挂临时可见订单 |
| `FIT` | `BOOKING_CODE` | 按 `hotel_id + BOOKING_CODE` 查找本地订单投影;不存在则创建临时或 Booking Code 订单;若匹配多条则进入人工复核,不自动选单 |
| `FIT` | `CONFIRMATION_NUMBER` | 按 `hotel_id + CONFIRMATION_NUMBER` 查找 ACTIVE 本地订单;不存在则创建可见订单 |
| 任意 | 任意字段未解决 | 创建可见临时订单,订单任务和相关卡片进入复核或阻塞状态,等待用户确认订单归属 |
`FIT + BOOKING_CODE` 第一版不建立 ACTIVE 唯一约束。`BOOKING_CODE` 是拿到 `CONFIRMATION_NUMBER` 前的临时定位字段,后续不作为 PMS 永久主键;系统只在业务绑定时要求匹配结果至多一条。
### 8.3 复核场景订单归属确认
V4 CP2 只设计复核场景下的订单归属确认,不开放普通任务任意切换订单。
规则:
- 用户只能在 `REVIEW_REQUIRED` 卡片或订单任务归属未解决时确认订单归属。
- 订单归属确认必须写业务审计。
- 若确认到其他订单,原临时订单可在无其它任务引用时逻辑删除。
- 已确认锁定卡片不能通过该接口二次迁移订单。
- CP7 第一版只支持在复核解阻请求中提交 `confirmed_order_id` 完成未解决归属的一次性确认;如果订单任务已解析到某个订单且状态为 `RESOLVED`,只能确认同一订单,不能借复核接口切换到其它订单。
- 如果确认到的订单下存在更早未完成 V4 订单任务,后端拒绝当前复核提交,避免绕过同订单阻塞规则。
## 9. 同订单阻塞规则
V4 当前不做 OPERA / PMS 执行,但仍需要保留同订单处理顺序,避免用户先确认后续邮件导致业务事实倒置。
### 9.1 同一订单任务内
推荐固定顺序:
```text
10 SOURCE_MESSAGE_DISPLAY
20 BASIC_INFORMATION
30 ROOM_INFORMATION
40 TRACE_RESERVATION_NOTES
50 ROOMING_LIST
60 PAYMENT
```
规则:
- `SOURCE_MESSAGE_DISPLAY` 只读,不阻塞。
- `BASIC_INFORMATION` 未确认时,其它业务卡只能查看,不能确认。
- 除 Basic Information 必须先确认外,第一版不强制业务卡之间逐张顺序确认;业务卡可独立确认,但页面仍按固定顺序展示。
- 同类型多张业务卡按 `source_event_index` 排序。
### 9.2 同一本地订单下多个订单任务
如果多个 SourceMessage 或多个回调包绑定到同一本地订单:
- 按来源邮件接收时间、AI batch 接收时间、订单任务创建时间排序。
- 更早订单任务仍有未完成可确认卡时,后续订单任务只能查看。
- 技术错误 transition 不参与阻塞。
- S10/S99 来源通知不参与阻塞,也不被阻塞。
## 10. 数据表设计草案
### 10.1 继续复用的表
| 表 | 复用方式 |
| --- | --- |
| `platform_source_message_inbox` | 继续作为来源消息事实和外部 `source_message_id` 查询入口 |
| `workflow_reservation_ai_batch` | 继续保存一次 SuperAgent 回调包的幂等和批次信息 |
| `workflow_reservation_ai_transition` | 继续保存 event 级原始 payload、route、adapter error 和追踪信息 |
| `workflow_reservation_order` | 继续作为本地订单投影和订单列表数据源,但需要补 V4 所需 `BOOKING_CODE` 或 locator 相关能力 |
| `workflow_reservation_audit_log` | 继续保存卡片确认、复核、订单归属确认等业务审计 |
### 10.2 已新增表:`workflow_reservation_v4_order_task`
一条记录表示一个 `source_message_id + order_ref` 形成的 V4 订单任务。
| 字段 | 中文说明 |
| --- | --- |
| `id` | V4 订单任务 ID |
| `hotel_id` | 酒店 ID |
| `source_message_id` | SourceMessage Inbox 内部 ID |
| `ai_batch_id` | AI 回调批次 ID |
| `order_ref` | V4 包内订单引用 |
| `order_context_index` | `order_contexts[]` 中的一基序号,用于同一 SourceMessage 下稳定排序 |
| `order_id` | 绑定的本地订单投影 ID |
| `target_booking_type` | `GROUP` / `FIT` |
| `target_locator_type` | `GROUP_CODE` / `BOOKING_CODE` / `CONFIRMATION_NUMBER` |
| `target_locator_value` | 目标订单定位值,未解决时为空 |
| `target_resolution_status` | `RESOLVED` / `UNRESOLVED` / `CONFLICT` |
| `order_task_status` | 落库快照,仅保存 `OPEN` / `COMPLETED``BLOCKED` 由查询响应实时派生 |
| `source_received_at` | 来源邮件接收 UTC 时间,用于排序 |
| `version` | 乐观锁版本 |
| `created_at` / `updated_at` | UTC 创建和更新时间 |
| `logic_deleted_at` / `logic_deleted_reason` | 逻辑删除时间和原因CP3 第一版只预留字段Repository 默认排除逻辑删除记录 |
建议唯一约束:
```text
uk_reservation_v4_order_task_source_ref(hotel_id, source_message_id, order_ref)
uk_reservation_v4_order_task_source_index(hotel_id, source_message_id, order_context_index)
```
### 10.3 已新增表:`workflow_reservation_v4_task_card`
一条记录表示 V4 订单任务下的一张卡。
| 字段 | 中文说明 |
| --- | --- |
| `id` | V4 任务卡 ID |
| `hotel_id` | 酒店 ID |
| `v4_order_task_id` | 所属 V4 订单任务 IDS10/S99 来源通知不挂订单任务,后续使用独立通知模型承载 |
| `source_message_id` | 来源消息 ID便于查邮件会话 |
| `ai_transition_id` | 对应 event 的 AI transition IDBasic Information 和来源邮件展示卡可为空 |
| `card_type` | 卡片类型 |
| `event_type` | 对应 V4 event_type非 event 卡为空 |
| `source_event_index` | event 在 `message_events[]` 中的一基序号;非 event 卡固定为 `0`,避免 MySQL 唯一索引因 `NULL` 失效 |
| `card_sort_order` | 页面固定排序值 |
| `card_status` | `READONLY` / `PENDING_CONFIRM` / `REVIEW_REQUIRED` / `CONFIRMED` 等 |
| `review_status` | `PENDING` / `RESOLVED`,仅复核卡使用 |
| `ai_payload_json` | 当前卡使用的 AI 原始片段,不可被用户覆盖 |
| `display_payload_json` | 后端给前端展示的字段快照,可由 AI 和目录派生 |
| `confirmed_payload_json` | 用户最终确认后的字段 |
| `review_resolution_json` | 人工复核修正和订单归属确认结果 |
| `validation_errors_json` | 目录或业务校验错误摘要,不保存敏感正文 |
| `confirmed_by` / `confirmed_at` | 确认人和确认 UTC 时间 |
| `version` | 乐观锁版本 |
| `created_at` / `updated_at` | UTC 创建和更新时间 |
| `logic_deleted_at` / `logic_deleted_reason` | 逻辑删除时间和原因CP3 第一版只预留字段Repository 默认排除逻辑删除记录 |
建议唯一约束:
```text
uk_reservation_v4_task_card_slot(hotel_id, v4_order_task_id, card_sort_order, source_event_index)
```
约束说明:
- `SOURCE_MESSAGE_DISPLAY``BASIC_INFORMATION` 等非 event 卡使用 `source_event_index=0`
- event 业务卡使用一基 `source_event_index`
- 同一个订单任务下,固定卡片顺序 + `source_event_index` 必须唯一,防止入站重试或幂等异常重复创建同一张卡。
### 10.4 已新增表:`workflow_reservation_v4_source_notification`
一条记录表示一个 S10/S99 来源通知。它不挂订单任务、不创建订单、不进入订单列表,只用于任务列表 / 工作台和纯通知详情页。
| 字段 | 中文说明 |
| --- | --- |
| `id` | 来源通知 ID |
| `hotel_id` | 酒店 ID第一版使用系统默认酒店或 SourceMessage 所属酒店 |
| `source_message_id` | SourceMessage Inbox 内部 ID |
| `ai_batch_id` | AI 回调批次 ID |
| `ai_transition_id` | S10/S99 对应 AI transition ID |
| `route_code` | `S10``S99` |
| `notification_status` | `ACK_REQUIRED` / `ACKED` |
| `raw_payload_json` | S10/S99 原始 AI 片段或包级摘要 |
| `ack_by` / `ack_at` | 确认人和确认 UTC 时间 |
| `source_received_at` | 来源邮件接收 UTC 时间,用于任务列表 / 工作台排序 |
| `version` | 乐观锁版本,用于确认按钮并发控制 |
| `created_at` / `updated_at` | UTC 创建和更新时间 |
| `logic_deleted_at` / `logic_deleted_reason` | 逻辑删除时间和原因CP3 第一版只预留字段Repository 默认排除逻辑删除记录 |
建议唯一约束:
```text
uk_reservation_v4_notification_message_batch(hotel_id, source_message_id, ai_batch_id)
```
### 10.5 暂不新增的表
| 表或能力 | 暂不新增原因 |
| --- | --- |
| V4 草稿表 | V4 已确认不保存草稿 |
| V4 OPERA operation 表 | 当前不做真实 OPERA / OHIP也不生成 OPERA 模拟 |
| 技术错误用户任务表 | 技术错误不进入用户任务体系,先用 AI transition 和 dispatch run |
| 目录表 | CP11 已新增 `workflow_reservation_catalog_account``workflow_reservation_catalog_code`;真实同步 run 和目录管理后台后置 |
## 11. Entity / Mapper / Repository 边界草案
后续 Java 实现必须遵守当前后端目录规范。
### 11.1 domain
已新增:
- `ReservationV4OrderTaskEntity`:映射 `workflow_reservation_v4_order_task`
- `ReservationV4TaskCardEntity`:映射 `workflow_reservation_v4_task_card`
- `ReservationV4SourceNotificationEntity`:映射 `workflow_reservation_v4_source_notification`
Entity 字段必须有中文注释,说明字段业务含义、来源和状态代码范围。
### 11.2 mapper
已新增:
- `ReservationV4OrderTaskMapper`
- `ReservationV4TaskCardMapper`
- `ReservationV4SourceNotificationMapper`
Mapper 只放 MyBatis-Plus 基础访问和必要语义化查询。自定义方法需要中文注释。
### 11.3 repository
已新增:
- `ReservationV4WorkflowRepository`
- `MybatisReservationV4WorkflowRepository`
- `ReservationV4SourceNotificationRepository`
- `MybatisReservationV4SourceNotificationRepository`
CP3 Repository 已封装:
- 按 SourceMessage + order_ref 幂等创建订单任务。
-`order_context_index` 保留同一 SourceMessage 下多个 `order_contexts[]` 的稳定顺序。
- 创建 Basic Information / SourceMessage / Event 卡的基础插入方法;非 event 卡 `source_event_index` 固定写入 `0`
- 按 SourceMessage + AI batch 幂等创建 S10/S99 来源通知。
- 查询订单任务详情和卡片列表。
- 查询来源通知列表和详情。
- 卡片状态的 version 乐观锁更新基础方法。
- 来源通知状态的 version 乐观锁更新基础方法。
- event 卡必须传入正数一基 `source_event_index`;漏传会拒绝写入,避免被误当成非 event 卡。
CP6 已实现普通卡片确认和 S10/S99 来源通知 ack 的业务 Service 和 APIV4 复核解阻、复核场景订单归属确认仍属于后续 checkpoint。
Service 不直接访问 Mapper。
### 11.4 service
建议新增或拆分:
- `ReservationV4TaskIntakeService`已实现V4 入站从 AI transition 落 V4 订单任务、卡片和 S10/S99 来源通知。
- `ReservationV4OrderTaskQueryService`:前端查询订单任务列表和详情。
- `ReservationV4CommandService`:已实现卡片确认和 S10/S99 来源通知确认;后续继续承接 V4 复核和订单归属确认。
- `ReservationV4SourceNotificationService`:来源通知查询已并入 V4 查询服务,确认已并入 V4 命令服务。
当前 `ReservationAiTaskIntakeServiceImpl` 已在 V4 分支调用 `ReservationV4TaskIntakeService` 完成新模型写入;查询和确认已拆到独立 V4 service后续复核仍应继续留在 V4 命令侧,避免主入站类继续膨胀。
## 12. 前端查询接口
以下查询接口已在 CP5 实现。所有接口都属于 `FRONTEND_USER`,必须带 Bearer token权限码为 `RESERVATION_TASK_READ`,并按酒店访问权校验。查询接口只返回安全摘要、展示 payload、确认 payload、复核结果和状态不返回邮件正文、附件 URL、`ai_payload_json` 或来源通知原始 payload。
V4 前端接口不继续扩展旧 `/api/reservation/tasks/**` 作为 V4 主模型入口。第一版中,工作台统一列表使用 `/api/reservation/workbench-items`,业务订单任务使用 `/api/reservation/order-tasks/**`S10/S99 来源通知详情使用 `/api/reservation/source-notifications/**`
### 12.1 工作台统一列表
```text
GET /api/reservation/workbench-items
分类FRONTEND_USER
权限RESERVATION_TASK_READ
```
用途:
- 作为 V4 任务列表 / 工作台的第一版统一入口。
- 同时返回业务订单任务和 S10/S99 来源通知。
- 前端按 `item_type` 区分跳转目标。
返回摘要应包含:
- `item_type``ORDER_TASK` / `SOURCE_NOTIFICATION`
- `target_id`:订单任务 ID 或来源通知 ID。
- `source_message_summary`
- `display_order_key`S10/S99 来源通知为空。
- `card_counts`S10/S99 来源通知为空或只返回通知状态。
- `next_action_card_id`S10/S99 来源通知为空。
- `notification_status`:仅 S10/S99 来源通知返回 `ACK_REQUIRED` / `ACKED`
- `order_task_status`:仅业务订单任务返回 `OPEN` / `COMPLETED`
- `display_status`:可返回 `OPEN` / `BLOCKED` / `COMPLETED` / `ACK_REQUIRED` / `ACKED`
- `readonly_reason_code`
- `created_at` / `updated_at`:工作台条目创建和更新时间,主要用于同一来源时间下的稳定排序和前端调试。
排序规则:
- 默认按 `source_received_at` 倒序。
- 同一来源时间下按记录 `updated_at``created_at`、数字 ID 倒序。
### 12.2 业务订单任务列表
```text
GET /api/reservation/order-tasks
分类FRONTEND_USER
权限RESERVATION_TASK_READ
```
查询参数:
| 参数 | 中文说明 |
| --- | --- |
| `hotel_id` | 可选,按用户酒店访问权校验 |
| `order_id` | 可选,查询某个本地订单下的订单任务 |
| `card_status` | 可选,筛选含某类待处理卡的订单任务 |
| `keyword` | 可选匹配邮件主题、发件人、Group Code、Booking Code、Confirmation Number |
| `page_num` / `page_size` | 分页 |
筛选说明:
- `order_task_status` 只接受 `OPEN` / `COMPLETED`,非法值返回 `V4_ORDER_TASK_STATUS_INVALID`
- `card_status` 只接受 `READONLY` / `PENDING_CONFIRM` / `REVIEW_REQUIRED` / `CONFIRMED`,非法值返回 `V4_CARD_STATUS_INVALID`
- `card_status` 按业务 / 可处理卡筛选,固定的 `SOURCE_MESSAGE_DISPLAY` 来源邮件只读卡不参与匹配,避免 `READONLY` 把所有普通业务任务都筛出来。
返回摘要应包含:
- `order_task_id`
- `order_ref`
- `order_id`
- `display_order_key`
- `source_message_summary`
- `card_counts`
- `next_action_card_id`
- `order_task_status`
- `display_status`
- `readonly_reason_code`
说明:
- 该接口只返回业务订单任务,不返回 S10/S99 来源通知。
- V4 任务列表 / 工作台页面第一版优先使用 `GET /api/reservation/workbench-items`
### 12.3 订单任务详情
```text
GET /api/reservation/order-tasks/{orderTaskId}
分类FRONTEND_USER
权限RESERVATION_TASK_READ
```
返回结构:
```json
{
"order_task": {},
"bound_order": {},
"source_message_card": {},
"basic_information_card": {},
"business_cards": [],
"adapter_contract_errors": [],
"availability": {}
}
```
前端应以返回的 `cards[]``availability` 为准渲染,不自行拼完整字段矩阵。
`adapter_contract_errors[]` 返回同一 AI 批次中未生成业务卡的 V4 event 级契约错误诊断块,只包含白名单诊断字段,例如 `event_type``source_event_index``contract_errors``reason_code``missing_fields` 等;不返回完整 AI payload、邮件正文、附件 URL 或 raw evidence。
来源邮件摘要必须按当前查询酒店过滤。如果 V4 订单任务因脏引用指向了其它酒店的 SourceMessage详情接口只能返回该 `source_message_id` 的空摘要占位,不得透出对方酒店的主题、外部消息 ID、发件人或会话信息。
前端 V4 来源消息卡只展示来源邮件安全摘要、邮件片段和附件名称 / 类型 / 大小等非 URL 摘要;如果 `source_message_card.display_payload` 中的 `attachments``uploaded_media``file_references` 异常包含直接 URL 字符串,前端必须兜底显示为未命名附件或隐藏,不能在普通业务页面渲染具体 URL。
### 12.4 S10/S99 来源通知详情
```text
GET /api/reservation/source-notifications/{notificationId}
分类FRONTEND_USER
权限RESERVATION_TASK_READ
```
返回结构:
```json
{
"notification": {},
"source_message_card": {},
"conversation_summary": {},
"availability": {}
}
```
说明:
- 只用于 S10/S99 来源通知详情页。
- 不返回 `order_task``bound_order``basic_information_card``business_cards`
- 邮件正文、附件 URL 和会话原文读取仍按 SourceMessage 权限和原文读取审计规则处理。
- 来源通知详情页复用 V4 来源消息卡展示规则:普通页面只显示安全摘要和附件名称;附件 URL、完整正文和 HTML 只允许通过 SourceMessage 会话详情权限链路查看。
### 12.5 订单详情时间线
已在旧订单详情接口内兼容扩展:
```text
GET /api/reservation/orders/{orderId}
分类FRONTEND_USER
权限RESERVATION_ORDER_READ
```
用于订单详情页展示 V4 订单任务时间线,同时保留旧 `tasks[]`。新增字段为 `v4_order_tasks[]``include_tasks=false``tasks[]``v4_order_tasks[]` 都返回空数组。
`v4_order_tasks[]` 每项返回:
- `order_task_id`
- `order_ref`
- `order_task_status`
- `card_counts`
- `source_message_summary`
- `source_received_at`
- `created_at`
- `updated_at`
- `latest_activity_at`
说明:
- 排序沿用 V4 Repository 的同订单顺序:`source_received_at``source_message_id``order_context_index``created_at`、数字 ID 正序。
- `source_message_summary` 只返回安全摘要不返回邮件正文、HTML、附件 URL 或 AI 原始 payload原文仍走 SourceMessage 会话接口。
- `latest_activity_at` 为 V4 订单任务自身 `updated_at` 与其下卡片 `updated_at` 的最大值。
- 接口权限仍使用 `RESERVATION_ORDER_READ`并按订单实际所属酒店校验访问权V4 任务读取时继续以该订单酒店过滤,避免跨酒店脏数据泄露。
### 12.6 订单列表 V4 继续处理入口
旧订单列表接口继续作为订单视角入口:
```text
GET /api/reservation/orders
分类FRONTEND_USER
权限RESERVATION_ORDER_READ
```
在保留旧字段 `open_task_count``next_processable_task_id` 的基础上M002 V4 CP14 已补齐以下 V4 字段:
| 字段 | 中文说明 |
| --- | --- |
| `next_v4_order_task_id` | 当前订单下第一条仍需用户处理的 V4 订单任务 ID |
| `next_v4_action_card_id` | 该 V4 订单任务下第一张仍需确认或复核的卡片 ID |
| `next_v4_action_type` | `CONFIRM` / `REVIEW` / `NONE` |
| `next_v4_action_status` | `PENDING_CONFIRM` / `REVIEW_REQUIRED`;没有待处理卡时为空 |
| `v4_open_order_task_count` | 当前订单下未完成 V4 订单任务数,`COMPLETED` 不计入 |
派生规则:
- 只统计绑定到订单的 V4 订单任务S10/S99 来源通知不创建订单,不进入订单列表字段统计。
- 同订单 V4 订单任务沿用 Repository 队列顺序:`source_received_at``source_message_id``order_context_index``created_at`、数字 ID 正序。
- `order_task_status=COMPLETED` 的 V4 订单任务不计入 `v4_open_order_task_count`,也不作为下一步入口。
- 同一 V4 订单任务内Basic Information 必须优先于业务卡。
- Basic 已确认后,业务卡中 `REVIEW_REQUIRED` 优先于普通 `PENDING_CONFIRM`
- `next_v4_action_type=CONFIRM` 时前端调用卡片确认接口;`REVIEW` 时调用复核解阻接口;`NONE` 表示该订单没有 V4 待处理入口。
前端订单列表“继续处理”应优先使用 `next_v4_order_task_id` 跳转 V4 订单任务详情;没有 V4 待处理入口时,再回退旧 `next_processable_task_id`
## 13. 前端写操作接口
### 13.1 确认卡片
```text
POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm
分类FRONTEND_USER
权限RESERVATION_TASK_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`
- 不提交草稿。
- 必须带 `version` 做并发校验。
- 后端确认后卡片 `CONFIRMED` 并锁定。
- 返回刷新后的 `GET /api/reservation/order-tasks/{orderTaskId}` 详情结构。
- Basic Information 必须先确认;业务卡第一版不强制逐张顺序确认。
- `SOURCE_MESSAGE_DISPLAY``CONFIRMED``REVIEW_REQUIRED` 卡不能通过该接口确认。
### 13.2 复核并确认卡片
```text
POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution
分类FRONTEND_USER
权限RESERVATION_MANUAL_REVIEW_RESOLVE
```
请求要点:
- 提交 `field_overrides[]`
- 可提交复核场景订单归属确认。
- 请求 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` 指向的目录错误字段,不能修改其它已有有效值、替换整个对象 / 数组或新增未知字段。
- 复核提交后同样执行目录校验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。
- 解阻过程不改写 `ai_payload_json`;用户修正写入 `review_resolution_json`,最终确认快照写入 `confirmed_payload_json`
- 校验通过后直接 `CONFIRMED`
- 写业务审计。
- 返回刷新后的 `GET /api/reservation/order-tasks/{orderTaskId}` 详情结构;对应卡片 `review_status=RESOLVED``confirmed_by``confirmed_at` 会返回。
### 13.3 S10/S99 通知确认
```text
POST /api/reservation/source-notifications/{notificationId}/ack
分类FRONTEND_USER
权限RESERVATION_TASK_CONFIRM
```
S10/S99 已确认采用来源通知模型,不继续复用隐藏技术订单或旧 `SOURCE_MESSAGE_ONLY` 任务确认方式。第一版通知详情只显示邮件展示卡和确认按钮,确认动作表示已读 / 已处理。
请求要点:
- 必须带 `version` 做并发校验。
- 仅允许 `route_code=S10/S99` 的来源通知;其它路由即使状态为 `ACK_REQUIRED` 也不能通过该接口确认。
- 确认后 `notification_status=ACKED`,写确认人和 UTC 确认时间。
- 重复提交已确认通知按幂等成功返回当前已确认状态,不新增审计,不允许回退到 `ACK_REQUIRED`
- 返回刷新后的 `GET /api/reservation/source-notifications/{notificationId}` 详情结构。
## 14. 旧 V3 / V2 兼容和废弃边界
开发阶段已确认可以清空数据,因此后续 V4 开发可以不迁移历史任务数据。
### 14.1 可继续保留
- `workflow_reservation_ai_batch`
- `workflow_reservation_ai_transition`
- `workflow_reservation_order`
- `workflow_reservation_audit_log`
- SuperAgent V3 / V2 入站兼容代码,直到正式切换前
### 14.2 V4 新数据不再使用
- `workflow_reservation_task_card.draft_payload_json`
- V3 `READY` 作为确认后等待 OPERA 的状态语义
- V3 OPERA 模拟自动生成
- V2 `ai_task_results[]` 字段矩阵作为新 V4 页面主模型
-`S000/S999` 作为新入站格式
- Fallback 转 New / Update / Cancel 作为 V4 主入口
### 14.3 可以后续专项废弃
- V2 `text/plain S000/S999` 兼容入口。
-`ai_task_results[]` 入站入口。
- V3 40 路由中不再被 V4 使用的历史 subtype。
- `workflow_reservation_task` 作为 V4 多卡主模型的直接承载方式。
- 旧 V2/V3 任务详情、草稿保存和最终确认接口,待 V4 新模型落地并完成前端切换后逐步废弃。
废弃前必须先确认前端、MCP、SuperAgent 联调方和测试 fixture 都已切到 V4。
## 15. 安全、权限和审计
后续实现接口时必须同步更新 `security-access-control-boundary.md`
第一版建议:
| 能力 | 分类 | 权限码 | 审计 |
| --- | --- | --- | --- |
| 查询工作台、订单任务列表 / 详情、来源通知详情 | `FRONTEND_USER` | `RESERVATION_TASK_READ` | 只读默认不写业务审计 |
| 确认卡片 | `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` | 写原文读取审计 |
| SuperAgent V4 回调 | `THIRD_PARTY_SUPERAGENT` | HMAC 机器鉴权 | 写 AI batch / transition |
AI 原始 payload、邮件正文、附件 URL 和技术 trace 不应直接进入普通列表接口。
## 16. 后续开发 checkpoint
| Checkpoint | 目标 | 主要交付 |
| --- | --- | --- |
| M002-V4-CP3 | V4 表结构和基础 Repository | 新增 V4 order task / card / source notification 表、Entity、Mapper、Repository、测试 |
| M002-V4-CP4 | V4 入站落新模型 | 已完成SuperAgent V4 回调创建订单任务、Basic Information 卡、业务卡、邮件展示卡和 S10/S99 来源通知 |
| M002-V4-CP5 | V4 查询接口 | 已完成:工作台统一列表、订单任务列表、订单任务详情、来源通知详情查询接口,以及旧订单详情响应内的 `v4_order_tasks[]` V4 订单任务时间线 |
| M002-V4-CP6 | V4 卡片确认和 S10/S99 ack | 已完成不保存草稿支持普通卡片确认、确认后锁定、Basic Information 前置约束、同订单前置任务写侧阻塞、业务审计、version 并发校验和 S10/S99 来源通知确认 |
| M002-V4-CP7 | V4 复核解阻与订单归属确认 | 已完成:支持 `REVIEW_REQUIRED` 卡字段修正、复核说明、复核场景订单归属确认、version 并发校验、直接 `CONFIRMED`、审计和 availability.reviewable |
| M002-V4-CP8 | 受控目录第一版 | 已完成第一版Account 固定目录校验、Market / Source 派生、RoomType / RateCode 固定种子校验、V4 任务卡 `fields[]` 字段白名单 |
| M002-V4-CP9 | V4 前端契约收口 | 字段、控件、availability、错误展示和旧任务入口切换 |
| M002-V4-CP10 | 旧 V3 / V2 能力收口评估 | 明确哪些兼容入口可以关闭,哪些仍保留只读历史 |
| M002-V4-CP11 | DB 管理目录与 Lookup API V1 | 已完成:新增 Account / Code 目录表、DirectoryService DB 实现、Account / Room Type / Rate Code lookup 查询接口、权限、酒店隔离和测试;同步 run 后置 |
| 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-CP15 | PMS / OPERA / OHIP 目录同步 | 同步 Adapter、同步 run、最后成功快照、失败重试和同步状态管理入口 |
## 17. 已确认设计决策
1. V4 前端接口不继续扩展旧 `/api/reservation/tasks/**`;工作台统一列表新开 `/api/reservation/workbench-items`,业务订单任务新开 `/api/reservation/order-tasks/**`S10/S99 来源通知使用 `/api/reservation/source-notifications/**`
2. `FIT + BOOKING_CODE` 第一版不建立 ACTIVE 唯一约束;业务绑定时要求匹配结果至多一条,匹配多条进入人工复核。
3. `BOOKING_CODE` 只是拿到 `CONFIRMATION_NUMBER` 前的临时定位字段,后续不作为 PMS 永久主键。
4. S10/S99 采用来源通知模型:任务列表 / 工作台展示,不挂隐藏技术订单;通知详情只显示邮件展示卡和确认按钮。
5. S10/S99 不创建订单、不进订单列表、不参与订单阻塞也不支持编辑、复核、OPERA 或人工终止。
6. 第一版不强制所有业务卡逐张顺序确认,但 Basic Information 必须先确认。
7. Basic Information 的 Account / Market / Source 目录当前使用本系统数据库目录;第一版初始化数据来自固定种子导入,但运行时不再读取后端固定 Map。
8. 普通业务邮件的来源邮件展示卡只读展示,不需要用户确认;用户只确认 Basic Information 和具体业务卡。
9. S10/S99 因为没有业务卡,邮件展示卡需要确认按钮,用来记录已读 / 已处理。
10. V4 新模型落地并完成前端切换后,旧 V2/V3 任务详情、草稿保存和最终确认接口可以逐步废弃。