Files
th-hotel-simple/docs/project/requirements/M002-v4-order-task-card-domain-model-cp2.md

898 lines
65 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.8 |
| 日期 | 2026-07-20 |
| 状态 | CP2 设计已确认CP3-CP8、CP11、CP13、CP14、V4 业务审计查询、停止 V4 普通业务双写旧任务和 Room Information 后端展示模型第一版已实现 |
| 适用范围 | M002 V4 入站后的订单任务、多卡、状态、查询和写操作设计 |
| 不适用范围 | 真实 PMS / OPERA / OHIP、前端页面视觉稿、生产历史数据迁移 |
## 1. 文档定位
M002 V4 CP1 已完成 SuperAgent V4 回调包入站解析、基础校验、路由适配和 AI transition 最小落库。早期 CP1 曾把可映射的 V4 event 临时接入 M002 V3 的订单 / 任务 / 任务卡链路;开发阶段最新决策已停止 V4 普通业务入站双写旧 `workflow_reservation_task`
本文是 CP2 设计文档,用于把 2026-07-18 V4 字段契约落成后续可开发的数据模型和接口草案。
截至 CP14、V4 业务审计查询、停止旧任务双写、Room Information 后端展示模型和 Rooming List 确认自动 DEF 后端联动,后端已实现本文第 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`。下一阶段已确认 Rate Code 需要按订单级 Account + `booking_type`GROUP / FIT过滤和校验当前后端 CP11 实现仍是酒店级 Rate Code 目录是待补齐缺口Payment 卡下一阶段需要展示付款凭证附件,图片为缩略图 + 点击大图预览,非图片为文件列表 + 下载,但附件 URL 仍必须走 SourceMessage 原文权限链路;真实 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 | V4 新业务主线已只创建 V4 order task / cards不再双写旧 `workflow_reservation_task` | 旧 V2/V3 入站代码仍可作为历史参考保留,但开发阶段不维护旧任务兼容,测试数据可重建 |
| 技术错误 | 已落 `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` | 是 | 是 | 房型信息卡,卡内按 New / Update / Cancel 展示最终值、差异和系统派生字段 |
| `TRACE_RESERVATION_NOTES` | `TRACE_RESERVATION_NOTES` | 是 | 是 | Trace 卡,卡内可有普通备注和加床备注多条事项 |
| `ROOMING_LIST` | `ROOMING_LIST` | 是 | 否 | 第一版只做事项确认;不在 Agent 回调里保存名单 rows不生成 Excel不导入 PMS |
| `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` 必须是当前卡 `fields[]` 白名单中允许编辑的 RFC 6901 JSON Pointer。`REVIEW_REQUIRED` 是整张原业务卡的复核状态,前端仍在原卡片内展示业务表单,问题字段用红字 / `validation_errors` 强调;用户可修改当前卡业务白名单内字段,不再限定只能改空值、`missing_fields[]` 或目录错误字段。
- Room Information 卡的 `fields[].editable``review-resolution` pointer 校验必须共用同一套字段策略;只要详情接口返回 `editable=true``write_target=review_resolution.field_overrides`,同一个 pointer 就不得再因为白名单不一致返回 `V4_REVIEW_POINTER_NOT_ALLOWED`。如果仍被拒绝,应先通过 `GET /api/health` 检查 `build_commit``runtime_marker=m002_v4_review_pointer_deployment_proof_v1`,确认测试机运行包是否包含最新修复;如果确认已部署,再看后端日志 `review_pointer_policy=m002_v4_review_pointer_runtime_trace_v1`,其中会输出 order task、card、incoming pointer、query-side editable pointers、command-side allowed pointers 和 reject reason但不得输出 payload、邮件正文、附件 URL 或敏感数据。
- 来源消息、路由、订单定位关系、诊断、缺失字段清单、`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`;下一阶段候选需按 Account + `booking_type` 过滤 |
说明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
### 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 BASIC_INFORMATION
30 ROOM_INFORMATION
40 TRACE_RESERVATION_NOTES
50 ROOMING_LIST
60 PAYMENT
90 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 同一本地订单下多个订单任务
如果多个 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`CP13 已完成目录管理后台 CP1真实同步 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 任务详情页的 `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 第一版不在卡内展示。
- 后端已在 `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[]` 或目录错误字段。
- `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日期缺失、非法或离店早于入住时`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。
- `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 显示。
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 卡片状态和订单任务派生状态;如果同订单为 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 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 或确认 payloadPayment 第一版确认卡片时只提交 `version` 和必要审计说明,不提交 `attachment_ids[]`、附件 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 订单总览和 V4 订单任务时间线,同时保留旧 `tasks[]`。M002 V4 CP15.1 已补齐 `order_overview``next_v4_action``related_source_messages[]``v4_order_tasks[].cards[]``include_tasks=false``tasks[]``v4_order_tasks[]``related_source_messages[]` 都返回空数组,`order_overview` 为空快照,`next_v4_action.action_type=NONE`
`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 展示模型实现后,可继续从确认快照派生 `nights``breakfast_included` 和 Group Booking Status
- Trace / Rooming List / Payment 返回对应最新卡片状态,方便订单详情展示待处理事项;
- 未确认 AI 建议不得进入 `order_overview`,避免把未处理内容展示成订单事实。
`next_v4_action` 使用和订单列表一致的下一步处理口径Basic Information 优先;业务卡中 `REVIEW_REQUIRED` 优先于 `PENDING_CONFIRM`;无待处理卡时 `action_type=NONE`。订单详情页应使用该字段跳转 V4 订单任务详情页,不在订单详情页直接确认或复核。
`v4_order_tasks[]` 每项返回:
- `order_task_id`
- `order_ref`
- `order_task_status`
- `card_counts`
- `cards[]`
- `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` 的最大值。
- `cards[]` 只返回卡片安全摘要,不返回业务字段 payload订单详情页如需处理字段必须跳转 V4 订单任务详情接口。
- 接口权限仍使用 `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 字段,并新增订单列表统一展示计数字段:
| 字段 | 中文说明 |
| --- | --- |
| `open_work_item_count` | 订单列表统一待处理工作项数量;开发阶段不考虑旧数据,第一版等于 `v4_open_order_task_count` |
| `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 来源通知不创建订单,不进入订单列表字段统计。
- `open_work_item_count` 是前端展示待处理数量的统一口径;第一版忽略旧 V2/V3 数据,不叠加 `open_task_count`
- 同订单 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` 仅作为历史 V2/V3 诊断兼容字段保留,测试数据清理后新 V4 订单不应返回该字段。
开发 / 测试阶段旧任务口径:
- V4 普通业务入站不再创建旧 `workflow_reservation_task`,也不再创建旧 `workflow_reservation_task_card`、旧任务草稿、旧 OPERA 模拟操作。
- `open_task_count` 仍表示旧任务表原始未关闭诊断计数;开发 / 测试环境应通过专项 SQL 清理旧任务及其直接依赖数据,清理后新 V4 订单列表中该值应为 0。
- `next_processable_task_id` 仍按旧 V2/V3 队列实时计算,但开发阶段不维护旧任务兼容;清理旧任务后,新 V4 订单不应再返回旧任务入口。
- 开发阶段不维护 V2/V3 旧任务兼容,测试数据可重建。
- 该策略仅限开发 / 测试阶段,不代表生产迁移方案;生产数据迁移策略不在本 checkpoint 处理,后续上线前另开迁移方案。
### 12.7 V4 业务审计查询
V4 卡片确认、复核解阻、复核场景订单归属确认和 S10/S99 来源通知 ack 已写入 `workflow_reservation_audit_log`。由于 V4 新模型不再使用旧 `workflow_reservation_task.id` 作为主承载,第一版审计查询按写入快照中的 V4 目标 ID 关联:
```text
GET /api/reservation/order-tasks/{orderTaskId}/audits
GET /api/reservation/source-notifications/{notificationId}/audits
```
接口规则:
- 两个接口都必须 Bearer 登录。
- 权限码统一使用 `RESERVATION_AUDIT_READ`
- 后端先反查 V4 订单任务或来源通知的真实 `hotel_id`,再校验当前用户酒店访问权。
- `order-tasks/{orderTaskId}/audits` 第一版返回 `V4_CARD_CONFIRM``V4_CARD_REVIEW_RESOLVE` 等订单任务相关业务审计。
- `source-notifications/{notificationId}/audits` 第一版返回 `V4_SOURCE_NOTIFICATION_ACK`
- 响应沿用旧审计行结构:`audit_id``order_id``task_id``operation_id``actor_type``actor_id``action``reason``before_snapshot``after_snapshot``occurred_at`
- 审计快照只作为前端时间线展示数据,后端会清理 `raw`、正文、HTML、token、secret、私有 URL 和完整 payload 字段,前端仍不能把未知 URL-like 字符串直渲成附件或正文。
## 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`
- Room Information 确认时,前端优先提交 `confirmed_payload.room_information.final_values` 中当前 `fields[]` 可编辑字段;后端会生成稳定确认快照 `confirmed_payload_json.room_information.final_values`。该快照不包含 Agent 原始 `target_order`,也不包含邮件正文、附件 URL、raw evidence、Adult 或前端注入字段。
- 业务卡确认时,当前已递归校验已有 `rate_code``room_items[].room_type_code` 是否在当前酒店数据库目录中;下一阶段 `rate_code` 还必须属于当前订单 Basic Information 已确认 Account + 当前业务 event `booking_type` 的适用关系。Room Information 新结构的错误路径形如 `room_information.final_values.room_items.0.room_type_code`;旧兼容结构可能返回类似 `business_fields.after.room_items.0.room_type_code` 的路径,失败返回 `V4_FIELD_VALIDATION_FAILED`
- 不提交草稿。
- 必须带 `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` 只允许指向当前卡 `fields[]` 中可编辑业务字段,不允许指向 `source_message``route_code``card_type``target_order``order_ref``missing_fields``manual_review``raw_evidence``validation_errors` 等只读诊断字段。
- 第一版允许的写入容器是 `basic_information`、Room Information 的 `room_information.final_values` 和历史兼容 `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。
- 解阻过程不改写 `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 开发可以不迁移历史任务数据。V4 后新业务主线只写 V4 模型;开发阶段不维护 V2/V3 旧任务兼容,测试数据可重建。该策略仅限开发 / 测试阶段,不代表生产迁移方案;生产数据迁移策略不在本 checkpoint 处理,后续上线前另开迁移方案。
### 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 新模型落地并完成前端切换后逐步废弃。
开发 / 测试环境旧任务清理参考 `../operations/dev-test-v4-legacy-task-cleanup.md`。废弃或删除生产历史链路前必须先确认前端、MCP、SuperAgent 联调方和测试 fixture 都已切到 V4并单独制定生产迁移方案。
## 15. 安全、权限和审计
后续实现接口时必须同步更新 `security-access-control-boundary.md`
第一版建议:
| 能力 | 分类 | 权限码 | 审计 |
| --- | --- | --- | --- |
| 查询工作台、订单任务列表 / 详情、来源通知详情 | `FRONTEND_USER` | `RESERVATION_TASK_READ` | 只读默认不写业务审计 |
| 查询 V4 订单任务 / 来源通知业务审计 | `FRONTEND_USER` | `RESERVATION_AUDIT_READ` | 查询审计不再写审计;审计快照脱敏后返回 |
| 确认卡片 | `FRONTEND_USER` | `RESERVATION_TASK_CONFIRM` | 写业务审计 |
| 复核解阻 | `FRONTEND_USER` | `RESERVATION_MANUAL_REVIEW_RESOLVE` | 写业务审计 |
| 确认 S10/S99 来源通知 | `FRONTEND_USER` | `RESERVATION_TASK_CONFIRM` | 写业务审计 |
| 读取邮件正文 / 附件 / Payment 凭证预览和下载 | `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-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 展示模型 | 已完成前后端第一版:后端返回 `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.10 | 复核态卡片字段白名单和统一确认交互 | 已完成前端第一版:`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、最后成功快照、失败重试和同步状态管理入口 |
## 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 任务详情、草稿保存和最终确认接口可以逐步废弃。