修复V4目录初始化和lookup空结果元数据

This commit is contained in:
andy
2026-07-19 14:56:11 +07:00
parent 88f98a0eb6
commit 8b37232ac9
12 changed files with 342 additions and 18 deletions

View File

@@ -58,9 +58,9 @@
| `GET /api/reservation/order-tasks` | 查询 V4 业务订单任务列表 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`;只返回 V4 业务订单任务,不包含 S10/S99 来源通知;支持 `hotel_id``order_id``order_task_status``card_status``keyword``page_num``page_size``order_task_status``OPEN` / `COMPLETED` 返回 400`card_status` 非 V4 卡状态返回 400`card_status` 只筛业务 / 可处理卡,固定来源邮件展示卡不参与筛选。 |
| `GET /api/reservation/order-tasks/{orderTaskId}` | 查询 V4 订单任务详情 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`,后端按订单任务实际酒店校验访问权;返回 `order_task``source_message_summary``source_message_card``basic_information_card``business_cards[]``card_counts``adapter_contract_errors[]``availability`;来源摘要按酒店过滤,邮件正文和附件仍走 SourceMessage 会话接口。CP8 起每张 V4 任务卡返回 `fields[]`,前端应以该字段白名单渲染可编辑控件。 |
| `GET /api/reservation/source-notifications/{notificationId}` | 查询 V4 S10/S99 来源通知详情 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`,后端按来源通知实际酒店校验访问权;只返回通知摘要、来源邮件通知卡、会话摘要和 `availability`;不返回订单任务、业务卡、邮件正文、附件 URL 或原始 AI payload。 |
| `GET /api/reservation/lookups/accounts` | 查询 V4 Account 目录 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`,支持 `hotel_id``keyword``page_num``page_size`;返回统一 wrapper`hotel_id``catalog_type=ACCOUNT``catalog_source``catalog_version``stale``items[]``page``warnings[]`。前端在 `options_source=reservation_v4_account_catalog` 时调用,只提交 `items[].code`Market / Source 以后端确认派生结果为准。 |
| `GET /api/reservation/lookups/room-types` | 查询 V4 Room Type 目录 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`,支持 `hotel_id``keyword``page_num``page_size`;第一版只返回当前酒店 `ACTIVE` 房型目录,不接日期过滤,不代表 PMS 全量房型。前端在 `options_source=reservation_v4_room_type_catalog` 时调用。 |
| `GET /api/reservation/lookups/rate-codes` | 查询 V4 Rate Code 目录 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`,支持 `hotel_id``keyword``page_num``page_size`;第一版只返回当前酒店 `ACTIVE` Rate Code`pricing_available=false` 表示后端未接真实价格,不要据此展示价格。前端在 `options_source=reservation_v4_rate_code_catalog` 时调用。 |
| `GET /api/reservation/lookups/accounts` | 查询 V4 Account 目录 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`,支持 `hotel_id``keyword``page_num``page_size`;返回统一 wrapper`hotel_id``catalog_type=ACCOUNT``catalog_source``catalog_version``stale``items[]``page``warnings[]``keyword` 无匹配时 `items=[]` / `page.total=0`,但只要酒店未过滤目录存在,`catalog_source/catalog_version` 仍保持真实目录元数据,不代表目录未初始化。前端在 `options_source=reservation_v4_account_catalog` 时调用,只提交 `items[].code`Market / Source 以后端确认派生结果为准。 |
| `GET /api/reservation/lookups/room-types` | 查询 V4 Room Type 目录 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`,支持 `hotel_id``keyword``page_num``page_size`;第一版只返回当前酒店 `ACTIVE` 房型目录,不接日期过滤,不代表 PMS 全量房型。`keyword` 无匹配时按空选项处理,不要当作目录不可用。前端在 `options_source=reservation_v4_room_type_catalog` 时调用。 |
| `GET /api/reservation/lookups/rate-codes` | 查询 V4 Rate Code 目录 | 必须带 Bearer token需要 `RESERVATION_TASK_READ`,支持 `hotel_id``keyword``page_num``page_size`;第一版只返回当前酒店 `ACTIVE` Rate Code`pricing_available=false` 表示后端未接真实价格,不要据此展示价格。`keyword` 无匹配时按空选项处理,不要当作目录不可用。前端在 `options_source=reservation_v4_rate_code_catalog` 时调用。 |
| `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/confirm` | 确认 V4 订单任务卡 | 必须带 Bearer token需要 `RESERVATION_TASK_CONFIRM`,请求 JSON 带 `version`,可选 `confirmed_payload`Basic Information 必须先确认,业务卡第一版不强制逐张顺序确认;前端只提交当前卡 `fields[]` 中可编辑字段,后端以展示快照为基准合并,未开放字段会被忽略;确认前会按当前酒店数据库目录校验 Account / Room Type / Rate Code嵌套字段错误会返回如 `business_fields.after.room_items.0.room_type_code` 的路径,失败返回 `V4_FIELD_VALIDATION_FAILED`;确认后卡片 `CONFIRMED`、写 `confirmed_payload_json/confirmed_at/confirmed_by` 并锁定,重复确认返回错误;成功返回刷新后的订单任务详情。 |
| `POST /api/reservation/order-tasks/{orderTaskId}/cards/{cardId}/review-resolution` | V4 复核解阻并确认卡片 | 必须带 Bearer token需要 `RESERVATION_MANUAL_REVIEW_RESOLVE`,仅用于 `card_status=REVIEW_REQUIRED`;请求 JSON 带 `version`,可选 `field_overrides[]``reason`;订单任务归属未解决时 `confirmed_order_id` 必填,且必须是当前酒店下真实可见订单;目录错误字段可按 `validation_errors_json` / `fields[].validation_errors` 指向的 pointer 修正;成功后卡片 `CONFIRMED``review_status=RESOLVED`,写 `review_resolution_json/confirmed_payload_json/confirmed_at/confirmed_by` 并返回刷新后的订单任务详情。 |
| `POST /api/reservation/source-notifications/{notificationId}/ack` | 确认 V4 S10/S99 来源通知已读 / 已处理 | 必须带 Bearer token需要 `RESERVATION_TASK_CONFIRM`,请求 JSON 带 `version`;仅允许 `route_code=S10/S99`;确认后 `notification_status=ACKED`,写 `ack_by/ack_at`,成功返回刷新后的来源通知详情;重复 ack 返回当前已确认状态且不新增审计;该动作不创建订单、不参与订单阻塞。 |
@@ -508,7 +508,7 @@ RESERVATION_ROOMING_LIST_GENERATE
- 业务卡目录校验会递归检查 `business_fields` 下的嵌套结构。例如 `UPDATE_BOOKING` 的房型可能位于 `/business_fields/after/room_items/0/room_type_code`,错误详情会使用 `business_fields.after.room_items.0.room_type_code`;前端展示错误时优先用 `fields[].validation_errors`,接口 400 时可直接展示 `details[]`
- `review-resolution` 请求示例:`{"version":0,"reason":"确认房型映射","confirmed_order_id":"123456","field_overrides":[{"field_pointer":"/business_fields/room_items/0/pms_room_type_code","value":"RM2"}]}``confirmed_order_id` 在订单任务归属未解决时必填;如果订单任务已经绑定订单且 `target_resolution_status=RESOLVED`,只能不传或传当前同一个订单 ID不能借该接口切换到其它订单。`field_pointer` 必须来自当前卡允许编辑的 `basic_information.*``business_fields.*` 叶子字段;展示 payload 有 `missing_fields[]` 时只提交清单里的 pointer没有显式清单时只提交当前值为 `null` / 空字符串的未解决叶子字段;如果是目录校验错误,也可以提交后端 `fields[].validation_errors` 对应的字段 pointer。前端不要提交来源邮件、路由、`target_order``order_ref`、缺失字段清单、`manual_review`、raw evidence、校验诊断字段也不能替换整个对象 / 数组。
- V4 `fields[]` 第一版字段说明Basic Information 固定返回 `/basic_information/account_code``/basic_information/market_code``/basic_information/source_code`;其中 Account `control_type=select``options_source=reservation_v4_account_catalog`Market / Source 为只读派生字段。业务卡会按展示 payload 里的业务叶子字段返回字段白名单,例如 `/room_items/0/room_type_code``/business_fields/room_items/0/pms_room_type_code`;前端不要自行补未返回字段。
- V4 CP11 已开放独立目录 lookup API。前端应使用 `GET /api/reservation/lookups/accounts``GET /api/reservation/lookups/room-types``GET /api/reservation/lookups/rate-codes` 渲染 Account / Room Type / Rate Code 选项;用户提交确认或复核时只提交稳定 `code`,不要提交显示名、派生 Market / Source 或目录完整对象;后端确认前仍会重新校验目录。
- V4 CP11 已开放独立目录 lookup API。前端应使用 `GET /api/reservation/lookups/accounts``GET /api/reservation/lookups/room-types``GET /api/reservation/lookups/rate-codes` 渲染 Account / Room Type / Rate Code 选项;用户提交确认或复核时只提交稳定 `code`,不要提交显示名、派生 Market / Source 或目录完整对象;后端确认前仍会重新校验目录。`keyword` 查不到只表示当前筛选无结果,不能仅凭 `items=[]` 判断目录未初始化,应结合 `catalog_source``catalog_version``warnings[]`
- V4 新模型确认口径是不保存后端草稿、卡片最终确认后锁定、技术异常不进入用户可处理卡、当前不生成 OPERA 模拟操作。Basic Information 必须先确认;其它业务卡第一版不强制逐张顺序确认。现有 V3 `draft``confirm``manual-review-resolutions` 和 OPERA 模拟接口仍只代表旧链路能力,不能直接等同 V4 多卡最终接口。
- V4 S10/S99 已采用来源通知模型入库:新 V4 `route_code=S10/S99` 不再挂隐藏技术订单,也不再创建旧 `SOURCE_MESSAGE_ONLY` 任务;对应工作台 / 来源通知详情查询接口和 ack 写接口已开放。旧 `SOURCE_MESSAGE_ONLY` 只读任务仅代表 V3 S10/S99 和旧 S000/S999 兼容数据。
- M002 V3 的结构化 `S10/S99` 入站、40 条 P0.1 路由枚举 / 稳定配置、`UNHANDLED_CURRENT_INTENT``adapter_contract_error` transition 最小落库、任务列表 / 订单时间线 / 任务详情 V3 路由字段和只读诊断块透出、type-known manual review 同卡解阻第一版、typed infrastructure error、P0 fixtures 回归基线和 Parent Group / Cancel Allotment 路由修订均已完成。

View File

@@ -52,7 +52,7 @@
- 源邮件只读通知卡上线前,必须确认 `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 创建,不能使用固定种子作为业务事实源,必须补正式目录数据或新增导入脚本。
- M002 V4 lookup 上线前,必须确认当前 `platform_hotel` 唯一 `ACTIVE` 酒店有可用目录数据。V24 会初始化 `HOTEL-TEST``HOTEL-DEV`,并给迁移执行时已经存在的 `ACTIVE` 酒店导入固定种子;如果生产真实酒店是在迁移后由 bootstrap 创建,`ReservationV4CatalogBootstrapRunner` 会在该酒店目录为空时补一份 `FIXED_SEED_IMPORT` 初始化目录。生产如不能使用固定种子作为业务事实源,必须在上线前补正式目录数据或新增导入脚本。
- 管理后台启用后,不要继续把手工改库作为常规运营方式;用户、角色、菜单和酒店变更应通过 `/api/admin/**` 并写入管理审计。
- 原文读取接口开启前,已经确认谁可以使用、在哪些场景使用、如何轮换访问 key。
- 日志采集、错误响应和监控面板都不会展示邮件正文、HTML、附件 URL、Token、Cookie、客户姓名、邮箱、电话或支付信息。
@@ -280,7 +280,7 @@ SourceMessage 原文和邮件会话完整正文已迁移到登录权限体系:
- 目标数据库为空库或 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 种子目录。发布后需要确认 Flyway 已执行到 V24且当前酒店 lookup 能返回目录项;真实 PMS 同步、目录管理后台和 `workflow_reservation_catalog_sync_run` 仍未实现。
- 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 或测试库已经跑过。

View File

@@ -485,5 +485,5 @@ M002 V4 CP2 设计文档已落地:
- V4 表结构和 Repository 落地已完成第一版:新增 V4 订单任务表、V4 任务卡表和 V4 来源通知表,并提供 Entity、Mapper、Repository、幂等创建、`order_context_index` 稳定排序、非 event 卡 `source_event_index=0` 和 version 乐观锁更新基础方法。
- V4 入站写入新模型已完成第一版:真正创建 SourceMessage 展示卡、Basic Information 卡、业务卡和 S10/S99 来源通知。
- V4 前端页面模型切换。
- 真实 PMS 目录同步、Rate Code 价格 / 适用范围配置中心、目录管理后台和 SuperAgent 目录机器接口仍后置;当前 CP11 只是把固定种子导入数据库并开放前端 lookup API不代表已接 PMS 全量目录。
- 真实 PMS 目录同步、Rate Code 价格 / 适用范围配置中心、目录管理后台和 SuperAgent 目录机器接口仍后置;当前 CP11 只是把固定种子导入数据库、为迁移后创建的默认酒店做启动补种子并开放前端 lookup API不代表已接 PMS 全量目录。
- 真实 OPERA / OHIP、普通任务任意切换订单、字段矩阵从当前扁平结构整体迁移到 0711 P0 新结构、历史旧 Parent Cancel Booking payload 批量迁移。

View File

@@ -19,13 +19,13 @@ M002 V4 CP8 已实现第一版固定种子目录校验M002 V4 CP11 已把该
- Room Type / Rate Code 第一版使用当前酒店数据库目录校验和字段选项提示。
- 目录错误会让对应 V4 卡片进入 `REVIEW_REQUIRED`,用户通过复核解阻选择合法 code。
本文记录真实目录和 lookup API 的设计与 CP11 第一版实现。CP11 新增 `workflow_reservation_catalog_account``workflow_reservation_catalog_code` 两张表,并通过 Flyway 初始化 `HOTEL-TEST``HOTEL-DEV` 以及迁移执行时已存在的 `ACTIVE` 平台酒店的固定种子目录;不再在运行时代码中把固定种子作为全局目录事实。`workflow_reservation_catalog_sync_run`、真实 PMS / OPERA / OHIP 同步、目录管理后台和 SuperAgent 机器目录供给接口继续后置。
本文记录真实目录和 lookup API 的设计与 CP11 第一版实现。CP11 新增 `workflow_reservation_catalog_account``workflow_reservation_catalog_code` 两张表,并通过 Flyway 初始化 `HOTEL-TEST``HOTEL-DEV` 以及迁移执行时已存在的 `ACTIVE` 平台酒店的固定种子目录;同时新增 `ReservationV4CatalogBootstrapRunner`,在平台默认酒店由启动流程创建后,如果该酒店目录为空,会补一份 `FIXED_SEED_IMPORT` 初始化目录。不再在运行时代码中把固定种子作为全局目录事实。`workflow_reservation_catalog_sync_run`、真实 PMS / OPERA / OHIP 同步、目录管理后台和 SuperAgent 机器目录供给接口继续后置。
后续若本文与 `M002-v4-agent-callback-field-contract.md` 的 Agent 输入字段冲突,以 Agent 字段契约为准;若与 `security-access-control-boundary.md` 的接口权限冲突,以安全边界为准。
## 2. 当前固定种子目录
CP11 之前固定种子实现位于后端 `ReservationV4DirectoryService``FixedReservationV4DirectoryServiceImpl`。CP11 起实现改为 `ReservationV4DatabaseDirectoryServiceImpl`,目录读取只走数据库目录表;固定种子通过 `V24__create_reservation_catalog_tables.sql` 初始化导入 dev/test 基准酒店和迁移时已有 `ACTIVE` 酒店,来源标记为 `FIXED_SEED_IMPORT`
CP11 之前固定种子实现位于后端 `ReservationV4DirectoryService``FixedReservationV4DirectoryServiceImpl`。CP11 起实现改为 `ReservationV4DatabaseDirectoryServiceImpl`,目录读取只走数据库目录表;固定种子通过 `V24__create_reservation_catalog_tables.sql` 初始化导入 dev/test 基准酒店和迁移时已有 `ACTIVE` 酒店,并由 `ReservationV4CatalogBootstrapRunner` 补齐迁移后才创建的默认 `ACTIVE` 酒店,来源标记为 `FIXED_SEED_IMPORT`
| 目录 | 当前用途 | 当前固定值 | 当前限制 |
| --- | --- | --- | --- |
@@ -54,7 +54,7 @@ CP11 之前固定种子实现位于后端 `ReservationV4DirectoryService` 和 `F
| 阶段 | 来源 | 中文说明 | 适用目录 |
| --- | --- | --- | --- |
| Phase 0 | `FIXED_SEED_IMPORT` | CP11 已将固定种子通过 Flyway 导入数据库,不再作为运行时代码全局 Map | 当前 Account、Market、Source、Room Type、Rate Code |
| Phase 0 | `FIXED_SEED_IMPORT` | CP11 已将固定种子通过 Flyway 和启动补种子流程导入数据库,不再作为运行时代码全局 Map | 当前 Account、Market、Source、Room Type、Rate Code |
| Phase 1 | `SYSTEM_MANAGED` | 本系统数据库目录,由初始化脚本、管理后台或导入文件维护 | Account、Market、Source也可临时维护 Room Type、Rate Code |
| Phase 2 | `PMS_SYNC` | 后端从 PMS / OPERA / OHIP 同步目录到本系统本地表,业务查询只读本地快照 | Room Type、Rate Code 优先Account、Market、Source 视 PMS 能力再接 |
@@ -203,6 +203,7 @@ Lookup API 属于前端业务查询接口,不给 SuperAgent 或 AgentBus 调
- 酒店隔离:`hotel_id` 可选;不传时按当前用户默认酒店解析,传入时必须校验用户可访问。
- 返回:只返回目录 code、显示名、状态、来源和安全元数据不返回外部 PMS 原始响应。
- 分页:`page_num` 从 1 开始,`page_size` 后端限制最大值。
- `keyword` 无匹配时,`items=[]``page.total=0`;如果当前酒店未过滤的 ACTIVE 目录仍存在,`catalog_source` / `catalog_version` 继续返回真实目录元数据,不把筛选无结果误报为 `DATABASE_EMPTY`
### 8.1 Account Lookup