# M002 Order Task Workflow 订单任务主流程 V2

## 文档信息

| 项目 | 内容 |
| --- | --- |
| 文档版本 | 0.3 |
| 日期 | 2026-07-10 |
| 状态 | 第二版需求与后端阶段实现记录 |
| 适用范围 | SourceMessage 之后的 AI 过渡层、订单挂靠、任务卡、人工确认、OPERA 模拟操作主流程 |
| 主要读者 | 产品、后端、前端、测试、后续协作 agent |

## 1. 文档定位

本文是 `M002-order-task-workflow-v1.md` 的第二版修正，目标是把本项目已经讨论确认的订单任务主流程，与 2026-07-06 导入的 AI 任务卡契约对齐。

本文定义业务边界、数据语义和后续实现约束。当前后端已经按拆分 checkpoint 实现了 AI 结果接收、S000/S999 特殊入口结果、订单任务基础流转、任务草稿保存、最终确认、审计列表、OPERA 模拟骨架、SuperAgent 查询上下文接口 1、2 的最小字段版，以及前端 P0 任务列表 / 订单详情查询接口；前端页面、真实 OPERA、普通任务切换订单、查询接口 3 和查询接口 4 仍未实现。

## 2. 本版核心修正

相对 V1，本版有以下修正：

- 增加 `AI 过渡层`：系统必须先保存 AI 原始输出，再生成订单、任务和任务卡。
- 不再使用 `EXCEPTION` 作为底层结果类型，SuperAgent 结构化 JSON 新入口只使用 `normal_task` 和 `manual_review`；`informational_message` 仅保留历史兼容。
- 新增 SuperAgent 纯文本入口结果 `S000,source_message_id` 和 `S999,source_message_id`：两者都生成只读 `SOURCE_MESSAGE_ONLY` 特殊任务，任务列表可见，订单列表不可见。
- 系统主任务类型收敛为 `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING`、`MANUAL_REVIEW`，另保留历史 `INFORMATIONAL_MESSAGE`，并新增 `SOURCE_MESSAGE_ONLY` 用于 S000/S999 特殊入口结果。
- 除 `New Booking`、`Cancel Booking`、`Fallback/manual_review` 外，其他业务处理类卡片原则上都归到 `UPDATE_BOOKING` 下的不同任务卡。
- `Message Notification` / `INFORMATIONAL_MESSAGE` 只作为历史兼容路径；新数据中的纯信息类邮件由 `S000` 表达。
- 任务卡后端完整规则以 `任务卡展示编辑矩阵.xlsx` 为权威来源，不在代码里随意扩展或重命名字段路径。
- 前端展示 / 编辑白名单以最新导入的 `任务卡前端展示字段表 3.0.xlsx` 为准；该文件只约束前端展示和可编辑范围，不替代后端完整校验、确认写入和 OPERA 映射规则。
- 系统必须同时保存 AI 原始 `task_type`、系统主任务类型、任务卡类型和业务动作 subtype。
- `ai_payload_json` 与 `confirmed_payload_json` 必须分离。OPERA 模拟只能读取用户确认后的 `confirmed_payload_json`。
- 订单业务号候选字段和 OPERA 模拟回填边界需要明确记录，但 OPERA 返回 JSON 字段名目前未知，不能写死。

## 3. 权威输入资料

本版需求参考以下资料：

| 资料 | 作用 |
| --- | --- |
| `docs/project/requirements/M001-source-message-inbox-prd.md` | 上游 SourceMessage Inbox 边界 |
| `docs/project/requirements/M002-order-task-workflow-v1.md` | 第一版整体业务流程 |
| `docs/import/20260706/开发AI先读_工作顺序.md` | AI 过渡表、任务卡、确认 payload 和 OPERA 写入工作顺序 |
| `docs/import/20260706/AI输出参数并集字典.xlsx` | AI 输出字段路径、字段含义、建议存储方式和索引建议 |
| `docs/import/20260706/任务卡展示编辑矩阵.xlsx` | 后端任务卡完整规则来源，包括校验、确认写入、OPERA 映射和展示条件 |
| `docs/import/20260708/任务卡前端展示字段表 3.0.xlsx` | 前端任务卡展示 / 编辑白名单，不替代后端完整规则矩阵 |
| `docs/import/20260706/0630AI_副本 2/01_main_agent_prompt.md` | Main Agent 输出职责和聚合 JSON 结构 |
| `docs/import/20260706/0630AI_副本 2/02_agent_workflow.md` | AI 侧事件拆分、Skill 调用和结果聚合流程 |
| `docs/import/20260706/0630AI_副本 2/03_skill_catalog.md` | Skill 目录、结果类型、任务类型和统一输出字段 |
| `docs/import/20260706/0630AI_副本 2/skill/S01-S08*.md` | 各业务任务卡的抽取规则和边界 |
| `docs/import/20260706/0630AI_副本 2/references/qbd_liantai_email_table_rules.md` | QBD / LianTai 表格邮件拆分和证据规则 |
| `docs/import/20260706/0630AI_副本 2/skill/*/references/rate_code_rules.md` | Rate Code 判断规则 |
| `docs/import/20260706/0630AI_副本 2/skill/*/references/room_type_mapping_rules.md` | 房型映射规则 |

说明：`开发AI先读_工作顺序.md` 中提到的 `AI过渡表开发契约_README.md` 和 `AI过渡表开发契约_两张样例表.xlsx` 在当前导入目录下未找到同名文件。当前以后端完整规则矩阵、AI 输出参数字典和前端 3.0 白名单共同作为字段来源；三者职责不同，不能互相覆盖。

## 4. 总体流程

```text
AgentBus / 未来其他入口
→ SourceMessage Inbox 记录原始来源事实
→ SuperAgent / Main Agent 读取 SourceMessage 并拆分 current 事件
→ 对每个事件调用对应 Skill
→ 聚合输出 source_message_id + ai_task_results[] + extraction_warnings[]
→ 本系统接收 AI 结果并写入 AI 过渡层
→ 本系统根据 result_type + task_type + task_subtype 生成订单、任务和任务卡
→ 用户在任务详情页查看、修改字段、确认订单归属
→ 系统生成 confirmed_payload_json
→ 用户触发 OPERA 模拟操作
→ 系统保存每条模拟操作结果、失败原因和重试记录
```

特殊分支：

```text
SuperAgent 返回 S000,source_message_id 或 S999,source_message_id
→ 本系统按默认酒店 + external_message_id 反查 SourceMessage Inbox
→ 创建隐藏技术订单 + SOURCE_MESSAGE_ONLY 只读任务
→ 任务列表可见，订单列表不可见
→ 不允许编辑、确认、转换、OPERA 模拟或重试
```

中文说明：

- `SourceMessage Inbox` 是来源事实层，不表达订单、任务或业务结论。
- AI 只输出 JSON，不直接写数据库、不直接创建真实任务卡、不直接写 OPERA。
- 本系统负责保存 AI 原始 JSON、创建任务卡、接收用户修改、生成确认后的 payload、执行 OPERA 模拟并记录结果。
- SuperAgent 不直连本系统数据库，只能通过本系统后端接口查询已有订单和任务上下文。
- S000/S999 不是业务任务，也不是人工复核任务，只是来源邮件入口处理结论。

## 5. AI 输出接收边界

导入文档已经定义了 SuperAgent / Main Agent 应输出的核心业务结构。当前正式 HTTP 接口同时支持结构化 JSON 和 S000/S999 纯文本入口结果。

AI 聚合输出的顶层结构应包含：

| 字段 | 中文说明 |
| --- | --- |
| `source_message_id` | SuperAgent / Main Agent 原样带回的外部来源消息 ID，对应 AgentBus `source.external_message_id`，不是本系统内部 SourceMessage Inbox 主键 |
| `ai_task_results[]` | AI 拆分出的一个或多个任务结果，顺序必须保留 |
| `extraction_warnings[]` | 抽取警告，不直接等同于业务任务 |

结构化 JSON 的 `ai_task_results[]` 中关键字段包括：

| 字段 | 中文说明 |
| --- | --- |
| `source_event_index` | AI 拆分出的 current 事件序号，同一次来源消息内用于排序 |
| `catalog_code` | Skill 目录代码，例如 S01、S02 |
| `skill_id` | 具体 Skill 标识 |
| `result_type` | AI 结果类型，新入口只使用 `normal_task`、`manual_review`；`informational_message` 仅历史兼容 |
| `task_type` | AI 原始任务类型，例如 `New Booking`、`Voucher Received` |
| `task_subtype` / 业务动作 | 更细的业务动作，用于任务卡字段路由 |
| `current_or_history` | 当前事件还是历史证据 |
| `case_keys` | 订单关联候选键，例如 `group_code`、`confirmation_number` |
| `visible_reason` | 给用户看的生成原因 |
| `relevant_message_excerpt` | 相关邮件片段 |
| `attachments` / `file_references` | 附件和文件引用 |
| `context_used` | AI 使用的上下文摘要 |
| `extracted_fields` | 业务字段主体 |
| `manual_review` | 人工复核结构化原因和证据 |
| `informational_message` | 信息提醒内容，仅历史兼容 |
| `additional_operations` | AI 建议的附加动作 |
| `idempotency_key` | 幂等键，最终来源和生成规则待确认 |

HTTP 契约已在 `docs/project/requirements/M002-superagent-task-result-api-contract.md` 和 `docs/project/integrations/superagent-api-contract.md` 中补充。正式契约要求任务结果通知携带 `hotel_id + source_message_id`，其中 `source_message_id` 是外部来源消息 ID；后端会反查内部 SourceMessage Inbox ID 后再写入 `workflow_*` 表。

纯文本入口结果格式：

```text
S000,source_message_id
S999,source_message_id
```

中文说明：

- `S000` 表示纯信息类邮件，不形成业务素材包。
- `S999` 表示入口阶段无法形成业务素材包。
- `source_message_id` 是外部来源消息 ID，对应 AgentBus `source.external_message_id`。
- 第一版使用系统默认酒店反查 SourceMessage Inbox。
- 命中 SourceMessage 后创建 `SOURCE_MESSAGE_ONLY` 只读任务，并挂到 `order_visibility=HIDDEN_SYSTEM` 的技术订单下。

## 6. AI 过渡层数据要求

系统接收 AI 输出后，应先写入 AI 过渡层，再创建任务卡。过渡层用于追溯、幂等、筛选、人工确认和 OPERA 参数组装。

推荐保存完整 JSON：

| JSON 字段 | 中文说明 |
| --- | --- |
| `ai_payload_json` | AI 原始结果，禁止被用户编辑覆盖 |
| `case_keys_json` | AI 给出的订单关联候选键 |
| `extracted_fields_json` | AI 抽取的业务字段 |
| `manual_review_json` | 人工复核信息 |
| `informational_message_json` | 信息提醒内容，仅历史兼容 |
| `attachments_json` | 附件和文件引用 |
| `context_used_json` | AI 使用的上下文 |
| `confirmed_payload_json` | 用户修改并确认后的最终参数 |

推荐冗余物理列：

| 物理列 | 中文说明 |
| --- | --- |
| `source_message_id` | 内部 SourceMessage Inbox ID，来自外部 `source_message_id` 反查后的 `platform_source_message_inbox.id` |
| `source_event_index` | AI 事件序号 |
| `catalog_code` | Skill 目录代码 |
| `skill_id` | Skill 标识 |
| `result_type` | AI 结果类型；S000/S999 内部保存为 `source_message_only` |
| `ai_task_type` | AI 原始任务类型 |
| `system_task_type` | 系统主任务类型，S000/S999 为 `SOURCE_MESSAGE_ONLY` |
| `task_card_type` | 任务卡类型 |
| `task_subtype` | 业务动作 subtype |
| `current_or_history` | 当前或历史标识 |
| `group_code` | 冗余的 Group Code 候选 |
| `confirmation_number` | 冗余的 Confirmation Number 候选 |
| `idempotency_key` | 幂等键 |
| `manual_reason_code` | 人工复核原因码 |
| `parent_source_event_index` | 父任务事件序号 |
| `linked_task_group_id` | 联动任务组 ID |
| `blocked_until_parent_completed` | 是否等待父任务完成 |
| `execution_order` | 同订单下执行顺序 |
| `created_at` / `updated_at` | 创建和更新时间 |

不要把所有嵌套字段都建成物理列。只有高频查询、筛选、幂等、排序、队列控制和状态机需要的字段才冗余。

## 7. 任务分类模型

### 7.1 AI 结果类型

AI 新入口结构化 JSON 结果类型只接受：

| `result_type` | 中文说明 | 是否可直接 OPERA 模拟 |
| --- | --- | --- |
| `normal_task` | 可生成业务任务卡的正常任务 | 需用户确认后才允许 |
| `manual_review` | 需要人工复核的结构化任务 | 不允许直接写 OPERA |
| `informational_message` | 只读信息提醒，仅历史兼容 | 不允许写 OPERA |

不使用 `exception_task` 和 `no_action`。原来讨论中的异常任务，在本版统一落为 `manual_review`；没有业务动作的信息类邮件，新入口不再生成 `informational_message` JSON，而是返回 `S000,source_message_id`。

S000/S999 不属于结构化 `result_type`，它们是纯文本入口结果，系统内部保存为 `source_message_only` 过渡记录和 `SOURCE_MESSAGE_ONLY` 只读任务。

### 7.2 系统主任务类型

系统内部主任务类型建议为：

| 系统主任务类型 | 中文说明 |
| --- | --- |
| `NEW_BOOKING` | 新建订单任务 |
| `UPDATE_BOOKING` | 更新订单任务，下面承载多个不同任务卡 |
| `CANCEL_BOOKING` | 取消订单任务 |
| `MANUAL_REVIEW` | 人工复核或 Fallback 任务 |
| `INFORMATIONAL_MESSAGE` | 信息提醒任务，只归档展示，不参与执行队列，仅历史兼容 |
| `SOURCE_MESSAGE_ONLY` | S000/S999 来源消息入口结果任务，只读展示，不参与执行队列 |

### 7.3 AI 任务类型到系统任务和卡片的映射

| AI `task_type` | 系统主任务类型 | 任务卡类型 | 说明 |
| --- | --- | --- | --- |
| `New Booking` | `NEW_BOOKING` | `NEW_BOOKING` | 新建 FIT、Group Block、Allotment / Control Block |
| `Update Booking` | `UPDATE_BOOKING` | `UPDATE_BOOKING` | 通用订单修改卡 |
| `Voucher Received` | `UPDATE_BOOKING` | `VOUCHER_RECEIVED` | 凭证类更新卡 |
| `Rooming List` | `UPDATE_BOOKING` | `ROOMING_LIST` | 名单/分房表处理卡 |
| `Amend Group Code` | `UPDATE_BOOKING` | `AMEND_GROUP_CODE` | 修改 Group Code 卡 |
| `Trace / Reservation Notes` | `UPDATE_BOOKING` | `TRACE_RESERVATION_NOTES` | Trace 或备注卡，可作为联动任务 |
| `TA Recorder` | `UPDATE_BOOKING` | `TA_RECORDER` | TA Recorder 维护卡，通常由 Rooming List 派生 |
| `Cancel Booking` | `CANCEL_BOOKING` | `CANCEL_BOOKING` | 取消 FIT、Group Block、Allotment / Control Block |
| `Message Notification` | `INFORMATIONAL_MESSAGE` | `MESSAGE_NOTIFICATION` | 历史兼容路径；新纯信息类邮件改用 S000 |
| `Fallback` | `MANUAL_REVIEW` | `FALLBACK_REVIEW` | 人工复核任务 |
| `S000` 文本结果 | `SOURCE_MESSAGE_ONLY` | `SOURCE_MESSAGE_ONLY` | 纯信息类邮件，任务列表可见，订单列表不可见 |
| `S999` 文本结果 | `SOURCE_MESSAGE_ONLY` | `SOURCE_MESSAGE_ONLY` | 无法形成业务素材包，任务列表可见，订单列表不可见 |

系统不得丢弃 AI 原始 `task_type`。任务落库时应同时保存 AI 原始任务类型、系统主任务类型、任务卡类型和业务动作 subtype。

## 8. S000/S999 和历史 Message Notification 处理规则

### 8.1 S000/S999 处理规则

`S000` / `S999` 是 SuperAgent 入口阶段返回的纯文本结果，不是结构化任务 JSON。

本系统处理规则：

- `S000` 表示纯信息类邮件。
- `S999` 表示无法形成业务素材包。
- 两者都按外部 `source_message_id` 反查 SourceMessage Inbox。
- 第一版使用系统默认酒店，不要求请求体携带 `hotel_id`。
- 创建 `SOURCE_MESSAGE_ONLY` 只读任务，`task_subtype` 分别为 `S000` 或 `S999`。
- 创建 `order_visibility=HIDDEN_SYSTEM` 的技术订单，仅用于满足任务归属，不进入订单列表和订单详情。
- 任务状态为 `COMPLETED`，`queue_participation=false`。
- 任务列表可见；订单列表不可见。
- 任务详情可查看来源邮件、会话、附件和 SuperAgent 原始返回。
- 不允许保存草稿、最终确认、人工转换、普通切换订单、执行 OPERA 模拟或重试 OPERA。
- 不阻塞任何订单任务，也不被任何订单任务阻塞。

### 8.2 历史 Message Notification 处理规则

`Message Notification` 用于感谢、知会、已收到、转发说明、信息同步等没有明确业务执行动作的消息。

该路径仅为历史兼容，新数据应由 SuperAgent 返回 `S000,source_message_id`。历史数据处理规则：

- 创建临时订单作为归档容器。
- 创建只读信息提醒任务卡。
- 不参与订单任务执行队列。
- 不阻塞同订单其他可执行任务。
- 不被同订单其他可执行任务阻塞。
- 不允许编辑业务字段。
- 不允许确认后执行 OPERA 模拟。
- 可在详情页展示 `visible_reason`、`relevant_message_excerpt`、`attachments` 和 `informational_message`。

## 9. 任务卡字段约束

任务卡字段来源按职责拆分：

- 后端校验、确认写入、OPERA 映射和展示条件以 `docs/import/20260706/任务卡展示编辑矩阵.xlsx` 为权威来源。
- 前端展示 / 编辑白名单以 `docs/import/20260708/任务卡前端展示字段表 3.0.xlsx` 为准。
- 系统不得直接把整个 `ai_task_results[]` 渲染成表单，也不得绕过矩阵自行扩展字段含义。
- 如果后端完整规则与前端白名单出现冲突，例如旧矩阵要求必填但 3.0 白名单未开放展示或编辑，应先记录为前后端契约待确认问题，不由前端或后端单方面放宽规则。

字段矩阵通过以下列控制任务详情页行为：

| 列 | 中文说明 |
| --- | --- |
| `任务卡名称` | 前端任务卡名称 |
| `result_type` | 适用的 AI 结果类型 |
| `task_type` | 适用的 AI 原始任务类型 |
| `task_subtype/业务动作` | 适用的业务动作 |
| `字段路径` | 从 AI JSON 或确认 JSON 中取值的路径 |
| `展示名` | 前端展示名称 |
| `是否展示` | 是否在任务卡展示 |
| `是否可编辑` | 用户是否可编辑 |
| `是否为输入方式编辑` | 是否普通输入 |
| `是否为下拉框方式编辑` | 是否下拉选择 |
| `是否日期选择` | 是否日期控件 |
| `是否数字输入` | 是否数字输入 |
| `是否文件展示` | 是否文件或附件展示 |
| `是否表格编辑` | 是否表格型编辑 |
| `下拉选项/枚举值` | 可选值，系统不得自行扩展 |
| `是否必填` | 用户确认前是否必填 |
| `展示条件` | 字段显示条件 |
| `校验规则` | 用户确认前校验规则 |
| `确认后写入位置` | 写入 `confirmed_payload_json` 的位置 |
| `是否参与Opera写入` | 是否允许进入 OPERA 参数 |
| `Opera API参数映射` | OPERA 参数映射意图 |

### 9.1 关键卡片字段摘要

本节只摘录核心字段，完整字段以 Excel 矩阵为准。

| 任务卡 | 关键字段 |
| --- | --- |
| New Booking | `case_keys.group_code`、`case_keys.confirmation_number`、入住/离店日期、房量、房型原文、Opera 房型代码、Rate Code、结算价、Fix Charge、子团房量、表格行证据 |
| Update Booking | `case_keys.group_code`、`case_keys.confirmation_number`、修改类型、修改前/后、Rate / 结算价结果、Fix Charge、表格行证据 |
| Cancel Booking | `case_keys.group_code`、`case_keys.confirmation_number`、取消对象类型、取消范围、取消生效日期、取消备注 |
| Voucher Received | 原始凭证、凭证类型、`case_keys.group_code`、确认后动作、凭证展示字段、部门流转、关联 Group Code |
| Rooming List | 名单文件、名单类型、目标 Key、文件类型判断、附件 / Sheet 绑定、后续动作意图、派生任务候选 |
| Amend Group Code | 旧 Group Code、新 Group Code |
| Trace / Reservation Notes | `case_keys.group_code`、`case_keys.confirmation_number`、Trace 类型、通知部门、Trace Text、入住人数更新、改价公式、父任务事件序号 |
| TA Recorder | Group Code、名单 / 分房附件、TA Recorder 状态、Rooming List 父任务 |
| Message Notification | 生成原因、邮件片段、附件、信息提醒内容 |
| Fallback / manual_review | 复核原因码、复核说明、复核记录类型、阻断点、缺失字段、冲突点、建议核对证据、建议人工动作、候选 Group Code、候选 Confirmation No. |

## 10. AI 原始值和人工确认值

AI 原始输出和用户确认值必须分离。

| 概念 | 中文说明 |
| --- | --- |
| `ai_payload_json` | AI 原始输出，作为证据和审计，不被用户编辑覆盖 |
| `confirmed_payload_json` | 用户修改并确认后的最终业务参数 |

系统规则：

- 任务详情首次展示可从 `ai_payload_json` 按矩阵字段路径取值。
- 用户编辑后必须写入 `confirmed_payload_json`。
- 用户确认前必须按矩阵和后端硬校验检查必填、类型、枚举和执行条件。
- OPERA 模拟只能从 `confirmed_payload_json` 取值。
- 不允许直接从 `ai_payload_json` 组装 OPERA 参数。
- 不允许因为用户修改而覆盖 AI 原始输出。

## 11. 订单业务号和临时订单

系统不应只用一个含义模糊的“订单号”。建议至少区分：

| 字段 | 中文说明 |
| --- | --- |
| `order_id` | 系统内部订单 ID，永远存在 |
| `order_key_type` | 业务号类型，例如 `GROUP_CODE`、`CONFIRMATION_NUMBER`、`TEMPORARY` |
| `order_business_key` | 真实业务号，例如 Group Code 或 Confirmation Number |
| `temporary_order_code` | 临时订单展示编号 |

### 11.1 可作为订单业务号候选的字段

订单业务号候选字段包括：

| 字段路径 | 适用场景 | 中文说明 |
| --- | --- | --- |
| `case_keys.group_code` | Group / Block / Allotment 类任务 | 主要 Group Code 候选 |
| `case_keys.confirmation_number` | FIT Reservation 类任务 | 主要 Confirmation Number 候选 |
| `extracted_fields.old_group_code` | Amend Group Code | 定位原订单 |
| `extracted_fields.new_group_code` | Amend Group Code | 修改成功后的新业务号候选 |
| `extracted_fields.group_code` | TA Recorder 等卡片 | 任务卡内 Group Code 候选 |
| `extracted_fields.target_key` | Rooming List | 目标 Key，需要判断实际是 Group Code 还是 Confirmation Number |
| `related_group_codes[]` | Voucher 等场景 | 关联展示或候选，不应自动作为唯一主订单号 |

系统匹配订单时应优先使用用户确认后的 `confirmed_payload_json`；如果尚未确认，只能把 AI 字段作为候选和展示依据。

### 11.2 New Booking 的订单号处理

`NEW_BOOKING` 不一定都需要等待 OPERA 模拟创建后才有真实业务号。

规则：

- 如果用户确认后的 payload 已有可用业务号，优先使用该业务号。
- FIT Reservation 优先看 `confirmation_number`。
- Group / Block / Allotment 优先看 `group_code`。
- 如果确认后的 payload 没有可用业务号，先创建临时订单并使用 `temporary_order_code` 展示和追踪。
- OPERA 模拟创建成功后，如果结果中返回真实业务号，再回填订单业务号。
- 第一版尚未实现确认 payload 时，Fallback 转 `NEW_BOOKING` 可先读取 AI 原始 `case_keys.group_code` / `case_keys.confirmation_number` 作为 `AI_CANDIDATE`，用于把原临时订单激活为 ACTIVE；后续用户确认字段时再把来源升级为 `USER_CONFIRMED`。

### 11.3 什么时候从 OPERA 模拟结果回填订单号

只有在 `NEW_BOOKING` 且确认后的 payload 没有可用业务号时，才需要从 OPERA 模拟结果回填订单号。

| 任务场景 | 回填业务含义 |
| --- | --- |
| New FIT Reservation | 从 OPERA 模拟结果中的 Confirmation No. / Reservation Confirmation Number 含义字段回填 |
| New Group Block | 从 OPERA 模拟结果中的 Group Code / Block Code 含义字段回填 |
| New Allotment / Control Block | 从 OPERA 模拟结果中的 Group Code / Block Code / Allotment Code 含义字段回填 |

当前导入文档没有定义 OPERA 模拟结果的 JSON 字段名，因此不能在需求中写死 `result.confirmationNumber`、`result.groupCode` 等具体路径。该字段名需要在 OPERA 模拟模块设计时另行确认。

### 11.4 非 New Booking 的订单号规则

`UPDATE_BOOKING`、`CANCEL_BOOKING`、`Voucher Received`、`Rooming List`、`Trace / Reservation Notes`、`TA Recorder` 等任务，原则上不应依赖 OPERA 模拟结果生成订单号。

这些任务应先通过 `case_keys` 或对应卡片字段挂靠已有订单；如果无法自动归属，则创建或使用临时订单，并等待人工确认订单关系。

## 12. 订单挂靠和临时订单

任务必须挂靠到订单下，便于详情查看、队列控制和审计。

| 场景 | 挂靠规则 |
| --- | --- |
| New Booking 有业务号 | 按确认后的业务号创建或匹配订单 |
| New Booking 无业务号 | 创建临时订单 |
| Update Booking 可匹配已有订单 | 挂靠已有订单 |
| Update Booking 无法匹配订单 | 挂靠临时订单，等待人工确认 |
| Cancel Booking 可匹配已有订单 | 挂靠已有订单 |
| Cancel Booking 无法匹配订单 | 挂靠临时订单，等待人工确认 |
| Fallback / manual_review | 先挂靠临时订单，人工处理时可转换类型和订单归属 |
| Message Notification | 挂靠临时订单，只读归档 |

任务从临时订单迁移到已有订单时，必须记录审计。若原临时订单已无有效任务，应逻辑删除，并记录删除原因。

## 13. 任务顺序和可处理状态

同一个订单下，任务必须按顺序处理。

排序依据：

```text
order_id + execution_order
```

同一次 AI 返回多个任务时：

- 必须保留 `ai_task_results[]` 列表顺序。
- 可使用 `source_event_index`、`execution_order` 或批次内 item index 保存顺序。
- 不得只依赖创建时间，因为同批次任务创建时间可能完全一致。

处理规则：

- 前置任务未完成时，后续任务只能查看。
- `FAILED` 视为当前前置任务已经结束，不阻塞后续任务；用户不能跳过失败的 OPERA 操作，但失败任务本身不再卡住后续任务查看和处理顺序。
- 后续任务不能编辑字段。
- 后续任务不能确认订单关系。
- 后续任务不能执行 OPERA 模拟。
- 后续任务不能重试 OPERA 模拟。
- `Message Notification` 不参与执行队列，不阻塞其他任务，也不被其他任务阻塞。
- 联动任务可通过 `parent_source_event_index`、`linked_task_group_id`、`blocked_until_parent_completed` 表达依赖。

## 14. 人工复核和 Fallback

`manual_review` 是结构化人工复核，不是空结果，也不是系统失败。

人工复核卡至少应展示：

- `manual_review.reason_code`
- `manual_review.visible_reason`
- `manual_review.review_record_type`
- `manual_review.blocking_points[]`
- `manual_review.missing_fields[]`
- `manual_review.conflicting_points[]`
- `manual_review.evidence_to_check[]`
- `manual_review.suggested_human_actions[]`

Fallback 处理规则：

- 创建 `MANUAL_REVIEW` 任务。
- 默认挂靠临时订单。
- 用户处理时可以选择转为 `NEW_BOOKING`、`UPDATE_BOOKING` 或 `CANCEL_BOOKING`。
- 转换时必须记录审计；原因字段第一版允许为空。
- 转为 `NEW_BOOKING` 时，临时订单可以继续保留；如果已有确认 payload 则优先使用确认业务号，第一版尚未实现确认 payload 时可先使用 AI 原始 `case_keys` 中的业务号候选。
- 转为 `UPDATE_BOOKING` 或 `CANCEL_BOOKING` 时，用户必须选择目标订单；任务迁移后原临时订单无有效任务时可逻辑删除。
- 类型转换、订单迁移和临时订单逻辑删除都必须记录审计。

## 15. OPERA 模拟操作

当前阶段没有真实 OHIP / OPERA 接口，只做 OPERA 模拟操作。

允许进入 OPERA 模拟的前置条件：

- `result_type = normal_task`。
- 当前任务是订单执行队列中可处理的任务。
- 用户已确认订单归属。
- 用户已确认字段，且系统已生成 `confirmed_payload_json`。
- 字段矩阵中 `是否参与Opera写入` 为 `是` 或满足 `条件参与`。
- 后端硬校验通过。

不允许进入 OPERA 模拟的场景：

- `manual_review`。
- `informational_message`。
- 未确认的 AI 原始值。
- 证据字段、只读字段、历史字段、uncertain 字段。
- S04 的 `voucher_display_fields` 等展示字段。
- 字段矩阵明确 `是否参与Opera写入=否` 的字段。

一个任务可能产生多条 OPERA 模拟操作。任务详情页应展示每条模拟操作的结果、状态、失败原因、重试次数和最近执行时间。重试必须保留历史记录，不能覆盖原始失败记录。

第一版后端实现中，任务最终确认后固定生成两条 OPERA 模拟操作：

| 顺序 | 操作代码 | 中文说明 |
| --- | --- | --- |
| 1 | `SIMULATE_PRECHECK` | OPERA 模拟预检查 |
| 2 | `SIMULATE_WRITE` | OPERA 模拟写入 |

当前模拟操作只保存请求/响应摘要和 attempt 记录，不接真实 OPERA，也不从模拟结果回填订单业务号。失败时只把对应操作标记为 `FAILED`，任务保持未完成，避免用户绕过失败操作。

## 16. 审计要求

以下行为必须记录审计：

- AI 结果接收批次。
- `ai_task_results[]` 中每个 item 的创建顺序。
- AI 原始 JSON 保存。
- 订单自动挂靠或临时订单创建。
- 用户修改任务字段。
- 用户确认订单关系。
- `confirmed_payload_json` 生成。
- 任务类型转换。
- 任务切换订单。
- 临时订单逻辑删除。
- OPERA 模拟操作执行。
- OPERA 模拟操作重试。
- 从 OPERA 模拟结果回填订单业务号。

审计至少包含：

| 字段 | 中文说明 |
| --- | --- |
| `actor` | 操作人或系统调用方 |
| `action` | 操作类型 |
| `reason` | 用户填写或系统记录的原因 |
| `before_snapshot` | 变更前摘要 |
| `after_snapshot` | 变更后摘要 |
| `occurred_at` | 发生时间 |

审计中不得记录 Secret、Token、完整邮件正文、真实附件 URL 或不必要的个人敏感信息。

## 17. 暂不包含范围

本版暂不定义：

- SuperAgent 查询上下文接口 3、4；接口 1、2 的第一版最小字段已单独定义并落地在 `M002-ai-query-minimal-fields.md`。
- OPERA 模拟结果 JSON 字段名。
- 真实 OHIP / OPERA 接口地址、鉴权和返回结构。
- 前端具体页面布局和交互细节。
- 全量 158 条任务卡字段配置复制版。
- Rate Code 和房型规则的代码实现。
- 普通任务切换订单接口和前端交互。
- 用户身份、权限和真实 actor 注入。

## 18. 待确认问题

- SuperAgent 创建任务接口的 URL、Method、Header、鉴权和错误响应格式。
- SuperAgent 查询上下文接口 1、2 已实现最小字段版，并已启用与任务结果接收接口一致的 HMAC；接口 3 文件解析和接口 4 订单及任务查询仍需后续确认与实现，后续梳理未完成事项时必须持续提醒。
- SuperAgent 任务结果通知中的 `source_message_id` 已确认是外部来源消息 ID，对应 AgentBus `source.external_message_id`；查询接口中的 `source_message_id` 和 `source_event_index` 第一版接收但忽略。
- `source_event_index`、批次 item index、`execution_order` 的最终编号规则是否都从 1 开始。
- SuperAgent 查询上下文接口 3、4 的 URL、入参、返回字段和鉴权方式。
- `Message Notification` 是否需要在前端订单列表上单独标识为只读提醒。
- OPERA 模拟结果中 Confirmation No.、Group Code、Block Code、Allotment Code 的具体字段路径。
- 临时订单在无任务后是否立即逻辑删除，还是保留一段时间便于追溯。
- 普通任务切换订单接口何时纳入实现。
- 用户身份和权限体系何时接入，审计 `actor` 如何从登录态获取。

## 19. 后续建议 checkpoint

建议后续按以下顺序拆分：

1. 定义 SuperAgent 调本系统的任务结果接收接口契约。
2. 定义 AI 过渡层、订单、任务、任务卡、审计、OPERA 模拟结果的最小数据模型。
3. 定义系统主任务类型、任务卡类型、`result_type`、任务状态和订单状态枚举。
4. 实现 AI 结果接收、幂等、顺序保存和任务卡创建。
5. 实现订单自动挂靠、临时订单创建和人工订单切换。
6. 实现任务详情字段展示、编辑、确认和 `confirmed_payload_json`。当前后端已实现保存草稿和最终确认接口，前端页面待定。
7. 实现任务队列阻塞规则和 `Message Notification` 只读归档规则。当前后端已实现阻塞规则和只读提醒归档的基础能力，前端页面待定。
8. 实现 OPERA 模拟操作结果底表、重试和订单业务号回填。当前后端已实现固定两条模拟操作、attempt、执行、重试和审计列表；订单业务号回填待真实 OPERA 返回结构确认后再做。
9. 根据前端页面范围实现列表、详情和只读/可处理状态展示。当前后端已实现 `GET /api/reservation/tasks` 和 `GET /api/reservation/orders/{orderId}` 第一版；前端页面和订单列表、Message Notification 独立列表 / 详情、任务卡字段白名单元数据接口仍待后续确认。
