实现酒店上下文单酒店收口
This commit is contained in:
@@ -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