Files
th-hotel-simple/docs/project/go-live-notes.md
2026-07-21 15:01:16 +07:00

498 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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如开启 M007则在入库后异步创建 SuperAgent dispatch run。
- SuperAgent 任务结果接收接口:当前代码接收一个外部 `source_message_id` 下的 AI 任务结果,反查 SourceMessage Inbox 后写入 AI 过渡层、订单、任务和任务卡;支持 V3 结构化 `S10/S99` 和业务根基础解析,同时兼容旧 `S000/S999,source_message_id` 文本入口结果并创建只读特殊任务。
- Reservation 任务详情接口:返回任务字段、队列可处理状态和 OPERA 模拟操作摘要。
- Reservation 任务草稿保存和最终确认接口:按任务卡矩阵做第一版后端校验,确认后生成 `confirmed_payload_json`
- Reservation OPERA 模拟骨架已确认任务固定生成两条模拟操作支持执行、失败重试、attempt 记录和任务审计列表。
- SuperAgent 查询上下文接口 1、2支持 HMAC 鉴权的订单上下文查询和对象详情查询。
- Debug EML 上传到 SuperAgent 调试链路:受控上传 `.eml`、转存阿里云 OSS、写入 SourceMessage Inbox、调用 SuperAgent Open API 并返回调试结果。
- Excel 转 PDF 手动上传接口:受控上传 `.xls` / `.xlsx`,通过 LibreOffice headless 转 PDF 后上传阿里云 OSS 并返回 PDF URL。
- Reservation Manual Invoice 后端生成接口:登录用户可通过 `POST /api/reservation/invoices/manual-generations` 手工生成 Proforma Invoice后端填充受控 Excel 模板、转 PDF、上传 OSS并写入生成记录和业务审计。
- 登录权限底座:支持用户名密码登录、登出、当前用户上下文、数据库 session token、可访问酒店、权限码和可见菜单。
- 系统管理后台 V1支持用户、角色权限、菜单、酒店和管理操作审计的受控维护接口与前端页面。
当前不要把以下能力当作已上线:
- SourceMessage Replay 到 MessageEvent / Evidence。
- 真实 AI 识别服务实现、Case 完整模型、Operation、Receipt。
- 自动 ACK、`task.result` 或客户回复。
- 业务前端页面展示邮件原文。
- AgentBus SourceMessage 入库后自动分发 SuperAgent 虽然后端 V1 已实现,但默认关闭;未经测试机验收前不要在生产启用。
- OHIP / OPERA 或其他业务系统真实写操作。
- 普通任务切换订单接口。
- M002 V3 字段矩阵从当前扁平结构整体迁移到 0711 P0 新结构。
- 现有业务接口强制登录和强制权限拦截。
- 业务审计 actor 全量迁移到当前登录用户。
- Debug EML 上传链路不属于生产普通业务页面能力,生产默认关闭;即使已有登录权限,也不要开放给普通用户。
- Excel 转 PDF 当前只完成手动上传后端接口和 M009 Manual Invoice 内部复用;邮件附件自动转换、持久化转换任务和 worker 尚未实现。生产启用前必须确认 LibreOffice、字体、OSS、临时目录和访问口令。
## 2. 上线前必须确认
上线前至少确认以下事项:
- 当前分支、提交和部署包来源清楚,不能混入本地临时文件、真实 Secret、真实客户邮件样本或构建产物测试机 / UAT 构建包必须在本次代码提交后再打包,且能通过 `GET /api/health``build_commit` 证明当前运行提交。
- `server` 后端通过完整检查:`cd server && ./mvnw verify`
- 生产或 UAT 数据库已经备份,并确认 Flyway migration 只新增不修改历史脚本。
- 所有 Secret 都通过环境变量、部署平台 Secret 或密钥管理系统注入,不写入仓库、镜像、前端环境变量或普通配置文件。
- 生产默认不保存 AgentBus raw frame 样本。
- AgentBus 实时链路开启前,已经确认 WebSocket URL、Token、Bot Address、外部消息幂等键和断线重连语义。
- M007 自动分发开启前,必须先确认 SourceMessage 入库稳定、SuperAgent Open API Key 可用、SSE 断流恢复通过测试、dispatch worker 开关和回滚方式明确。
- 源邮件只读通知卡上线前,必须确认 `platform_hotel` 中存在且只存在一家 `ACTIVE` 酒店,并且已有 SourceMessage Inbox 数据的 `hotel_id` 与该酒店一致。旧 `S000/S999` 和新结构化 `S10/S99` 都沿用该酒店解析约束。
- 系统管理后台上线前,必须确认至少存在一个 `ACTIVE` 超级管理员账号,且该账号拥有 `SYSTEM_ADMIN_CONSOLE_ACCESS` 和各系统管理权限。
- 单酒店阶段上线前,必须确认 `platform_hotel` 中只有一家 `ACTIVE` 酒店;新增酒店可以存在但应保持 `DISABLED`
- M002 V4 lookup 上线前,必须确认当前 `platform_hotel` 唯一 `ACTIVE` 酒店有可用目录数据。V24 会初始化 `HOTEL-TEST``HOTEL-DEV`,并给迁移执行时已经存在的 `ACTIVE` 酒店导入固定种子;如果生产真实酒店是在迁移后由 bootstrap 创建,`ReservationV4CatalogBootstrapRunner` 会在该酒店目录为空时补一份 `FIXED_SEED_IMPORT` 初始化目录。生产如不能使用固定种子作为业务事实源,必须在上线前补正式目录数据或新增导入脚本。
- 管理后台启用后,不要继续把手工改库作为常规运营方式;用户、角色、菜单和酒店变更应通过 `/api/admin/**` 并写入管理审计。
- 原文读取接口开启前,已经确认谁可以使用、在哪些场景使用、如何轮换访问 key。
- 日志采集、错误响应和监控面板都不会展示邮件正文、HTML、附件 URL、Token、Cookie、客户姓名、邮箱、电话或支付信息。
## 3. 环境变量
### 3.0 构建与部署证明
| 变量 | 是否 Secret | 上线注意事项 |
| --- | --- | --- |
| `TH_HOTEL_BUILD_COMMIT` | 否 | 构建包时设置为已提交后的当前 Git commit例如 `TH_HOTEL_BUILD_COMMIT=$(git rev-parse --short HEAD) ./mvnw clean package`;该值会写入 Jar 内嵌 build-info`/api/health` 返回内嵌值;未设置时 `/api/health` 返回 `build_commit=UNKNOWN`,不能作为“已部署指定提交”的证明。不要用未提交工作区代码打包后仍标记旧 HEAD否则 build commit 只能证明旧提交,不能证明本次修复。 |
说明:
- `GET /api/health` 会返回 `runtime_marker``build_commit``build_time``build_version`,这些字段不包含 Secret只用于部署排查`build_commit` 应以构建阶段写入 Jar 的 build-info 为准,不把运行时临时环境变量或未提交工作区状态当作已部署代码证明。
- 应用启动日志也会输出同一组 build info如果测试机接口不可访问可先看启动日志确认运行包。
- 如果 `build_commit` 不是预期提交,先修部署或重新打包,不要继续改业务逻辑。
### 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>`
- 初始管理员 bootstrap 只以“启用状态超级管理员”为阻断条件;如果测试库或生产库只剩禁用超级管理员,应通过环境变量恢复一个可登录超级管理员后再排查账号运营问题。
- 内置角色权限矩阵在启动时按代码同步,矩阵移除的旧权限关系会被清理;管理后台 V1 也不允许修改内置角色权限,临时权限应通过自定义角色承载。
- 普通用户默认酒店由后端写入逻辑和数据库唯一索引共同保持单默认V10 migration 会在建约束前把历史重复默认清理为每个用户保留 id 最大的一条。
- 单酒店阶段系统酒店由 `platform_hotel` 唯一 `ACTIVE` 酒店决定V12 migration 会通过唯一索引阻止第二家 `ACTIVE` 酒店。上线前如果已有多家 `ACTIVE` 酒店,必须先调整数据,否则迁移或运行时解析会失败。
- 管理后台 V1 写操作会记录 `platform_admin_audit_log`;重置密码只允许临时密码出现在本次响应中,不得进入日志、审计快照或前端持久化存储。
### 3.3 SourceMessage
SourceMessage 原文和邮件会话完整正文已迁移到登录权限体系:
- 前端请求必须携带 `Authorization: Bearer <access_token>`
- 当前用户必须同时拥有 `SOURCE_MESSAGE_READ``SOURCE_MESSAGE_ORIGINAL_READ`
- 后端按 SourceMessage 实际所属酒店校验酒店访问权。
- 后端内部写入原文读取审计actor 使用当前登录用户稳定 ID。
-`SOURCE_MESSAGE_DEV_ORIGINAL_READ_ACCESS_KEY``SOURCE_MESSAGE_TEST_ORIGINAL_READ_ACCESS_KEY``SOURCE_MESSAGE_PROD_ORIGINAL_READ_ACCESS_KEY``SOURCE_MESSAGE_ORIGINAL_READ_ACCESS_KEY` 已废弃,不再作为部署必备 Secret。
### 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_SUPERAGENT_DISPATCH_ENABLED` | 否 | AgentBus 新邮件入库后是否创建 SuperAgent dispatch 记录,默认关闭。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_WORKER_ENABLED` | 否 | 是否启动异步 worker 调用 SuperAgent Open API默认关闭。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_MAX_ATTEMPTS` | 否 | 单条 dispatch 最大尝试次数,建议默认 `3`。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_BATCH_SIZE` | 否 | worker 每轮领取数量,建议默认 `10`。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_LOCK_TTL` | 否 | worker 抢占锁有效期,建议默认 `5m`。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_INITIAL_BACKOFF` | 否 | 首次失败后的重试等待时间,建议默认 `30s`。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_MAX_BACKOFF` | 否 | 最大重试等待时间,建议默认 `15m`。 |
| `AGENTBUS_SUPERAGENT_DISPATCH_WORKER_FIXED_DELAY_MS` | 否 | worker 调度间隔毫秒,建议默认 `10000`。 |
注意:
- `AGENTBUS_PROBE_ENABLED=true` 只表示启用连接和入站接收,不代表可以回复客户。
- `AGENTBUS_CAPTURE_ENABLED=false` 时,业务 frame 会被忽略,不会写入 SourceMessage Inbox。
- `AGENTBUS_DEFAULT_HOTEL_ID` 是 M005 前旧变量,当前后端不再读取;系统酒店来自 `platform_hotel` 唯一 `ACTIVE` 酒店。
- 当前实现不发送 ACK、不发送 `task.result`、不自动回复客户。
- M007 自动分发即使开启,也只能在 SourceMessage 入库后通过 dispatch / outbox 异步调用 SuperAgent不能在 AgentBus WebSocket 回调内同步等待外部返回。
- M007 dispatch 创建使用 SourceMessage + dispatch_source 幂等;重复 `RECEIVED` 投递会尝试补偿缺失 outbox锁已过期的 `RUNNING` 记录会被 worker 重新领取。
### 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`。 |
| `SUPERAGENT_TEST_ALLOW_LEGACY_INTERNAL_SOURCE_MESSAGE_ID` | 否 | 仅用于本地 / test 旧夹具兼容内部 SourceMessage ID正式联调和生产不得开启。 |
注意:
- 查询接口和任务结果通知接口使用同一套 Header、签名串、secret、timestamp 和 nonce 规则。
- SuperAgent 侧也需要配置同一个 secret并按原始请求体计算 SHA-256。
- 当前第一版只支持一个 HMAC secretsecret 轮换需要协调部署窗口。
- 任务结果通知接口 JSON body 里的 `source_message_id` 是外部来源消息 ID对应 AgentBus `source.external_message_id`SuperAgent 默认不传 `hotel_id`,后端用系统酒店 `hotel_id + provider + channel + external_message_id` 反查内部 SourceMessage Inbox。
- 当前代码的任务结果通知接口也支持 `text/plain``S000,source_message_id``S999,source_message_id`。这类请求不在 body 里带 `hotel_id`,后端同样使用平台酒店表唯一 `ACTIVE` 酒店查询 SourceMessage Inbox。
- M002 V3 已支持结构化 `S10/S99`、V3 业务根基础解析、`UNHANDLED_CURRENT_INTENT``adapter_contract_error` 最小落库:上线前必须单独验证新 JSON 入站、旧 S000/S999 兼容、隐藏技术订单、任务列表可见、订单列表不可见和只读限制。
- 0712 P0.1 后SuperAgent runtime 必须同步使用 P0.1 Main Agent prompt / booking-desk-event skill。完整 Parent split 的父事件必须提交为 `Cancel Allotment + cancel_allotment_control_block`;当前新入站 `Cancel Booking + linked_parent_release_after_child_split` 会被后端按 `adapter_contract_error` 处理,不创建业务任务。旧历史 payload 只读兼容,不做批量迁移。
- `application/json``text/plain` 都必须使用原始请求体计算 SHA-256 并参与 HMAC 签名SuperAgent 侧不能签名格式化后的 JSON 或二次拼接字符串。
- 旧 S000/S999 会创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务和隐藏技术订单,任务列表可见,订单列表不可见,不允许编辑、确认、转换订单或执行 OPERA。V3 S10/S99 应保持同等只读和不可执行边界。
- 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`。 |
| `DEBUG_EML_UPLOAD_SSE_HEARTBEAT_INTERVAL` | 否 | Debug EML 页面到后端的 SSE 心跳间隔,默认 `15s`;测试机如仍遇到空闲断流可调小到 `10s`。 |
| `DEBUG_EML_UPLOAD_SSE_REQUEST_TIMEOUT` | 否 | Spring MVC 异步请求总超时,默认 `1800s`;用于保障 Debug EML 页面长流程不被后端 MVC 容器提前关闭。注意 Spring MVC async timeout 是应用级全局设置,后续如增加其他 async/SSE 接口需一起评估。 |
| `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默认不配置时复用实时 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 机制处理。 |
| `SUPERAGENT_OPEN_API_SSE_RECOVERY_MAX_ATTEMPTS` | 否 | SSE EOF 后通过 `/runs/{run_id}/events` 恢复的最大尝试次数,默认 `5`。 |
| SuperAgent Open API CSRF | 否 | 不需要额外环境变量;后端每次请求自动生成临时 `X-CSRF-Token`,并用同值 `csrf_token` Cookie 做 double-submit。 |
| `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` 不进入发给 SuperAgent 的主 payload需要排查时看原始 EML OSS 文件、Debug run 和 SuperAgent Open API metadata。
- 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 调用 SuperAgent Open API 时metadata 中 `source=DEBUG_EML_UPLOAD` 只表示来源类型,`model_contract=M002_V4` 表示后续业务写入期望按 V4 契约处理profile 路由必须使用 `external_subject_id`,不要把 metadata.source 当 profile 名。
- 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 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`
### 3.7 Excel 转 PDF / LibreOffice
| 变量 | 是否 Secret | 上线注意事项 |
| --- | --- | --- |
| `DOCUMENT_CONVERSION_DEV_ENABLED` / `DOCUMENT_CONVERSION_TEST_ENABLED` / `DOCUMENT_CONVERSION_PROD_ENABLED` | 否 | 是否启用 Excel 转 PDF 手动上传接口prod 默认必须保持 `false`,确认运行环境后再开启。 |
| `DOCUMENT_CONVERSION_DEV_ACCESS_KEY` / `DOCUMENT_CONVERSION_TEST_ACCESS_KEY` / `DOCUMENT_CONVERSION_PROD_ACCESS_KEY` | 是 | 文件转换访问口令;未配置时可兜底 `DOCUMENT_CONVERSION_ACCESS_KEY`。不得进入前端源码、镜像、普通日志或文档真实值。 |
| `DOCUMENT_CONVERSION_SOFFICE_PATH` / `DOCUMENT_CONVERSION_*_SOFFICE_PATH` | 否 | `soffice` 可执行文件路径,默认 `soffice`。测试机和生产机路径可能不同。 |
| `DOCUMENT_CONVERSION_TEMP_DIR` / `DOCUMENT_CONVERSION_*_TEMP_DIR` | 否 | 文件转换临时目录。后端运行用户必须有读写权限,目录应有系统清理策略。 |
| `DOCUMENT_CONVERSION_MAX_FILE_BYTES` / `DOCUMENT_CONVERSION_*_MAX_FILE_BYTES` | 否 | 单个 Excel 文件大小上限,默认 `20971520`。 |
| `DOCUMENT_CONVERSION_TIMEOUT_SECONDS` / `DOCUMENT_CONVERSION_*_TIMEOUT_SECONDS` | 否 | 单次 LibreOffice 转换超时秒数,默认 `60`。 |
| `DOCUMENT_CONVERSION_MAX_CONCURRENT` / `DOCUMENT_CONVERSION_*_MAX_CONCURRENT` | 否 | 最大并发转换数,默认 `2`。不要盲目调大,避免 LibreOffice 进程拖垮后端。 |
| `DOCUMENT_CONVERSION_OUTPUT_OSS_PREFIX` / `DOCUMENT_CONVERSION_*_OUTPUT_OSS_PREFIX` | 否 | PDF 输出 OSS 前缀,默认 `document-conversions/excel-to-pdf/`。 |
| `DOCUMENT_CONVERSION_WORKER_ENABLED` / `DOCUMENT_CONVERSION_*_WORKER_ENABLED` | 否 | 自动转换 worker 预留开关CP2 不使用,默认关闭。 |
| `DOCUMENT_CONVERSION_MULTIPART_MAX_FILE_BYTES` / `DOCUMENT_CONVERSION_MULTIPART_MAX_REQUEST_BYTES` | 否 | Spring multipart 框架上限;默认分别为 `25165824` / `29360128`,应高于业务 `MAX_FILE_BYTES`,否则请求会在进入 Controller 前被框架拦截。 |
注意:
- 服务器必须安装 LibreOffice / LibreOffice Calc并确认后端运行用户可以执行 `soffice`
- 服务器必须安装中文、英文、泰文等业务字体;缺少字体会导致 PDF 乱码、缺字或分页变化。
- 每次转换会创建独立临时目录和 LibreOffice profile正常结束、失败或超时后后端会清理但仍建议运维配置临时目录兜底清理。
- 当前接口路径为 `POST /api/system/document-conversions/excel-to-pdf`Header 为 `X-TH-Hotel-Document-Conversion-Key`
- 当前接口只支持 `.xls` / `.xlsx`,不支持 `.xlsm`;后端会校验扩展名和文件头,改后缀的非 Excel 文件会返回受控错误。
- PDF 上传到阿里云 OSS返回 `pdf_url``object_key``pdf_file_name``pdf_size_bytes``duration_millis`
- CP2 不落库,不提供转换历史查询;如果需要自动处理邮件附件,应先进入 M008 后续持久化任务和 worker checkpoint。
- M009 Manual Invoice 不调用上述调试接口,也不使用 `X-TH-Hotel-Document-Conversion-Key`;它通过登录 Bearer token、`RESERVATION_INVOICE_GENERATE` 权限和后端内部转换 Adapter 生成 PDF。
- M009 Manual Invoice 当前模板资源为 `server/src/main/resources/templates/reservation-invoice/proforma-invoice-v1.xlsx`,部署包必须包含该资源;模板为 A4 纵向单页版式,缺少字体或 LibreOffice 版本差异仍可能影响最终分页,测试环境上线后需用真实浏览器打开生成 PDF 复核。
- M009 Manual Invoice 会写入 `workflow_reservation_invoice_generation`,上线前需确认 Flyway 已执行到 V22。
- M009 Manual Invoice 失败记录也可能保留已上传成功的 Excel / PDF object key排查或清理 OSS 生成物时应以生成记录为主,不只看 `SUCCEEDED` 状态。
## 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`
- `server/src/main/resources/db/migration/V11__add_reservation_order_visibility.sql`
- `server/src/main/resources/db/migration/V16__add_m002_v3_ai_route_fields.sql`
- `server/src/main/resources/db/migration/V21__add_reservation_order_latest_activity.sql`
- `server/src/main/resources/db/migration/V23__create_reservation_v4_order_task_card.sql`
- `server/src/main/resources/db/migration/V24__create_reservation_catalog_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`
当前 M007 AgentBus 自动分发 SuperAgent 相关 migration
- `server/src/main/resources/db/migration/V20__create_superagent_dispatch_run.sql`
当前 M009 Manual Invoice 相关 migration
- `server/src/main/resources/db/migration/V22__create_reservation_invoice_generation.sql`
当前 M003 登录权限相关 migration
- `server/src/main/resources/db/migration/V9__create_identity_access_hotel_menu.sql`
- `server/src/main/resources/db/migration/V10__enforce_single_default_user_hotel.sql`
- `server/src/main/resources/db/migration/V12__enforce_single_active_platform_hotel.sql`
当前 M006 系统管理相关 migration
- `server/src/main/resources/db/migration/V15__create_platform_admin_audit_log.sql`
上线前确认:
- 目标数据库为空库或 Flyway history 与当前代码一致。
- 如果某个环境已经在缺少 V10 的临时提交上执行过 V11 / V12不能直接用默认 Flyway 策略补跑 V10应先重建测试库或按运维窗口明确 out-of-order / repair 策略。
- V21 会为 `workflow_reservation_order` 增加 `latest_activity_at`,并按订单更新时间和历史任务最新来源 / 创建时间回填一次;上线后订单列表依赖该字段排序,不再在列表查询时聚合全量任务。发布后需要确认 Flyway 已执行到 V21且订单列表能按最新业务活动倒序返回。
- V24 会新增 `workflow_reservation_catalog_account``workflow_reservation_catalog_code`,并初始化 `HOTEL-TEST``HOTEL-DEV` 以及迁移执行时已有 `ACTIVE` 酒店的 Account、Market、Source、Room Type、Rate Code 种子目录。启动后 `ReservationV4CatalogBootstrapRunner` 会补齐迁移后由平台 bootstrap 创建且目录为空的 `ACTIVE` 酒店。发布后需要确认 Flyway 已执行到 V24且当前酒店 lookup 能返回目录项;真实 PMS 同步、目录管理后台和 `workflow_reservation_catalog_sync_run` 仍未实现。
- V22 会新增 `workflow_reservation_invoice_generation`,用于记录 Manual Invoice 生成状态、Excel / PDF OSS 对象、金额摘要和安全错误摘要;发布后需要确认 `RESERVATION_INVOICE_GENERATE` 权限已由启动同步写入平台权限表,预订操作员或目标角色已拥有该权限。
- 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` 等时间点统一按 UTC 理解SourceMessage `received_at` 优先保存 AgentBus payload `received_at`,缺失时回退本系统接收时间,前端展示时再按用户或酒店时区格式化。
- 入住日期、离店日期、酒店营业日属于酒店本地业务日期,不应因为 UTC 换算而自动前后偏移。
- 详细时间设计、页面展示和按酒店本地日期筛选规则见 `docs/project/backend-time-design.md`
- 执行 V4 前,如果目标库已有 M002 试运行数据,必须先检查 ACTIVE 订单业务号重复和同订单任务队列序号重复。
- 执行 V5 / V6 前,如果目标库已有 M002 试运行数据,必须确认任务草稿、确认 payload 和 OPERA 模拟操作表允许从空数据开始补齐;不要手工伪造已确认 payload 或 attempt 历史。
- 执行 V16 前,确认 `workflow_reservation_ai_transition.result_type``workflow_reservation_task.result_type` 扩容到 `VARCHAR(64)` 不会被历史手工约束阻断V16 会新增 `route_code``system_process_category``adapter_error_code``adapter_error_message` 和对应查询索引。
- 执行 V11 前,如果目标库已有手工造数或历史隐藏订单方案,必须确认是否需要回填 `order_visibility`;默认值 `VISIBLE` 会让历史订单继续出现在订单列表。
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: Authorization: Bearer <access_token>
Required permission: SOURCE_MESSAGE_READ + SOURCE_MESSAGE_ORIGINAL_READ
```
前端展示 `htmlBody` 前必须 sanitize。后端返回 `htmlSanitizeRequired=true` 是提醒前端不要直接信任 HTML。
## 6. AgentBus 上线注意事项
AgentBus 是消息入口,不是 AI Provider。生产实时链路必须保持以下边界
- WebSocket 回调内只做 SourceMessage Inbox 入库和轻量 dispatch 标记。
- 如启用 M007只能在入库后通过受控异步 dispatch / outbox 调用 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。
8. 如需开启 M007 自动分发,先保持 worker 关闭并确认 dispatch run 能正确创建,再打开 worker 处理少量合成邮件。
9. 验证 SuperAgent dispatch 成功不会直接创建订单或任务,业务任务仍只来自 SuperAgent 后续任务结果通知或 MCP 提交。
如果出现异常:
- 先关闭 `AGENTBUS_PROBE_ENABLED`,停止接收入站 frame。
- 如果只是想暂停入库但保留连接,可关闭 `AGENTBUS_CAPTURE_ENABLED`
- 如果只想暂停 M007 自动分发,优先关闭 `AGENTBUS_SUPERAGENT_DISPATCH_WORKER_ENABLED`;如需停止新建 dispatch再关闭 `AGENTBUS_SUPERAGENT_DISPATCH_ENABLED`
- 保留状态接口、应用日志和数据库记录用于排查,但不要导出真实邮件正文或附件 URL。
## 7. 上线后冒烟验证
后端服务启动后,按顺序验证:
```text
GET /api/health
```
期望:
- HTTP 200。
- `status = UP`
- `runtime_marker = m002_v4_review_pointer_deployment_proof_v1`
- `build_commit` 为本次预期部署提交;如果是 `UNKNOWN` 或旧提交,先修部署包。
```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
```
说明:
- 关闭 `AGENTBUS_PROBE_ENABLED` 可以停止 WebSocket 入站连接。
- 关闭 `AGENTBUS_CAPTURE_ENABLED` 可以保留连接但暂停写入 Inbox。
- 如需临时关闭前端邮件原文 / 会话完整正文读取,应从相关角色移除 `SOURCE_MESSAGE_ORIGINAL_READ``SOURCE_MESSAGE_READ`,或禁用对应用户入口;旧原文读取 key 已废弃,不能作为关闭开关。
数据库回滚注意:
- 已执行的 migration 不应直接删除或手工回滚。
- 如果新版本已写入 SourceMessage 数据,回滚应用前要确认旧版本是否能兼容新表存在。
- 需要修复表结构时,应新增 migration而不是修改已发布 migration。
## 10. 上线责任确认
上线前需要有人明确确认:
- 部署版本:确认本次上线的分支、提交和构建产物。
- 数据库:确认 migration、备份和连接信息。
- Secret确认所有密钥由部署平台注入。
- AgentBus确认是否开启 WebSocket是否允许捕获入库。
- 安全:确认日志、错误响应、状态接口和监控面板没有敏感数据。
- 业务:确认当前上线范围不包含 replay、AI、Case、Task、客户回复或 OHIP 写操作。
只要上述任何一项没人确认,就不要打开生产实时入口。