From 038fdc3c8d4c51beb4cc7559e5f81ed04e1964c8 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 20 Jul 2026 19:53:44 +0700 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E9=BD=90Debug=20EML=20V4=E5=85=A5?= =?UTF-8?q?=E7=AB=99=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PROJECT_STATE.md | 12 ++-- .../backend-to-frontend-notes.md | 7 ++- .../debug-eml-page-integration-guide.md | 2 +- .../frontend-to-backend-api-requests.md | 2 +- docs/project/go-live-notes.md | 4 +- ...gent-agentbus-project-integration-guide.md | 1 + .../integrations/superagent-api-contract.md | 2 + .../dev-test-v4-legacy-task-cleanup.md | 62 ++++++++++++++++++- .../M004-debug-eml-superagent-upload-v1.md | 26 +++++--- .../impl/SuperAgentOpenApiProperties.java | 4 +- .../DebugEmlSuperAgentRunServiceImpl.java | 4 +- server/src/main/resources/application-dev.yml | 4 +- .../src/main/resources/application-prod.yml | 3 +- .../src/main/resources/application-test.yml | 4 +- .../impl/SuperAgentOpenApiClientImplTest.java | 4 +- .../DebugEmlSuperAgentControllerTest.java | 16 ++++- .../SuperAgentTaskResultControllerTest.java | 55 +++++++++++++++- 17 files changed, 175 insertions(+), 37 deletions(-) diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index 9f57ddc..f939e2e 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -4,16 +4,16 @@ | --- | --- | | 最近更新 | 2026-07-20 | | 当前分支 | `feature/huangting` | -| 当前阶段 | M002 V4 入站、多卡模型、持久化基线、入站写入、查询接口、卡片确认、复核解阻、目录校验、订单详情 V4 总览、DB 目录、Lookup API、前端 lookup 接入、目录管理后台 CP1 前后端、订单列表 V4 继续处理入口 / open count 收口、V4 业务审计查询和停止旧任务双写 | -| 当前重点 | M002 V4 已停止普通业务入站双写旧 `workflow_reservation_task`,V4 后新业务主线只写 V4 order task / cards / source notification;开发阶段不维护 V2/V3 旧任务兼容,测试数据可重建,生产迁移策略后续上线前单独设计。`GET /api/reservation/orders` 可返回 V4 下一步订单任务、卡片、动作类型、动作状态、V4 open 数和统一展示字段 `open_work_item_count`;旧 `open_task_count` / `next_processable_task_id` 仅作历史诊断兼容。后续可继续做测试机 V4 smoke 复测、真实 PMS / OPERA / OHIP 同步或 SuperAgent 目录供给方案。 | +| 当前阶段 | M002 V4 入站、多卡模型、持久化基线、入站写入、查询接口、卡片确认、复核解阻、目录校验、订单详情 V4 总览、DB 目录、Lookup API、前端 lookup 接入、目录管理后台 CP1 前后端、订单列表 V4 继续处理入口 / open count 收口、V4 业务审计查询、停止旧任务双写和 Debug EML V4 profile 对齐 | +| 当前重点 | M002 V4 已停止普通业务入站双写旧 `workflow_reservation_task`,V4 后新业务主线只写 V4 order task / cards / source notification;Debug EML V4 smoke 默认复用实时 AgentBus V4 Open API subject,避免误走历史 Debug V2/V3 profile。开发阶段不维护 V2/V3 旧任务兼容,测试数据可重建,生产迁移策略后续上线前单独设计。`GET /api/reservation/orders` 可返回 V4 下一步订单任务、卡片、动作类型、动作状态、V4 open 数和统一展示字段 `open_work_item_count`;旧 `open_task_count` / `next_processable_task_id` 仅作历史诊断兼容。后续可继续做测试机 V4 smoke 复测、真实 PMS / OPERA / OHIP 同步或 SuperAgent 目录供给方案。 | ## 1. 当前 Checkpoint -- 名称:`M002-V4-stop-legacy-task-dual-write-and-dev-cleanup` -- 状态:Done,后端已停止 V4 普通业务入站创建旧 `workflow_reservation_task`,补充开发 / 测试旧任务数据清理说明,并通过相关回归与全量后端测试。 -- 目标:V4 普通业务只写 V4 模型,避免 V4 order task 已完成后旧兼容任务仍作为订单列表继续处理 fallback。 +- 名称:`M002-V4-debug-eml-route-v4-model-alignment` +- 状态:Done,已定位 Debug EML 测试机旧任务残留主要来自默认 Open API subject 指向历史 Debug V2/V3 profile;后端已把 Debug EML 默认 subject 对齐实时 AgentBus V4 subject,并补充 Debug provider 的 V4 回调回归测试。 +- 目标:Debug EML V4 smoke 与正式 V4 SuperAgent 入站保持一致;Debug 服务自身不直接写业务模型,SuperAgent 后续通过正式回调 / MCP 写入时只创建 V4 order task / cards,不再创建旧 `workflow_reservation_task`。 - 边界:不做生产数据迁移,不推进真实 PMS / OPERA / OHIP,不推进 M011 CP4,不删除 SourceMessage、V4 order task / cards、V4 source notification、目录、用户权限、酒店配置或 SuperAgent dispatch run。 -- 联调备注:开发 / 测试环境允许清理旧任务、旧任务卡、旧任务审计和旧 OPERA 模拟数据;清理 SQL 见 `docs/project/operations/dev-test-v4-legacy-task-cleanup.md`。该策略仅限开发 / 测试阶段,不代表生产迁移方案。 +- 联调备注:历史 `th-hotel-debug-eml-upload` profile 如仍需排查旧 V2/V3 页面问题,必须显式配置 `SUPERAGENT_{ENV}_DEBUG_EML_EXTERNAL_SUBJECT_ID`,不能作为 M002 V4 smoke 默认入口。本次测试机 Debug EML 残留旧 task 可按开发 / 测试旧任务清理 SQL 传入旧 task ID 后限定清理。 ## 2. 当前优先级 diff --git a/docs/project/frontend-backend/backend-to-frontend-notes.md b/docs/project/frontend-backend/backend-to-frontend-notes.md index 1b986e0..86c56bc 100644 --- a/docs/project/frontend-backend/backend-to-frontend-notes.md +++ b/docs/project/frontend-backend/backend-to-frontend-notes.md @@ -89,7 +89,7 @@ | `GET /api/source-messages/{id}` | 查询来源消息安全详情 | 必须带 Bearer token,需要 `SOURCE_MESSAGE_READ`,后端按消息所属酒店做访问校验;只用于安全摘要详情。 | | `GET /api/source-messages/{id}/original` | 读取来源消息原文 | 必须带 Bearer token,需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;后端按消息所属酒店做访问校验;返回 HTML 时前端展示前必须 sanitize。 | | `GET /api/source-messages/{sourceMessageId}/conversation` | 读取邮件会话详情 | 必须带 Bearer token,需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;返回同一外部会话全部邮件的完整 text/html、`html_body_sanitized`、附件外链、内联图片和关联订单 / 任务摘要;前端不传原文读取 key,展示 HTML 时优先使用 `html_body_sanitized`。 | -| `POST /api/system/debug/eml-superagent-runs` | Debug 页面上传 `.eml` 并调用 SuperAgent | 仅 dev/test 受控调试使用;会写入 SourceMessage Inbox,但不创建订单和任务。 | +| `POST /api/system/debug/eml-superagent-runs` | Debug 页面上传 `.eml` 并调用 SuperAgent | 仅 dev/test 受控调试使用;会写入 SourceMessage Inbox。Debug 服务自身不直接创建订单和任务;如 SuperAgent 通过正式回调 / MCP 写入业务结果,必须按当前 M002 V4 契约创建 V4 order task / cards,不再创建旧 `workflow_reservation_task`。 | | `GET/POST/PUT /api/admin/users...` | 系统管理用户维护 | 需要 Bearer token 和 `SYSTEM_USER_MANAGE`;用户 ID 返回字符串;禁用用户会撤销其 ACTIVE session。 | | `GET/POST/PUT /api/admin/roles...` | 系统管理角色权限维护 | 需要 `SYSTEM_ROLE_MANAGE`;内置角色只读,自定义角色可新增、编辑和分配权限。 | | `GET /api/admin/permissions` | 权限码只读列表 | 需要 `SYSTEM_ROLE_MANAGE`;前端只展示和选择已有权限码,不自行造权限码。 | @@ -109,7 +109,7 @@ | `GET /api/reservation/orders/{orderId}` | 补齐旧 `tasks[]` 来源邮件会话摘要字段,并新增 V4 总览和 `v4_order_tasks[]` 订单任务时间线;前端订单详情总览页已接入。 | `include_tasks=false` 可只取轻量摘要,此时 `tasks[]`、`v4_order_tasks[]` 和 `related_source_messages[]` 都为空,`order_overview` 为空快照,`next_v4_action.action_type=NONE`;旧 `tasks[]` 顺序由后端按订单队列返回,V4 `v4_order_tasks[]` 按 `source_received_at`、`source_message_id`、`order_context_index`、`created_at`、数字 ID 正序返回;前端不要自行重排。订单详情页只展示 V4 确认快照、下一步入口、关联来源消息和任务卡安全摘要,不在该页确认、复核或编辑任务卡,也不展示 payload、邮件正文或附件 URL。`include_source_summary` 第一版不作为前端裁剪字段的强约束,前端暂不要依赖它减少返回字段。 | | `GET /api/reservation/tasks/{taskId}` | 补齐顶层来源邮件字段,并扩展 `fields[]` 元数据。 | 顶层来源字段用于打开邮件会话;`fields[]` 中的 `result_type`、`task_type`、`task_subtype`、`default_value_source` 用于前端字段分组、调试和白名单对齐。 | | `GET /api/source-messages/{sourceMessageId}/conversation` | 新增邮件会话详情接口,并补齐 `html_body_sanitized` / `html_render_mode`。 | 当前唯一推荐路径是这个接口;前端渲染邮件 HTML 时优先使用 `html_body_sanitized`;不要调用历史讨论过的 `/api/source-message-conversations/{externalConversationId}`。 | -| `POST /api/system/debug/eml-superagent-runs` | 新增 Debug EML 上传到 SuperAgent 调试接口,并补齐独立 Debug 外部消息 ID、原始 Message-ID 保留、安全 HTML 字段和入口通知识别。 | 只用于调试页面;请求为 multipart/form-data;必须传 `X-TH-Hotel-Debug-Upload-Key`,但该 key 不能写进前端源码、构建产物、URL、localStorage 或错误上报;SuperAgent 返回旧 S000/S999 或新 S10/S99 入口通知时都不应被前端视为 JSON 解析失败。 | +| `POST /api/system/debug/eml-superagent-runs` | 新增 Debug EML 上传到 SuperAgent 调试接口,并补齐独立 Debug 外部消息 ID、原始 Message-ID 保留、安全 HTML 字段和入口通知识别。 | 只用于调试页面;请求为 multipart/form-data;必须传 `X-TH-Hotel-Debug-Upload-Key`,但该 key 不能写进前端源码、构建产物、URL、localStorage 或错误上报;SuperAgent 返回旧 S000/S999 或新 S10/S99 入口通知时都不应被前端视为 JSON 解析失败。测试机 V4 smoke 默认使用实时 AgentBus V4 subject,历史 Debug V2/V3 profile 只能由后端显式配置,不应作为 V4 smoke 入口。 | | `GET /api/reservation/workbench-items` / `/api/reservation/order-tasks/**` / `/api/reservation/source-notifications/{notificationId}` | 新增 M002 V4 CP5 查询接口,并在 CP6 打开卡片确认 / 来源通知 ack availability。 | 这是 V4 新模型前端主入口;前端应按每张卡或通知返回的 `availability.confirmable`、`availability.ackable`、`readonly_reason_code` 控制按钮。工作台条目已返回 `created_at` / `updated_at` 作为排序兜底和调试字段;前端不要继续从旧 `/api/reservation/tasks/**` 推断 V4 多卡详情。 | | `GET /api/reservation/lookups/accounts` / `/room-types` / `/rate-codes` | 新增 M002 V4 CP11 数据库目录 lookup。 | 前端从任务详情 `fields[].options_source` 选择调用哪个 lookup;只提交返回项的 `code`,不要提交显示名、派生 Market / Source、目录完整对象或前端自造 code。`warnings[]` 非空时可做非阻塞提示。 | | `GET/POST/PUT /api/admin/reservation/catalogs/...` | 新增 M002 V4 目录管理后台 CP1 后端接口。 | 仅供系统设置 / 管理后台页面使用,必须带 `RESERVATION_CATALOG_MANAGE`;支持 Account、Room Type、Rate Code 列表、新增、启用 / 停用;停用后普通 lookup 不再返回该目录项。 | @@ -341,9 +341,10 @@ run_label: 可选调试标签 - `external_message_id` 是后端生成的 Debug 独立 ID,格式类似 `debug-eml-run-{debugRunId}-{sha256前缀}`;原始邮件 `Message-ID` 不再放入 `agentbus_like_payload`,需要排查时看原始 EML OSS 文件和后端 Debug run / SuperAgent metadata。 - 邮件会话解析支持 `References`、`In-Reply-To` 和 `Thread-Index`,但 Debug EML 的 `external_message_id` 不使用原始 `Message-ID` 做幂等。 - `agentbus_like_payload` 是后端发送给 SuperAgent 的 AgentBus Outlook-like 主输入,前端只做只读展示;该对象会包含普通 `reply_policy.mode=manual`、`reply_policy.final_only=true`,但不再包含 `schema_version`、`source.provider=DEBUG_EML_UPLOAD`、`debug_context` 或旧的 Debug 专属 `reply_policy.mode=debug_only`。 +- Debug 服务自身只展示 SuperAgent Open API 结果;如果 SuperAgent 后续调用正式 `task-results` / MCP 写入业务结果,V4 smoke 应进入 `/api/reservation/workbench-items` 和 `/api/reservation/order-tasks/**`,不应再生成旧 `/api/reservation/tasks/**` 任务。 - M011 Debug EML 开关启用后,后端会在 `agentbus_like_payload.attachment_extractions[]` 和 Debug 响应中返回 Booking Excel 附件预处理结果;AgentBus dispatch 开关启用后,后端会在调用 SuperAgent 前把同结构结果追加到后台分发 payload。前端只做只读调试展示,不允许编辑后重新提交,也不得把其中的附件 URL、客户敏感字段或解析 JSON 写入普通日志、埋点、localStorage 或 URL query。 - Debug 来源版本仍由后端 SourceMessage payload 表 `schemaVersion=debug-eml-upload-v1` 记录,前端页面不要再依赖 `agentbus_like_payload.schema_version`。 -- 第一版只返回 SuperAgent 结果,不创建订单、不创建任务、不调用任务结果通知接口。 +- Debug 服务自身只返回 SuperAgent 结果,不直接创建订单、不直接创建任务、不调用任务结果通知接口;业务结果由 SuperAgent 后续正式回调 / MCP 写入时,按当前 V4 模型处理。 - `X-TH-Hotel-Debug-Upload-Key` 只能由调试人员在受控环境手动提供,不能放入 `VITE_*`、源码、构建产物、URL query、localStorage、错误上报或普通日志。 - 返回的 `html_body_sanitized` 复用邮件会话详情的安全策略,前端展示 HTML 时优先使用;`html_body_with_oss_urls` 只作为调试原始处理结果,不建议直接渲染。 - 返回的 `uploaded_media[]`、`original_eml_oss_url`、`html_body_with_oss_urls`、`html_body_sanitized` 可能包含 OSS URL;前端不要写入普通日志、埋点、错误上报或 URL query。 diff --git a/docs/project/frontend-backend/debug-eml-page-integration-guide.md b/docs/project/frontend-backend/debug-eml-page-integration-guide.md index 1226933..772e7ea 100644 --- a/docs/project/frontend-backend/debug-eml-page-integration-guide.md +++ b/docs/project/frontend-backend/debug-eml-page-integration-guide.md @@ -290,7 +290,7 @@ idle - Debug EML 页面是调试工具,不是 Message Notification 页面。 - Debug EML 页面写入的 SourceMessage `provider=DEBUG_EML_UPLOAD`,用于和 AgentBus 来源区分。 - Debug 页面发给 SuperAgent 的 `agentbus_like_payload` 会尽量贴近实时 AgentBus Outlook payload;前端不要用 `DEBUG_EML_UPLOAD` 或 `schema_version` 判断该对象内容。 -- Debug EML 页面第一版不创建订单和任务,所以上传成功后任务列表和订单列表不会因为这次上传自动新增业务数据。 +- Debug EML 服务自身不直接创建订单和任务;上传成功只代表 SourceMessage、OSS 和 SuperAgent Open API 调试链路完成。若 SuperAgent 后续通过正式 `task-results` / MCP 写入 V4 业务结果,前端应从 V4 工作台和 V4 订单任务接口查看新数据,不应再出现旧 `/api/reservation/tasks/**` 任务。 - AgentBus 实时收到邮件后自动推 SuperAgent 已由 M007 后端 V1 实现,但默认关闭且不走 Debug 页面;不能用 Debug EML 页面代表生产实时链路。 ## 11. 当前后置事项 diff --git a/docs/project/frontend-backend/frontend-to-backend-api-requests.md b/docs/project/frontend-backend/frontend-to-backend-api-requests.md index 3b9d5e1..b4be238 100644 --- a/docs/project/frontend-backend/frontend-to-backend-api-requests.md +++ b/docs/project/frontend-backend/frontend-to-backend-api-requests.md @@ -45,7 +45,7 @@ | `GET /api/reservation/orders` | 已完成第一版 | 可以 | 默认查询全部订单状态;订单列表待处理展示使用 `open_work_item_count`;V4 普通业务已停止双写旧任务,旧 `open_task_count` 仅作为历史诊断计数。 | | `GET /api/source-messages/{sourceMessageId}/conversation` | 已完成第一版,已补 `html_body_sanitized` 和 `html_render_mode` | 可以 | 必须带 Bearer token,需要同时拥有 `SOURCE_MESSAGE_READ` 和 `SOURCE_MESSAGE_ORIGINAL_READ`;返回完整 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 结果,不创建订单和任务;已能识别旧 S000/S999 和新结构化 S10/S99。 | +| `POST /api/system/debug/eml-superagent-runs` | 已完成第一版 | 仅 dev/test Debug 页面可用 | 默认关闭,必须后端配置访问口令、阿里云 OSS 和 SuperAgent Open API;Debug 服务自身只展示 SuperAgent 结果,不直接创建订单和任务;如 SuperAgent 通过正式回调 / MCP 写入业务结果,V4 smoke 必须创建 V4 order task / cards,不再创建旧 `workflow_reservation_task`;已能识别旧 S000/S999 和新结构化 S10/S99。 | | `GET /api/source-message-conversations/{externalConversationId}` | 未发现后端实现 | 不可以 | 历史讨论过的候选路径,当前不提供;前端统一使用 `GET /api/source-messages/{sourceMessageId}/conversation`。 | | `GET /api/reservation/message-notifications` | 未发现后端实现 | 不可以 | 历史候选路径,当前不提供。旧 S000/S999 和 V3 S10/S99 兼容数据通过 `SOURCE_MESSAGE_ONLY` 任务展示;V4 S10/S99 新数据走 V4 工作台和 `/api/reservation/source-notifications/{notificationId}`,不要再请求本候选路径。 | | `GET /api/reservation/task-card-field-whitelist` | 未发现后端实现 | 不可以 | 若任务详情 `fields[]` 已补齐 3.0 元数据,可后置。 | diff --git a/docs/project/go-live-notes.md b/docs/project/go-live-notes.md index f6d7a5b..a43c480 100644 --- a/docs/project/go-live-notes.md +++ b/docs/project/go-live-notes.md @@ -174,7 +174,7 @@ SourceMessage 原文和邮件会话完整正文已迁移到登录权限体系: | `DEERFLOW_DEV_BASE_URL` / `DEERFLOW_TEST_BASE_URL` / `DEERFLOW_PROD_BASE_URL` | 否 | SuperAgent / DeerFlow Open API 基础地址,未配置时可兜底 `DEERFLOW_BASE_URL`。 | | `DEERFLOW_DEV_OPEN_API_KEY` / `DEERFLOW_TEST_OPEN_API_KEY` / `DEERFLOW_PROD_OPEN_API_KEY` | 是 | SuperAgent Open API Key,未配置时可兜底 `DEERFLOW_OPEN_API_KEY`。 | | `SUPERAGENT_DEV_OPEN_API_ENABLED` / `SUPERAGENT_TEST_OPEN_API_ENABLED` / `SUPERAGENT_PROD_OPEN_API_ENABLED` | 否 | 是否启用真实 SuperAgent Open API 调用;prod 默认关闭。 | -| `SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID` | 否 | Debug EML 创建 SuperAgent session 的 external subject id。 | +| `SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID` | 否 | Debug EML 创建 SuperAgent session 的 external subject id;默认不配置时复用实时 AgentBus V4 subject。历史 V2/V3 Debug profile 只能显式配置用于旧链路排查,不得作为 V4 smoke 默认入口。 | | `SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID` | 否 | AgentBus 自动分发创建 SuperAgent session 的 external subject id。 | | `SUPERAGENT_DEBUG_EML_CONNECT_TIMEOUT` | 否 | SuperAgent Open API 建连超时,默认 `15s`。 | | `SUPERAGENT_DEBUG_EML_READ_TIMEOUT` | 否 | 旧版 RestClient 读取超时兼容变量;当前 JDK SSE 客户端不设置整段 SSE 固定读取超时,断流恢复由 run/events 机制处理。 | @@ -196,7 +196,7 @@ SourceMessage 原文和邮件会话完整正文已迁移到登录权限体系: - Debug EML 返回的 `agentbus_like_payload` 是 AgentBus Outlook-like 主输入,会包含普通 `reply_policy.mode=manual`、`reply_policy.final_only=true`;不再包含 `schema_version`、`source.provider=DEBUG_EML_UPLOAD`、`debug_context` 或旧的 Debug 专属 `reply_policy.mode=debug_only`。Debug 来源区分仍依赖 SourceMessage `provider=DEBUG_EML_UPLOAD` 和 payload 表 `schemaVersion=debug-eml-upload-v1`;拿到 AgentBus 真实 `reply_policy.mode` 枚举后需统一替换占位值。 - Debug EML 返回 `html_body_sanitized`、`html_sanitize_required`、`html_render_mode`,前端展示 HTML 时应优先使用清洗字段。 - Debug EML 当前会识别 SuperAgent Open API 返回的旧 `S000/S999,source_message_id`,并在 `superagent_parsed_json` 中返回结构化入口结果;这不是 JSON 解析失败。结构化 `S10/S99` 可通过任务结果通知接口入站,Debug EML 页面若要直接展示完整 V3 入口结构,前端展示仍需继续补齐。 -- Debug EML 第一版只展示 SuperAgent 结果,不创建订单、不创建任务、不调用任务结果通知接口。 +- Debug EML 服务自身只展示 SuperAgent Open API 结果,不直接创建订单、不直接创建任务、不调用任务结果通知接口;如 SuperAgent 后续通过正式回调 / MCP 写入业务结果,必须按当前 M002 V4 契约创建 V4 order task / cards,不再创建旧 `workflow_reservation_task`。 - AgentBus 实时收到邮件后自动推 SuperAgent 已由 M007 后端 V1 实现,不能把 Debug EML 链路等同于生产实时自动处理链路。 - Debug EML 和 AgentBus 自动分发复用同一个 SuperAgent Open API SSE 稳定客户端;上线前必须验证 `run.completed + end + final answer` 严格成功条件和 EOF 后 `/events` 恢复。 - 当前共享 Open API client 会自动携带临时 CSRF double-submit header / cookie;如果测试机仍返回 `CSRF token missing`,优先检查部署包版本和反向代理是否转发 `X-CSRF-Token`、`Cookie`。 diff --git a/docs/project/integrations/superagent-agentbus-project-integration-guide.md b/docs/project/integrations/superagent-agentbus-project-integration-guide.md index aae7be4..3012f6d 100644 --- a/docs/project/integrations/superagent-agentbus-project-integration-guide.md +++ b/docs/project/integrations/superagent-agentbus-project-integration-guide.md @@ -476,6 +476,7 @@ AgentBus 新业务 frame - `FAILED` SourceMessage 不进入 dispatch。 - dispatch 成功不代表已经创建订单或任务。 - 任务创建仍由 SuperAgent 后续调用本系统任务结果通知接口或 MCP 写入工具触发。 +- Debug EML 虽然不进入生产 dispatch,但 V4 smoke 默认复用实时 AgentBus V4 Open API subject:`th-hotel-agentbus-source-message`。历史 `th-hotel-debug-eml-upload` profile 如仍需排查旧 V2/V3 页面问题,必须通过环境变量显式指定,不能作为 M002 V4 smoke 默认 profile。 M011 CP3 已在 worker 调用 SuperAgent Open API 前增加可配置的 Booking Excel 附件预处理: diff --git a/docs/project/integrations/superagent-api-contract.md b/docs/project/integrations/superagent-api-contract.md index be908f3..4c4a65a 100644 --- a/docs/project/integrations/superagent-api-contract.md +++ b/docs/project/integrations/superagent-api-contract.md @@ -142,6 +142,8 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID,也不需要为 M011 已在 Debug EML 和 AgentBus 自动分发链路中接入 Booking Excel 附件预处理。该能力发生在 TH Hotel 后端调用 SuperAgent Open API 前,不属于 SuperAgent 调本系统的 `task-results` 请求体字段,但会影响 SuperAgent 实际看到的邮件 payload。 +Debug EML 是人工调试入口,不进入 AgentBus 生产 dispatch。M002 V4 smoke 下,Debug EML 默认使用实时 AgentBus V4 Open API subject,期望 SuperAgent 后续按本契约调用 `task-results` 或 MCP 写入 V4 业务结果;历史 Debug V2/V3 profile 只能显式配置用于旧链路排查,不得作为 V4 smoke 默认入口。 + 处理边界: - 后端只处理邮件附件中的 `.xls` / `.xlsx`,识别并排除 `PASSENGER_ROSTER` 人员名单类 Excel。 diff --git a/docs/project/operations/dev-test-v4-legacy-task-cleanup.md b/docs/project/operations/dev-test-v4-legacy-task-cleanup.md index 7a5ef65..ea598c8 100644 --- a/docs/project/operations/dev-test-v4-legacy-task-cleanup.md +++ b/docs/project/operations/dev-test-v4-legacy-task-cleanup.md @@ -123,7 +123,67 @@ COMMIT; 如果确认要清理所有开发 / 测试酒店的旧任务,可去掉上述 SQL 中所有 `hotel_id = @hotel_id` 条件。不要在生产环境执行这种全量清理。 -## 6. 执行后验证 +## 6. 单个 Debug EML 旧任务残留定点清理 + +当联调时已明确某个旧 `workflow_reservation_task` 是 V4 Debug EML 误走历史 V2/V3 profile 产生的残留,可按旧 task ID 定点清理。该脚本仍只适用于开发 / 测试环境,不清理 SourceMessage、V4 order task、V4 cards、来源通知、目录、账号权限或酒店配置。 + +执行前应先从当次排查记录中确认旧 `workflow_reservation_task.id`,只把该 ID 填入 `@legacy_task_id`。Debug run ID、SourceMessage ID 只作为人工核对线索,不写入清理条件,避免误清理来源消息或 V4 数据。 + +```sql +-- 必填:替换为当次排查确认的旧 workflow_reservation_task.id。 +-- 保持 NULL 时下方 SQL 不会命中任何旧任务。 +SET @legacy_task_id = NULL; + +SELECT id, + hotel_id, + source_message_id, + field_contract_version, + system_task_type, + route_code, + task_status +FROM workflow_reservation_task +WHERE id = @legacy_task_id; + +START TRANSACTION; + +DELETE attempt +FROM workflow_reservation_opera_operation_attempt attempt +WHERE attempt.task_id = @legacy_task_id; + +DELETE operation_row +FROM workflow_reservation_opera_operation operation_row +WHERE operation_row.task_id = @legacy_task_id; + +DELETE audit_row +FROM workflow_reservation_audit_log audit_row +WHERE audit_row.task_id = @legacy_task_id; + +UPDATE workflow_reservation_invoice_generation invoice_row +SET invoice_row.task_id = NULL, + invoice_row.updated_at = UTC_TIMESTAMP(6) +WHERE invoice_row.task_id = @legacy_task_id; + +DELETE card +FROM workflow_reservation_task_card card +WHERE card.task_id = @legacy_task_id; + +UPDATE workflow_reservation_order order_row +SET order_row.created_from_task_id = NULL, + order_row.updated_at = UTC_TIMESTAMP(6) +WHERE order_row.created_from_task_id = @legacy_task_id; + +DELETE task +FROM workflow_reservation_task task +WHERE task.id = @legacy_task_id; + +COMMIT; + +SELECT COUNT(*) AS remaining_legacy_task_count +FROM workflow_reservation_task +WHERE id = @legacy_task_id; +``` + +## 7. 执行后验证 单酒店清理时使用以下验证 SQL: diff --git a/docs/project/requirements/M004-debug-eml-superagent-upload-v1.md b/docs/project/requirements/M004-debug-eml-superagent-upload-v1.md index ec2c8d6..d5371db 100644 --- a/docs/project/requirements/M004-debug-eml-superagent-upload-v1.md +++ b/docs/project/requirements/M004-debug-eml-superagent-upload-v1.md @@ -17,13 +17,14 @@ 再组装成 SuperAgent 可处理的邮件输入并调用 SuperAgent Open API。 该能力是平台调试能力,不属于 `workflows.reservation` 主业务流。第一版必须写入 SourceMessage -Inbox 作为来源事实,但不创建订单、不创建任务、不写 AI 任务结果通知接口,也不执行 OPERA。 +Inbox 作为来源事实;Debug 服务自身不直接创建订单、不直接创建任务、不写 AI 任务结果通知接口,也不执行 OPERA。 +如果 SuperAgent 在 Debug 运行后通过正式回调 / MCP 写入业务结果,应按当前 M002 V4 主线落 V4 订单任务和任务卡。 ## 2. 已确认决策 - Debug 页面上传的邮件文件格式为 `.eml`。 -- 第一版通过 SuperAgent Open API 调用 SuperAgent,不走 SuperAgent 调用本系统的任务结果通知接口。 -- 第一版只展示 SuperAgent 生成结果,不落业务订单和任务。 +- 第一版通过 SuperAgent Open API 调用 SuperAgent;Debug EML 服务自身不调用本系统任务结果通知接口。 +- Debug EML 服务自身只展示 SuperAgent 生成结果,不直接落业务订单和任务;如果 SuperAgent 在运行过程中通过正式 `task-results` 或 MCP 写入业务结果,必须按当前 M002 V4 契约落库。 - 上传邮件仍要写入 SourceMessage Inbox。 - Debug 上传来源必须和 AgentBus 来源区分,建议 `provider=DEBUG_EML_UPLOAD`。 - AgentBus 实时收到邮件后自动推 SuperAgent 已由 M007 后端 V1 实现;M004 仍只代表人工 Debug 上传链路,不代表生产实时自动处理链路。 @@ -74,10 +75,10 @@ Inbox 作为来源事实,但不创建订单、不创建任务、不写 AI 任 第一版不做以下能力: -- 不创建 Reservation 订单。 -- 不创建 Reservation 任务。 -- 不写 `workflow_reservation_*` 表。 -- 不调用 `POST /api/integrations/superagent/task-results`。 +- Debug EML 服务自身不创建 Reservation 订单。 +- Debug EML 服务自身不创建 Reservation 任务。 +- Debug EML 服务自身不直接写 `workflow_reservation_*` 业务模型表;如 SuperAgent 通过正式回调 / MCP 写入,按当前 M002 V4 契约落库。 +- Debug EML 服务自身不调用 `POST /api/integrations/superagent/task-results`。 - 不做真实 OPERA / OHIP 接入。 - 不做邮件多封批量上传。 - 不做 ZIP、MSG、PDF、图片 OCR 或 Excel 解析。 @@ -120,7 +121,8 @@ Debug 页面上传 .eml - SourceMessage Inbox 仍然只表达来源事实,不表达 AI 结论、订单归属或任务状态。 - Debug 上传链路不能伪装成 AgentBus;必须在 `provider`、`schema_version` 或 debug run 中留下可追溯来源。 -- SuperAgent 返回内容第一版只作为调试展示,不进入 M002 订单任务主流程。 +- Debug 服务返回的 SuperAgent raw answer / parsed JSON 只作为调试展示;业务入站仍必须由 SuperAgent 后续调用正式 `task-results` 或 MCP 写入工具触发。 +- V4 smoke 默认使用实时 AgentBus V4 Open API subject,避免误走历史 Debug V2/V3 profile 并制造旧任务残留。 - M011 的 Excel 附件预处理只作为 SuperAgent 输入增强和 Debug 预览,不直接创建订单或任务;详细规则见 `docs/project/requirements/M011-booking-excel-pre-superagent-enrichment-v1.md`。 ## 6. 后端接口设计 @@ -288,7 +290,9 @@ POST /api/open/agent-sessions/{sessionId}/messages/stream - 后端使用 `DEERFLOW_BASE_URL` 和 `DEERFLOW_OPEN_API_KEY` 调用 SuperAgent。 - 状态变更请求使用 CSRF double-submit:`X-CSRF-Token` 和 `Cookie: csrf_token=`;当前共享 Open API client 已自动生成临时随机 token 并同时写入 header / cookie。 -- 创建 session 时使用 `SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID` 作为 `external_subject_id`。 +- 创建 session 时优先使用 `SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID` / 环境专属 `SUPERAGENT_{ENV}_DEBUG_EML_EXTERNAL_SUBJECT_ID` 覆盖值作为 `external_subject_id`。 +- 如未显式覆盖,Debug EML 默认复用实时 AgentBus V4 subject:`SUPERAGENT_{ENV}_AGENTBUS_EXTERNAL_SUBJECT_ID` / `SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID` / `th-hotel-agentbus-source-message`。V4 smoke 不应默认使用历史 `th-hotel-debug-eml-upload` profile。 +- 历史 V2/V3 Debug profile 如仍需排查旧页面问题,必须显式配置 `SUPERAGENT_{ENV}_DEBUG_EML_EXTERNAL_SUBJECT_ID=th-hotel-debug-eml-upload`,且不得用于 M002 V4 smoke。 - `idempotency_key` 使用 `debug_run_id` 派生,保证同一次 Debug 运行不会重复创建不可追溯请求。 - 发送消息时把 AgentBus Outlook-like payload 序列化为 JSON 文本,并附加中文指令,要求 SuperAgent 输出结构化 JSON 或 S10/S99 特殊结果。 - SSE 解析仍按项目现有经验,从后期 `values.messages[]` 中寻找 `type=ai` 且 `finish_reason=stop` 的最终回答。 @@ -456,7 +460,9 @@ ALIYUN_OSS_DEBUG_EML_PREFIX=debug/eml/ DEERFLOW_BASE_URL= DEERFLOW_OPEN_API_KEY= -SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID=th-hotel-debug-eml-upload +# 默认可不配置,Debug EML 会复用实时 AgentBus V4 subject。 +# 如需历史 V2/V3 Debug profile,才显式设置为 th-hotel-debug-eml-upload。 +# SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID=th-hotel-debug-eml-upload SUPERAGENT_DEBUG_EML_CONNECT_TIMEOUT=15s SUPERAGENT_DEBUG_EML_READ_TIMEOUT=180s ``` diff --git a/server/src/main/java/cn/nianxx/thhotel/integrations/ai/superagent/service/impl/SuperAgentOpenApiProperties.java b/server/src/main/java/cn/nianxx/thhotel/integrations/ai/superagent/service/impl/SuperAgentOpenApiProperties.java index cc0592a..2177a13 100644 --- a/server/src/main/java/cn/nianxx/thhotel/integrations/ai/superagent/service/impl/SuperAgentOpenApiProperties.java +++ b/server/src/main/java/cn/nianxx/thhotel/integrations/ai/superagent/service/impl/SuperAgentOpenApiProperties.java @@ -17,8 +17,8 @@ public class SuperAgentOpenApiProperties { private String baseUrl = ""; /** SuperAgent Open API Key。 */ private String apiKey = ""; - /** Debug EML 创建 session 时使用的 external_subject_id。 */ - private String externalSubjectId = "th-hotel-debug-eml-upload"; + /** Debug EML 创建 session 时使用的 external_subject_id;默认对齐实时 AgentBus V4 profile。 */ + private String externalSubjectId = "th-hotel-agentbus-source-message"; /** AgentBus 自动分发创建 session 时使用的 external_subject_id。 */ private String agentbusExternalSubjectId = "th-hotel-agentbus-source-message"; /** 建立连接超时。 */ diff --git a/server/src/main/java/cn/nianxx/thhotel/platform/debug/service/impl/DebugEmlSuperAgentRunServiceImpl.java b/server/src/main/java/cn/nianxx/thhotel/platform/debug/service/impl/DebugEmlSuperAgentRunServiceImpl.java index 05336f0..a196ba9 100644 --- a/server/src/main/java/cn/nianxx/thhotel/platform/debug/service/impl/DebugEmlSuperAgentRunServiceImpl.java +++ b/server/src/main/java/cn/nianxx/thhotel/platform/debug/service/impl/DebugEmlSuperAgentRunServiceImpl.java @@ -157,7 +157,7 @@ public class DebugEmlSuperAgentRunServiceImpl implements DebugEmlSuperAgentRunSe } /** - * 处理单封 Debug EML 上传;第一版只展示 SuperAgent 结果,不创建订单或任务。 + * 处理单封 Debug EML 上传;本服务只展示 SuperAgent 结果,业务模型写入仍由正式回调 / MCP 负责。 */ @Override public DebugEmlSuperAgentRunResult uploadAndRun( @@ -1098,7 +1098,7 @@ public class DebugEmlSuperAgentRunServiceImpl implements DebugEmlSuperAgentRunSe putIfNotNull(debugContext, "original_conversation_id", originalConversationId); Map metadata = new LinkedHashMap<>(); - metadata.put("source", "th-hotel-debug-eml-upload"); + metadata.put("source", "th-hotel-debug-eml-v4-upload"); putIfNotNull(metadata, "debug_run_id", runId == null ? null : runId.toString()); putIfNotNull(metadata, "source_message_id", sourceMessageId == null ? null : sourceMessageId.toString()); putIfNotNull(metadata, "hotel_id", hotelId); diff --git a/server/src/main/resources/application-dev.yml b/server/src/main/resources/application-dev.yml index 8772eb5..39e462f 100644 --- a/server/src/main/resources/application-dev.yml +++ b/server/src/main/resources/application-dev.yml @@ -47,7 +47,9 @@ superagent: enabled: ${SUPERAGENT_DEV_OPEN_API_ENABLED:${SUPERAGENT_OPEN_API_ENABLED:false}} base-url: ${DEERFLOW_DEV_BASE_URL:${DEERFLOW_BASE_URL:}} api-key: ${DEERFLOW_DEV_OPEN_API_KEY:${DEERFLOW_OPEN_API_KEY:}} - external-subject-id: ${SUPERAGENT_DEV_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID:th-hotel-debug-eml-upload}} + # Debug EML 默认复用实时 AgentBus V4 subject,避免 V4 smoke 误打到历史 Debug V2/V3 profile。 + # 如需调历史 profile,必须显式配置 SUPERAGENT_DEV_DEBUG_EML_EXTERNAL_SUBJECT_ID。 + external-subject-id: ${SUPERAGENT_DEV_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_DEV_AGENTBUS_EXTERNAL_SUBJECT_ID:${SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID:th-hotel-agentbus-source-message}}}} agentbus-external-subject-id: ${SUPERAGENT_DEV_AGENTBUS_EXTERNAL_SUBJECT_ID:${SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID:th-hotel-agentbus-source-message}} connect-timeout: ${SUPERAGENT_DEV_DEBUG_EML_CONNECT_TIMEOUT:${SUPERAGENT_DEBUG_EML_CONNECT_TIMEOUT:15s}} read-timeout: ${SUPERAGENT_DEV_DEBUG_EML_READ_TIMEOUT:${SUPERAGENT_DEBUG_EML_READ_TIMEOUT:180s}} diff --git a/server/src/main/resources/application-prod.yml b/server/src/main/resources/application-prod.yml index e412ab7..5317bcc 100644 --- a/server/src/main/resources/application-prod.yml +++ b/server/src/main/resources/application-prod.yml @@ -45,7 +45,8 @@ superagent: enabled: ${SUPERAGENT_PROD_OPEN_API_ENABLED:false} base-url: ${DEERFLOW_PROD_BASE_URL:${DEERFLOW_BASE_URL}} api-key: ${DEERFLOW_PROD_OPEN_API_KEY:${DEERFLOW_OPEN_API_KEY}} - external-subject-id: ${SUPERAGENT_PROD_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID:th-hotel-debug-eml-upload}} + # Debug EML 默认复用实时 AgentBus V4 subject;生产如需启用 Debug EML,必须额外确认权限、审计和 profile。 + external-subject-id: ${SUPERAGENT_PROD_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_PROD_AGENTBUS_EXTERNAL_SUBJECT_ID:${SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID:th-hotel-agentbus-source-message}}}} agentbus-external-subject-id: ${SUPERAGENT_PROD_AGENTBUS_EXTERNAL_SUBJECT_ID:${SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID:th-hotel-agentbus-source-message}} connect-timeout: ${SUPERAGENT_PROD_DEBUG_EML_CONNECT_TIMEOUT:${SUPERAGENT_DEBUG_EML_CONNECT_TIMEOUT:15s}} read-timeout: ${SUPERAGENT_PROD_DEBUG_EML_READ_TIMEOUT:${SUPERAGENT_DEBUG_EML_READ_TIMEOUT:180s}} diff --git a/server/src/main/resources/application-test.yml b/server/src/main/resources/application-test.yml index 841dcd2..d6575a6 100644 --- a/server/src/main/resources/application-test.yml +++ b/server/src/main/resources/application-test.yml @@ -49,7 +49,9 @@ superagent: enabled: ${SUPERAGENT_TEST_OPEN_API_ENABLED:${SUPERAGENT_OPEN_API_ENABLED:false}} base-url: ${DEERFLOW_TEST_BASE_URL:${DEERFLOW_BASE_URL:}} api-key: ${DEERFLOW_TEST_OPEN_API_KEY:${DEERFLOW_OPEN_API_KEY:}} - external-subject-id: ${SUPERAGENT_TEST_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID:th-hotel-debug-eml-upload}} + # Debug EML 默认复用实时 AgentBus V4 subject,避免 V4 smoke 误打到历史 Debug V2/V3 profile。 + # 如需调历史 profile,必须显式配置 SUPERAGENT_TEST_DEBUG_EML_EXTERNAL_SUBJECT_ID。 + external-subject-id: ${SUPERAGENT_TEST_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_DEBUG_EML_EXTERNAL_SUBJECT_ID:${SUPERAGENT_TEST_AGENTBUS_EXTERNAL_SUBJECT_ID:${SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID:th-hotel-agentbus-source-message}}}} agentbus-external-subject-id: ${SUPERAGENT_TEST_AGENTBUS_EXTERNAL_SUBJECT_ID:${SUPERAGENT_AGENTBUS_EXTERNAL_SUBJECT_ID:th-hotel-agentbus-source-message}} connect-timeout: ${SUPERAGENT_TEST_DEBUG_EML_CONNECT_TIMEOUT:${SUPERAGENT_DEBUG_EML_CONNECT_TIMEOUT:15s}} read-timeout: ${SUPERAGENT_TEST_DEBUG_EML_READ_TIMEOUT:${SUPERAGENT_DEBUG_EML_READ_TIMEOUT:180s}} diff --git a/server/src/test/java/cn/nianxx/thhotel/integrations/ai/superagent/service/impl/SuperAgentOpenApiClientImplTest.java b/server/src/test/java/cn/nianxx/thhotel/integrations/ai/superagent/service/impl/SuperAgentOpenApiClientImplTest.java index 14040aa..9f9ac62 100644 --- a/server/src/test/java/cn/nianxx/thhotel/integrations/ai/superagent/service/impl/SuperAgentOpenApiClientImplTest.java +++ b/server/src/test/java/cn/nianxx/thhotel/integrations/ai/superagent/service/impl/SuperAgentOpenApiClientImplTest.java @@ -42,11 +42,13 @@ class SuperAgentOpenApiClientImplTest { void shouldRequestStreamingMessagesWithIncludeTraceAndEmitPublicTraceEvents() throws Exception { HttpServer server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); AtomicReference streamQuery = new AtomicReference<>(); + AtomicReference sessionBody = new AtomicReference<>(); AtomicReference sessionCsrfToken = new AtomicReference<>(); AtomicReference sessionCookie = new AtomicReference<>(); AtomicReference streamCsrfToken = new AtomicReference<>(); AtomicReference streamCookie = new AtomicReference<>(); server.createContext("/api/open/agent-sessions", exchange -> { + sessionBody.set(new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8)); sessionCsrfToken.set(exchange.getRequestHeaders().getFirst("X-CSRF-Token")); sessionCookie.set(exchange.getRequestHeaders().getFirst("Cookie")); byte[] response = "{\"session_id\":\"session-http-001\"}".getBytes(StandardCharsets.UTF_8); @@ -91,7 +93,6 @@ class SuperAgentOpenApiClientImplTest { properties.setEnabled(true); properties.setBaseUrl("http://127.0.0.1:" + server.getAddress().getPort()); properties.setApiKey("df_open_test"); - properties.setExternalSubjectId("debug-subject"); properties.setConnectTimeout(Duration.ofSeconds(5)); properties.setReadTimeout(Duration.ofSeconds(5)); SuperAgentOpenApiClientImpl client = new SuperAgentOpenApiClientImpl( @@ -106,6 +107,7 @@ class SuperAgentOpenApiClientImplTest { Map.of("source", "unit-test")), traceEvents::add); assertThat(streamQuery.get()).isEqualTo("include_trace=true"); + assertThat(sessionBody.get()).contains("\"external_subject_id\":\"th-hotel-agentbus-source-message\""); assertThat(sessionCsrfToken.get()).isNotBlank(); assertThat(sessionCookie.get()).contains("csrf_token=" + sessionCsrfToken.get()); assertThat(streamCsrfToken.get()).isNotBlank(); diff --git a/server/src/test/java/cn/nianxx/thhotel/platform/debug/control/DebugEmlSuperAgentControllerTest.java b/server/src/test/java/cn/nianxx/thhotel/platform/debug/control/DebugEmlSuperAgentControllerTest.java index e1d7e68..d0a414c 100644 --- a/server/src/test/java/cn/nianxx/thhotel/platform/debug/control/DebugEmlSuperAgentControllerTest.java +++ b/server/src/test/java/cn/nianxx/thhotel/platform/debug/control/DebugEmlSuperAgentControllerTest.java @@ -22,6 +22,7 @@ import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentOpen import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentOpenApiTraceEvent; import cn.nianxx.thhotel.integrations.ai.superagent.service.SuperAgentOpenApiClient; import cn.nianxx.thhotel.integrations.ai.superagent.service.impl.SuperAgentOpenApiException; +import cn.nianxx.thhotel.integrations.ai.superagent.service.impl.SuperAgentOpenApiProperties; import cn.nianxx.thhotel.integrations.storage.aliyunoss.common.request.ObjectStoragePutRequest; import cn.nianxx.thhotel.integrations.storage.aliyunoss.common.result.ObjectStoragePutResult; import cn.nianxx.thhotel.integrations.storage.aliyunoss.service.ObjectStorageService; @@ -68,8 +69,7 @@ import org.springframework.web.client.RestClientResponseException; "debug.eml-upload.include-booking-excel-extractions=true", "aliyun.oss.debug-eml-prefix=debug/eml/", "reservation.booking-excel-extraction.enabled=true", - "superagent.open-api.enabled=true", - "superagent.open-api.external-subject-id=test-debug-eml" + "superagent.open-api.enabled=true" }) @AutoConfigureMockMvc @ActiveProfiles("test") @@ -87,6 +87,9 @@ class DebugEmlSuperAgentControllerTest { @Autowired private DebugEmlSuperAgentRunService runService; + @Autowired + private SuperAgentOpenApiProperties superAgentOpenApiProperties; + @Autowired private BookingExcelExtractionProperties bookingExcelExtractionProperties; @@ -96,6 +99,13 @@ class DebugEmlSuperAgentControllerTest { @MockBean private SuperAgentOpenApiClient superAgentOpenApiClient; + @Test + void shouldUseAgentBusV4SubjectByDefaultForDebugEmlSmoke() { + org.assertj.core.api.Assertions.assertThat(superAgentOpenApiProperties.getExternalSubjectId()) + .isEqualTo("th-hotel-agentbus-source-message") + .isEqualTo(superAgentOpenApiProperties.getAgentbusExternalSubjectId()); + } + @Test void shouldRejectUploadWhenDebugKeyMissing() throws Exception { mockMvc.perform(multipart(ENDPOINT) @@ -218,7 +228,7 @@ class DebugEmlSuperAgentControllerTest { .contains("\"mode\":\"manual\"") .contains("\"final_only\":true"); org.assertj.core.api.Assertions.assertThat(superAgentRequest.metadata()) - .containsEntry("source", "th-hotel-debug-eml-upload") + .containsEntry("source", "th-hotel-debug-eml-v4-upload") .containsKey("debug_context"); Long debugRunCount = jdbcTemplate.queryForObject(""" diff --git a/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/SuperAgentTaskResultControllerTest.java b/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/SuperAgentTaskResultControllerTest.java index 0358082..c94d750 100644 --- a/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/SuperAgentTaskResultControllerTest.java +++ b/server/src/test/java/cn/nianxx/thhotel/workflows/reservation/control/SuperAgentTaskResultControllerTest.java @@ -1117,6 +1117,44 @@ class SuperAgentTaskResultControllerTest { assertThat(replayCardCount).isEqualTo(4L); } + @Test + void shouldRouteDebugEmlV4BusinessResultToV4ModelWithoutLegacyTask() throws Exception { + SourceMessageCaptureResult source = captureDebugEmlSourceMessage("debug-eml-run-v4-business-001"); + String body = v4BusinessRootBody("debug-eml-run-v4-business-001"); + + mockMvc.perform(signedPost(body, "nonce-debug-eml-v4-business-001")) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.source_message_id").value("debug-eml-run-v4-business-001")) + .andExpect(jsonPath("$.accepted_count").value(2)) + .andExpect(jsonPath("$.items[0].task_id").doesNotExist()) + .andExpect(jsonPath("$.items[1].task_id").doesNotExist()); + + Long legacyTaskCount = jdbcTemplate.queryForObject(""" + SELECT COUNT(*) + FROM workflow_reservation_task + WHERE source_message_id = ? + """, Long.class, source.inboxId()); + Long v4OrderTaskCount = jdbcTemplate.queryForObject(""" + SELECT COUNT(*) + FROM workflow_reservation_v4_order_task + WHERE source_message_id = ? + AND order_ref = 'order-1' + """, Long.class, source.inboxId()); + List v4CardTypes = jdbcTemplate.queryForList(""" + SELECT card_type + FROM workflow_reservation_v4_task_card + WHERE source_message_id = ? + ORDER BY card_sort_order, source_event_index + """, String.class, source.inboxId()); + assertThat(legacyTaskCount).isZero(); + assertThat(v4OrderTaskCount).isEqualTo(1L); + assertThat(v4CardTypes).containsExactly( + "SOURCE_MESSAGE_DISPLAY", + "BASIC_INFORMATION", + "ROOM_INFORMATION", + "PAYMENT"); + } + @Test void shouldCreateV4OrderTasksForMultipleOrderRefs() throws Exception { SourceMessageCaptureResult source = captureSourceMessage("mail-v4-two-order-refs-001"); @@ -3441,13 +3479,26 @@ class SuperAgentTaskResultControllerTest { } private SourceMessageCaptureResult captureSourceMessage(String externalMessageId, String hotelId, String channel) { + return captureSourceMessage(externalMessageId, hotelId, channel, "AGENTBUS", "agentbus-outlook-v1"); + } + + private SourceMessageCaptureResult captureDebugEmlSourceMessage(String externalMessageId) { + return captureSourceMessage(externalMessageId, "HOTEL-TEST", "EMAIL", "DEBUG_EML_UPLOAD", "debug-eml-upload-v1"); + } + + private SourceMessageCaptureResult captureSourceMessage( + String externalMessageId, + String hotelId, + String channel, + String provider, + String schemaVersion) { String providerFrameId = "frame-" + externalMessageId; if (providerFrameId.length() > 128) { providerFrameId = "frame-" + Integer.toHexString(externalMessageId.hashCode()); } return captureService.capture(new CaptureSourceMessageCommand( hotelId, - "AGENTBUS", + provider, channel, externalMessageId, "thread-" + externalMessageId, @@ -3459,7 +3510,7 @@ class SuperAgentTaskResultControllerTest { "Please handle booking message.", "Please handle booking message.", "{\"source\":{\"external_message_id\":\"" + externalMessageId + "\"}}", - "agentbus-outlook-v1", + schemaVersion, List.of() )); }