# M002 V4 CP2 订单任务与多卡领域模型设计 ## 文档信息 | 项目 | 内容 | | --- | --- | | 文档版本 | 0.4 | | 日期 | 2026-07-19 | | 状态 | CP2 设计已确认;CP3 表结构、Entity、Mapper、Repository 基线已实现;CP4 入站写入新模型已实现;CP5 查询接口和订单详情 V4 时间线已实现;CP6 卡片确认和 S10/S99 ack 已实现 | | 适用范围 | M002 V4 入站后的订单任务、多卡、状态、查询和写操作设计 | | 不适用范围 | V4 复核接口、真实 PMS / OPERA / OHIP、前端页面视觉稿、历史数据迁移 | ## 1. 文档定位 M002 V4 CP1 已完成 SuperAgent V4 回调包入站解析、基础校验、路由适配和 AI transition 最小落库。CP1 仍然把可映射的 V4 event 临时接入 M002 V3 的订单 / 任务 / 任务卡链路。 本文是 CP2 设计文档,用于把 2026-07-18 V4 字段契约落成后续可开发的数据模型和接口草案。 截至 CP8,后端已实现本文第 10、11 节中的持久化基线,并已把 SuperAgent V4 入站结果写入新表:普通业务包创建 V4 订单任务、来源邮件展示卡、Basic Information 卡和业务卡;V4 S10/S99 创建来源通知。当前已开放 V4 工作台、订单任务列表 / 详情、来源通知详情查询接口、订单详情 V4 订单任务时间线、V4 卡片确认接口、S10/S99 来源通知 ack 接口、V4 `REVIEW_REQUIRED` 卡复核解阻接口,以及 Account / Room Type / Rate Code 固定种子目录第一版校验和卡片 `fields[]` 白名单。 后续如本文与 `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 已支持 Account 固定目录校验、Market / Source 派生和 `fields[]` 白名单 | | 业务 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`。 - CP8 起确认和复核都会校验目录字段;Basic Information 的 `account_code` 必须来自第一版 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`。 第一版 Account / Market / Source 目录使用后端固定种子数据,不依赖 SuperAgent 动态提供目录文件。后续如目录由管理后台维护或从外部系统同步,应以专项 checkpoint 设计目录版本、变更审计和回放影响。 CP8 第一版固定种子: | 目录 | 第一版代码 | | --- | --- | | 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 当前只作为确认和字段控件的第一版校验 / 选项来源代码;尚未接真实 PMS 房型目录、Rate Code 配置中心或通用 lookup API。 ## 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 订单任务 ID;S10/S99 来源通知不挂订单任务,后续使用独立通知模型承载 | | `source_message_id` | 来源消息 ID,便于查邮件会话 | | `ai_transition_id` | 对应 event 的 AI transition ID;Basic 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 | | 目录表 | Account、Market、Source 第一版已确认用后端固定种子数据;CP2 只设计边界,后续目录 checkpoint 再决定是否建表、导入或管理后台维护 | ## 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 和 API;V4 复核解阻、复核场景订单归属确认仍属于后续 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 任务读取时继续以该订单酒店过滤,避免跨酒店脏数据泄露。 ## 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 能力收口评估 | 明确哪些兼容入口可以关闭,哪些仍保留只读历史 | ## 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 目录第一版使用后端固定种子数据。 8. 普通业务邮件的来源邮件展示卡只读展示,不需要用户确认;用户只确认 Basic Information 和具体业务卡。 9. S10/S99 因为没有业务卡,邮件展示卡需要确认按钮,用来记录已读 / 已处理。 10. V4 新模型落地并完成前端切换后,旧 V2/V3 任务详情、草稿保存和最终确认接口可以逐步废弃。