完成M002 V3 CP4列表详情展示
This commit is contained in:
@@ -48,9 +48,9 @@
|
||||
| `GET /api/auth/me` | 恢复当前登录态 | 前端启动后带 `Authorization: Bearer <access_token>` 调用;401 时清理 token 并进入登录页。 |
|
||||
| `POST /api/auth/logout` | 登出当前 session | 带 `Authorization: Bearer <access_token>`;成功后前端必须清理本地 token 和当前用户上下文。 |
|
||||
| `GET /api/reservation/orders` | 查询订单列表 | 默认返回全部订单状态;`open_task_count` 排除 `COMPLETED` 和 `FAILED`;隐藏技术订单不返回,因此 S10/S99 和旧 S000/S999 不会在订单列表形成订单。 |
|
||||
| `GET /api/reservation/tasks` | 查询任务列表 / 工作台 | 用 `can_process` 和 `readonly_reason_code` 控制入口按钮;列表不返回 AI 原始 payload、邮件正文或附件 URL;已返回来源邮件会话摘要字段,并支持 `order_status` 按任务所属订单状态筛选;旧 S000/S999 和新 S10/S99 当前都会以 `task_type=SOURCE_MESSAGE_ONLY` 只读任务返回,任务结果回调响应已包含 `route_code/system_process_category`,列表接口完整透出 V3 路由字段后续补齐。 |
|
||||
| `GET /api/reservation/orders/{orderId}` | 查询订单详情与任务时间线 | `include_tasks=false` 可只取订单摘要;时间线按后端队列顺序返回,前端不要自行按创建时间重排;`tasks[]` 已返回来源邮件会话摘要字段;隐藏技术订单详情不可作为普通订单页打开。 |
|
||||
| `GET /api/reservation/tasks/{taskId}` | 查询任务详情 | 以返回的可处理状态和只读原因控制按钮,不只看任务状态;`fields[]` 已包含 P0 字段元数据;源邮件只读通知卡字段列表和 OPERA 操作列表为空,旧数据通过 `source_message_only_result` 返回 S000/S999 入口结果;结构化 S10/S99 已可入站并保存,详情接口完整透出 `agent_assessment/notification/manual_review` 后续补齐。 |
|
||||
| `GET /api/reservation/tasks` | 查询任务列表 / 工作台 | 用 `can_process` 和 `readonly_reason_code` 控制入口按钮;列表不返回 AI 原始 payload、邮件正文或附件 URL;已返回来源邮件会话摘要字段,并支持 `order_status` 按任务所属订单状态筛选;旧 S000/S999 和新 S10/S99 都以 `task_type=SOURCE_MESSAGE_ONLY` 只读任务返回,列表已透出 `result_type`、`ai_task_type`、`route_code`、`system_process_category`。 |
|
||||
| `GET /api/reservation/orders/{orderId}` | 查询订单详情与任务时间线 | `include_tasks=false` 可只取订单摘要;时间线按后端队列顺序返回,前端不要自行按创建时间重排;`tasks[]` 已返回来源邮件会话摘要字段和 V3 路由字段;隐藏技术订单详情不可作为普通订单页打开。 |
|
||||
| `GET /api/reservation/tasks/{taskId}` | 查询任务详情 | 以返回的可处理状态和只读原因控制按钮,不只看任务状态;`fields[]` 已包含 P0 字段元数据;源邮件只读通知卡字段列表和 OPERA 操作列表为空;结构化 S10/S99 通过 `source_message_only_result.agent_assessment`、`notification`、`manual_review` 展示;普通业务任务可通过 `adapter_contract_errors[]` 和 `unhandled_intents[]` 查看同批次未建任务的诊断信息。 |
|
||||
| `PUT /api/reservation/tasks/{taskId}/draft` | 保存任务草稿 | 只保存草稿,不代表用户最终确认。 |
|
||||
| `POST /api/reservation/tasks/{taskId}/confirm` | 最终确认任务 | 后端会做第一版字段校验,通过后进入 `READY`。 |
|
||||
| `POST /api/reservation/tasks/{taskId}/manual-review-conversions` | Fallback 人工转换 | 只用于 manual_review / fallback,不用于普通任务切换订单。 |
|
||||
@@ -142,7 +142,9 @@ POST /api/auth/logout
|
||||
- 源邮件只读通知卡背后有隐藏技术订单用于满足后端任务外键,但订单列表不会返回该订单;任务列表中该类任务的 `display_order_key`、`temporary_order_no`、`group_code`、`confirmation_number` 可能为空,前端不要因此隐藏整条任务。
|
||||
- 当前前端已按 `SOURCE_MESSAGE_ONLY` 展示旧 S000/S999;后端回调已支持结构化 `route_code=S10/S99`、`result_type=source_message_review_notification` 的新入口通知,并继续只在任务列表和任务详情提供只读查看入口;`INFORMATIONAL_MESSAGE` 仅作为历史 Message Notification 兼容路径保留。
|
||||
- 任务列表里旧 `task_type=SOURCE_MESSAGE_ONLY`、`task_subtype=S000/S999` 或新 `task_subtype=S10/S99` 的记录只展示邮件来源和 SuperAgent 入口结果,不展示处理按钮。
|
||||
- 任务详情里 `source_message_only_result` 仅对 `SOURCE_MESSAGE_ONLY` 返回,包含 `entry_result_code`、`entry_result_meaning`、`entry_result_description`、`entry_result_source_message_id` 和 `raw_answer`;结构化 S10/S99 已在入站 payload 中保存 `route_code`,但详情接口完整透出 `agent_assessment`、`notification` 和入口 `manual_review` 仍待后续 checkpoint;普通业务任务该字段为空。
|
||||
- 任务详情里 `source_message_only_result` 仅对 `SOURCE_MESSAGE_ONLY` 返回,包含 `entry_result_code`、`entry_result_meaning`、`entry_result_description`、`entry_result_source_message_id`、`result_type`、`route_code`、`agent_assessment`、`notification`、`manual_review` 和 `raw_answer`;普通业务任务该字段为空。
|
||||
- 任务列表、订单任务时间线和任务详情顶层已透出 `result_type`、`ai_task_type`、`route_code`、`system_process_category`。前端展示任务卡标题和标签时优先用这些稳定 code,不要只靠旧 `task_type` 判断。
|
||||
- `adapter_contract_errors[]` 和 `unhandled_intents[]` 只在任务详情返回,表示同一 SuperAgent 入站批次中没有生成业务任务的诊断块;前端只读展示并提供来源邮件入口,不显示保存、确认、执行或重试按钮。
|
||||
|
||||
### 5.6 前端联调演示数据 seed 接口
|
||||
|
||||
@@ -268,7 +270,7 @@ run_label: 可选调试标签
|
||||
## 7. 需要持续提醒的后置事项
|
||||
|
||||
- 普通任务切换订单接口继续后置。
|
||||
- M002 V3 的结构化 `S10/S99` 入站、42 路由枚举 / 稳定配置、`UNHANDLED_CURRENT_INTENT` 和 `adapter_contract_error` transition 最小落库已完成;type-known manual review 同卡解阻、复核场景订单归属确认、任务列表 / 详情完整透出 V3 路由字段仍需后续后端 checkpoint。前端新页面应预留字段和状态,不要继续只写死旧 `S000/S999`。
|
||||
- M002 V3 的结构化 `S10/S99` 入站、42 路由枚举 / 稳定配置、`UNHANDLED_CURRENT_INTENT`、`adapter_contract_error` transition 最小落库,以及任务列表 / 订单时间线 / 任务详情 V3 路由字段和只读诊断块透出已完成;type-known manual review 同卡解阻、复核场景订单归属确认仍需后续后端 checkpoint。
|
||||
- 系统管理后台 V1 已完成;后续若要做用户搜索更多筛选、批量操作、密码策略增强、MFA、登录设备管理,应单独开需求。
|
||||
- 现有 Reservation / SourceMessage 业务接口的强制登录、强制权限和业务审计 actor 全量迁移仍后置。
|
||||
- 真实 OPERA / OHIP 接入继续后置。
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
| P0 | 邮件会话详情接口 `GET /api/source-messages/{sourceMessageId}/conversation` | 邮件会话详情页 | 已完成第一版 |
|
||||
| 联调 | 演示数据 seed 接口 `POST /api/system/reservation/demo-data` | 本地 / test 前端页面看效果 | 已完成;仅 dev/test 受控使用 |
|
||||
| 联调 | Debug EML 上传接口 `POST /api/system/debug/eml-superagent-runs` | Debug 页面上传 `.eml` 看 SuperAgent 结果 | 已完成第一版;仅 dev/test 受控使用 |
|
||||
| P1 | S000/S999 特殊只读任务展示 | 任务列表、任务详情来源邮件查看 | 已完成后端第一版;前端已按 `SOURCE_MESSAGE_ONLY` 完成列表筛选、S000/S999 展示和详情只读展示,不做独立 Message Notification 接口 |
|
||||
| P1 | S10/S99 源邮件只读通知卡与旧 S000/S999 兼容 | 任务列表、任务详情来源邮件查看 | 已完成第一版:旧 S000/S999 兼容,新结构化 S10/S99 可入站并在任务列表 / 详情只读展示 |
|
||||
| P1 | 任务卡前端字段白名单元数据接口 | 字段白名单调试、版本对齐 | 未完成;若任务详情已透出完整元数据,可后置 |
|
||||
| 后置 | 普通任务切换订单接口 | 任务详情订单归属调整 | 未完成;已确认后置 |
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
| `GET /api/reservation/orders` | 已完成第一版 | 可以 | 默认查询全部订单状态;`open_task_count` 排除 `COMPLETED` 和 `FAILED`。 |
|
||||
| `GET /api/source-messages/{sourceMessageId}/conversation` | 已完成第一版,已补 `html_body_sanitized` 和 `html_render_mode` | 可以 | 返回完整 text/html、后端清洗后的 HTML、媒体外链和关联订单 / 任务摘要;前端不传原文读取 key,页面展示优先使用 `html_body_sanitized`。 |
|
||||
| `POST /api/system/reservation/demo-data` | 已完成 | 仅本地 / test 联调可用 | 默认关闭,必须后端配置访问口令;不能作为生产页面接口。 |
|
||||
| `POST /api/system/debug/eml-superagent-runs` | 已完成第一版 | 仅 dev/test Debug 页面可用 | 默认关闭,必须后端配置访问口令、阿里云 OSS 和 SuperAgent Open API;第一版只展示 SuperAgent 结果,不创建订单和任务。 |
|
||||
| `POST /api/system/debug/eml-superagent-runs` | 已完成第一版 | 仅 dev/test Debug 页面可用 | 默认关闭,必须后端配置访问口令、阿里云 OSS 和 SuperAgent Open API;第一版只展示 SuperAgent 结果,不创建订单和任务;已能识别旧 S000/S999 和新结构化 S10/S99。 |
|
||||
| `GET /api/source-message-conversations/{externalConversationId}` | 未发现后端实现 | 不可以 | 历史讨论过的候选路径,当前不提供;前端统一使用 `GET /api/source-messages/{sourceMessageId}/conversation`。 |
|
||||
| `GET /api/reservation/message-notifications` | 未发现后端实现 | 不可以 | 第一版不做独立接口;新入口 S000/S999 已通过 `SOURCE_MESSAGE_ONLY` 任务展示。 |
|
||||
| `GET /api/reservation/message-notifications` | 未发现后端实现 | 不可以 | 第一版不做独立接口;旧 S000/S999 已通过 `SOURCE_MESSAGE_ONLY` 任务展示,0711 P0 新 S10/S99 也继续复用任务列表 / 任务详情只读展示。 |
|
||||
| `GET /api/reservation/task-card-field-whitelist` | 未发现后端实现 | 不可以 | 若任务详情 `fields[]` 已补齐 3.0 元数据,可后置。 |
|
||||
|
||||
## 3. 任务列表 / 工作台接口字段补齐
|
||||
@@ -68,7 +68,11 @@ GET /api/reservation/tasks
|
||||
| `display_order_key` | 前端优先展示的业务号或临时订单号。 |
|
||||
| `temporary_order_no` | 临时订单号。 |
|
||||
| `task_type` | 系统主任务类型。 |
|
||||
| `result_type` | AI 结果类型,例如 `normal_task`、`manual_review`、`source_message_review_notification`。 |
|
||||
| `ai_task_type` | SuperAgent 原始任务类型,例如 `New Booking`、`S10`、`S99`。 |
|
||||
| `task_subtype` | 任务 subtype。 |
|
||||
| `route_code` | M002 V3 路由码,例如 `R01_NEW_FIT_RESERVATION_NORMAL`、`S10`、`S99`。 |
|
||||
| `system_process_category` | 系统处理分类,例如 `BUSINESS_TASK`、`SOURCE_MESSAGE_NOTIFICATION`。 |
|
||||
| `task_status` | 任务状态。 |
|
||||
| `card_name` | 任务卡展示名称。 |
|
||||
| `queue_sequence` | 同订单队列顺序。 |
|
||||
@@ -86,7 +90,7 @@ GET /api/reservation/tasks
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 否 | 酒店 ID。单酒店阶段默认可为空,由后端按当前用户上下文或平台酒店表唯一 `ACTIVE` 酒店解析;显式传值时后端会校验访问权限。 |
|
||||
| `order_id` | 否 | 按订单过滤。 |
|
||||
| `task_type` | 否 | `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING`、`MANUAL_REVIEW`、`INFORMATIONAL_MESSAGE`、`SOURCE_MESSAGE_ONLY`。其中 `INFORMATIONAL_MESSAGE` 仅历史兼容,新入口 S000/S999 使用 `SOURCE_MESSAGE_ONLY`。 |
|
||||
| `task_type` | 否 | `NEW_BOOKING`、`UPDATE_BOOKING`、`CANCEL_BOOKING`、`MANUAL_REVIEW`、`INFORMATIONAL_MESSAGE`、`SOURCE_MESSAGE_ONLY`。其中 `INFORMATIONAL_MESSAGE` 仅历史兼容;旧入口 S000/S999 和 0711 P0 结构化 S10/S99 均复用 `SOURCE_MESSAGE_ONLY` 只读源邮件通知卡。 |
|
||||
| `task_status` | 否 | 任务状态过滤。 |
|
||||
| `task_subtype` | 否 | 任务卡 subtype 过滤。 |
|
||||
| `order_status` | 否 | 按任务所属订单状态过滤,支持 `TEMPORARY`、`ACTIVE`、`ENDED`、`LOGIC_DELETED`;不传时保持当前行为。 |
|
||||
@@ -107,7 +111,11 @@ GET /api/reservation/tasks
|
||||
"display_order_key": "GRP-001",
|
||||
"temporary_order_no": "TMP-20260708-001",
|
||||
"task_type": "UPDATE_BOOKING",
|
||||
"result_type": "normal_task",
|
||||
"ai_task_type": "Update Booking",
|
||||
"task_subtype": "RATE_CHANGE",
|
||||
"route_code": "R07_UPDATE_RATE_CODE_NORMAL",
|
||||
"system_process_category": "BUSINESS_TASK",
|
||||
"task_status": "PENDING_CONFIRM",
|
||||
"card_name": "Rate Change",
|
||||
"queue_sequence": 2,
|
||||
@@ -140,6 +148,7 @@ GET /api/reservation/tasks
|
||||
| `source_received_at` | 邮件来源接收时间,优先取 AgentBus payload `received_at`,用于任务列表排序和展示。 |
|
||||
| `external_conversation_id` | 来源消息所属邮件会话 ID,用于打开完整邮件会话详情。 |
|
||||
| `conversation_message_count` | 会话内邮件数量,用于提示用户该入口是整段会话,不是单封邮件。 |
|
||||
| `result_type` / `ai_task_type` / `route_code` / `system_process_category` | V3 路由展示字段,用于任务卡标签、筛选和 S10/S99 只读卡判断。 |
|
||||
|
||||
## 4. 订单详情与任务时间线接口字段补齐
|
||||
|
||||
@@ -183,7 +192,11 @@ GET /api/reservation/orders/{orderId}
|
||||
{
|
||||
"task_id": "10001",
|
||||
"task_type": "NEW_BOOKING",
|
||||
"result_type": "normal_task",
|
||||
"ai_task_type": "New Booking",
|
||||
"task_subtype": "NEW_BOOKING",
|
||||
"route_code": "R01_NEW_FIT_RESERVATION_NORMAL",
|
||||
"system_process_category": "BUSINESS_TASK",
|
||||
"task_status": "COMPLETED",
|
||||
"card_name": "New Booking",
|
||||
"queue_sequence": 1,
|
||||
@@ -213,6 +226,7 @@ GET /api/reservation/orders/{orderId}
|
||||
| `source_received_at` | 邮件来源接收时间,优先取 AgentBus payload `received_at`。 |
|
||||
| `external_conversation_id` | 来源消息所属邮件会话 ID。 |
|
||||
| `conversation_message_count` | 会话内邮件数量。 |
|
||||
| `result_type` / `ai_task_type` / `route_code` / `system_process_category` | V3 路由展示字段,和任务列表字段语义一致。 |
|
||||
|
||||
## 5. 订单列表接口
|
||||
|
||||
@@ -299,7 +313,21 @@ POST /api/system/reservation/demo-data
|
||||
| `tasks[]` | 生成的任务 ID、任务类型、任务 subtype 和任务状态。 |
|
||||
| `entrypoints` | 可直接访问的任务列表、订单列表、订单详情、任务详情、邮件会话详情 URL。 |
|
||||
|
||||
第一版 seed 覆盖:队列阻塞、已完成 OPERA 模拟、OPERA 失败可重试、Fallback 人工复核、历史 Message Notification 只读任务、邮件会话完整 HTML / 附件 / 内联图片。S000/S999 特殊只读任务可通过 SuperAgent 回调或后续专用夹具补充。
|
||||
第一版 seed 覆盖:队列阻塞、已完成 OPERA 模拟、OPERA 失败可重试、Fallback 人工复核、历史 Message Notification 只读任务、邮件会话完整 HTML / 附件 / 内联图片。旧 S000/S999 特殊只读任务可通过 SuperAgent 回调或后续专用夹具补充;0711 P0 新入口的 S10/S99 需要后端后续 checkpoint 补充结构化 fixture。
|
||||
|
||||
## 6.1 M002 V3 当前状态和后续待补能力
|
||||
|
||||
本节记录 0711 P0 基线确认后,前端关心的 V3 能力状态。已完成项可以直接接入;后置项需要另开 checkpoint。
|
||||
|
||||
| 能力 | 页面 / 场景 | 状态 | 前端最小诉求 |
|
||||
| --- | --- | --- | --- |
|
||||
| 结构化 `S10/S99` 入站 | 任务列表、任务详情、Debug EML 结果展示 | 已完成第一版 | 后端接收 `result_type=source_message_review_notification + route_code=S10/S99`,创建只读源邮件通知卡;任务列表可见,订单列表不可见;返回 `route_code`、入口说明、`agent_assessment`、`notification` 和 S99 的入口 `manual_review`。 |
|
||||
| 旧 `S000/S999` 兼容映射 | 任务列表、任务详情 | 已完成第一版 | 旧数据继续可见;前端可按 `S000→S10`、`S999→S99` 展示统一文案。 |
|
||||
| 42 路由元数据 | 任务列表筛选、订单任务时间线、任务详情标题、字段展示 | 已完成第一版 | 后端保存并返回 AI 原始 `result_type/ai_task_type/task_subtype`、`route_code` 和系统处理分类;前端不要只依赖系统主任务类型判断卡片。 |
|
||||
| `unhandled_current_intents[]` 展示块 | 任务详情 | 已完成第一版 | 后端保存并在任务详情 `unhandled_intents[]` 返回未覆盖业务意图,只用于展示和源邮件查看,不自动建业务任务卡。 |
|
||||
| `adapter_contract_error` | 任务详情、错误提示 | 已完成第一版 | 命中 P1/P2 未闭合或路由冲突时,任务详情 `adapter_contract_errors[]` 返回稳定错误 code 和原始片段,不转成 Fallback。 |
|
||||
| type-known manual review 同卡解阻 | 任务详情复核 | 后置 | `manual_review` 不再全部等同 Fallback;已知业务卡型应返回原业务卡信息、`review_status`、`review_resolution` 和可编辑 pointer 字段。 |
|
||||
| 复核场景订单归属确认 | 任务详情复核 | 后置 | 后端提供复核确认时的订单归属确认 / 选择能力;这不等于普通任务任意切换订单。 |
|
||||
|
||||
前端注意:不要把访问口令写入前端仓库、浏览器环境变量或构建产物;该接口只能由本地联调人员手动调用或由受控测试脚本调用。
|
||||
|
||||
@@ -404,7 +432,7 @@ GET /api/source-message-conversations/{externalConversationId}
|
||||
GET /api/reservation/tasks/{taskId}
|
||||
```
|
||||
|
||||
当前状态:后端已有任务详情接口,前端任务详情页可以接入。该接口已返回 `fields[]`、草稿、确认 payload、可处理状态和 OPERA 模拟操作;本轮已透出来源邮件会话字段,以及 `docs/import/20260708/任务卡前端展示字段表 3.0.xlsx` 中 P0 需要的字段元数据。
|
||||
当前状态:后端已有任务详情接口,前端任务详情页可以接入。该接口已返回 `fields[]`、草稿、确认 payload、可处理状态和 OPERA 模拟操作;本轮已透出来源邮件会话字段,以及旧 `docs/import/20260708/任务卡前端展示字段表 3.0.xlsx` 中 P0 需要的字段元数据。0711 P0 后续开发应迁移到 `docs/import/20260711/开发交付_P0冻结基线_2026-07-11/01_Adapter_Frontend/任务卡前端字段变更说明_3.0_to_当前版_2026-07-10.xlsx` 和同目录路由说明。
|
||||
|
||||
任务详情页面相关已完成接口:
|
||||
|
||||
@@ -474,7 +502,7 @@ GET /api/reservation/tasks/{taskId}
|
||||
"system_task_type": "NEW_BOOKING",
|
||||
"task_card_type": "NEW_BOOKING",
|
||||
"task_status": "PENDING_CONFIRM",
|
||||
"field_contract_version": "20260708-3.0",
|
||||
"field_contract_version": "20260711-p0",
|
||||
"fields": [
|
||||
{
|
||||
"row_number": 2,
|
||||
@@ -501,19 +529,26 @@ GET /api/reservation/tasks/{taskId}
|
||||
- 如果后端已有更细的字段来源或适用场景元数据,可后续再扩展 `field_source`、`applicable_scenario`,不作为本轮 P0 阻塞项。
|
||||
- 前端默认不需要为 `GET /api/reservation/orders`、`GET /api/reservation/tasks`、`GET /api/reservation/orders/{orderId}` 自动拼 `hotel_id`;如已接入酒店选择器,可以传当前选中酒店,后端会校验访问权限。当前 `GET /api/reservation/tasks/{taskId}` 以及任务写操作 Controller 不接收 `hotel_id`;第一版先按 ID 定位,后续多酒店隔离 / 权限方案统一补齐。
|
||||
|
||||
## 9. S000/S999 特殊只读任务与历史 Message Notification
|
||||
## 9. S10/S99 源邮件只读通知卡与历史兼容
|
||||
|
||||
当前状态:后端不提供独立 Message Notification 列表 / 详情接口。SuperAgent 新入口返回 `S000,source_message_id` 或 `S999,source_message_id` 时,后端会创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务。前端已通过 `GET /api/reservation/tasks?task_type=SOURCE_MESSAGE_ONLY` 和 `GET /api/reservation/tasks/{taskId}` 展示,并在任务列表筛选中支持 `SOURCE_MESSAGE_ONLY`、`S000`、`S999`。
|
||||
当前状态:后端不提供独立 Message Notification 列表 / 详情接口。旧 SuperAgent 入口返回 `S000,source_message_id` 或 `S999,source_message_id` 时,后端会创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务;0711 P0 结构化 `S10/S99` 也复用同一只读任务模型。前端已通过 `GET /api/reservation/tasks?task_type=SOURCE_MESSAGE_ONLY` 和 `GET /api/reservation/tasks/{taskId}` 展示,并在任务列表筛选中支持 `SOURCE_MESSAGE_ONLY`、`S000`、`S999`、`S10`、`S99`。
|
||||
|
||||
0711 P0 新入口已迁移为结构化 `S10/S99`:
|
||||
|
||||
- `S10`:`result_type=source_message_review_notification`,`route_code=S10`,表示未匹配当前支持的业务事件。
|
||||
- `S99`:`result_type=source_message_review_notification`,`route_code=S99`,表示输入不足或无法形成业务素材包。
|
||||
- 旧 `S000` 前端语义映射为 `S10`。
|
||||
- 旧 `S999` 前端语义映射为 `S99`。
|
||||
|
||||
历史 `INFORMATIONAL_MESSAGE` 仍可通过任务列表 / 任务详情兼容展示,但新数据不要依赖它。
|
||||
|
||||
展示规则:
|
||||
|
||||
- `task_type=SOURCE_MESSAGE_ONLY`、`task_subtype=S000`:纯信息类邮件。
|
||||
- `task_type=SOURCE_MESSAGE_ONLY`、`task_subtype=S999`:无法形成业务素材包。
|
||||
- 旧 `task_type=SOURCE_MESSAGE_ONLY`、`task_subtype=S000/S999`:按只读源邮件通知卡展示。
|
||||
- 新 `route_code=S10/S99`:按只读源邮件通知卡展示。
|
||||
- 任务列表可见,订单列表不可见。
|
||||
- 任务详情只允许查看来源邮件、会话、附件和 SuperAgent 原始返回。
|
||||
- 任务详情通过 `source_message_only_result` 返回 `entry_result_code`、`entry_result_meaning`、`entry_result_description`、`entry_result_source_message_id` 和 `raw_answer`。
|
||||
- 任务详情通过 `source_message_only_result` 返回 `entry_result_code`、`entry_result_meaning`、`entry_result_description`、`entry_result_source_message_id`、`result_type`、`route_code`、`agent_assessment`、`notification`、S99 的入口 `manual_review` 和 `raw_answer`。
|
||||
- 不显示编辑、确认、人工转换、执行 OPERA 或重试 OPERA 按钮。
|
||||
- 不参与订单任务执行顺序阻塞。
|
||||
|
||||
@@ -556,6 +591,55 @@ GET /api/reservation/message-notifications/{taskId}
|
||||
}
|
||||
```
|
||||
|
||||
V3 S10 结构化详情当前增量:
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "10010",
|
||||
"readonly": true,
|
||||
"result_type": "source_message_review_notification",
|
||||
"route_code": "S10",
|
||||
"agent_assessment": {
|
||||
"status": "no_booking_action_detected",
|
||||
"reason_code": "no_booking_action_detected"
|
||||
},
|
||||
"notification": {
|
||||
"notification_type": "source_message_review",
|
||||
"show_source_message": true,
|
||||
"requires_user_decision": true
|
||||
},
|
||||
"manual_review": null,
|
||||
"source_message_id": "30010"
|
||||
}
|
||||
```
|
||||
|
||||
## 9.1 V3 诊断展示块
|
||||
|
||||
任务详情接口已新增两个只读数组:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `adapter_contract_errors[]` | 同一 SuperAgent 入站批次中未生成任务的 Adapter 契约错误。 |
|
||||
| `unhandled_intents[]` | 同一 SuperAgent 入站批次中无法映射到业务任务卡的未处理意图。 |
|
||||
|
||||
数组元素字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `transition_id` | AI transition ID。 |
|
||||
| `source_event_index` | AI current 事件序号。 |
|
||||
| `array_index` | AI 返回数组顺序。 |
|
||||
| `result_type` | AI 结果类型。 |
|
||||
| `ai_task_type` | AI 原始任务类型。 |
|
||||
| `task_subtype` | 业务动作 subtype,可能为空。 |
|
||||
| `route_code` | V3 路由码。 |
|
||||
| `system_process_category` | 系统处理分类。 |
|
||||
| `adapter_error_code` | 契约错误代码;未处理意图通常为空。 |
|
||||
| `adapter_error_message` | 契约错误安全摘要。 |
|
||||
| `payload_fragment` | 对前端展示安全的 AI item 片段。 |
|
||||
|
||||
前端注意:这两个数组不是任务队列,不提供编辑、确认、执行 OPERA 或重试入口;只用于解释为什么同一封邮件中的某些 event 没有变成业务任务。
|
||||
|
||||
## 10. 任务卡前端字段白名单元数据接口
|
||||
|
||||
是否需要该接口待确认。如果任务详情接口 `fields[]` 已透出 3.0 所需元数据,则第一版可以不做独立白名单接口;如果后续需要字段矩阵调试页、版本对齐页或前端预加载全部任务卡配置,再补独立接口。
|
||||
@@ -572,13 +656,13 @@ GET /api/reservation/task-card-field-whitelist
|
||||
| --- | --- | --- |
|
||||
| `task_type` | 否 | 按系统主任务类型过滤。 |
|
||||
| `task_subtype` | 否 | 按任务卡 subtype 过滤。 |
|
||||
| `version` | 否 | 字段白名单版本,例如 `20260708-3.0`。 |
|
||||
| `version` | 否 | 字段白名单版本,例如 `20260711-p0`。 |
|
||||
|
||||
建议返参:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "20260708-3.0",
|
||||
"version": "20260711-p0",
|
||||
"items": [
|
||||
{
|
||||
"task_type": "UPDATE_BOOKING",
|
||||
@@ -628,5 +712,5 @@ POST /api/reservation/tasks/{taskId}/order-binding
|
||||
- 订单列表、任务列表当前统一使用 `items + page` 分页结构;邮件会话详情不分页,返回同一会话全部邮件。
|
||||
- 邮件会话详情接口已优先使用 `GET /api/source-messages/{sourceMessageId}/conversation`。
|
||||
- 任务详情 `fields[]` 已由后端直接透出 P0 需要的 3.0 元数据;独立字段白名单接口后置。
|
||||
- 独立 Message Notification 页面继续后置;新入口 S000/S999 第一版先在任务列表和任务详情展示。
|
||||
- 独立 Message Notification 页面继续后置;旧 S000/S999 和新 S10/S99 都先在任务列表和任务详情展示。
|
||||
- 邮件会话全文读取的审计策略由后端内部处理;前端不保存原文读取 key。
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
| --- | --- |
|
||||
| 文档版本 | 0.1 |
|
||||
| 日期 | 2026-07-11 |
|
||||
| 状态 | 0711 P0 基线确认版;后端已完成 M002 V3 CP1-CP2 入站解析与路由基线 |
|
||||
| 状态 | 0711 P0 基线确认版;后端已完成 M002 V3 CP1-CP4 入站、路由持久化和列表 / 详情展示基线 |
|
||||
| 适用范围 | SourceMessage 之后的 SuperAgent 输出适配、任务路由、只读通知卡、人工复核同卡解阻、前后端协作边界 |
|
||||
| 主要读者 | 产品、后端、前端、测试、SuperAgent 对接方、后续协作 agent |
|
||||
|
||||
@@ -310,7 +310,7 @@ Agent payload 不可变。本系统在同一张卡上维护复核状态:
|
||||
|
||||
- 任务列表支持展示旧 `S000/S999` 和新 `S10/S99`,但新文案以 `S10/S99` 为主。
|
||||
- `S10/S99` 只读卡只出现在任务列表和任务详情,不出现在订单列表。
|
||||
- 任务列表不应仅按旧 `SOURCE_MESSAGE_ONLY` 判断;应兼容后端后续返回的 `route_code=S10/S99`、`result_type=source_message_review_notification`。
|
||||
- 任务列表不应仅按旧 `SOURCE_MESSAGE_ONLY` 判断;当前后端已返回 `route_code=S10/S99`、`result_type=source_message_review_notification`。
|
||||
- type-known manual review 不再统一展示成 Fallback,应展示原业务卡名称和 subtype,并显示复核状态。
|
||||
- 复核解阻页需要能提交 `field_overrides[]`,并在复核场景下确认订单归属。
|
||||
- `unhandled_current_intents[]` 只作为展示块,不提供执行按钮。
|
||||
@@ -324,8 +324,8 @@ V3 建议拆成以下 checkpoint,避免一次性重构过大:
|
||||
| --- | --- | --- |
|
||||
| M002-V3-CP1 | 文档和枚举基线 | 已完成:建立 42 路由枚举 / 稳定配置,作为入站路由唯一代码源 |
|
||||
| M002-V3-CP2 | 入站解析兼容 | 已完成:正式回调支持结构化 S10/S99 和 V3 业务根,保留旧 S000/S999 兼容 |
|
||||
| M002-V3-CP3 | 路由持久化 | 部分完成:已保存 AI 原始三元组、route_code、system_process_category、unhandled_current_intents 和 adapter_contract_error;message_events / unhandled_current_intents 的前端完整展示仍后置 |
|
||||
| M002-V3-CP4 | 列表 / 详情展示 | 任务列表和详情支持 S10/S99、42 路由只读展示、type-known review 展示 |
|
||||
| M002-V3-CP3 | 路由持久化 | 已完成第一版:已保存 AI 原始三元组、route_code、system_process_category、unhandled_current_intents 和 adapter_contract_error |
|
||||
| M002-V3-CP4 | 列表 / 详情展示 | 已完成第一版:任务列表、订单任务时间线和任务详情透出 V3 路由字段;任务详情支持 S10/S99 入口通知结构、unhandled intent 展示块和 adapter contract error 展示块 |
|
||||
| M002-V3-CP5 | 同卡复核解阻 | 支持 review_status、field_overrides、复核场景订单归属确认和 READY 流转 |
|
||||
| M002-V3-CP6 | P0 fixtures 回归 | 引入 0711 P0 fixtures / validator 作为后端适配测试参考,补充项目级测试 |
|
||||
|
||||
@@ -343,7 +343,7 @@ V3 P0 不做以下事项:
|
||||
|
||||
## 14. 当前代码现状提醒
|
||||
|
||||
截至 M002 V3 CP1-CP2 落地后,当前后端已经实现:
|
||||
截至 M002 V3 CP4 落地后,当前后端已经实现:
|
||||
|
||||
- `S000/S999` 文本结果兼容处理。
|
||||
- 结构化 `S10/S99` 入站处理,复用 `SOURCE_MESSAGE_ONLY` 只读特殊任务。
|
||||
@@ -351,6 +351,9 @@ V3 P0 不做以下事项:
|
||||
- V3 业务根 `source_message + message_events[]` 基础解析;能派生到稳定路由的 event 创建业务任务,无法派生的 event、显式 `contract_errors`、根 `missing_fields`、不完整 `manual_review` 和不完整 parent split 候选只落 `adapter_contract_error` transition。
|
||||
- `unhandled_current_intents[]` 只落 `UNHANDLED_CURRENT_INTENT` transition,不创建订单和任务,也不伪装成 adapter 契约错误。
|
||||
- AI transition 最小保存 `route_code`、`system_process_category`、`adapter_error_code`、`adapter_error_message`。
|
||||
- 任务列表、订单任务时间线和任务详情顶层透出 `result_type`、`ai_task_type`、`task_subtype`、`route_code`、`system_process_category`。
|
||||
- `SOURCE_MESSAGE_ONLY` 任务详情透出 `source_message_only_result.result_type`、`route_code`、`agent_assessment`、`notification`、`manual_review` 和 `raw_answer`。
|
||||
- 业务任务详情按同一 AI 批次透出 `adapter_contract_errors[]` 与 `unhandled_intents[]` 只读展示块。
|
||||
- 订单 / 任务列表、任务详情、草稿保存、最终确认、OPERA 模拟骨架和审计列表。
|
||||
- SuperAgent 查询上下文接口 1、2,以及邮件会话相关查询。
|
||||
|
||||
@@ -360,5 +363,4 @@ V3 P0 不做以下事项:
|
||||
- 复核场景订单归属确认。
|
||||
- `manual_review.missing_fields[]` 到任务卡可编辑字段白名单的完整映射校验。
|
||||
- `source_message.source_message_id` 缺失时按 V3 typed `infrastructure_input_error` 结构响应。
|
||||
- 任务列表 / 详情完整透出 V3 `route_code`、入口通知结构、unhandled intent 展示块和 adapter contract error 展示。
|
||||
- 真实 OPERA / OHIP、普通任务任意切换订单、P0 fixtures / validator 全量回归。
|
||||
|
||||
@@ -29,6 +29,10 @@ import java.time.LocalDateTime;
|
||||
* @param transitionSourceEventIndex AI transition 来源事件序号
|
||||
* @param catalogCode Skill 目录代码
|
||||
* @param skillId Skill 标识
|
||||
* @param routeCode M002 V3 路由码
|
||||
* @param systemProcessCategory 系统处理分类
|
||||
* @param adapterErrorCode Adapter 契约错误代码
|
||||
* @param adapterErrorMessage Adapter 契约错误说明
|
||||
*/
|
||||
public record ReservationAiQueryTaskSnapshot(
|
||||
Long id,
|
||||
@@ -54,6 +58,10 @@ public record ReservationAiQueryTaskSnapshot(
|
||||
LocalDateTime updatedAt,
|
||||
Integer transitionSourceEventIndex,
|
||||
String catalogCode,
|
||||
String skillId
|
||||
String skillId,
|
||||
String routeCode,
|
||||
String systemProcessCategory,
|
||||
String adapterErrorCode,
|
||||
String adapterErrorMessage
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package cn.nianxx.thhotel.workflows.reservation.common.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* AI transition 展示快照。用于任务详情展示同一批次的 V3 路由、契约错误和未处理意图,不直接暴露 Entity。
|
||||
*
|
||||
* @param id AI transition 主键
|
||||
* @param hotelId 酒店上下文 ID
|
||||
* @param batchId AI 入站批次 ID
|
||||
* @param sourceMessageId 来源 SourceMessage Inbox ID
|
||||
* @param sourceEventIndex AI current 事件序号
|
||||
* @param arrayIndex AI 返回数组顺序,从 1 开始
|
||||
* @param executionOrder 初始执行顺序
|
||||
* @param catalogCode Skill 目录代码
|
||||
* @param skillId Skill 标识
|
||||
* @param resultType AI 结果类型
|
||||
* @param aiTaskType AI 原始任务类型
|
||||
* @param routeCode M002 V3 路由码
|
||||
* @param systemProcessCategory 系统处理分类
|
||||
* @param systemTaskType 系统主任务类型
|
||||
* @param taskCardType 任务卡类型
|
||||
* @param taskSubtype 业务动作 subtype
|
||||
* @param currentOrHistory 当前或历史标识
|
||||
* @param adapterErrorCode Adapter 契约错误代码
|
||||
* @param adapterErrorMessage Adapter 契约错误说明
|
||||
* @param aiPayloadJson AI 原始 item JSON
|
||||
* @param createdAt 创建 UTC 时间
|
||||
* @param updatedAt 最近更新 UTC 时间
|
||||
*/
|
||||
public record ReservationAiTransitionSnapshot(
|
||||
Long id,
|
||||
String hotelId,
|
||||
Long batchId,
|
||||
Long sourceMessageId,
|
||||
Integer sourceEventIndex,
|
||||
Integer arrayIndex,
|
||||
Integer executionOrder,
|
||||
String catalogCode,
|
||||
String skillId,
|
||||
String resultType,
|
||||
String aiTaskType,
|
||||
String routeCode,
|
||||
String systemProcessCategory,
|
||||
String systemTaskType,
|
||||
String taskCardType,
|
||||
String taskSubtype,
|
||||
String currentOrHistory,
|
||||
String adapterErrorCode,
|
||||
String adapterErrorMessage,
|
||||
String aiPayloadJson,
|
||||
LocalDateTime createdAt,
|
||||
LocalDateTime updatedAt
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.nianxx.thhotel.workflows.reservation.common.result;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* 任务详情中的 AI transition 展示块。用于展示未建任务的关联 V3 诊断信息。
|
||||
*
|
||||
* @param transitionId AI transition ID,按字符串返回避免前端长整型精度问题
|
||||
* @param sourceEventIndex AI current 事件序号
|
||||
* @param arrayIndex AI 返回数组顺序
|
||||
* @param resultType AI 结果类型
|
||||
* @param aiTaskType AI 原始任务类型
|
||||
* @param taskSubtype 业务动作 subtype
|
||||
* @param routeCode M002 V3 路由码
|
||||
* @param systemProcessCategory 系统处理分类
|
||||
* @param adapterErrorCode Adapter 契约错误代码
|
||||
* @param adapterErrorMessage Adapter 契约错误说明
|
||||
* @param payloadFragment 对前端展示安全的 AI item 片段,当前不包含邮件原文
|
||||
*/
|
||||
public record ReservationAiTransitionDisplayResult(
|
||||
@JsonProperty("transition_id")
|
||||
String transitionId,
|
||||
@JsonProperty("source_event_index")
|
||||
Integer sourceEventIndex,
|
||||
@JsonProperty("array_index")
|
||||
Integer arrayIndex,
|
||||
@JsonProperty("result_type")
|
||||
String resultType,
|
||||
@JsonProperty("ai_task_type")
|
||||
String aiTaskType,
|
||||
@JsonProperty("task_subtype")
|
||||
String taskSubtype,
|
||||
@JsonProperty("route_code")
|
||||
String routeCode,
|
||||
@JsonProperty("system_process_category")
|
||||
String systemProcessCategory,
|
||||
@JsonProperty("adapter_error_code")
|
||||
String adapterErrorCode,
|
||||
@JsonProperty("adapter_error_message")
|
||||
String adapterErrorMessage,
|
||||
@JsonProperty("payload_fragment")
|
||||
Object payloadFragment
|
||||
) {
|
||||
}
|
||||
@@ -8,7 +8,11 @@ import java.time.OffsetDateTime;
|
||||
*
|
||||
* @param taskId 任务 ID
|
||||
* @param taskType 系统主任务类型
|
||||
* @param resultType AI 结果类型
|
||||
* @param aiTaskType AI 原始任务类型
|
||||
* @param taskSubtype 任务 subtype
|
||||
* @param routeCode M002 V3 路由码
|
||||
* @param systemProcessCategory 系统处理分类
|
||||
* @param taskStatus 任务状态
|
||||
* @param cardName 任务卡展示名称
|
||||
* @param queueSequence 同订单队列顺序
|
||||
@@ -28,8 +32,16 @@ public record ReservationOrderTaskTimelineItemResult(
|
||||
String taskId,
|
||||
@JsonProperty("task_type")
|
||||
String taskType,
|
||||
@JsonProperty("result_type")
|
||||
String resultType,
|
||||
@JsonProperty("ai_task_type")
|
||||
String aiTaskType,
|
||||
@JsonProperty("task_subtype")
|
||||
String taskSubtype,
|
||||
@JsonProperty("route_code")
|
||||
String routeCode,
|
||||
@JsonProperty("system_process_category")
|
||||
String systemProcessCategory,
|
||||
@JsonProperty("task_status")
|
||||
String taskStatus,
|
||||
@JsonProperty("card_name")
|
||||
|
||||
@@ -9,6 +9,11 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
* @param entryResultMeaning 入口结果语义
|
||||
* @param entryResultDescription 入口结果中文说明
|
||||
* @param entryResultSourceMessageId SuperAgent 回传的外部来源消息 ID
|
||||
* @param resultType AI 结果类型
|
||||
* @param routeCode M002 V3 路由码
|
||||
* @param agentAssessment S10/S99 入口评估对象
|
||||
* @param notification S10/S99 入口通知对象
|
||||
* @param manualReview S99 人工复核对象;S10 通常为空
|
||||
* @param rawAnswer SuperAgent 原始文本返回
|
||||
*/
|
||||
public record ReservationSourceMessageOnlyResult(
|
||||
@@ -20,6 +25,16 @@ public record ReservationSourceMessageOnlyResult(
|
||||
String entryResultDescription,
|
||||
@JsonProperty("entry_result_source_message_id")
|
||||
String entryResultSourceMessageId,
|
||||
@JsonProperty("result_type")
|
||||
String resultType,
|
||||
@JsonProperty("route_code")
|
||||
String routeCode,
|
||||
@JsonProperty("agent_assessment")
|
||||
Object agentAssessment,
|
||||
@JsonProperty("notification")
|
||||
Object notification,
|
||||
@JsonProperty("manual_review")
|
||||
Object manualReview,
|
||||
@JsonProperty("raw_answer")
|
||||
String rawAnswer
|
||||
) {
|
||||
|
||||
@@ -17,6 +17,11 @@ import java.util.List;
|
||||
* @param conversationMessageCount 同一外部邮件会话下的消息数量
|
||||
* @param systemTaskType 系统主任务类型
|
||||
* @param taskCardType 任务卡类型
|
||||
* @param resultType AI 结果类型
|
||||
* @param aiTaskType AI 原始任务类型
|
||||
* @param taskSubtype 业务动作 subtype
|
||||
* @param routeCode M002 V3 路由码
|
||||
* @param systemProcessCategory 系统处理分类
|
||||
* @param taskStatus 任务状态
|
||||
* @param fieldContractVersion 字段矩阵契约版本
|
||||
* @param draftPayload 草稿 payload
|
||||
@@ -25,6 +30,8 @@ import java.util.List;
|
||||
* @param availability 当前可处理状态
|
||||
* @param fields 按字段矩阵生成的字段列表
|
||||
* @param operaOperations OPERA 模拟操作列表
|
||||
* @param adapterContractErrors 同批次未建任务的 Adapter 契约错误展示块
|
||||
* @param unhandledIntents 同批次未建任务的未处理意图展示块
|
||||
*/
|
||||
public record ReservationTaskDetailResult(
|
||||
@JsonProperty("task_id")
|
||||
@@ -47,6 +54,16 @@ public record ReservationTaskDetailResult(
|
||||
String systemTaskType,
|
||||
@JsonProperty("task_card_type")
|
||||
String taskCardType,
|
||||
@JsonProperty("result_type")
|
||||
String resultType,
|
||||
@JsonProperty("ai_task_type")
|
||||
String aiTaskType,
|
||||
@JsonProperty("task_subtype")
|
||||
String taskSubtype,
|
||||
@JsonProperty("route_code")
|
||||
String routeCode,
|
||||
@JsonProperty("system_process_category")
|
||||
String systemProcessCategory,
|
||||
@JsonProperty("task_status")
|
||||
String taskStatus,
|
||||
@JsonProperty("field_contract_version")
|
||||
@@ -60,6 +77,10 @@ public record ReservationTaskDetailResult(
|
||||
ReservationTaskAvailabilityResult availability,
|
||||
List<ReservationTaskFieldResult> fields,
|
||||
@JsonProperty("opera_operations")
|
||||
List<ReservationOperaOperationResult> operaOperations
|
||||
List<ReservationOperaOperationResult> operaOperations,
|
||||
@JsonProperty("adapter_contract_errors")
|
||||
List<ReservationAiTransitionDisplayResult> adapterContractErrors,
|
||||
@JsonProperty("unhandled_intents")
|
||||
List<ReservationAiTransitionDisplayResult> unhandledIntents
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@ import java.time.OffsetDateTime;
|
||||
* @param displayOrderKey 前端优先展示的订单业务号或临时订单号
|
||||
* @param temporaryOrderNo 临时订单号
|
||||
* @param taskType 系统主任务类型
|
||||
* @param resultType AI 结果类型
|
||||
* @param aiTaskType AI 原始任务类型
|
||||
* @param taskSubtype 任务 subtype
|
||||
* @param routeCode M002 V3 路由码
|
||||
* @param systemProcessCategory 系统处理分类
|
||||
* @param taskStatus 任务状态
|
||||
* @param cardName 任务卡展示名称,第一版使用任务卡类型
|
||||
* @param queueSequence 同订单队列顺序
|
||||
@@ -41,8 +45,16 @@ public record ReservationTaskWorkbenchItemResult(
|
||||
String temporaryOrderNo,
|
||||
@JsonProperty("task_type")
|
||||
String taskType,
|
||||
@JsonProperty("result_type")
|
||||
String resultType,
|
||||
@JsonProperty("ai_task_type")
|
||||
String aiTaskType,
|
||||
@JsonProperty("task_subtype")
|
||||
String taskSubtype,
|
||||
@JsonProperty("route_code")
|
||||
String routeCode,
|
||||
@JsonProperty("system_process_category")
|
||||
String systemProcessCategory,
|
||||
@JsonProperty("task_status")
|
||||
String taskStatus,
|
||||
@JsonProperty("card_name")
|
||||
|
||||
@@ -5,6 +5,7 @@ import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiBatchSnap
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiQueryOrderSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiQueryTaskSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiTransitionDraft;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiTransitionSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAuditLogDraft;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAuditLogSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOperaOperationAttemptDraft;
|
||||
@@ -288,6 +289,40 @@ public class MybatisReservationAiWorkflowRepository implements ReservationAiWork
|
||||
return Optional.ofNullable(entity).map(this::toTaskCardSnapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 AI transition ID 查询展示快照,用于任务详情补充 V3 入站路由上下文。
|
||||
*/
|
||||
@Override
|
||||
public Optional<ReservationAiTransitionSnapshot> findTransitionById(String hotelId, Long transitionId) {
|
||||
if (transitionId == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
ReservationAiTransitionEntity entity = transitionMapper.selectOne(
|
||||
Wrappers.<ReservationAiTransitionEntity>lambdaQuery()
|
||||
.eq(ReservationAiTransitionEntity::getHotelId, hotelId)
|
||||
.eq(ReservationAiTransitionEntity::getId, transitionId)
|
||||
.last("LIMIT 1"));
|
||||
return Optional.ofNullable(entity).map(this::toAiTransitionSnapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 AI 批次查询全部 transition 展示快照,按 AI 返回顺序稳定排序。
|
||||
*/
|
||||
@Override
|
||||
public List<ReservationAiTransitionSnapshot> findTransitionsByBatchId(String hotelId, Long batchId) {
|
||||
if (batchId == null) {
|
||||
return List.of();
|
||||
}
|
||||
return transitionMapper.selectList(Wrappers.<ReservationAiTransitionEntity>lambdaQuery()
|
||||
.eq(ReservationAiTransitionEntity::getHotelId, hotelId)
|
||||
.eq(ReservationAiTransitionEntity::getBatchId, batchId)
|
||||
.orderByAsc(ReservationAiTransitionEntity::getArrayIndex)
|
||||
.orderByAsc(ReservationAiTransitionEntity::getId))
|
||||
.stream()
|
||||
.map(this::toAiTransitionSnapshot)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询同订单前置队列任务,Message Notification 等非队列任务不会进入结果。
|
||||
*/
|
||||
@@ -1021,7 +1056,40 @@ public class MybatisReservationAiWorkflowRepository implements ReservationAiWork
|
||||
entity.getUpdatedAt(),
|
||||
transition == null ? null : transition.getSourceEventIndex(),
|
||||
transition == null ? null : transition.getCatalogCode(),
|
||||
transition == null ? null : transition.getSkillId());
|
||||
transition == null ? null : transition.getSkillId(),
|
||||
transition == null ? null : transition.getRouteCode(),
|
||||
transition == null ? null : transition.getSystemProcessCategory(),
|
||||
transition == null ? null : transition.getAdapterErrorCode(),
|
||||
transition == null ? null : transition.getAdapterErrorMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换 AI transition 实体为展示快照。
|
||||
*/
|
||||
private ReservationAiTransitionSnapshot toAiTransitionSnapshot(ReservationAiTransitionEntity entity) {
|
||||
return new ReservationAiTransitionSnapshot(
|
||||
entity.getId(),
|
||||
entity.getHotelId(),
|
||||
entity.getBatchId(),
|
||||
entity.getSourceMessageId(),
|
||||
entity.getSourceEventIndex(),
|
||||
entity.getArrayIndex(),
|
||||
entity.getExecutionOrder(),
|
||||
entity.getCatalogCode(),
|
||||
entity.getSkillId(),
|
||||
entity.getResultType(),
|
||||
entity.getAiTaskType(),
|
||||
entity.getRouteCode(),
|
||||
entity.getSystemProcessCategory(),
|
||||
entity.getSystemTaskType(),
|
||||
entity.getTaskCardType(),
|
||||
entity.getTaskSubtype(),
|
||||
entity.getCurrentOrHistory(),
|
||||
entity.getAdapterErrorCode(),
|
||||
entity.getAdapterErrorMessage(),
|
||||
entity.getAiPayloadJson(),
|
||||
entity.getCreatedAt(),
|
||||
entity.getUpdatedAt());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiBatchDraf
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiBatchSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiQueryOrderSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiQueryTaskSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiTransitionSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiTransitionDraft;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAuditLogDraft;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAuditLogSnapshot;
|
||||
@@ -82,6 +83,16 @@ public interface ReservationAiWorkflowRepository {
|
||||
*/
|
||||
Optional<ReservationTaskCardSnapshot> findTaskCardByTaskId(String hotelId, Long taskId);
|
||||
|
||||
/**
|
||||
* 按 AI transition ID 查询展示快照,用于任务详情补充 V3 路由和批次上下文。
|
||||
*/
|
||||
Optional<ReservationAiTransitionSnapshot> findTransitionById(String hotelId, Long transitionId);
|
||||
|
||||
/**
|
||||
* 查询同一 AI 批次下的 transition 展示快照,用于任务详情展示未建任务的契约错误和未处理意图。
|
||||
*/
|
||||
List<ReservationAiTransitionSnapshot> findTransitionsByBatchId(String hotelId, Long batchId);
|
||||
|
||||
/**
|
||||
* 查询同订单中排在当前任务之前且参与队列的任务。
|
||||
*/
|
||||
|
||||
@@ -329,7 +329,11 @@ public class ReservationFrontendQueryServiceImpl implements ReservationFrontendQ
|
||||
displayOrderKey(order),
|
||||
order == null || isHiddenSystemOrder(order) ? null : order.temporaryOrderCode(),
|
||||
task.systemTaskType(),
|
||||
task.resultType(),
|
||||
task.aiTaskType(),
|
||||
task.taskSubtype(),
|
||||
task.routeCode(),
|
||||
task.systemProcessCategory(),
|
||||
task.taskStatus(),
|
||||
task.taskCardType(),
|
||||
task.executionOrder(),
|
||||
@@ -356,7 +360,11 @@ public class ReservationFrontendQueryServiceImpl implements ReservationFrontendQ
|
||||
return new ReservationOrderTaskTimelineItemResult(
|
||||
task.id().toString(),
|
||||
task.systemTaskType(),
|
||||
task.resultType(),
|
||||
task.aiTaskType(),
|
||||
task.taskSubtype(),
|
||||
task.routeCode(),
|
||||
task.systemProcessCategory(),
|
||||
task.taskStatus(),
|
||||
task.taskCardType(),
|
||||
task.executionOrder(),
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.nianxx.thhotel.platform.message.common.dto.SourceMessageSummaryRespons
|
||||
import cn.nianxx.thhotel.platform.message.service.SourceMessageQueryService;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAuditLogDraft;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAuditLogSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationAiTransitionSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOperaOperationAttemptDraft;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOperaOperationAttemptSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOperaOperationDraft;
|
||||
@@ -12,6 +13,7 @@ import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationOrderSnapsh
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskCardFieldDefinition;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskCardSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.dto.ReservationTaskSnapshot;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationAiSystemProcessCategory;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationBusinessKeySource;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationOperaAttemptStatus;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.enums.ReservationOperaOperationStatus;
|
||||
@@ -24,6 +26,7 @@ import cn.nianxx.thhotel.workflows.reservation.common.request.ManualReviewConver
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationOperaSimulationRequest;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationTaskPayloadMutationRequest;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.result.ManualReviewConversionResult;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationAiTransitionDisplayResult;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationOperaOperationAttemptResult;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationOperaOperationResult;
|
||||
import cn.nianxx.thhotel.workflows.reservation.common.result.ReservationSourceMessageOnlyResult;
|
||||
@@ -112,6 +115,8 @@ public class ReservationTaskWorkflowServiceImpl implements ReservationTaskWorkfl
|
||||
List<ReservationOperaOperationResult> operaOperations = findOperaOperationResults(task);
|
||||
ReservationSourceMessageOnlyResult sourceMessageOnlyResult = buildSourceMessageOnlyResult(task, taskCard);
|
||||
SourceMessageDetailContext sourceContext = findSourceMessageDetailContext(task);
|
||||
ReservationAiTransitionSnapshot transition = findTaskTransition(task);
|
||||
List<ReservationAiTransitionSnapshot> relatedTransitions = findRelatedTransitions(task, transition);
|
||||
return new ReservationTaskDetailResult(
|
||||
task.id().toString(),
|
||||
task.orderId().toString(),
|
||||
@@ -123,6 +128,11 @@ public class ReservationTaskWorkflowServiceImpl implements ReservationTaskWorkfl
|
||||
sourceContext.conversationMessageCount(),
|
||||
task.systemTaskType(),
|
||||
task.taskCardType(),
|
||||
task.resultType(),
|
||||
task.aiTaskType(),
|
||||
task.taskSubtype(),
|
||||
transition == null ? null : transition.routeCode(),
|
||||
transition == null ? null : transition.systemProcessCategory(),
|
||||
task.taskStatus(),
|
||||
taskCard.fieldContractVersion(),
|
||||
jsonPayloadToObject(taskCard.draftPayloadJson()),
|
||||
@@ -130,7 +140,13 @@ public class ReservationTaskWorkflowServiceImpl implements ReservationTaskWorkfl
|
||||
sourceMessageOnlyResult,
|
||||
availability,
|
||||
fields,
|
||||
operaOperations);
|
||||
operaOperations,
|
||||
buildTransitionDisplayResults(
|
||||
relatedTransitions,
|
||||
ReservationAiSystemProcessCategory.ADAPTER_CONTRACT_ERROR),
|
||||
buildTransitionDisplayResults(
|
||||
relatedTransitions,
|
||||
ReservationAiSystemProcessCategory.UNHANDLED_CURRENT_INTENT));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -854,9 +870,86 @@ public class ReservationTaskWorkflowServiceImpl implements ReservationTaskWorkfl
|
||||
textValue(payload, "entry_result_meaning"),
|
||||
textValue(payload, "entry_result_description"),
|
||||
textValue(payload, "source_message_id"),
|
||||
textValue(payload, "result_type"),
|
||||
textValue(payload, "route_code"),
|
||||
rawAnswerField(payload, "agent_assessment"),
|
||||
rawAnswerField(payload, "notification"),
|
||||
rawAnswerField(payload, "manual_review"),
|
||||
textValue(payload, "raw_answer"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前任务对应的 AI transition,用于详情展示 V3 route 和系统处理分类。
|
||||
*/
|
||||
private ReservationAiTransitionSnapshot findTaskTransition(ReservationTaskSnapshot task) {
|
||||
return workflowRepository.findTransitionById(task.hotelId(), task.aiTransitionId()).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询同一 AI 批次下的关联 transition,用于展示没有生成任务的诊断类结果。
|
||||
*/
|
||||
private List<ReservationAiTransitionSnapshot> findRelatedTransitions(
|
||||
ReservationTaskSnapshot task,
|
||||
ReservationAiTransitionSnapshot transition) {
|
||||
if (transition == null) {
|
||||
return List.of();
|
||||
}
|
||||
return workflowRepository.findTransitionsByBatchId(task.hotelId(), transition.batchId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按系统处理分类构造详情页展示块。
|
||||
*/
|
||||
private List<ReservationAiTransitionDisplayResult> buildTransitionDisplayResults(
|
||||
List<ReservationAiTransitionSnapshot> transitions,
|
||||
ReservationAiSystemProcessCategory category) {
|
||||
if (transitions == null || transitions.isEmpty() || category == null) {
|
||||
return List.of();
|
||||
}
|
||||
return transitions.stream()
|
||||
.filter(transition -> category.name().equals(transition.systemProcessCategory()))
|
||||
.map(this::toTransitionDisplayResult)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换 AI transition 快照为前端详情展示块。
|
||||
*/
|
||||
private ReservationAiTransitionDisplayResult toTransitionDisplayResult(ReservationAiTransitionSnapshot transition) {
|
||||
return new ReservationAiTransitionDisplayResult(
|
||||
transition.id().toString(),
|
||||
transition.sourceEventIndex(),
|
||||
transition.arrayIndex(),
|
||||
transition.resultType(),
|
||||
transition.aiTaskType(),
|
||||
transition.taskSubtype(),
|
||||
transition.routeCode(),
|
||||
transition.systemProcessCategory(),
|
||||
transition.adapterErrorCode(),
|
||||
transition.adapterErrorMessage(),
|
||||
jsonPayloadToObject(transition.aiPayloadJson()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 S10/S99 原始回答中提取指定对象字段;旧 S000/S999 文本无法解析时返回 null。
|
||||
*/
|
||||
private Object rawAnswerField(JsonNode payload, String fieldName) {
|
||||
String rawAnswer = textValue(payload, "raw_answer");
|
||||
if (trimToNull(rawAnswer) == null || trimToNull(fieldName) == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode root = objectMapper.readTree(rawAnswer);
|
||||
JsonNode fieldNode = root.path(fieldName);
|
||||
if (fieldNode.isMissingNode() || fieldNode.isNull()) {
|
||||
return null;
|
||||
}
|
||||
return objectMapper.convertValue(fieldNode, Object.class);
|
||||
} catch (Exception exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入草稿保存或最终确认审计。审计只记录 payload 摘要,不保存完整客户字段。
|
||||
*/
|
||||
|
||||
@@ -745,6 +745,78 @@ class SuperAgentTaskResultControllerTest {
|
||||
assertThat(taskCount).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeStructuredS99NotificationInTaskListAndDetail() throws Exception {
|
||||
captureSourceMessage("mail-v3-s99-frontend-visible-001");
|
||||
String body = """
|
||||
{
|
||||
"source_message": {
|
||||
"source_message_id": "mail-v3-s99-frontend-visible-001",
|
||||
"subject": null,
|
||||
"from": null,
|
||||
"cc": [],
|
||||
"received_at": null,
|
||||
"source_channel": "Email"
|
||||
},
|
||||
"route_code": "S99",
|
||||
"handler_type": "main_agent_outcome",
|
||||
"result_type": "source_message_review_notification",
|
||||
"current_or_history": "current",
|
||||
"agent_assessment": {
|
||||
"status": "insufficient_business_material",
|
||||
"reason_code": "cannot_form_business_material_package",
|
||||
"automation_action": "none"
|
||||
},
|
||||
"notification": {
|
||||
"required": true,
|
||||
"notification_type": "source_message_review",
|
||||
"show_source_message": true,
|
||||
"requires_user_decision": true
|
||||
},
|
||||
"manual_review": {
|
||||
"reason_code": "cannot_form_business_material_package",
|
||||
"review_notes": "需要人工查看原邮件。"
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
MvcResult result = mockMvc.perform(signedPost(body, "nonce-v3-s99-frontend-visible-001"))
|
||||
.andExpect(status().isCreated())
|
||||
.andReturn();
|
||||
String taskId = com.jayway.jsonpath.JsonPath.read(
|
||||
result.getResponse().getContentAsString(),
|
||||
"$.items[0].task_id"
|
||||
);
|
||||
|
||||
mockMvc.perform(get("/api/reservation/tasks")
|
||||
.param("hotel_id", "HOTEL-TEST")
|
||||
.param("task_type", "SOURCE_MESSAGE_ONLY")
|
||||
.param("keyword", "mail-v3-s99-frontend-visible-001"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items[0].task_id").value(taskId))
|
||||
.andExpect(jsonPath("$.items[0].result_type").value("source_message_review_notification"))
|
||||
.andExpect(jsonPath("$.items[0].ai_task_type").value("S99"))
|
||||
.andExpect(jsonPath("$.items[0].route_code").value("S99"))
|
||||
.andExpect(jsonPath("$.items[0].system_process_category").value("SOURCE_MESSAGE_NOTIFICATION"));
|
||||
|
||||
mockMvc.perform(get("/api/reservation/tasks/{taskId}", taskId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.result_type").value("source_message_review_notification"))
|
||||
.andExpect(jsonPath("$.ai_task_type").value("S99"))
|
||||
.andExpect(jsonPath("$.task_subtype").value("S99"))
|
||||
.andExpect(jsonPath("$.route_code").value("S99"))
|
||||
.andExpect(jsonPath("$.system_process_category").value("SOURCE_MESSAGE_NOTIFICATION"))
|
||||
.andExpect(jsonPath("$.source_message_only_result.result_type")
|
||||
.value("source_message_review_notification"))
|
||||
.andExpect(jsonPath("$.source_message_only_result.route_code").value("S99"))
|
||||
.andExpect(jsonPath("$.source_message_only_result.agent_assessment.status")
|
||||
.value("insufficient_business_material"))
|
||||
.andExpect(jsonPath("$.source_message_only_result.notification.notification_type")
|
||||
.value("source_message_review"))
|
||||
.andExpect(jsonPath("$.source_message_only_result.manual_review.reason_code")
|
||||
.value("cannot_form_business_material_package"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateBusinessTaskFromV3BusinessRootMessageEvent() throws Exception {
|
||||
SourceMessageCaptureResult source = captureSourceMessage("mail-v3-business-root-001");
|
||||
@@ -968,6 +1040,116 @@ class SuperAgentTaskResultControllerTest {
|
||||
assertThat(taskCount).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeV3RouteAndRelatedTransitionBlocksInFrontendQueries() throws Exception {
|
||||
captureSourceMessage("mail-v3-frontend-route-blocks-001");
|
||||
String body = """
|
||||
{
|
||||
"source_message": {
|
||||
"source_message_id": "mail-v3-frontend-route-blocks-001",
|
||||
"subject": "Mixed events",
|
||||
"from": null,
|
||||
"cc": [],
|
||||
"received_at": null,
|
||||
"source_channel": "Email"
|
||||
},
|
||||
"message_events": [
|
||||
{
|
||||
"event_type": "Trace",
|
||||
"source_event_index": "E1",
|
||||
"current_or_history": "current",
|
||||
"case_keys": {
|
||||
"group_code": "GRP-V3-FRONTEND-BLOCK-001",
|
||||
"confirmation_number": null
|
||||
},
|
||||
"extracted_fields": {},
|
||||
"contract_errors": [
|
||||
{
|
||||
"path": "/extracted_fields",
|
||||
"code": "unsupported_contract"
|
||||
}
|
||||
],
|
||||
"manual_review": null
|
||||
},
|
||||
{
|
||||
"event_type": "New Booking",
|
||||
"source_event_index": "E2",
|
||||
"current_or_history": "current",
|
||||
"case_keys": {
|
||||
"group_code": null,
|
||||
"confirmation_number": "CNF-V3-FRONTEND-BLOCK-001"
|
||||
},
|
||||
"extracted_fields": {
|
||||
"booking_object_type": "FIT Reservation"
|
||||
},
|
||||
"manual_review": null
|
||||
}
|
||||
],
|
||||
"case_candidates": [],
|
||||
"extraction_warnings": [],
|
||||
"unhandled_current_intents": [
|
||||
{
|
||||
"intent_type": "unsupported_active_request",
|
||||
"reason_code": "no_task_card_available"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
MvcResult result = mockMvc.perform(signedPost(body, "nonce-v3-frontend-route-blocks-001"))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.accepted_count").value(3))
|
||||
.andReturn();
|
||||
String orderId = com.jayway.jsonpath.JsonPath.read(
|
||||
result.getResponse().getContentAsString(),
|
||||
"$.items[1].order_id"
|
||||
);
|
||||
String taskId = com.jayway.jsonpath.JsonPath.read(
|
||||
result.getResponse().getContentAsString(),
|
||||
"$.items[1].task_id"
|
||||
);
|
||||
|
||||
mockMvc.perform(get("/api/reservation/tasks")
|
||||
.param("hotel_id", "HOTEL-TEST")
|
||||
.param("keyword", "mail-v3-frontend-route-blocks-001"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items[0].task_id").value(taskId))
|
||||
.andExpect(jsonPath("$.items[0].result_type").value("normal_task"))
|
||||
.andExpect(jsonPath("$.items[0].ai_task_type").value("New Booking"))
|
||||
.andExpect(jsonPath("$.items[0].route_code").value("R01_NEW_FIT_RESERVATION_NORMAL"))
|
||||
.andExpect(jsonPath("$.items[0].system_process_category").value("BUSINESS_TASK"));
|
||||
|
||||
mockMvc.perform(get("/api/reservation/orders/{orderId}", orderId)
|
||||
.param("hotel_id", "HOTEL-TEST")
|
||||
.param("include_tasks", "true"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.tasks[0].task_id").value(taskId))
|
||||
.andExpect(jsonPath("$.tasks[0].result_type").value("normal_task"))
|
||||
.andExpect(jsonPath("$.tasks[0].ai_task_type").value("New Booking"))
|
||||
.andExpect(jsonPath("$.tasks[0].route_code").value("R01_NEW_FIT_RESERVATION_NORMAL"))
|
||||
.andExpect(jsonPath("$.tasks[0].system_process_category").value("BUSINESS_TASK"));
|
||||
|
||||
mockMvc.perform(get("/api/reservation/tasks/{taskId}", taskId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.result_type").value("normal_task"))
|
||||
.andExpect(jsonPath("$.ai_task_type").value("New Booking"))
|
||||
.andExpect(jsonPath("$.task_subtype").value("new_fit_reservation"))
|
||||
.andExpect(jsonPath("$.route_code").value("R01_NEW_FIT_RESERVATION_NORMAL"))
|
||||
.andExpect(jsonPath("$.system_process_category").value("BUSINESS_TASK"))
|
||||
.andExpect(jsonPath("$.adapter_contract_errors.length()").value(1))
|
||||
.andExpect(jsonPath("$.adapter_contract_errors[0].adapter_error_code")
|
||||
.value("EVENT_CONTRACT_ERROR"))
|
||||
.andExpect(jsonPath("$.adapter_contract_errors[0].payload_fragment.contract_errors[0].code")
|
||||
.value("unsupported_contract"))
|
||||
.andExpect(jsonPath("$.unhandled_intents.length()").value(1))
|
||||
.andExpect(jsonPath("$.unhandled_intents[0].route_code")
|
||||
.value("R42_UNHANDLED_CURRENT_INTENT"))
|
||||
.andExpect(jsonPath("$.unhandled_intents[0].system_process_category")
|
||||
.value("UNHANDLED_CURRENT_INTENT"))
|
||||
.andExpect(jsonPath("$.unhandled_intents[0].payload_fragment.intent_type")
|
||||
.value("unsupported_active_request"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPersistAdapterContractErrorForIncompleteV3ManualReview() throws Exception {
|
||||
SourceMessageCaptureResult source = captureSourceMessage("mail-v3-manual-review-incomplete-001");
|
||||
|
||||
Reference in New Issue
Block a user