# TH Hotel 上线注意事项

本文给产品、研发、测试、运维和后续协作 agent 使用，目标是把上线前必须确认的事项放在同一个地方。这里记录的是当前项目专属要求，不作为可整份复制到其他项目的通用模板。

## 1. 当前上线范围

当前后端已经具备以下能力：

- `GET /api/health`：后端健康检查。
- `GET /api/source-messages`：查询 SourceMessage Inbox 安全摘要。
- `GET /api/source-messages/{id}`：查询单条 SourceMessage 安全摘要。
- `GET /api/source-messages/{id}/original`：受控读取邮件原文、HTML 和媒体 URL，并记录访问审计。
- `GET /api/system/agentbus-probe`：查看 AgentBus WebSocket 连接状态和安全计数器。
- AgentBus WebSocket 入站链路：默认关闭，开启后只把业务 frame 写入 SourceMessage Inbox。
- SuperAgent 任务结果接收接口：接收一个外部 `source_message_id` 下的 AI 任务结果，反查 SourceMessage Inbox 后写入 AI 过渡层、订单、任务和任务卡。
- Reservation 任务详情接口：返回任务字段、队列可处理状态和 OPERA 模拟操作摘要。
- Reservation 任务草稿保存和最终确认接口：按任务卡矩阵做第一版后端校验，确认后生成 `confirmed_payload_json`。
- Reservation OPERA 模拟骨架：已确认任务固定生成两条模拟操作，支持执行、失败重试、attempt 记录和任务审计列表。
- SuperAgent 查询上下文接口 1、2：支持 HMAC 鉴权的订单上下文查询和对象详情查询。
- Debug EML 上传到 SuperAgent 调试链路：受控上传 `.eml`、转存阿里云 OSS、写入 SourceMessage Inbox、调用 SuperAgent Open API 并返回调试结果。
- 登录权限底座：支持用户名密码登录、登出、当前用户上下文、数据库 session token、可访问酒店、权限码和可见菜单。

当前不要把以下能力当作已上线：

- SourceMessage Replay 到 MessageEvent / Evidence。
- 真实 AI 识别服务实现、Case 完整模型、Operation、Receipt。
- 自动 ACK、`task.result` 或客户回复。
- 业务前端页面展示邮件原文。
- OHIP / OPERA 或其他业务系统真实写操作。
- 普通任务切换订单接口。
- 用户、角色、权限、菜单和酒店管理后台 CRUD。
- 现有业务接口强制登录和强制权限拦截。
- 业务审计 actor 全量迁移到当前登录用户。
- Debug EML 上传链路不属于生产普通业务页面能力，生产默认关闭；未接入正式用户权限前不要开放给普通用户。

## 2. 上线前必须确认

上线前至少确认以下事项：

- 当前分支、提交和部署包来源清楚，不能混入本地临时文件、真实 Secret、真实客户邮件样本或构建产物。
- `server` 后端通过完整检查：`cd server && ./mvnw verify`。
- 生产或 UAT 数据库已经备份，并确认 Flyway migration 只新增不修改历史脚本。
- 所有 Secret 都通过环境变量、部署平台 Secret 或密钥管理系统注入，不写入仓库、镜像、前端环境变量或普通配置文件。
- 生产默认不保存 AgentBus raw frame 样本。
- AgentBus 实时链路开启前，已经确认 WebSocket URL、Token、Bot Address、外部消息幂等键和断线重连语义。
- 原文读取接口开启前，已经确认谁可以使用、在哪些场景使用、如何轮换访问 key。
- 日志采集、错误响应和监控面板都不会展示邮件正文、HTML、附件 URL、Token、Cookie、客户姓名、邮箱、电话或支付信息。

## 3. 环境变量

### 3.1 数据库

| 变量 | 是否 Secret | 上线注意事项 |
| --- | --- | --- |
| `TH_HOTEL_DB_URL` | 否 | 指向目标环境数据库。URL 包含 `&` 时要加引号。 |
| `TH_HOTEL_DB_USERNAME` | 是 | 使用最小权限账号，不使用个人账号。 |
| `TH_HOTEL_DB_PASSWORD` | 是 | 只能通过 Secret 注入，不写入仓库。 |
| `TH_HOTEL_DB_DRIVER` | 否 | MySQL 使用 `com.mysql.cj.jdbc.Driver`。 |

### 3.2 登录权限

| 变量 | 是否 Secret | 上线注意事项 |
| --- | --- | --- |
| `AUTH_DEV_BOOTSTRAP_ADMIN_USERNAME` / `AUTH_TEST_BOOTSTRAP_ADMIN_USERNAME` / `AUTH_PROD_BOOTSTRAP_ADMIN_USERNAME` | 否 | 初始超级管理员用户名；系统已有超级管理员后不会覆盖。 |
| `AUTH_DEV_BOOTSTRAP_ADMIN_PASSWORD` / `AUTH_TEST_BOOTSTRAP_ADMIN_PASSWORD` / `AUTH_PROD_BOOTSTRAP_ADMIN_PASSWORD` | 是 | 初始超级管理员密码，只能通过 Secret 注入；系统已有超级管理员后不会覆盖。 |
| `AUTH_DEV_BOOTSTRAP_ADMIN_DISPLAY_NAME` / `AUTH_TEST_BOOTSTRAP_ADMIN_DISPLAY_NAME` / `AUTH_PROD_BOOTSTRAP_ADMIN_DISPLAY_NAME` | 否 | 初始超级管理员展示名。 |
| `AUTH_DEV_BOOTSTRAP_DEFAULT_HOTEL_ID` / `AUTH_TEST_BOOTSTRAP_DEFAULT_HOTEL_ID` / `AUTH_PROD_BOOTSTRAP_DEFAULT_HOTEL_ID` | 否 | 登录权限底座初始化默认酒店；prod 必须配置真实酒店 ID。 |
| `AUTH_DEV_BOOTSTRAP_DEFAULT_HOTEL_NAME` / `AUTH_TEST_BOOTSTRAP_DEFAULT_HOTEL_NAME` / `AUTH_PROD_BOOTSTRAP_DEFAULT_HOTEL_NAME` | 否 | 默认酒店展示名称。 |
| `AUTH_DEV_BOOTSTRAP_DEFAULT_HOTEL_TIME_ZONE` / `AUTH_TEST_BOOTSTRAP_DEFAULT_HOTEL_TIME_ZONE` / `AUTH_PROD_BOOTSTRAP_DEFAULT_HOTEL_TIME_ZONE` | 否 | 默认酒店本地时区，默认 `Asia/Bangkok`。 |
| `AUTH_DEV_SESSION_TTL_MINUTES` / `AUTH_TEST_SESSION_TTL_MINUTES` / `AUTH_PROD_SESSION_TTL_MINUTES` | 否 | 数据库 session token 有效分钟数，默认 `720`。 |
| `AUTH_BOOTSTRAP_ADMIN_USERNAME` / `AUTH_BOOTSTRAP_ADMIN_PASSWORD` / `AUTH_SESSION_TTL_MINUTES` 等通用变量 | 视具体变量而定 | 兼容兜底变量；新环境优先使用环境专属变量。 |

注意：

- 数据库只保存 `token_hash`，不保存明文 `access_token`。
- `access_token` 只在登录成功响应中返回一次；前端只能放 `sessionStorage`，不能放 `localStorage`、URL、日志或错误上报。
- 当前第一版只做可选 Bearer token 解析，现有 Reservation / SourceMessage 业务接口仍不强制登录。
- `/api/auth/me` 和 `/api/auth/logout` 需要 `Authorization: Bearer <access_token>`。
- 管理后台还未上线时，不要把数据库手工改用户、角色、权限作为常规运营手段。

### 3.3 SourceMessage

| 变量 | 是否 Secret | 上线注意事项 |
| --- | --- | --- |
| `SOURCE_MESSAGE_DEV_ORIGINAL_READ_ACCESS_KEY` | 是 | dev 原文读取临时访问 key。未配置时可兜底使用旧通用变量。 |
| `SOURCE_MESSAGE_TEST_ORIGINAL_READ_ACCESS_KEY` | 是 | test 原文读取临时访问 key。未配置时可兜底使用旧通用变量。 |
| `SOURCE_MESSAGE_PROD_ORIGINAL_READ_ACCESS_KEY` | 是 | prod 原文读取临时访问 key。未配置时原文读取默认关闭。 |
| `SOURCE_MESSAGE_ORIGINAL_READ_ACCESS_KEY` | 是 | 旧通用原文读取 key，仅作为兼容兜底。 |

注意：

- 原文读取 key 不是用户体系，后续接入正式登录和角色权限后应替换。
- 任何能读取原文的调用都必须有调用方和访问场景，并写入审计表。

### 3.4 AgentBus

| 变量 | 是否 Secret | 上线注意事项 |
| --- | --- | --- |
| `AGENTBUS_PROBE_ENABLED` | 否 | 是否启用 WebSocket 长连接。生产首次上线建议先保持 `false`，完成连通性窗口后再打开。 |
| `AGENTBUS_WS_URL` | 否 | AgentBus WebSocket 地址。 |
| `AGENTBUS_WS_TOKEN` | 是 | WebSocket 鉴权 Token，只能通过 Secret 注入。 |
| `AGENTBUS_WS_RECONNECT_DELAY` | 否 | 断线重连间隔，默认 `5s`。 |
| `AGENTBUS_CONNECT_TIMEOUT` | 否 | 连接超时，默认 `15s`。 |
| `AGENTBUS_MAX_FRAME_BYTES` | 否 | 单个入站 frame 最大字节数，默认 `1048576`。 |
| `AGENTBUS_CAPTURE_ENABLED` | 否 | 是否把业务 frame 写入 SourceMessage Inbox。 |
| `AGENTBUS_DEFAULT_HOTEL_ID` | 否 | AgentBus 未提供酒店上下文时的默认业务上下文。 |

注意：

- `AGENTBUS_PROBE_ENABLED=true` 只表示启用连接和入站接收，不代表可以回复客户。
- `AGENTBUS_CAPTURE_ENABLED=false` 时，业务 frame 会被忽略，不会写入 SourceMessage Inbox。
- 当前实现不发送 ACK、不发送 `task.result`、不自动回复客户。

### 3.5 SuperAgent HMAC

| 变量 | 是否 Secret | 上线注意事项 |
| --- | --- | --- |
| `SUPERAGENT_DEV_TASK_RESULT_HMAC_SECRET` | 是 | dev SuperAgent 查询接口 1、2 和任务结果通知接口共用的 HMAC secret。 |
| `SUPERAGENT_TEST_TASK_RESULT_HMAC_SECRET` | 是 | test SuperAgent 查询接口 1、2 和任务结果通知接口共用的 HMAC secret。 |
| `SUPERAGENT_PROD_TASK_RESULT_HMAC_SECRET` | 是 | prod SuperAgent 查询接口 1、2 和任务结果通知接口共用的 HMAC secret。生产不能为空，只能通过 Secret 注入。 |
| `SUPERAGENT_TASK_RESULT_HMAC_SECRET` | 是 | 旧通用 HMAC 变量，仅作为兼容兜底；新环境优先配置环境专属变量。 |
| `SUPERAGENT_TASK_RESULT_CLOCK_SKEW_SECONDS` | 否 | 请求时间允许偏移，默认 `300` 秒。上线前确认本系统、SuperAgent 和 AgentBus 所在机器时间已通过 NTP 同步。 |
| `SUPERAGENT_TASK_RESULT_NONCE_TTL_SECONDS` | 否 | nonce 防重放窗口，默认 `600` 秒。 |
| `SUPERAGENT_TASK_RESULT_MAX_BODY_BYTES` | 否 | SuperAgent 入站请求体最大字节数，默认 `1048576`。 |

注意：

- 查询接口和任务结果通知接口使用同一套 Header、签名串、secret、timestamp 和 nonce 规则。
- SuperAgent 侧也需要配置同一个 secret，并按原始请求体计算 SHA-256。
- 当前第一版只支持一个 HMAC secret，secret 轮换需要协调部署窗口。
- 任务结果通知接口里的 `source_message_id` 是外部来源消息 ID，对应 AgentBus `source.external_message_id`；正式请求必须带 `hotel_id`，后端用 `hotel_id + provider + channel + external_message_id` 反查内部 SourceMessage Inbox。
- SuperAgent 查询上下文接口中的 `source_message_id`、`source_event_index` 第一版仅兼容接收，不参与查询和校验；不要依赖它们限制查询范围。

### 3.6 Debug EML / SuperAgent Open API / 阿里云 OSS

| 变量 | 是否 Secret | 上线注意事项 |
| --- | --- | --- |
| `DEBUG_EML_UPLOAD_DEV_ENABLED` | 否 | dev 是否启用 Debug EML 上传接口，未配置时可兜底 `DEBUG_EML_UPLOAD_ENABLED`。 |
| `DEBUG_EML_UPLOAD_TEST_ENABLED` | 否 | test 是否启用 Debug EML 上传接口，默认关闭。 |
| `DEBUG_EML_UPLOAD_PROD_ENABLED` | 否 | prod 默认必须保持 `false`；未接入正式用户权限前不要开放。 |
| `DEBUG_EML_UPLOAD_DEV_ACCESS_KEY` | 是 | dev Debug EML 上传访问口令，未配置时可兜底 `DEBUG_EML_UPLOAD_ACCESS_KEY`。 |
| `DEBUG_EML_UPLOAD_TEST_ACCESS_KEY` | 是 | test Debug EML 上传访问口令，未配置时可兜底 `DEBUG_EML_UPLOAD_ACCESS_KEY`。 |
| `DEBUG_EML_UPLOAD_PROD_ACCESS_KEY` | 是 | prod Debug EML 上传访问口令；生产通常不应启用该接口。 |
| `DEBUG_EML_UPLOAD_MAX_FILE_BYTES` | 否 | `.eml` 上传大小上限，默认 `10485760`。 |
| `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_CONNECT_TIMEOUT` | 否 | SuperAgent Open API 建连超时，默认 `15s`。 |
| `SUPERAGENT_DEBUG_EML_READ_TIMEOUT` | 否 | SuperAgent SSE 读取超时，默认 `180s`。 |
| `ALIYUN_OSS_DEV_ENDPOINT` / `ALIYUN_OSS_TEST_ENDPOINT` / `ALIYUN_OSS_PROD_ENDPOINT` | 否 | 阿里云 OSS Endpoint，未配置时可兜底 `ALIYUN_OSS_ENDPOINT`。 |
| `ALIYUN_OSS_DEV_BUCKET` / `ALIYUN_OSS_TEST_BUCKET` / `ALIYUN_OSS_PROD_BUCKET` | 否 | 阿里云 OSS Bucket，未配置时可兜底 `ALIYUN_OSS_BUCKET`。 |
| `ALIYUN_OSS_DEV_ACCESS_KEY_ID` / `ALIYUN_OSS_TEST_ACCESS_KEY_ID` / `ALIYUN_OSS_PROD_ACCESS_KEY_ID` | 是 | 阿里云 OSS AccessKey ID，未配置时可兜底 `ALIYUN_OSS_ACCESS_KEY_ID`。 |
| `ALIYUN_OSS_DEV_ACCESS_KEY_SECRET` / `ALIYUN_OSS_TEST_ACCESS_KEY_SECRET` / `ALIYUN_OSS_PROD_ACCESS_KEY_SECRET` | 是 | 阿里云 OSS AccessKey Secret，未配置时可兜底 `ALIYUN_OSS_ACCESS_KEY_SECRET`。 |
| `ALIYUN_OSS_DEV_PUBLIC_BASE_URL` / `ALIYUN_OSS_TEST_PUBLIC_BASE_URL` / `ALIYUN_OSS_PROD_PUBLIC_BASE_URL` | 否 | OSS 对外访问基础 URL，未配置时可兜底 `ALIYUN_OSS_PUBLIC_BASE_URL`。 |
| `ALIYUN_OSS_DEBUG_EML_PREFIX` | 否 | Debug EML 上传对象路径前缀，默认 `debug/eml/`。 |

注意：

- Debug EML 上传接口会接收原始邮件、上传 OSS 并调用 SuperAgent，风险和成本高于普通查询接口。
- Debug EML 上传 key、SuperAgent Open API Key、阿里云 OSS AccessKey 都不得进入前端源码、`VITE_*`、镜像、普通日志或文档真实值。
- Debug EML 写入 SourceMessage Inbox 时 `provider=DEBUG_EML_UPLOAD`，不能伪装为 AgentBus 来源。
- Debug EML 写入 SourceMessage Inbox 时 `external_message_id` 是后端生成的 `debug-eml-run-{debugRunId}-{sha256前缀}`；原始邮件 `Message-ID` 只保存在 payload 的 `source.original_message_id`。
- Debug EML 返回 `html_body_sanitized`、`html_sanitize_required`、`html_render_mode`，前端展示 HTML 时应优先使用清洗字段。
- Debug EML 第一版只展示 SuperAgent 结果，不创建订单、不创建任务、不调用任务结果通知接口。
- AgentBus 实时收到邮件后自动推 SuperAgent 当前未实现，不能把 Debug EML 链路等同于生产实时自动处理链路。

## 4. 数据库上线注意事项

当前 SourceMessage 相关 migration：

- `server/src/main/resources/db/migration/V1__create_source_message_inbox.sql`
- `server/src/main/resources/db/migration/V2__create_source_message_original_access_audit.sql`

当前 M002 订单任务相关 migration：

- `server/src/main/resources/db/migration/V3__create_reservation_ai_task_workflow.sql`
- `server/src/main/resources/db/migration/V4__harden_reservation_task_queue_and_manual_conversion.sql`
- `server/src/main/resources/db/migration/V5__add_reservation_task_draft_and_confirmation.sql`
- `server/src/main/resources/db/migration/V6__create_reservation_opera_simulation_tables.sql`

当前 M004 Debug EML 相关 migration：

- `server/src/main/resources/db/migration/V7__create_debug_eml_superagent_run.sql`

当前 SourceMessage duplicate 诊断相关 migration：

- `server/src/main/resources/db/migration/V8__create_source_message_payload_duplicate.sql`

当前 M003 登录权限相关 migration：

- `server/src/main/resources/db/migration/V9__create_identity_access_hotel_menu.sql`

上线前确认：

- 目标数据库为空库或 Flyway history 与当前代码一致。
- MySQL 版本满足项目要求，默认使用 MySQL 8.0+。
- migration 在 UAT 或测试库已经跑过。
- 表和字段中文注释能正常创建。
- 数据库业务时间点按 UTC 写入，接口层返回带 `Z` 的 ISO 8601 UTC 时间，例如 `2026-07-08T03:00:00Z`。
- MySQL JDBC URL 建议明确 `serverTimezone=UTC`；部署容器和 JVM 也应使用 UTC，或至少确认应用代码所有入库时间均通过 UTC 时钟生成。
- AgentBus 邮件来源时间、SuperAgent HMAC timestamp、本系统 `created_at` / `updated_at` / `received_at` 等时间点统一按 UTC 理解；前端展示时再按用户或酒店时区格式化。
- 入住日期、离店日期、酒店营业日属于酒店本地业务日期，不应因为 UTC 换算而自动前后偏移。
- 执行 V4 前，如果目标库已有 M002 试运行数据，必须先检查 ACTIVE 订单业务号重复和同订单任务队列序号重复。
- 执行 V5 / V6 前，如果目标库已有 M002 试运行数据，必须确认任务草稿、确认 payload 和 OPERA 模拟操作表允许从空数据开始补齐；不要手工伪造已确认 payload 或 attempt 历史。

V4 前置检查 SQL：

```sql
SELECT hotel_id, order_key_type, order_business_key, COUNT(*) AS duplicate_count
FROM workflow_reservation_order
WHERE order_status = 'ACTIVE'
  AND order_key_type IN ('GROUP_CODE', 'CONFIRMATION_NUMBER')
  AND order_business_key IS NOT NULL
GROUP BY hotel_id, order_key_type, order_business_key
HAVING COUNT(*) > 1;

SELECT hotel_id, order_id, queue_participation, execution_order, COUNT(*) AS duplicate_count
FROM workflow_reservation_task
GROUP BY hotel_id, order_id, queue_participation, execution_order
HAVING COUNT(*) > 1;
```

处理要求：

- 如果任一 SQL 返回记录，不要继续执行 V4。
- ACTIVE 订单业务号重复时，先由业务确认保留哪一条 ACTIVE，其他订单应转为 `LOGIC_DELETED`、`ENDED` 或完成任务迁移后再上线。
- 同订单任务队列序号重复时，先按来源顺序和审计证据重新分配 `execution_order`，确认前置任务关系正确后再上线。
- 不要为了让唯一索引创建成功而随意删除订单、任务或 AI 原始记录。
- OPERA 模拟操作失败只代表单条模拟操作失败，当前任务不会因此自动进入 `FAILED`；上线验证时不能把失败操作当作真实 OPERA 失败处理。

禁止事项：

- 禁止直接修改已发布 migration。
- 禁止手工改生产表结构后再让代码“凑合跑”。
- 禁止把真实邮件正文、附件 URL 或客户数据做成测试种子数据提交。

## 5. 安全与日志

上线前必须确认普通日志、错误响应和状态接口不会输出：

- Authorization、Cookie、CSRF Token。
- Provider API Key、AgentBus Token、数据库密码。
- 邮件正文、HTML、附件 URL、原始 payload。
- 客户姓名、完整邮箱、电话、证件号。
- 支付信息。

SourceMessage 普通列表和普通详情只能返回安全摘要：

- 可以返回：SourceMessage ID、酒店 ID、provider、channel、外部邮件 ID、外部邮件链 ID、状态、时间、发送人摘要、主题摘要、安全短摘要。
- 不应返回：完整正文、HTML、附件 URL、原始 payload。

原文读取接口只允许在明确授权场景下使用：

```text
GET /api/source-messages/{id}/original
Header: X-TH-Hotel-Source-Original-Read-Key
Header: X-TH-Hotel-Actor
Header: X-TH-Hotel-Access-Scene
```

前端展示 `htmlBody` 前必须 sanitize。后端返回 `htmlSanitizeRequired=true` 是提醒前端不要直接信任 HTML。

## 6. AgentBus 上线注意事项

AgentBus 是消息入口，不是 AI Provider。生产实时链路必须保持以下边界：

- 只写 SourceMessage Inbox。
- 不自动调用 SuperAgent。
- 不创建 MessageEvent、Evidence、Case、Task、Operation、Receipt。
- 不调用 OHIP、ERP、支付系统等业务写接口。
- 不自动发送 ACK、`task.result` 或客户回复。
- 不在普通日志里输出 raw frame、邮件正文、HTML 或附件 URL。

建议上线顺序：

1. 保持 `AGENTBUS_PROBE_ENABLED=false`，先部署服务并确认健康检查。
2. 确认数据库 migration 和 SourceMessage 查询接口正常。
3. 配置 AgentBus URL 和 Token，但仍保持连接关闭。
4. 在约定观察窗口打开 `AGENTBUS_PROBE_ENABLED=true`。
5. 观察 `/api/system/agentbus-probe`，确认连接状态、`sessionReady`、计数器和最近错误代码。
6. 用合成测试邮件验证 SourceMessage Inbox 是否写入。
7. 确认日志和监控没有泄露 raw frame、正文或附件 URL。

如果出现异常：

- 先关闭 `AGENTBUS_PROBE_ENABLED`，停止接收入站 frame。
- 如果只是想暂停入库但保留连接，可关闭 `AGENTBUS_CAPTURE_ENABLED`。
- 保留状态接口、应用日志和数据库记录用于排查，但不要导出真实邮件正文或附件 URL。

## 7. 上线后冒烟验证

后端服务启动后，按顺序验证：

```text
GET /api/health
```

期望：

- HTTP 200。
- `status = UP`。

```text
GET /api/system/agentbus-probe
```

期望：

- HTTP 200。
- 返回 `enabled`、`connected`、`sessionReady`、计数器和最近错误代码。
- 响应中不包含 Token、Authorization、raw frame、payload 或邮件正文。

```text
GET /api/source-messages?pageNum=1&pageSize=20
```

期望：

- HTTP 200。
- 只返回安全摘要。
- 不包含正文、HTML、附件 URL 或原始 payload。

```text
GET /api/source-messages/{id}
```

期望：

- HTTP 200 或 404。
- 如果存在记录，只返回安全摘要。

```text
GET /api/source-messages/{id}/original
```

期望：

- 未携带正确访问 key 时返回 403。
- 携带正确访问 key、调用方和访问场景时返回原文内容，并写入 `platform_source_message_original_access_audit`。

## 8. 监控建议

至少监控：

- 应用进程是否存活。
- `GET /api/health` 是否正常。
- AgentBus `connected` 和 `sessionReady` 状态。
- AgentBus `failedFrameCount`、`rejectedFrameCount` 是否持续增长。
- SourceMessage Inbox 每小时入库数量是否异常突增或归零。
- `FAILED` SourceMessage 数量和安全错误摘要。
- 原文读取审计数量是否异常。
- 数据库连接池、慢 SQL、磁盘空间和 migration 状态。

告警信息不得包含 Secret、正文、HTML、附件 URL 或客户个人信息。

## 9. 回滚与降级

优先降级开关：

```text
AGENTBUS_PROBE_ENABLED=false
AGENTBUS_CAPTURE_ENABLED=false
SOURCE_MESSAGE_PROD_ORIGINAL_READ_ACCESS_KEY=
SOURCE_MESSAGE_ORIGINAL_READ_ACCESS_KEY=
```

说明：

- 关闭 `AGENTBUS_PROBE_ENABLED` 可以停止 WebSocket 入站连接。
- 关闭 `AGENTBUS_CAPTURE_ENABLED` 可以保留连接但暂停写入 Inbox。
- 清空当前 profile 对应的 `SOURCE_MESSAGE_*_ORIGINAL_READ_ACCESS_KEY`，且不配置旧通用变量，可以关闭原文读取接口。

数据库回滚注意：

- 已执行的 migration 不应直接删除或手工回滚。
- 如果新版本已写入 SourceMessage 数据，回滚应用前要确认旧版本是否能兼容新表存在。
- 需要修复表结构时，应新增 migration，而不是修改已发布 migration。

## 10. 上线责任确认

上线前需要有人明确确认：

- 部署版本：确认本次上线的分支、提交和构建产物。
- 数据库：确认 migration、备份和连接信息。
- Secret：确认所有密钥由部署平台注入。
- AgentBus：确认是否开启 WebSocket，是否允许捕获入库。
- 安全：确认日志、错误响应、状态接口和监控面板没有敏感数据。
- 业务：确认当前上线范围不包含 replay、AI、Case、Task、客户回复或 OHIP 写操作。

只要上述任何一项没人确认，就不要打开生产实时入口。
