# Debug EML 页面前端对接指南

## 1. 文档定位

本文给前端 Debug 页面开发使用，说明 `.eml` 邮件上传到 SuperAgent 调试链路应该做什么、调用哪个接口、如何组织请求、如何展示返回结果，以及哪些安全边界不能越过。

相关背景文档：

- `docs/project/requirements/M004-debug-eml-superagent-upload-v1.md`
- `docs/project/frontend-backend/backend-to-frontend-notes.md`

## 2. 页面目标

Debug EML 页面第一版只做一件事：

上传单封 `.eml` 邮件，后端解析邮件、上传原始邮件 / 内联图片 / 附件到本系统阿里云 OSS，替换 HTML 中的 `cid:` 图片，写入 SourceMessage Inbox，然后调用 SuperAgent Open API，并把 SuperAgent 返回结果展示给调试人员。

第一版明确不做：

- 不创建 Reservation 订单。
- 不创建 Reservation 任务。
- 不调用 SuperAgent 任务结果通知接口。
- 不触发 AgentBus 实时链路。
- 不做批量上传。
- 不提供 Debug run 历史列表或详情查询。
- 不在生产普通业务页面开放。

## 3. 页面建议结构

建议页面分为以下区域：

| 区域 | 展示 / 操作 | 说明 |
| --- | --- | --- |
| 上传配置区 | 可选酒店覆盖、Debug 上传口令、`run_label`、`.eml` 文件选择、提交按钮 | Debug 上传口令只能由调试人员临时输入，不能写进前端源码、环境变量、localStorage 或 URL；单酒店阶段默认不需要手填酒店。 |
| 执行状态区 | loading、成功、失败、耗时、本次 `debug_run_id` | 提交后禁用按钮，避免重复点击；失败时展示安全错误摘要。 |
| SourceMessage 追溯区 | `source_message_id`、`source_provider`、`external_message_id`、`external_conversation_id` | 用于确认已写入 SourceMessage Inbox。 |
| 邮件内容预览区 | `html_body_sanitized`、纯文本、附件列表、内联图片列表 | HTML 展示必须优先使用 `html_body_sanitized`。 |
| SuperAgent 结果区 | `superagent_parsed_json`、`superagent_raw_answer`、`warnings[]` | JSON 可以格式化展示；旧 S000/S999 和新 S10/S99 入口通知都不应被前端当成普通解析失败；raw answer 用于排查其他非 JSON 输出。 |
| 调试 Payload 区 | `agentbus_like_payload` | 只用于调试展示，不让用户编辑后重新提交。 |

## 4. 接口

```text
POST /api/system/debug/eml-superagent-runs
Content-Type: multipart/form-data
Accept: application/json
Header: X-TH-Hotel-Debug-Upload-Key: <调试上传口令>
```

注意：

- 该接口只有在后端 `debug.eml-upload.enabled=true` 时存在；如果后端未开启，前端可能收到 `404`。
- 请求必须使用 `FormData`。
- 前端不要手动设置 `Content-Type`，让浏览器自动生成 multipart boundary。
- `X-TH-Hotel-Debug-Upload-Key` 不能进入 `VITE_*`、源码、构建产物、localStorage、URL query、错误上报或普通日志。

请求参数：

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `file` | File | 是 | `.eml` 邮件文件。前端文件选择器建议 `accept=".eml,message/rfc822"`。 |
| `hotel_id` | string | 否 | 可选酒店上下文覆盖；缺省由后端按当前登录用户上下文或平台酒店表唯一 `ACTIVE` 酒店解析。 |
| `run_label` | string | 否 | 调试标签，例如 `frontend-debug-smoke`，方便后端日志和数据库排查。 |

前端调用示例：

```ts
export async function uploadDebugEml(input: {
  baseUrl: string
  hotelId?: string | null
  debugUploadKey: string
  file: File
  runLabel?: string
}) {
  const form = new FormData()
  form.append('file', input.file)
  if (input.hotelId?.trim()) {
    form.append('hotel_id', input.hotelId.trim())
  }
  if (input.runLabel?.trim()) {
    form.append('run_label', input.runLabel.trim())
  }

  const response = await fetch(`${input.baseUrl}/api/system/debug/eml-superagent-runs`, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'X-TH-Hotel-Debug-Upload-Key': input.debugUploadKey,
    },
    body: form,
  })

  const data = await response.json()
  if (!response.ok) {
    throw data
  }
  return data
}
```

## 5. 成功响应

成功状态码：`201 Created`

核心字段：

| 字段 | 类型 | 前端用途 |
| --- | --- | --- |
| `debug_run_id` | string | 展示本次调试运行 ID。 |
| `source_message_id` | string | 展示 SourceMessage 内部 ID；可用于后续排查。 |
| `source_provider` | string | 固定为 `DEBUG_EML_UPLOAD`。 |
| `external_message_id` | string | Debug 独立外部消息 ID，格式类似 `debug-eml-run-{debugRunId}-{sha256前缀}`。 |
| `external_conversation_id` | string | 邮件会话 ID，可能来自 `References` / `In-Reply-To` / `Thread-Index`。 |
| `original_eml_oss_url` | string | 原始 `.eml` OSS URL；只在受控调试页面展示。 |
| `original_eml_sha256` | string | 原始 `.eml` SHA-256。 |
| `uploaded_media[]` | array | 原始邮件、内联图片和附件列表。 |
| `html_body_with_oss_urls` | string | 替换 `cid:` 后的 HTML，作为调试原始处理结果展示，不建议直接渲染。 |
| `html_body_sanitized` | string | 后端清洗后的 HTML，前端预览邮件正文时优先使用。 |
| `html_sanitize_required` | boolean | 当前为 `true`，提醒前端不要直接信任原始 HTML。 |
| `html_render_mode` | string | 当前可能为 `SANITIZED_HTML` 或 `TEXT_ONLY`。 |
| `agentbus_like_payload` | object | 后端发送给 SuperAgent 的结构化 payload。 |
| `superagent_session_id` | string | SuperAgent session ID。 |
| `superagent_run_id` | string | SuperAgent run ID。 |
| `superagent_raw_answer` | string | SuperAgent 最终原始文本回答。 |
| `superagent_parsed_json` | object/null | 后端尝试解析出的 JSON；旧 S000/S999 会返回识别后的对象，0711 P0 后续结构化 S10/S99 会直接作为 JSON 展示，其他解析失败时可能为空。 |
| `warnings[]` | string[] | 安全或解析警告，可在页面顶部或结果区展示。 |
| `status` | string | Debug run 状态。 |

`uploaded_media[]` 字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `media_type` | string | `ORIGINAL_EMAIL`、`INLINE_IMAGE`、`ATTACHMENT`。 |
| `file_name` | string | 文件名。 |
| `content_type` | string | MIME 类型。 |
| `size_bytes` | number | 文件大小。 |
| `external_url` | string | OSS URL。 |
| `external_media_id` | string | 调试链路媒体 ID，例如 `cid:inline-001`。 |
| `object_key` | string | OSS 对象路径。 |

关键展示规则：

- 邮件正文 iframe / 富文本预览优先使用 `html_body_sanitized`。
- `html_body_with_oss_urls` 可以放在“原始处理 HTML”折叠面板中，不作为默认渲染内容。
- `agentbus_like_payload.source.original_message_id` 是原始邮件 `Message-ID`。
- `external_message_id` 是 Debug 链路生成的独立 ID，不等同于原始 `Message-ID`。
- `superagent_parsed_json` 有值时优先展示格式化 JSON；没有值时展示 `superagent_raw_answer`。
- 如果 SuperAgent 返回旧 `S000,source_message_id` 或 `S999,source_message_id`，后端会把它识别为特殊入口结果，不会作为 JSON 解析失败处理。前端可在结果区展示 `entry_result_code`、`entry_result_source_message_id`、`entry_result_meaning` 和 `entry_result_description`。
- 如果 SuperAgent 返回 0711 P0 新结构化 `S10/S99`，前端应按 JSON 展示 `route_code`、`result_type=source_message_review_notification`、`agent_assessment`、`notification` 和 S99 的入口 `manual_review`。

旧 S000/S999 解析示例：

```json
{
  "entry_result_code": "S000",
  "entry_result_source_message_id": "mail-20260708-0001",
  "entry_result_meaning": "PURE_INFORMATION",
  "entry_result_description": "纯信息类邮件"
}
```

V3 S10 结构化示例：

```json
{
  "route_code": "S10",
  "result_type": "source_message_review_notification",
  "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
}
```

## 6. 错误响应

错误响应结构：

```json
{
  "error_code": "DEBUG_UPLOAD_KEY_INVALID",
  "message": "Debug 上传访问口令缺失或错误。"
}
```

常见错误：

| HTTP 状态 | `error_code` | 前端建议 |
| --- | --- | --- |
| `400` | `REQUEST_FIELD_REQUIRED` | 提示缺少必填字段，检查文件或请求参数。 |
| `400` | `EML_FILE_REQUIRED` | 提示请选择 `.eml` 文件。 |
| `400` | `EML_FILE_TOO_LARGE` | 提示文件超过后端限制；当前默认上限通常为 10MB，以环境配置为准。 |
| `400` | `INVALID_FILE_TYPE` | 提示只支持 `.eml`。 |
| `401` | `DEBUG_UPLOAD_KEY_INVALID` | 提示调试上传口令缺失或错误。 |
| `404` | 无固定结构 | 后端未开启 Debug EML 接口或路径错误。 |
| `422` | `EML_PARSE_FAILED` | 提示邮件文件无法解析。 |
| `502` | `OSS_UPLOAD_FAILED` | 提示 OSS 上传失败，调试人员联系后端排查。 |
| `502` | `SUPERAGENT_OPEN_API_FAILED` | 提示 SuperAgent 调用失败；SourceMessage 可能已经写入。 |
| `500` | `DEBUG_EML_RUN_FAILED` | 提示 Debug 链路内部失败。 |

错误展示原则：

- 页面只展示 `message` 和 `error_code`。
- 不展示请求 header、上传口令、完整邮件正文、完整 HTML、OSS 签名 URL 或 Provider 原始错误。
- `SUPERAGENT_OPEN_API_FAILED` 时，不要把页面理解为“邮件上传没有发生”；后端可能已经完成 OSS 上传和 SourceMessage 写入。

## 7. 前端状态流转

建议页面状态：

```text
idle
→ validating
→ uploading
→ waiting_superagent
→ succeeded
或
→ failed
```

实现建议：

- 文件未选择或 Debug 上传口令为空时禁用提交按钮；`hotel_id` 为空是允许的，表示使用后端系统酒店。
- 提交后禁用文件选择和提交按钮，避免重复上传。
- SuperAgent 调用可能耗时较长，页面 loading 文案不要只写“上传中”，建议写“正在解析邮件并等待 SuperAgent 返回”。
- 成功后保留本次响应在页面内存中；当前没有 Debug run 查询接口，刷新页面后需要重新上传。
- 再次上传同一封 `.eml` 会生成新的 `external_message_id` 和新的 SourceMessage，前端不要按原始 `Message-ID` 去重。

## 8. 安全与日志

前端必须遵守：

- 不把 Debug 上传口令写入源码、`.env`、`VITE_*`、localStorage、sessionStorage、URL query、错误上报或普通日志。
- 不把 `uploaded_media[].external_url`、`original_eml_oss_url`、邮件 HTML、附件 URL 写入普通日志或埋点。
- 不在生产普通业务菜单暴露该页面。
- 不允许前端直接调用 SuperAgent、AgentBus、OSS、数据库或 OPERA。
- 不使用 Debug EML 页面结果改变订单、任务或 OPERA 状态。

## 9. 联调前置条件

后端环境需要具备：

- `DEBUG_EML_UPLOAD_*_ENABLED=true`。
- `DEBUG_EML_UPLOAD_*_ACCESS_KEY` 已配置。
- 阿里云 OSS endpoint、bucket、access key、public base URL 已配置。
- `SUPERAGENT_*_OPEN_API_ENABLED=true`。
- SuperAgent / DeerFlow Open API base URL 和 API key 已配置。

前端需要具备：

- 可配置后端 `BASE_URL`。
- 可配置或输入可选 `hotel_id`；单酒店联调默认可留空。
- 调试人员临时输入 Debug 上传口令。
- 准备一封 `.eml` 样例邮件。

## 10. 与其他页面的关系

- Debug EML 页面是调试工具，不是 Message Notification 页面。
- Debug EML 页面写入的 SourceMessage `provider=DEBUG_EML_UPLOAD`，用于和 AgentBus 来源区分。
- Debug EML 页面第一版不创建订单和任务，所以上传成功后任务列表和订单列表不会因为这次上传自动新增业务数据。
- AgentBus 实时收到邮件后自动推 SuperAgent 当前还没做，不能用 Debug EML 页面代表生产实时链路。

## 11. 当前后置事项

- Debug run 历史列表 / 详情查询接口未做。
- Debug run 取消或超时轮询接口未做。
- 批量上传未做。
- 前端白名单元数据独立接口未做。
- 用户身份 / 权限体系未接入；当前依赖调试上传口令保护。
