实现酒店上下文单酒店收口
This commit is contained in:
@@ -71,6 +71,8 @@
|
||||
| `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 字段和 S000/S999 识别。 | 只用于调试页面;请求为 multipart/form-data;必须传 `X-TH-Hotel-Debug-Upload-Key`,但该 key 不能写进前端源码、构建产物、URL、localStorage 或错误上报;SuperAgent 返回 S000/S999 时不是 JSON 解析失败。 |
|
||||
|
||||
酒店上下文注意:Reservation 列表、订单详情、任务列表和 Debug EML 上传的 `hotel_id` 第一版都是可选参数。前端默认可以不传;后端会按当前登录用户酒店上下文或平台酒店表唯一 `ACTIVE` 酒店解析。如果前端传了当前选中酒店,后端会校验该酒店是否可访问。
|
||||
|
||||
### 5.2 登录权限接入注意
|
||||
|
||||
后端已提供 M003 登录和权限底座第一版接口:
|
||||
@@ -109,7 +111,7 @@ POST /api/auth/logout
|
||||
### 5.4 邮件会话详情接入注意
|
||||
|
||||
- `GET /api/source-messages/{sourceMessageId}/conversation` 只接收路径参数 `sourceMessageId`;第一版不接收 `hotelId`、`includeBody`、`includeRelated`。
|
||||
- 前端当前通过 `VITE_RESERVATION_HOTEL_ID` 统一配置 Reservation 默认酒店上下文,并会在订单列表、任务列表和订单详情查询中传 `hotel_id`;任务详情、任务写操作和邮件会话详情当前后端接口不接收该参数。
|
||||
- Reservation 列表、任务列表和订单详情默认不需要前端传 `hotel_id`;如果前端已经接入酒店选择器,可以把当前选中酒店作为可选 `hotel_id` 传给后端。任务详情、任务写操作和邮件会话详情当前仍按对象 ID 定位,不接收该参数。
|
||||
- 后端会根据 `sourceMessageId` 定位 `external_conversation_id`,并返回同一会话下全部邮件;如果来源消息没有外部会话 ID,会降级返回当前单封邮件。
|
||||
- `messages[]` 按后端接收时间正序返回,前端不要重新按创建时间或任务时间排序。
|
||||
- 返回内容包含完整 `text_body`、`html_body`、`inline_images[]`、`attachments[]`、`related_orders[]`、`related_tasks[]`。
|
||||
@@ -140,7 +142,6 @@ Header: X-TH-Hotel-Demo-Data-Key: <本地演示数据访问口令>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"hotel_id": "HOTEL-TEST",
|
||||
"run_label": "frontend-smoke"
|
||||
}
|
||||
```
|
||||
@@ -186,7 +187,7 @@ Header: X-TH-Hotel-Debug-Upload-Key: <调试访问口令>
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file: .eml 文件
|
||||
hotel_id: HOTEL-TEST
|
||||
hotel_id: 可选;缺省使用后端系统酒店,显式传值时必须是当前可访问酒店
|
||||
run_label: 可选调试标签
|
||||
```
|
||||
|
||||
@@ -195,6 +196,7 @@ run_label: 可选调试标签
|
||||
前端注意:
|
||||
|
||||
- 该接口只用于 dev/test 调试页面,不是生产普通业务页面接口。
|
||||
- `hotel_id` 第一版可不传;单酒店阶段后端按平台酒店表唯一 `ACTIVE` 酒店解析。只有在调试人员明确要覆盖当前酒店时,前端才传当前选中酒店。
|
||||
- 接口会解析 `.eml`,上传原始邮件、内联图片和附件到本系统阿里云 OSS,替换 HTML 内 `cid:` 图片,再写入 SourceMessage Inbox。
|
||||
- SourceMessage 来源 provider 固定为 `DEBUG_EML_UPLOAD`,用于和 AgentBus 入库邮件区分。
|
||||
- 当前 AgentBus 实时收到邮件后自动推 SuperAgent 还没有做;这个接口是人工 Debug 上传链路,不代表实时生产链路。
|
||||
|
||||
@@ -31,7 +31,7 @@ Debug EML 页面第一版只做一件事:
|
||||
|
||||
| 区域 | 展示 / 操作 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 上传配置区 | `hotel_id`、Debug 上传口令、`run_label`、`.eml` 文件选择、提交按钮 | Debug 上传口令只能由调试人员临时输入,不能写进前端源码、环境变量、localStorage 或 URL。 |
|
||||
| 上传配置区 | 可选酒店覆盖、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`。 |
|
||||
@@ -59,7 +59,7 @@ Header: X-TH-Hotel-Debug-Upload-Key: <调试上传口令>
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `file` | File | 是 | `.eml` 邮件文件。前端文件选择器建议 `accept=".eml,message/rfc822"`。 |
|
||||
| `hotel_id` | string | 是 | 酒店上下文 ID;本地 / test 可使用当前前端配置的 `VITE_RESERVATION_HOTEL_ID`。 |
|
||||
| `hotel_id` | string | 否 | 可选酒店上下文覆盖;缺省由后端按当前登录用户上下文或平台酒店表唯一 `ACTIVE` 酒店解析。 |
|
||||
| `run_label` | string | 否 | 调试标签,例如 `frontend-debug-smoke`,方便后端日志和数据库排查。 |
|
||||
|
||||
前端调用示例:
|
||||
@@ -67,14 +67,16 @@ Header: X-TH-Hotel-Debug-Upload-Key: <调试上传口令>
|
||||
```ts
|
||||
export async function uploadDebugEml(input: {
|
||||
baseUrl: string
|
||||
hotelId: string
|
||||
hotelId?: string | null
|
||||
debugUploadKey: string
|
||||
file: File
|
||||
runLabel?: string
|
||||
}) {
|
||||
const form = new FormData()
|
||||
form.append('file', input.file)
|
||||
form.append('hotel_id', input.hotelId)
|
||||
if (input.hotelId?.trim()) {
|
||||
form.append('hotel_id', input.hotelId.trim())
|
||||
}
|
||||
if (input.runLabel?.trim()) {
|
||||
form.append('run_label', input.runLabel.trim())
|
||||
}
|
||||
@@ -171,7 +173,7 @@ S000/S999 解析示例:
|
||||
|
||||
| HTTP 状态 | `error_code` | 前端建议 |
|
||||
| --- | --- | --- |
|
||||
| `400` | `REQUEST_FIELD_REQUIRED` | 提示缺少必填字段,检查文件、`hotel_id` 或请求参数。 |
|
||||
| `400` | `REQUEST_FIELD_REQUIRED` | 提示缺少必填字段,检查文件或请求参数。 |
|
||||
| `400` | `EML_FILE_REQUIRED` | 提示请选择 `.eml` 文件。 |
|
||||
| `400` | `EML_FILE_TOO_LARGE` | 提示文件超过后端限制;当前默认上限通常为 10MB,以环境配置为准。 |
|
||||
| `400` | `INVALID_FILE_TYPE` | 提示只支持 `.eml`。 |
|
||||
@@ -204,7 +206,7 @@ idle
|
||||
|
||||
实现建议:
|
||||
|
||||
- 文件未选择、`hotel_id` 为空、Debug 上传口令为空时禁用提交按钮。
|
||||
- 文件未选择或 Debug 上传口令为空时禁用提交按钮;`hotel_id` 为空是允许的,表示使用后端系统酒店。
|
||||
- 提交后禁用文件选择和提交按钮,避免重复上传。
|
||||
- SuperAgent 调用可能耗时较长,页面 loading 文案不要只写“上传中”,建议写“正在解析邮件并等待 SuperAgent 返回”。
|
||||
- 成功后保留本次响应在页面内存中;当前没有 Debug run 查询接口,刷新页面后需要重新上传。
|
||||
@@ -233,7 +235,7 @@ idle
|
||||
前端需要具备:
|
||||
|
||||
- 可配置后端 `BASE_URL`。
|
||||
- 可配置或输入 `hotel_id`。
|
||||
- 可配置或输入可选 `hotel_id`;单酒店联调默认可留空。
|
||||
- 调试人员临时输入 Debug 上传口令。
|
||||
- 准备一封 `.eml` 样例邮件。
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ GET /api/reservation/tasks
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 否 | 酒店 ID。第一版如果只有单酒店,可为空。 |
|
||||
| `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_status` | 否 | 任务状态过滤。 |
|
||||
@@ -158,7 +158,7 @@ GET /api/reservation/orders/{orderId}
|
||||
| 参数 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `orderId` | 是 | 订单 ID。 |
|
||||
| `hotel_id` | 否 | 酒店 ID。第一版如果只有单酒店,可为空。 |
|
||||
| `hotel_id` | 否 | 酒店 ID。单酒店阶段默认可为空,由后端解析;显式传值时后端会校验访问权限。 |
|
||||
| `include_tasks` | 否 | 是否返回任务时间线,默认 `true`。 |
|
||||
| `include_source_summary` | 否 | 是否返回来源消息摘要,默认 `true`;第一版参数保留,前端暂不要依赖它做字段裁剪。 |
|
||||
|
||||
@@ -228,7 +228,7 @@ GET /api/reservation/orders
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 否 | 酒店 ID。 |
|
||||
| `hotel_id` | 否 | 酒店 ID。单酒店阶段默认可为空,由后端解析;显式传值时后端会校验访问权限。 |
|
||||
| `order_status` | 否 | `TEMPORARY`、`ACTIVE`、`ENDED`、`LOGIC_DELETED`。 |
|
||||
| `group_code` | 否 | 按 Group Code 精确或模糊查询,后端决定。 |
|
||||
| `confirmation_number` | 否 | 按 Confirmation No 查询。 |
|
||||
@@ -285,7 +285,6 @@ POST /api/system/reservation/demo-data
|
||||
|
||||
```json
|
||||
{
|
||||
"hotel_id": "HOTEL-TEST",
|
||||
"run_label": "frontend-smoke"
|
||||
}
|
||||
```
|
||||
@@ -500,7 +499,7 @@ GET /api/reservation/tasks/{taskId}
|
||||
- 如果前端只做“按后端字段直接渲染”,现有 `fields[]` 可以支撑第一版表单展示;本轮已经扩展 `ReservationTaskFieldResult`,避免前端维护第二套字段矩阵。
|
||||
- `result_type`、`task_type`、`task_subtype`、`default_value_source` 当前从后端字段矩阵定义透出。
|
||||
- 如果后端已有更细的字段来源或适用场景元数据,可后续再扩展 `field_source`、`applicable_scenario`,不作为本轮 P0 阻塞项。
|
||||
- 前端已统一配置 `VITE_RESERVATION_HOTEL_ID`,并会在 `GET /api/reservation/orders`、`GET /api/reservation/tasks`、`GET /api/reservation/orders/{orderId}` 自动传 `hotel_id`。当前 `GET /api/reservation/tasks/{taskId}` 以及任务写操作 Controller 不接收 `hotel_id`;第一版先按 ID 定位,后续多酒店隔离 / 权限方案统一补齐。
|
||||
- 前端默认不需要为 `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
|
||||
|
||||
@@ -529,7 +528,7 @@ GET /api/reservation/message-notifications/{taskId}
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 否 | 酒店 ID。 |
|
||||
| `hotel_id` | 否 | 酒店 ID。单酒店阶段默认可为空,由后端解析;显式传值时后端会校验访问权限。 |
|
||||
| `order_id` | 否 | 按临时订单或真实订单过滤。 |
|
||||
| `keyword` | 否 | 邮件主题、摘要、发送人关键词。 |
|
||||
| `page_num` | 否 | 页码。 |
|
||||
|
||||
@@ -177,7 +177,6 @@ th-TH
|
||||
VITE_API_BASE_URL=http://localhost:8080
|
||||
VITE_APP_ENV=local
|
||||
VITE_DEFAULT_LOCALE=zh-CN
|
||||
VITE_RESERVATION_HOTEL_ID=HOTEL-TEST
|
||||
VITE_ENABLE_MOCKS=false
|
||||
```
|
||||
|
||||
@@ -191,7 +190,8 @@ VITE_API_PROXY_TARGET=http://127.0.0.1:8081
|
||||
|
||||
- 公开配置可以放 `VITE_*`。
|
||||
- Secret 一律不进入 `VITE_*`。
|
||||
- `VITE_RESERVATION_HOTEL_ID` 是当前前端 Reservation 页面默认酒店上下文,用于订单列表、任务列表和订单详情查询;它不是 Secret,但需要随本地 / test / prod 环境显式配置,避免页面查到错误酒店数据。
|
||||
- Reservation 查询默认不再依赖 `VITE_RESERVATION_HOTEL_ID`。单酒店阶段可不传 `hotel_id`,由后端按平台酒店表唯一 `ACTIVE` 酒店或当前登录用户上下文解析;如前端有酒店选择器,只传当前选中的酒店,后端负责校验访问权限。
|
||||
- `VITE_RESERVATION_HOTEL_ID` 仅允许作为本地夹具或临时调试覆盖,不作为 test / prod 业务事实来源。
|
||||
- 生产需要运行时配置时,应由部署系统生成公开配置文件,例如 `/app-config.json`。
|
||||
- 需要秘密的外部调用一律经后端代理或适配器。
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
- 所有 Secret 都通过环境变量、部署平台 Secret 或密钥管理系统注入,不写入仓库、镜像、前端环境变量或普通配置文件。
|
||||
- 生产默认不保存 AgentBus raw frame 样本。
|
||||
- AgentBus 实时链路开启前,已经确认 WebSocket URL、Token、Bot Address、外部消息幂等键和断线重连语义。
|
||||
- S000/S999 特殊入口结果上线前,必须确认 `AGENTBUS_DEFAULT_HOTEL_ID` 已配置且对应 SourceMessage Inbox 入库酒店一致。
|
||||
- S000/S999 特殊入口结果上线前,必须确认 `platform_hotel` 中存在且只存在一家 `ACTIVE` 酒店,并且已有 SourceMessage Inbox 数据的 `hotel_id` 与该酒店一致。
|
||||
- 原文读取接口开启前,已经确认谁可以使用、在哪些场景使用、如何轮换访问 key。
|
||||
- 日志采集、错误响应和监控面板都不会展示邮件正文、HTML、附件 URL、Token、Cookie、客户姓名、邮箱、电话或支付信息。
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
- `access_token` 只在登录成功响应中返回一次;前端只能放 `sessionStorage`,不能放 `localStorage`、URL、日志或错误上报。
|
||||
- 当前第一版只做可选 Bearer token 解析,现有 Reservation / SourceMessage 业务接口仍不强制登录。
|
||||
- `/api/auth/me` 和 `/api/auth/logout` 需要 `Authorization: Bearer <access_token>`。
|
||||
- 初始管理员 bootstrap 只以“启用状态超级管理员”为阻断条件;如果测试库或生产库只剩禁用超级管理员,应通过环境变量恢复一个可登录超级管理员后再排查账号运营问题。
|
||||
- 内置角色权限矩阵在启动时按代码同步,矩阵移除的旧权限关系会被清理;管理后台上线前不要手工给内置角色追加临时权限作为长期方案。
|
||||
- 普通用户默认酒店由后端写入逻辑和数据库唯一索引共同保持单默认;V10 migration 会在建约束前把历史重复默认清理为每个用户保留 id 最大的一条。
|
||||
- 单酒店阶段系统酒店由 `platform_hotel` 唯一 `ACTIVE` 酒店决定;V12 migration 会通过唯一索引阻止第二家 `ACTIVE` 酒店。上线前如果已有多家 `ACTIVE` 酒店,必须先调整数据,否则迁移或运行时解析会失败。
|
||||
- 管理后台还未上线时,不要把数据库手工改用户、角色、权限作为常规运营手段。
|
||||
|
||||
### 3.3 SourceMessage
|
||||
@@ -104,7 +108,7 @@
|
||||
| `AGENTBUS_CONNECT_TIMEOUT` | 否 | 连接超时,默认 `15s`。 |
|
||||
| `AGENTBUS_MAX_FRAME_BYTES` | 否 | 单个入站 frame 最大字节数,默认 `1048576`。 |
|
||||
| `AGENTBUS_CAPTURE_ENABLED` | 否 | 是否把业务 frame 写入 SourceMessage Inbox。 |
|
||||
| `AGENTBUS_DEFAULT_HOTEL_ID` | 否 | AgentBus 未提供酒店上下文时的默认业务上下文。 |
|
||||
| `AGENTBUS_DEFAULT_HOTEL_ID` | 否 | 旧兼容变量;M005 后 AgentBus 捕获不再使用它作为运行时酒店来源,系统酒店来自 `platform_hotel` 唯一 `ACTIVE` 酒店。 |
|
||||
|
||||
注意:
|
||||
|
||||
@@ -129,8 +133,8 @@
|
||||
- 查询接口和任务结果通知接口使用同一套 Header、签名串、secret、timestamp 和 nonce 规则。
|
||||
- SuperAgent 侧也需要配置同一个 secret,并按原始请求体计算 SHA-256。
|
||||
- 当前第一版只支持一个 HMAC secret,secret 轮换需要协调部署窗口。
|
||||
- 任务结果通知接口 JSON body 里的 `source_message_id` 是外部来源消息 ID,对应 AgentBus `source.external_message_id`;正式 JSON 请求必须带 `hotel_id`,后端用 `hotel_id + provider + channel + external_message_id` 反查内部 SourceMessage Inbox。
|
||||
- 任务结果通知接口也支持 `text/plain` 的 `S000,source_message_id` 和 `S999,source_message_id`。这类请求不在 body 里带 `hotel_id`,后端使用 `AGENTBUS_DEFAULT_HOTEL_ID` 查询 SourceMessage Inbox。
|
||||
- 任务结果通知接口 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。
|
||||
- `application/json` 和 `text/plain` 都必须使用原始请求体计算 SHA-256 并参与 HMAC 签名;SuperAgent 侧不能签名格式化后的 JSON 或二次拼接字符串。
|
||||
- S000/S999 会创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务和隐藏技术订单,任务列表可见,订单列表不可见,不允许编辑、确认、转换订单或执行 OPERA。
|
||||
- SuperAgent 查询上下文接口中的 `source_message_id`、`source_event_index` 第一版仅兼容接收,不参与查询和校验;不要依赖它们限制查询范围。
|
||||
@@ -196,6 +200,7 @@
|
||||
当前 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`
|
||||
|
||||
上线前确认:
|
||||
|
||||
|
||||
@@ -268,8 +268,8 @@ AGENTBUS_SAMPLE_DIR=var/agentbus-samples
|
||||
AGENTBUS_MAX_FRAME_BYTES=1048576
|
||||
AGENTBUS_MAX_SAMPLES=100
|
||||
AGENTBUS_CAPTURE_ENABLED=true
|
||||
AGENTBUS_DEFAULT_HOTEL_ID=HOTEL-TEST
|
||||
AGENTBUS_REPLY_MODE=NONE
|
||||
AUTH_DEV_BOOTSTRAP_DEFAULT_HOTEL_ID=HOTEL-DEV
|
||||
SOURCE_MESSAGE_DEV_ORIGINAL_READ_ACCESS_KEY=
|
||||
SOURCE_MESSAGE_ORIGINAL_READ_ACCESS_KEY=
|
||||
```
|
||||
@@ -289,7 +289,7 @@ SOURCE_MESSAGE_ORIGINAL_READ_ACCESS_KEY=
|
||||
| `AGENTBUS_MAX_FRAME_BYTES` | 否 | 单个入站 frame 最大字节数。 |
|
||||
| `AGENTBUS_MAX_SAMPLES` | 否 | 最多保留的本地样本数。 |
|
||||
| `AGENTBUS_CAPTURE_ENABLED` | 否 | 是否写入 SourceMessage Inbox。 |
|
||||
| `AGENTBUS_DEFAULT_HOTEL_ID` | 否 | AgentBus 未提供租户上下文时的默认业务上下文。 |
|
||||
| `AUTH_DEV_BOOTSTRAP_DEFAULT_HOTEL_ID` | 否 | dev 初始化平台酒店;M005 后 AgentBus 捕获运行时从 `platform_hotel` 唯一 `ACTIVE` 酒店解析系统酒店,不再依赖 `AGENTBUS_DEFAULT_HOTEL_ID`。 |
|
||||
| `AGENTBUS_REPLY_MODE` | 否 | 调试回复模式。真实客户渠道应保持 `NONE`。 |
|
||||
| `SOURCE_MESSAGE_DEV_ORIGINAL_READ_ACCESS_KEY` | 是 | dev 原文读取接口的临时受控访问 key,后续可替换为正式权限体系。 |
|
||||
| `SOURCE_MESSAGE_TEST_ORIGINAL_READ_ACCESS_KEY` | 是 | test 原文读取接口的临时受控访问 key。 |
|
||||
@@ -390,6 +390,8 @@ reply_policy.final_only
|
||||
hotel_id + provider + channel + external_message_id
|
||||
```
|
||||
|
||||
中文说明:M005 后 `hotel_id` 由本系统后端解析,不要求 AgentBus payload 携带酒店;单酒店阶段要求 `platform_hotel` 只有一家 `ACTIVE` 酒店。
|
||||
|
||||
重复投递时返回已有 Inbox,不覆盖原始 payload,不创建重复记录。
|
||||
|
||||
如果 payload 缺少必要字段或格式不符合预期,也应保存为 `FAILED` Inbox,并记录安全错误
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 文档版本 | 0.4 |
|
||||
| 文档版本 | 0.5 |
|
||||
| 日期 | 2026-07-10 |
|
||||
| 状态 | 已增加 S000/S999 特殊入口结果处理 |
|
||||
| 状态 | 已增加 S000/S999 特殊入口结果处理,并完成单酒店阶段 hotel_id 后端解析收口 |
|
||||
| 适用范围 | SuperAgent 调用本系统查询上下文、查询邮件会话、提交 AI 任务结果 |
|
||||
| 主要读者 | SuperAgent 对接方、后端、测试、运维 |
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
| 参数 | 当前联调值 | 中文说明 |
|
||||
| --- | --- | --- |
|
||||
| `TH_HOTEL_API_BASE_URL` | `http://8.138.234.141:18087` | 本系统后端基础地址;本地联调用 8080,部署环境改为实际网关或服务地址。 |
|
||||
| `HOTEL_ID` | `HOTEL-DEV` | 当前 dev profile 下 AgentBus 入库默认酒店 ID;查询接口和 JSON 任务结果请求体应传入 `hotel_id`,S000/S999 文本结果使用本系统默认酒店。 |
|
||||
| `SYSTEM_HOTEL` | 后端平台酒店表唯一 `ACTIVE` 酒店 | SuperAgent 不需要配置或传入 `hotel_id`;单酒店阶段由 TH Hotel 后端从 `platform_hotel` 解析。 |
|
||||
| `SUPERAGENT_CLIENT_ID` | `superagent-debug` | SuperAgent 调用方 ID,对应 Header `X-TH-Hotel-SuperAgent-Client-Id`。 |
|
||||
| `SUPERAGENT_HMAC_SECRET` | `th-hotel-superagent-debug-20260709-change-before-prod` | dev/test 联调临时 HMAC 密钥;生产上线前必须更换为新的高强度随机密钥。 |
|
||||
|
||||
@@ -99,11 +99,11 @@ X-TH-Hotel-SuperAgent-Signature: sha256=<lowercase-hex-signature>
|
||||
| 外部来源消息 ID | AgentBus 邮件 payload 中的 `source.external_message_id`,SuperAgent / Main Agent 在最终 JSON 中原样带回为 `source_message_id` | SuperAgent 任务结果通知接口入参和响应回显 |
|
||||
| 内部 SourceMessage Inbox ID | `platform_source_message_inbox.id`,本系统数据库内部主键 | `workflow_*` 表的 `source_message_id` 外键、前端和运维排查 |
|
||||
|
||||
SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通知接口收到外部 `source_message_id` 后,后端使用 `hotel_id + provider + channel + external_message_id` 反查内部 Inbox 记录,再用内部 ID 写入业务表。
|
||||
SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通知接口收到外部 `source_message_id` 后,后端先解析系统酒店,再使用 `hotel_id + provider + channel + external_message_id` 反查内部 Inbox 记录,最后用内部 ID 写入业务表。
|
||||
|
||||
查询接口 1、2 在 SuperAgent 查询阶段不依赖当前邮件是否已经入库。若请求体兼容旧契约传入 `source_message_id` 或 `source_event_index`,第一版后端会接收但忽略,不校验它们的格式,也不把它们作为查询边界。
|
||||
|
||||
查询接口 3、4 面向已经入库的邮件会话:`external_conversation_id` 按 `hotel_id + source_provider + source_channel + external_conversation_id` 查询;`source_message_id` 表示外部来源消息 ID,可作为锚点反查该邮件所属会话。
|
||||
查询接口 3、4 面向已经入库的邮件会话:缺省酒店由后端解析,`external_conversation_id` 最终仍按 `hotel_id + source_provider + source_channel + external_conversation_id` 查询;`source_message_id` 表示外部来源消息 ID,可作为锚点反查该邮件所属会话。
|
||||
|
||||
## 4. 接口 1:查询订单上下文
|
||||
|
||||
@@ -121,7 +121,6 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
```json
|
||||
{
|
||||
"hotel_id": "<HOTEL_ID>",
|
||||
"group_code": "GRP-001",
|
||||
"confirmation_number": null,
|
||||
"reservation_no": null,
|
||||
@@ -135,7 +134,7 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
| 字段 | 是否必填 | 中文说明 |
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 是 | 酒店上下文 ID |
|
||||
| `hotel_id` | 否 | 酒店上下文 ID;SuperAgent 默认不传,后端按平台酒店表唯一 `ACTIVE` 酒店解析。若兼容旧契约传入,单酒店阶段必须与系统酒店一致。 |
|
||||
| `group_code` | 条件必填 | Group / Allotment 查询 key |
|
||||
| `confirmation_number` | 条件必填 | FIT Confirmation Number 查询 key |
|
||||
| `reservation_no` | 条件必填 | OPERA reservation no;当前系统无可靠表源,只传该字段时会返回人工复核原因 |
|
||||
@@ -145,7 +144,7 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
`group_code`、`confirmation_number`、`reservation_no` 至少一个非空。当前稳定查询能力优先支持 `group_code` 和 `confirmation_number`。
|
||||
|
||||
全局上下文查询只依赖 `hotel_id + 业务 key`;`source_message_id` 和 `source_event_index` 不作为查询边界,传入时也不会影响查询结果。
|
||||
全局上下文查询最终依赖“后端解析出的酒店 ID + 业务 key”;`source_message_id` 和 `source_event_index` 不作为查询边界,传入时也不会影响查询结果。
|
||||
|
||||
### 4.3 成功响应
|
||||
|
||||
@@ -205,7 +204,6 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
```json
|
||||
{
|
||||
"hotel_id": "<HOTEL_ID>",
|
||||
"object_id": "ORDER:1900000000000000100",
|
||||
"object_type": "group_block"
|
||||
}
|
||||
@@ -215,7 +213,7 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
| 字段 | 是否必填 | 中文说明 |
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 是 | 酒店上下文 ID |
|
||||
| `hotel_id` | 否 | 酒店上下文 ID;SuperAgent 默认不传,后端解析系统酒店。 |
|
||||
| `object_id` | 是 | 查询对象 ID,第一版只支持 `ORDER:{order_id}` |
|
||||
| `object_type` | 否 | 调用方对象类型提示,第一版不作为强校验 |
|
||||
|
||||
@@ -285,7 +283,6 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
```json
|
||||
{
|
||||
"hotel_id": "<HOTEL_ID>",
|
||||
"source_provider": "AGENTBUS",
|
||||
"source_channel": "EMAIL",
|
||||
"external_conversation_id": "thread-20260708-0001"
|
||||
@@ -296,7 +293,6 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
```json
|
||||
{
|
||||
"hotel_id": "<HOTEL_ID>",
|
||||
"source_provider": "AGENTBUS",
|
||||
"source_channel": "EMAIL",
|
||||
"source_message_id": "mail-20260708-0001"
|
||||
@@ -307,7 +303,7 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
| 字段 | 是否必填 | 中文说明 |
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 是 | 酒店上下文 ID |
|
||||
| `hotel_id` | 否 | 酒店上下文 ID;SuperAgent 默认不传,后端解析系统酒店。 |
|
||||
| `source_provider` | 否 | 来源提供方,按会话 ID 查询和按 `source_message_id` 反查时都参与隔离,缺省为 `AGENTBUS` |
|
||||
| `source_channel` | 否 | 来源渠道,按会话 ID 查询和按 `source_message_id` 反查时都参与隔离,缺省为 `EMAIL` |
|
||||
| `external_conversation_id` | 条件必填 | 外部邮件会话 ID,对应 AgentBus `source.external_conversation_id` |
|
||||
@@ -383,7 +379,6 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
```json
|
||||
{
|
||||
"hotel_id": "<HOTEL_ID>",
|
||||
"source_provider": "AGENTBUS",
|
||||
"source_channel": "EMAIL",
|
||||
"source_message_id": "mail-20260708-0001"
|
||||
@@ -444,7 +439,6 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
```json
|
||||
{
|
||||
"hotel_id": "<HOTEL_ID>",
|
||||
"source_message_id": "mail-20260708-0001",
|
||||
"ai_task_results": [
|
||||
{
|
||||
@@ -479,7 +473,7 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
|
||||
| 字段 | 是否必填 | 中文说明 |
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 是 | 酒店上下文 ID,用于反查 SourceMessage Inbox 幂等键 |
|
||||
| `hotel_id` | 否 | 酒店上下文 ID;SuperAgent 默认不传,后端解析系统酒店后用于反查 SourceMessage Inbox 幂等键。若兼容旧契约传入,单酒店阶段必须与系统酒店一致。 |
|
||||
| `source_message_id` | 是 | SuperAgent / Main Agent 原样带回的外部来源消息 ID,对应 AgentBus `source.external_message_id`,一次请求只能有一个 |
|
||||
| `source_provider` | 否 | 来源提供方,第一版缺省为 `AGENTBUS` |
|
||||
| `source_channel` | 否 | 来源渠道,第一版缺省为 `EMAIL` |
|
||||
@@ -493,7 +487,7 @@ SuperAgent 不应知道或依赖内部 SourceMessage Inbox ID。任务结果通
|
||||
| `ai_task_results[].case_keys` | 否 | 订单关联候选键 |
|
||||
| `ai_task_results[].extracted_fields` | 否 | 业务字段主体 |
|
||||
|
||||
正式联调时,后端通过 `hotel_id + source_provider(默认 AGENTBUS) + source_channel(默认 EMAIL) + source_message_id` 查找 `platform_source_message_inbox.external_message_id`。如果没有找到,返回 `SOURCE_MESSAGE_NOT_FOUND`。本地旧夹具允许在缺少 `hotel_id` 时使用内部数字 SourceMessage ID,但该兼容路径不作为 SuperAgent 正式契约。
|
||||
正式联调时,SuperAgent 不需要传 `hotel_id`。后端通过系统酒店 `hotel_id + source_provider(默认 AGENTBUS) + source_channel(默认 EMAIL) + source_message_id` 查找 `platform_source_message_inbox.external_message_id`。如果没有找到,返回 `SOURCE_MESSAGE_NOT_FOUND`。本地旧夹具允许在缺少 `hotel_id` 时使用内部数字 SourceMessage ID,但该兼容路径不作为 SuperAgent 正式契约。
|
||||
|
||||
`informational_message` 结构化任务仅用于历史兼容。新入口如果是纯信息类邮件或无法形成业务素材包,不要提交空数组,也不要生成 `informational_message`;应使用下面的 S000/S999 文本请求体。
|
||||
|
||||
@@ -519,7 +513,7 @@ S999,mail-20260708-0001
|
||||
| `S999` | 入口阶段无法形成业务素材包,不需要进入业务执行。 |
|
||||
| `mail-20260708-0001` | 外部来源消息 ID,对应 SourceMessage Inbox 的 `external_message_id`。 |
|
||||
|
||||
第一版 S000/S999 不在 body 里传 `hotel_id`,后端使用系统默认酒店 `AGENTBUS_DEFAULT_HOTEL_ID` 查询 SourceMessage Inbox。命中后创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务:任务列表可见,订单列表不可见,不允许编辑、确认、转换订单、执行 OPERA 或重试 OPERA,也不参与同订单任务执行顺序阻塞。
|
||||
第一版 S000/S999 不在 body 里传 `hotel_id`,后端使用平台酒店表唯一 `ACTIVE` 酒店查询 SourceMessage Inbox。命中后创建 `SOURCE_MESSAGE_ONLY` 只读特殊任务:任务列表可见,订单列表不可见,不允许编辑、确认、转换订单、执行 OPERA 或重试 OPERA,也不参与同订单任务执行顺序阻塞。
|
||||
|
||||
### 8.4 成功响应
|
||||
|
||||
@@ -621,7 +615,9 @@ S000/S999 成功响应示例:
|
||||
| `MESSAGE_CONVERSATION_QUERY_KEY_REQUIRED` | 400 | 会话查询缺少 `external_conversation_id` 或 `source_message_id` |
|
||||
| `OBJECT_NOT_FOUND` | 404 | 对象详情查询目标不存在 |
|
||||
| `MESSAGE_CONVERSATION_NOT_FOUND` | 404 | 外部邮件会话尚未写入 SourceMessage Inbox |
|
||||
| `HOTEL_ID_REQUIRED` | 400 | 查询接口和 JSON 任务结果缺少必填 `hotel_id`;S000/S999 使用系统默认酒店 |
|
||||
| `SYSTEM_HOTEL_NOT_CONFIGURED` | 409 | 平台酒店表没有可用 `ACTIVE` 酒店 |
|
||||
| `SYSTEM_HOTEL_AMBIGUOUS` | 409 | 单酒店阶段平台酒店表存在多家 `ACTIVE` 酒店 |
|
||||
| `HOTEL_ACCESS_DENIED` | 403 | 显式传入的 `hotel_id` 与系统酒店或当前用户授权酒店不一致 |
|
||||
| `SOURCE_MESSAGE_NOT_FOUND` | 404 | 任务结果通知或会话锚点引用的外部来源消息尚未写入 SourceMessage Inbox |
|
||||
|
||||
## 10. HMAC 上线配置
|
||||
|
||||
@@ -32,10 +32,11 @@
|
||||
|
||||
| 用例 ID | 场景 | 输入要点 | 期望结果 |
|
||||
| --- | --- | --- | --- |
|
||||
| MCP-T01-001 | 按 group code 查询 | `hotel_id` + `group_code` | 返回成功 envelope |
|
||||
| MCP-T01-002 | 按 confirmation 查询 | `hotel_id` + `confirmation_number` | 返回成功 envelope |
|
||||
| MCP-T01-003 | 缺少查询 key | 只有 `hotel_id` | 返回 `QUERY_KEY_REQUIRED` |
|
||||
| MCP-T01-004 | 缺少 hotel id | 不传 `hotel_id` | 返回 `HOTEL_ID_REQUIRED` |
|
||||
| MCP-T01-001 | 按 group code 查询 | `group_code`,`hotel_id` 可选 | 返回成功 envelope |
|
||||
| MCP-T01-002 | 按 confirmation 查询 | `confirmation_number`,`hotel_id` 可选 | 返回成功 envelope |
|
||||
| MCP-T01-003 | 缺少查询 key | 不传 `group_code` 和 `confirmation_number` | 返回 `QUERY_KEY_REQUIRED` |
|
||||
| MCP-T01-004 | 缺省 hotel id | 不传 `hotel_id`,传 `group_code` | 后端按系统唯一 ACTIVE 酒店查询并返回成功 envelope |
|
||||
| MCP-T01-005 | hotel id 不一致 | 传非系统酒店 `hotel_id` | 返回 `HOTEL_ACCESS_DENIED` 或 `HOTEL_ID_MISMATCH` |
|
||||
|
||||
## 5. th_hotel_query_object_detail
|
||||
|
||||
@@ -87,8 +88,9 @@
|
||||
| MCP-T05-002 | 提交 manual review | `result_type=manual_review` | 写入人工复核任务 |
|
||||
| MCP-T05-003 | 提交 informational message | `result_type=informational_message` | 写入提示类信息 |
|
||||
| MCP-T05-004 | source message 不存在 | 不存在的外部 `source_message_id` | 返回 `SOURCE_MESSAGE_NOT_FOUND` |
|
||||
| MCP-T05-005 | 缺少 hotel id | 不传 `hotel_id` | 返回 `HOTEL_ID_REQUIRED` |
|
||||
| MCP-T05-005 | 缺省 hotel id | 不传 `hotel_id`,source message 属于系统酒店 | 后端按系统唯一 ACTIVE 酒店写入成功 |
|
||||
| MCP-T05-006 | 重复提交同一幂等任务 | 使用相同幂等信息 | 不重复创建业务任务 |
|
||||
| MCP-T05-007 | hotel id 不一致 | 显式传非系统酒店 `hotel_id` | 返回 `HOTEL_ID_MISMATCH` |
|
||||
|
||||
写入验证:
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
| 字段 | 默认值 | 中文说明 |
|
||||
| --- | --- | --- |
|
||||
| `hotel_id` | 后端解析 | SuperAgent 默认不传;单酒店阶段由 TH Hotel 后端从 `platform_hotel` 唯一 `ACTIVE` 酒店解析,兼容旧调用传入时必须与系统酒店一致。 |
|
||||
| `source_provider` | `AGENTBUS` | 来源提供方 |
|
||||
| `source_channel` | `EMAIL` | 来源渠道 |
|
||||
|
||||
@@ -65,8 +66,8 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"hotel_id": {
|
||||
"type": "string",
|
||||
"description": "酒店上下文 ID"
|
||||
"type": ["string", "null"],
|
||||
"description": "可选酒店上下文 ID;默认由 TH Hotel 后端解析"
|
||||
},
|
||||
"group_code": {
|
||||
"type": ["string", "null"],
|
||||
@@ -93,7 +94,7 @@
|
||||
"description": "历史线程 key 是否仅作为证据"
|
||||
}
|
||||
},
|
||||
"required": ["hotel_id"]
|
||||
"required": []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -135,8 +136,8 @@ POST /api/ai-query/v1/case-context
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"hotel_id": {
|
||||
"type": "string",
|
||||
"description": "酒店上下文 ID"
|
||||
"type": ["string", "null"],
|
||||
"description": "可选酒店上下文 ID;默认由 TH Hotel 后端解析"
|
||||
},
|
||||
"object_id": {
|
||||
"type": "string",
|
||||
@@ -147,7 +148,7 @@ POST /api/ai-query/v1/case-context
|
||||
"description": "调用方对象类型提示"
|
||||
}
|
||||
},
|
||||
"required": ["hotel_id", "object_id"]
|
||||
"required": ["object_id"]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -193,8 +194,8 @@ SuperAgent 提交任务结果时的外部 `source_message_id`。
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"hotel_id": {
|
||||
"type": "string",
|
||||
"description": "酒店上下文 ID"
|
||||
"type": ["string", "null"],
|
||||
"description": "可选酒店上下文 ID;默认由 TH Hotel 后端解析"
|
||||
},
|
||||
"source_provider": {
|
||||
"type": ["string", "null"],
|
||||
@@ -213,7 +214,7 @@ SuperAgent 提交任务结果时的外部 `source_message_id`。
|
||||
"description": "外部来源消息 ID,可作为锚点反查会话"
|
||||
}
|
||||
},
|
||||
"required": ["hotel_id"]
|
||||
"required": []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -221,7 +222,7 @@ SuperAgent 提交任务结果时的外部 `source_message_id`。
|
||||
|
||||
- `external_conversation_id`、`source_message_id` 至少一个非空。
|
||||
- 两者同时传入时,后端按 `external_conversation_id` 查询为准。
|
||||
- 查询按 `hotel_id + source_provider + source_channel + external_conversation_id` 隔离。
|
||||
- 查询最终按“后端解析出的酒店 ID + source_provider + source_channel + external_conversation_id”隔离。
|
||||
|
||||
### 5.4 输出
|
||||
|
||||
@@ -282,8 +283,8 @@ POST /api/ai-query/v1/message-conversation/tasks
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"hotel_id": {
|
||||
"type": "string",
|
||||
"description": "酒店上下文 ID"
|
||||
"type": ["string", "null"],
|
||||
"description": "可选酒店上下文 ID;默认由 TH Hotel 后端解析"
|
||||
},
|
||||
"source_provider": {
|
||||
"type": ["string", "null"],
|
||||
@@ -302,7 +303,7 @@ POST /api/ai-query/v1/message-conversation/tasks
|
||||
"description": "外部来源消息 ID,可作为锚点反查会话"
|
||||
}
|
||||
},
|
||||
"required": ["hotel_id"]
|
||||
"required": []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -334,14 +335,14 @@ POST /api/ai-query/v1/message-conversation/messages
|
||||
### 7.2 何时使用
|
||||
|
||||
- SuperAgent 已完成当前邮件的最终任务拆分。
|
||||
- 已确认 `hotel_id` 和外部 `source_message_id` 来自 AgentBus payload。
|
||||
- 已确认外部 `source_message_id` 来自 AgentBus payload;`hotel_id` 由 TH Hotel 后端解析。
|
||||
- 需要把 AI 任务结果交给 TH Hotel 后端进入人工确认流程。
|
||||
|
||||
### 7.3 不应使用
|
||||
|
||||
- 不应在试探、草稿、未完成推理阶段调用。
|
||||
- 不应把历史邮件中的外部来源消息 ID 当作当前邮件 ID 提交。
|
||||
- 不应在缺少 `hotel_id` 或 `source_message_id` 时调用。
|
||||
- 不应在缺少 `source_message_id` 时调用;`hotel_id` 不需要 SuperAgent 提供。
|
||||
|
||||
### 7.4 输入 Schema
|
||||
|
||||
@@ -351,8 +352,8 @@ POST /api/ai-query/v1/message-conversation/messages
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"hotel_id": {
|
||||
"type": "string",
|
||||
"description": "酒店上下文 ID"
|
||||
"type": ["string", "null"],
|
||||
"description": "可选酒店上下文 ID;默认由 TH Hotel 后端解析"
|
||||
},
|
||||
"source_provider": {
|
||||
"type": ["string", "null"],
|
||||
@@ -381,7 +382,7 @@ POST /api/ai-query/v1/message-conversation/messages
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["hotel_id", "source_message_id", "ai_task_results"]
|
||||
"required": ["source_message_id", "ai_task_results"]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 文档版本 | 0.2 |
|
||||
| 文档版本 | 0.3 |
|
||||
| 日期 | 2026-07-08 |
|
||||
| 状态 | 第一版后端实现依据与落地记录 |
|
||||
| 适用范围 | SuperAgent / Main Agent 调用本系统查询订单和任务上下文 |
|
||||
@@ -33,7 +33,7 @@
|
||||
- 如果查询 key 来自历史线程,调用方必须传 `target_key_source=body_thread_evidence` 和 `body_thread_used_only_as_evidence=true`。
|
||||
- 第一版不伪造 OPERA 字段。当前系统没有可靠来源的字段返回 `null`,并在 `warnings` 或 `hard_validation_warnings` 中说明。
|
||||
- SuperAgent 允许作为全局上下文查询方按任意业务 key 查询;`source_message_id` 和 `source_event_index` 在查询阶段对本系统没有业务作用,第一版接收但忽略,不做格式校验,也不作为查询边界。
|
||||
- 导入契约没有显式要求 `hotel_id`,但本系统订单、任务和 AI 过渡表均按 `hotel_id` 隔离。第一版请求体必须显式传 `hotel_id`。
|
||||
- 导入契约没有显式要求 `hotel_id`,本系统订单、任务和 AI 过渡表仍按 `hotel_id` 隔离。M005 后 SuperAgent 默认不传 `hotel_id`,后端按平台酒店表唯一 `ACTIVE` 酒店解析;兼容旧调用传入时必须与系统酒店一致。
|
||||
|
||||
## 3. Skill 对接口 1、2 的实际需要
|
||||
|
||||
@@ -146,7 +146,7 @@ POST /api/ai-query/v1/case-context
|
||||
|
||||
| 字段 | 是否必填 | 中文说明 | 当前系统来源或用途 |
|
||||
| --- | --- | --- | --- |
|
||||
| `hotel_id` | 是 | 酒店或业务上下文 ID | 用于隔离 `workflow_reservation_*` 表 |
|
||||
| `hotel_id` | 否 | 酒店或业务上下文 ID | SuperAgent 默认不传;后端解析系统酒店后用于隔离 `workflow_reservation_*` 表 |
|
||||
| `source_message_id` | 否 | SuperAgent 透传的外部来源消息 ID | 全局上下文查询可不传;传入时后端接收但忽略,不做格式校验 |
|
||||
| `source_event_index` | 否 | SuperAgent 透传的 current 事件序号 | 全局上下文查询可不传;传入时后端接收但忽略,不做正整数校验 |
|
||||
| `group_code` | 条件必填 | Group / Allotment 优先业务 key | 查询 `GROUP_CODE` 类型订单和 AI 过渡记录 |
|
||||
@@ -321,7 +321,7 @@ POST /api/ai-query/v1/object-detail
|
||||
|
||||
| 字段 | 是否必填 | 中文说明 | 当前系统来源或用途 |
|
||||
| --- | --- | --- | --- |
|
||||
| `hotel_id` | 是 | 酒店或业务上下文 ID | 用于隔离订单和任务 |
|
||||
| `hotel_id` | 否 | 酒店或业务上下文 ID | SuperAgent 默认不传;后端解析系统酒店后用于隔离订单和任务 |
|
||||
| `object_id` | 是 | 接口 1 返回的对象 ID | 第一版支持 `ORDER:{order_id}` |
|
||||
| `object_type` | 否 | 对象类型提示 | 用于校验调用方预期和实际对象类型 |
|
||||
|
||||
@@ -425,8 +425,8 @@ POST /api/ai-query/v1/object-detail
|
||||
|
||||
| 能力 | 当前来源 |
|
||||
| --- | --- |
|
||||
| 按 `hotel_id + GROUP_CODE` 查询 ACTIVE 订单 | `workflow_reservation_order.order_key_type`、`active_business_key` |
|
||||
| 按 `hotel_id + CONFIRMATION_NUMBER` 查询 ACTIVE 订单 | `workflow_reservation_order.order_key_type`、`active_business_key` |
|
||||
| 按“后端解析出的酒店 ID + GROUP_CODE”查询 ACTIVE 订单 | `workflow_reservation_order.order_key_type`、`active_business_key` |
|
||||
| 按“后端解析出的酒店 ID + CONFIRMATION_NUMBER”查询 ACTIVE 订单 | `workflow_reservation_order.order_key_type`、`active_business_key` |
|
||||
| 查询临时订单、终止订单、逻辑删除订单 | `workflow_reservation_order.order_status` |
|
||||
| 查询同订单任务队列 | `workflow_reservation_task.order_id`、`queue_participation`、`execution_order` |
|
||||
| 查询 pending/open task | `workflow_reservation_task.task_status` |
|
||||
@@ -463,7 +463,7 @@ POST /api/ai-query/v1/object-detail
|
||||
|
||||
已落地能力:
|
||||
|
||||
- 接口 1 可按 `hotel_id + group_code` 或 `hotel_id + confirmation_number` 查询订单上下文,允许不传 `source_message_id` 和 `source_event_index` 的全局上下文查询;即使传入这两个字段,后端也不把它们作为查询或校验条件。
|
||||
- 接口 1 可按“后端解析出的酒店 ID + group_code”或“后端解析出的酒店 ID + confirmation_number”查询订单上下文,允许不传 `source_message_id` 和 `source_event_index` 的全局上下文查询;即使传入这两个字段,后端也不把它们作为查询或校验条件。
|
||||
- 接口 1 返回 `matched_order_records`、`pending_or_open_tasks`、`active_workflows`、`terminated_records`、`target_object_validation` 和 `key_relationships`。
|
||||
- `active_workflows` 当前无独立表源,固定返回空数组。
|
||||
- 接口 2 支持 `ORDER:{order_id}` 查询本系统订单快照。
|
||||
|
||||
@@ -444,7 +444,9 @@ S000 / S999 文本结果创建成功时,同样返回 `201 Created`。这类结
|
||||
| 409 | `AUTH_NONCE_REPLAY` | Nonce 重放 |
|
||||
| 413 | `REQUEST_BODY_TOO_LARGE` | 请求体过大 |
|
||||
| 400 | `INVALID_JSON` | JSON 不可解析 |
|
||||
| 400 | `HOTEL_ID_REQUIRED` | 使用外部 `source_message_id` 时缺少 `hotel_id` |
|
||||
| 400 | `HOTEL_ID_MISMATCH` | 显式 `hotel_id` 或历史内部 SourceMessage ID 所属酒店与系统酒店不一致 |
|
||||
| 409 | `SYSTEM_HOTEL_NOT_CONFIGURED` | 平台酒店表没有 ACTIVE 酒店,无法解析系统酒店 |
|
||||
| 409 | `SYSTEM_HOTEL_AMBIGUOUS` | 单酒店阶段平台酒店表存在多家 ACTIVE 酒店 |
|
||||
| 400 | `SOURCE_MESSAGE_REQUIRED` | `source_message_id` 缺失 |
|
||||
| 404 | `SOURCE_MESSAGE_NOT_FOUND` | 外部来源消息尚未写入 SourceMessage Inbox |
|
||||
| 400 | `TASK_RESULTS_EMPTY` | `ai_task_results[]` 为空 |
|
||||
|
||||
391
docs/project/requirements/M005-hotel-context-unification-plan.md
Normal file
391
docs/project/requirements/M005-hotel-context-unification-plan.md
Normal file
@@ -0,0 +1,391 @@
|
||||
# M005 Hotel Context 统一收口改造计划
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 文档版本 | 0.2 |
|
||||
| 日期 | 2026-07-10 |
|
||||
| 状态 | 已落地第一版:单酒店阶段严格要求 `platform_hotel` 必须且只能有一家 `ACTIVE` 酒店 |
|
||||
| 适用范围 | `hotel_id` 来源、用户酒店上下文、SuperAgent / MCP、AgentBus、Reservation 查询、SourceMessage 查询、Debug / Demo 入口 |
|
||||
| 主要读者 | 产品、后端、前端、测试、后续协作 agent |
|
||||
|
||||
## 1. 文档定位
|
||||
|
||||
本文记录当前系统 `hotel_id` 使用方式的统一收口方案。目标不是删除业务表里的
|
||||
`hotel_id`,而是把运行时 `hotel_id` 的来源从“外部系统传入、前端环境变量、后端硬编码和配置默认值混用”
|
||||
调整为“后端从平台酒店表和当前用户上下文解析”。
|
||||
|
||||
当前已明确:
|
||||
|
||||
- SuperAgent 不会传 `hotel_id`。
|
||||
- AgentBus 不会传 `hotel_id`。
|
||||
- 本系统上线前暂时只服务一家酒店。
|
||||
- 后端已经有平台酒店表 `platform_hotel` 和用户酒店授权表 `platform_user_hotel`。
|
||||
- 登录态第一版采用数据库 session token,不使用 JWT;token 字符串本身不携带酒店 ID。
|
||||
- 单酒店阶段采用严格规则:`platform_hotel` 没有 `ACTIVE` 酒店时报错,多家 `ACTIVE` 酒店也报错;数据库通过唯一约束阻止新增第二家 `ACTIVE` 酒店。
|
||||
|
||||
因此,后续业务入口不应再要求 SuperAgent、AgentBus 或前端环境变量作为酒店上下文事实来源。
|
||||
系统内部仍然必须保留 `hotel_id`,用于数据隔离、幂等键、查询过滤、审计和未来多酒店扩展。
|
||||
|
||||
## 2. 核心结论
|
||||
|
||||
这次想法与现有系统没有根本冲突,可以实现,但需要把现有几个来源不一致的入口统一改造。
|
||||
|
||||
关键判断如下:
|
||||
|
||||
- `hotel_id` 字段继续保留在 SourceMessage、Reservation、Task、OPERA、审计等业务表中。
|
||||
- 外部系统不传 `hotel_id` 是合理的,酒店归属应由本系统后端根据平台酒店表解析。
|
||||
- token 不是 JWT,不会把 `hotel_id` 放在 token 字符串里;后端可通过 session token 解析出
|
||||
`AuthenticatedUserContext.defaultHotelId` 和 `accessibleHotelIds`。
|
||||
- 前端展示酒店已经可以从 `/api/auth/me` 返回的 `hotels[]` 和 `default_hotel_id` 获取;Reservation 查询默认不再依赖 `VITE_RESERVATION_HOTEL_ID` 自动拼查询参数。
|
||||
- 任务详情、任务写操作和 OPERA 操作当前多处已从任务自身读取 `task.hotelId()`,这类逻辑方向正确,
|
||||
后续重点是补权限校验和查询隔离,不需要前端再传 `hotel_id`。
|
||||
|
||||
## 3. 非目标范围
|
||||
|
||||
本次 M005 不做以下事情:
|
||||
|
||||
- 不删除任何业务表中的 `hotel_id` 字段。
|
||||
- 不把 token 改成 JWT。
|
||||
- 不实现完整多酒店路由策略,例如按邮箱、AgentBus channel、SuperAgent org 或 OHIP 配置自动映射酒店。
|
||||
- 不新增酒店管理后台 CRUD。
|
||||
- 不改变 SuperAgent 和 AgentBus 的系统定位:AgentBus 仍是消息入口适配器,SuperAgent 仍是 AI / Agent 能力提供方。
|
||||
- 不让前端直接调用数据库、SuperAgent、AgentBus 或任何持有 Secret 的外部系统。
|
||||
|
||||
## 4. 当前现状梳理
|
||||
|
||||
| 入口或模块 | 当前 `hotel_id` 来源 | 问题 | 目标方向 |
|
||||
| --- | --- | --- | --- |
|
||||
| AgentBus 入站捕获 | 改造前使用 `agentbus.capture.default-hotel-id`,代码默认 `HOTEL-TEST` | AgentBus 不会传酒店,配置默认值和平台酒店表割裂 | 已改为从平台酒店表解析系统酒店 |
|
||||
| SuperAgent 任务结果 REST | 改造前 JSON 正式请求要求 `hotel_id`;S000 / S999 文本请求使用 AgentBus 默认酒店 | SuperAgent 不会传酒店,正式契约与真实能力冲突 | 已支持缺省 `hotel_id` 使用系统酒店,再用 SourceMessage 自身酒店写业务表 |
|
||||
| SuperAgent MCP 查询工具 | 改造前 tools schema 要求 `hotel_id` | SuperAgent MCP 配置页面和调用方不应承担酒店上下文 | 已将 `hotel_id` 改为可选,服务端解析系统酒店 |
|
||||
| Reservation AI 查询服务 | 改造前 `ReservationAiQueryServiceImpl` 校验 `hotel_id` 必填 | 机器查询入口无法在无酒店参数时工作 | 已统一通过酒店上下文服务解析 |
|
||||
| 前端 Reservation 列表 / 详情 | 改造前 `VITE_RESERVATION_HOTEL_ID` 自动拼 `hotel_id` | 前端环境变量成为业务事实来源,容易和平台酒店表不一致 | 已改为默认不传;如有当前选中酒店才传,后端校验 |
|
||||
| 任务详情 / 任务写操作 / OPERA | 先按 taskId 找任务,再使用 `task.hotelId()` | 方向正确,但后续需要当前用户可访问酒店校验 | 保持从业务对象自身取酒店,并补权限边界 |
|
||||
| SourceMessage 列表 | 改造前 `hotelId` 查询参数可选;为空时仓储不加酒店过滤 | 单酒店阶段可能还能工作,未来会有跨酒店暴露风险 | 已默认解析当前或系统酒店,不允许无边界列表 |
|
||||
| SourceMessage 会话详情 | 先按内部 SourceMessage ID 找源消息,再用 `source.hotelId()` 查同会话 | 方向正确 | 保持源消息自身酒店上下文 |
|
||||
| Debug EML | 改造前 multipart 必填 `hotel_id` | 测试人员需要手填,和平台酒店表割裂 | 已改为可选,缺省使用系统酒店,显式传值必须校验 |
|
||||
| Demo seed | 改造前请求可传 `hotel_id`,否则使用 demo 配置默认值,默认 `HOTEL-TEST` | 演示数据入口也有独立默认来源 | 已改为系统酒店,返回入口 URL 不再强依赖手写酒店参数 |
|
||||
| 登录 / 当前用户 | `/api/auth/me` 已返回 `default_hotel_id` 和 `hotels[]` | 后端业务接口尚未统一消费当前用户酒店上下文 | 作为用户请求的酒店上下文来源 |
|
||||
|
||||
## 5. 目标酒店上下文模型
|
||||
|
||||
建议新增一个平台级服务,统一承担酒店上下文解析。命名可在实现时二选一:
|
||||
|
||||
```text
|
||||
cn.nianxx.thhotel.platform.hotel.service.HotelContextService
|
||||
cn.nianxx.thhotel.platform.hotel.service.impl.HotelContextServiceImpl
|
||||
```
|
||||
|
||||
或:
|
||||
|
||||
```text
|
||||
cn.nianxx.thhotel.platform.hotel.service.HotelContextResolver
|
||||
cn.nianxx.thhotel.platform.hotel.service.impl.HotelContextResolverImpl
|
||||
```
|
||||
|
||||
职责建议:
|
||||
|
||||
| 方法 | 使用场景 | 规则 |
|
||||
| --- | --- | --- |
|
||||
| `resolveSystemHotelId()` | AgentBus、SuperAgent、MCP、Debug、Demo 等机器入口 | 从 `platform_hotel` 解析当前系统酒店;单酒店阶段要求只有一个启用酒店,或存在明确默认酒店 |
|
||||
| `resolveCurrentHotelId(String requestedHotelId)` | 前端用户查询入口 | 有 token 时优先校验请求酒店是否在 `accessibleHotelIds`;请求为空时使用 `defaultHotelId` |
|
||||
| `requireAccessibleHotel(String hotelId)` | 后续写操作、详情操作、审计查询 | 当前用户必须可访问该酒店;无 token 的兼容策略需单独声明 |
|
||||
|
||||
单酒店阶段建议使用严格规则:
|
||||
|
||||
1. 如果平台酒店表没有启用酒店,启动或首次调用时返回明确错误,例如 `SYSTEM_HOTEL_NOT_CONFIGURED`。
|
||||
2. 如果平台酒店表存在多家启用酒店,但没有明确默认酒店,返回明确错误,例如 `SYSTEM_HOTEL_AMBIGUOUS`。
|
||||
3. 当前阶段不要继续用 `HOTEL-TEST` 作为运行时兜底值;`HOTEL-TEST` 只能保留在测试夹具、示例文档或 bootstrap 默认配置中。
|
||||
|
||||
## 6. 各入口目标规则
|
||||
|
||||
### 6.1 AgentBus 入站捕获
|
||||
|
||||
当前代码位置:
|
||||
|
||||
- `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/adapter/AgentBusFrameProcessor.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/integrations/messaging/agentbus/adapter/AgentBusProperties.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/platform/message/service/impl/SourceMessageCaptureServiceImpl.java`
|
||||
|
||||
目标规则:
|
||||
|
||||
- `AgentBusFrameProcessor` 不再从 `AgentBusProperties.capture.defaultHotelId` 取酒店。
|
||||
- 捕获前调用 `HotelContextService.resolveSystemHotelId()`。
|
||||
- `SourceMessageCaptureServiceImpl` 仍要求 `CaptureSourceMessageCommand.hotelId` 非空,因为落库必须有稳定酒店上下文。
|
||||
- `agentbus.capture.default-hotel-id` 后续可以废弃,或仅作为临时兼容配置,优先级低于平台酒店表。
|
||||
|
||||
### 6.2 SuperAgent 任务结果 REST
|
||||
|
||||
当前代码位置:
|
||||
|
||||
- `server/src/main/java/cn/nianxx/thhotel/integrations/ai/superagent/control/SuperAgentTaskResultController.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationAiTaskIntakeServiceImpl.java`
|
||||
|
||||
目标规则:
|
||||
|
||||
- JSON 正式请求不再要求 SuperAgent 传 `hotel_id`。
|
||||
- 当请求体没有 `hotel_id` 时,后端使用 `resolveSystemHotelId()` 查 SourceMessage。
|
||||
- 命中 SourceMessage 后,后续 Reservation 批次、订单、任务、审计都继续使用 `sourceMessage.hotelId()`,不直接信任外部请求体。
|
||||
- 如果外部请求体仍传了 `hotel_id`,单酒店阶段建议校验它必须等于系统酒店;不一致时返回明确错误,避免静默写错酒店。
|
||||
- 旧的“无 `hotel_id` 时按内部 SourceMessage ID 查询”的本地兼容路径应继续限制为 dev / test 或明确标注为非正式契约。
|
||||
|
||||
### 6.3 SuperAgent MCP
|
||||
|
||||
当前代码位置:
|
||||
|
||||
- `server/src/main/java/cn/nianxx/thhotel/integrations/mcp/superagent/service/impl/SuperAgentMcpServiceImpl.java`
|
||||
- `docs/project/integrations/superagent-mcp/tools.md`
|
||||
- `docs/project/integrations/superagent-mcp/test-cases.md`
|
||||
|
||||
目标规则:
|
||||
|
||||
- MCP tools schema 中的 `hotel_id` 从 `required` 移除。
|
||||
- 查询类工具在参数缺少 `hotel_id` 时,由 MCP 服务端补入系统酒店。
|
||||
- 写入类工具 `submit_ai_task_results` 也不再要求 `hotel_id`,但仍必须要求 `source_message_id` 和结果列表。
|
||||
- 文档中应明确:SuperAgent 不需要知道酒店 ID;酒店上下文由 TH Hotel 后端托管。
|
||||
- 现有 `HOTEL_ID_REQUIRED` 的 MCP 测试用例要改成“不传 `hotel_id` 仍成功或进入业务查询缺失 key 错误”。
|
||||
|
||||
### 6.4 Reservation AI 查询服务
|
||||
|
||||
当前代码位置:
|
||||
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationAiQueryServiceImpl.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/common/request/ReservationAiCaseContextQueryRequest.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/common/request/ReservationAiObjectDetailQueryRequest.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/common/request/ReservationMessageConversationQueryRequest.java`
|
||||
|
||||
目标规则:
|
||||
|
||||
- `hotel_id` 在请求 DTO 中保留,但语义改为可选。
|
||||
- 进入查询服务时统一调用酒店上下文服务解析有效酒店 ID。
|
||||
- 查询仓储层继续显式按 `hotel_id` 过滤,保证业务数据隔离。
|
||||
- 对 SuperAgent / MCP 机器入口,缺省酒店来自系统酒店。
|
||||
- 对用户入口,缺省酒店来自当前用户默认酒店,显式酒店必须通过可访问酒店校验。
|
||||
|
||||
### 6.5 前端 Reservation 查询
|
||||
|
||||
当前代码位置:
|
||||
|
||||
- `client/src/services/reservationService.ts`
|
||||
- `client/src/config/reservationConfig.ts`
|
||||
- `client/src/stores/authStore.ts`
|
||||
- `client/src/layouts/ReservationAppShell.vue`
|
||||
|
||||
目标规则:
|
||||
|
||||
- 前端酒店展示以 `authStore.hotels`、`authStore.defaultHotelId`、`authStore.selectedHotelId` 为准。
|
||||
- Reservation 列表、任务列表、订单详情默认不再依赖 `VITE_RESERVATION_HOTEL_ID`。
|
||||
- 单酒店阶段可以默认不传 `hotel_id`,由后端按当前用户上下文解析。
|
||||
- 如果保留酒店选择器,前端传当前选中的 `selectedHotelId`,后端必须校验该酒店在当前用户可访问列表中。
|
||||
- `VITE_RESERVATION_HOTEL_ID` 后续只适合保留为 fixture / 本地兼容配置,不作为真实业务上下文。
|
||||
|
||||
### 6.6 前端 Reservation 查询 Controller
|
||||
|
||||
当前代码位置:
|
||||
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/control/ReservationFrontendQueryController.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationFrontendQueryServiceImpl.java`
|
||||
|
||||
目标规则:
|
||||
|
||||
- Controller 可以继续接收可选 `hotel_id`,用于未来用户切换酒店。
|
||||
- Service 不再用 `DEFAULT_HOTEL_ID = "HOTEL-TEST"` 兜底。
|
||||
- Service 调用 `resolveCurrentHotelId(request.hotelId())` 得到有效酒店 ID。
|
||||
- 没有登录态的兼容行为需要明确:建议 dev / test 可回退系统酒店,生产逐步要求 token。
|
||||
|
||||
### 6.7 SourceMessage 查询
|
||||
|
||||
当前代码位置:
|
||||
|
||||
- `server/src/main/java/cn/nianxx/thhotel/platform/message/control/SourceMessageController.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/platform/message/repository/MybatisSourceMessageInboxRepository.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/platform/message/service/impl/SourceMessageConversationServiceImpl.java`
|
||||
|
||||
目标规则:
|
||||
|
||||
- SourceMessage 列表接口不应在 `hotelId` 为空时返回跨酒店结果。
|
||||
- 列表接口缺省酒店时调用 `resolveCurrentHotelId(null)` 或 `resolveSystemHotelId()`,具体取决于是否要求登录。
|
||||
- 会话详情和原文读取已经能从 SourceMessage 自身获取 `source.hotelId()`,方向正确;后续只补当前用户是否可访问该酒店的校验。
|
||||
|
||||
### 6.8 Debug EML 与 Demo Seed
|
||||
|
||||
当前代码位置:
|
||||
|
||||
- `server/src/main/java/cn/nianxx/thhotel/platform/debug/control/DebugEmlSuperAgentController.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/platform/debug/service/impl/DebugEmlSuperAgentRunServiceImpl.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationDemoDataServiceImpl.java`
|
||||
- `server/src/main/java/cn/nianxx/thhotel/workflows/reservation/service/impl/ReservationDemoDataProperties.java`
|
||||
|
||||
目标规则:
|
||||
|
||||
- Debug EML 的 `hotel_id` 从必填改为可选。
|
||||
- dev / test 缺省时使用系统酒店。
|
||||
- 如果调试人员显式传 `hotel_id`,后端校验它必须存在于平台酒店表;生产不建议开放该覆盖能力。
|
||||
- Demo seed 缺省酒店改为系统酒店,不再有独立 `defaultHotelId = "HOTEL-TEST"` 运行时兜底。
|
||||
|
||||
## 7. 配置调整建议
|
||||
|
||||
| 配置 | 当前用途 | 调整建议 |
|
||||
| --- | --- | --- |
|
||||
| `AUTH_*_BOOTSTRAP_DEFAULT_HOTEL_ID` | 初始化平台酒店和管理员默认酒店 | 保留,用于 bootstrap,不作为每次请求的运行时兜底 |
|
||||
| `AGENTBUS_*_DEFAULT_HOTEL_ID` | 旧 AgentBus fallback | 第一版运行时已不再使用,后续清理配置文件和环境模板 |
|
||||
| `RESERVATION_*_DEMO_DATA_DEFAULT_HOTEL_ID` | 旧 Demo seed 默认酒店 | 第一版运行时已不再使用,后续清理配置文件和环境模板 |
|
||||
| `VITE_RESERVATION_HOTEL_ID` | 前端 Reservation 默认酒店 | 从真实联调和生产配置中移除,保留 fixture / 本地兼容说明 |
|
||||
|
||||
配置文件调整原则:
|
||||
|
||||
- `application-test.yml` 和 `application-prod.yml` 不应继续写死运行时端口、数据库、酒店 ID 等会由环境覆盖的值。
|
||||
- 平台酒店初始化可以继续通过 bootstrap env 控制,但业务请求解析应查平台酒店表。
|
||||
- 如果系统酒店无法解析,应快速失败并给出明确错误,而不是默默回到 `HOTEL-TEST`。
|
||||
|
||||
## 8. 分阶段落地计划
|
||||
|
||||
### Phase 1:酒店上下文服务底座
|
||||
|
||||
修改范围:
|
||||
|
||||
- 新增 `platform.hotel.service.HotelContextService`。
|
||||
- 新增 `platform.hotel.service.impl.HotelContextServiceImpl`。
|
||||
- 扩展 `PlatformHotelRepository`,支持解析启用酒店和默认酒店。
|
||||
- 为系统酒店解析、当前用户默认酒店、无酒店、多酒店歧义写单元测试。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 单启用酒店时可解析系统酒店。
|
||||
- 无启用酒店时报 `SYSTEM_HOTEL_NOT_CONFIGURED`。
|
||||
- 多启用酒店且无默认规则时报 `SYSTEM_HOTEL_AMBIGUOUS`。
|
||||
- 当前用户有默认酒店时,用户请求解析使用默认酒店。
|
||||
- 显式请求酒店不在当前用户可访问列表时拒绝。
|
||||
|
||||
### Phase 2:机器入口收口
|
||||
|
||||
修改范围:
|
||||
|
||||
- AgentBus 捕获改用 `resolveSystemHotelId()`。
|
||||
- SuperAgent REST 入站缺省 `hotel_id` 时使用系统酒店。
|
||||
- MCP tools schema 移除 `hotel_id` required,并由服务端补酒店。
|
||||
- Reservation AI 查询服务把 `hotel_id` 必填校验改成上下文解析。
|
||||
|
||||
验收标准:
|
||||
|
||||
- AgentBus 入站 frame 不带 `hotel_id` 也能入库 SourceMessage。
|
||||
- SuperAgent JSON 任务结果不带 `hotel_id` 也能通过外部 `source_message_id` 找到 SourceMessage。
|
||||
- MCP 查询工具不传 `hotel_id` 也能执行。
|
||||
- 业务写入最终使用 SourceMessage 自身 `hotelId`。
|
||||
|
||||
### Phase 3:用户入口和前端收口
|
||||
|
||||
修改范围:
|
||||
|
||||
- Reservation 前端查询服务不再默认拼 `VITE_RESERVATION_HOTEL_ID`。
|
||||
- Reservation 后端查询服务移除 `DEFAULT_HOTEL_ID = "HOTEL-TEST"`。
|
||||
- SourceMessage 列表接口缺省酒店时按当前用户或系统酒店过滤。
|
||||
- 任务详情、任务写操作、OPERA 操作增加当前用户可访问酒店校验。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 登录后前端酒店名称来自 `/api/auth/me` 的 `hotels[]`。
|
||||
- Reservation 列表、任务列表、订单详情在不传 `hotel_id` 时也能按默认酒店查询。
|
||||
- 用户显式切换酒店时,后端校验酒店访问权限。
|
||||
- SourceMessage 列表不会因为缺少 `hotelId` 返回跨酒店数据。
|
||||
|
||||
### Phase 4:配置、文档和兼容清理
|
||||
|
||||
修改范围:
|
||||
|
||||
- 更新 `README.md` 中 `VITE_RESERVATION_HOTEL_ID` 的说明。
|
||||
- 更新 SuperAgent API / MCP 文档,明确外部系统不需要传 `hotel_id`。
|
||||
- 更新 go-live notes,移除“正式 JSON 请求必须带 `hotel_id`”的上线要求。
|
||||
- 清理 `application-*.yml` 中和运行时默认酒店相关的硬编码。
|
||||
- 保留测试夹具里的 `HOTEL-TEST`,但标注为测试数据。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 文档与真实契约一致。
|
||||
- prod / test 启动依赖平台酒店表,不依赖散落默认酒店配置。
|
||||
- 搜索运行时代码时,不再存在作为兜底逻辑的 `DEFAULT_HOTEL_ID = "HOTEL-TEST"`。
|
||||
|
||||
## 9. 数据和迁移注意事项
|
||||
|
||||
上线前需要确认:
|
||||
|
||||
- `platform_hotel` 中存在当前酒店的启用记录。
|
||||
- 单酒店阶段 `platform_hotel` 只能有一家 `ACTIVE` 酒店;迁移脚本 `V12__enforce_single_active_platform_hotel.sql` 会通过唯一索引阻止第二家 `ACTIVE` 酒店。
|
||||
- 现有 SourceMessage、Reservation、Task、OPERA、审计数据的 `hotel_id` 与 `platform_hotel.hotel_id` 一致。
|
||||
- 如果现有数据使用 `HOTEL-TEST`,而正式平台酒店 ID 不是 `HOTEL-TEST`,需要先设计数据迁移脚本。
|
||||
- 管理员用户在 `platform_user_hotel` 中有当前酒店默认授权,或超级管理员可访问全部启用酒店。
|
||||
- SuperAgent / MCP 文档和配置页面中的服务地址、鉴权 token、工具 schema 已同步更新。
|
||||
|
||||
建议先在测试环境执行检查:
|
||||
|
||||
```sql
|
||||
SELECT hotel_id, hotel_name, hotel_status, time_zone
|
||||
FROM platform_hotel
|
||||
ORDER BY sort_order, id;
|
||||
|
||||
SELECT hotel_id, COUNT(*) AS source_message_count
|
||||
FROM platform_source_message_inbox
|
||||
GROUP BY hotel_id;
|
||||
|
||||
SELECT hotel_id, COUNT(*) AS reservation_task_count
|
||||
FROM workflow_reservation_task
|
||||
GROUP BY hotel_id;
|
||||
```
|
||||
|
||||
中文说明:第一条确认平台酒店表;第二条和第三条确认已有消息与任务数据的酒店 ID 是否和平台酒店表一致。
|
||||
|
||||
## 10. 测试建议
|
||||
|
||||
后端建议覆盖:
|
||||
|
||||
- `HotelContextServiceImplTest`
|
||||
- `AgentBusFrameProcessorTest`
|
||||
- `SuperAgentTaskResultControllerTest`
|
||||
- `ReservationAiTaskIntakeServiceImplTest`
|
||||
- `SuperAgentMcpServiceImplTest`
|
||||
- `ReservationAiQueryServiceImplTest`
|
||||
- `ReservationFrontendQueryServiceImplTest`
|
||||
- `SourceMessageController` 或相关查询服务测试
|
||||
|
||||
前端建议覆盖:
|
||||
|
||||
- `reservationService.spec.ts`:不再默认拼 `VITE_RESERVATION_HOTEL_ID`,或改为使用当前选中酒店。
|
||||
- `authStore.spec.ts`:继续验证默认酒店和可访问酒店选择逻辑。
|
||||
- `reservationAppShell.spec.ts`:酒店展示来自登录上下文。
|
||||
|
||||
集成验收建议:
|
||||
|
||||
- AgentBus frame 入站不带酒店字段,SourceMessage 落库有正确 `hotel_id`。
|
||||
- SuperAgent 任务结果 JSON 不带 `hotel_id`,仍能写入正确酒店下的订单和任务。
|
||||
- MCP `tools/call` 不带 `hotel_id`,仍可查询订单上下文。
|
||||
- 前端登录后不配置 `VITE_RESERVATION_HOTEL_ID`,订单列表和任务列表仍可查询。
|
||||
- 非授权用户访问其他酒店数据时被拒绝。
|
||||
|
||||
## 11. 当前决策和后置事项
|
||||
|
||||
已确认并落地:
|
||||
|
||||
1. 单酒店阶段“系统酒店”采用严格规则:`platform_hotel` 必须且只能有一家 `ACTIVE` 酒店。
|
||||
2. 如果机器入口或前端兼容旧契约显式传入 `hotel_id`,后端严格校验它必须等于系统酒店或当前用户可访问酒店。
|
||||
3. SuperAgent、AgentBus、MCP tools 和 Debug EML 默认都不需要传 `hotel_id`。
|
||||
4. `HOTEL-TEST` 只能保留在测试夹具、示例文档或 bootstrap 默认配置中,不作为业务运行时兜底酒店。
|
||||
|
||||
后置事项:
|
||||
|
||||
1. 生产环境是否强制 Reservation / SourceMessage 用户接口必须带登录 token,等权限拦截策略整体收口时再确认。
|
||||
2. 如果正式酒店 ID 不是 `HOTEL-TEST`,需要先迁移测试机或存量数据里的 `hotel_id`。
|
||||
3. `AGENTBUS_*_DEFAULT_HOTEL_ID`、`RESERVATION_*_DEMO_DATA_DEFAULT_HOTEL_ID` 等旧配置项后续可以从配置模板中清理;本次第一版先保证运行时代码不再依赖它们。
|
||||
|
||||
## 12. 最终验收口径
|
||||
|
||||
M005 完成后,应满足以下口径:
|
||||
|
||||
- SuperAgent 和 AgentBus 不传 `hotel_id`,系统仍能正常入库、查询和写入 Reservation 任务。
|
||||
- 前端酒店展示来自平台酒店表和当前登录用户上下文。
|
||||
- 前端 Reservation 查询不依赖 `VITE_RESERVATION_HOTEL_ID` 作为真实业务上下文。
|
||||
- 后端所有查询和写入最终都带有明确 `hotel_id` 过滤或落库值。
|
||||
- `hotel_id` 的运行时来源统一为平台酒店表或当前用户上下文。
|
||||
- 运行时代码中不再出现 `HOTEL-TEST` 作为业务兜底酒店。
|
||||
- 文档、测试用例、SuperAgent MCP tools schema 与真实能力一致。
|
||||
Reference in New Issue
Block a user