完善酒店接口确认字段与回读核对并同步工作台最新改动
verify / booking-verify (push) Has been cancelled

This commit is contained in:
鲨鱼辣椒 committed 2026-09-18 15:38:52 +08:00
1 parent 262831af42
commit bc71026246
217 files changed
+11157 -1307

No files matched your search

@@ -0,0 +1,44 @@
# 工作台确认字段接入(2026-09-18)
本次按用户逐项确认后的要求修改接口适配及回读,已完成本地代码验证。现有运行服务、酒店配置、排队任务和真实酒店数据未操作。相关CR为[确认字段完整写入](../requirements/CR-20260918-confirmed-hotel-fields.md)。
## 最终字段与接口
| 工作台确认值 | GROUP NEW / UPDATE | FIT NEW / UPDATE |
| --- | --- | --- |
| Account | blockProfiles.blockProfile:配置对应的账户和联系人Profile ID,Agent / AgentContact;UPDATE使用fullOverlay并保留其他关联 | reservationProfiles.reservationProfile:配置的TravelAgent Profile ID;UPDATE保留其他关联 |
| Market / Source | blockDetails.marketCode.marketCode / sourceOfSale.sourceCode.sourceCode | roomStay.roomRates[].marketCode / sourceCode |
| Rate Code | reservationDetails.ratePlanCode[].ratePlanCode | roomStay.roomRates[].ratePlanCode |
| Meal | reservationDetails.breakfast.breakfastIncluded,来自确认Rate的目录解析 | 通过Rate Code体现,不新增独立早餐接口 |
| Note | comments.commentInfo[].comment.text.value | comments[].comment.text.value |
| Arrival / Departure | blockDetails.timeSpan | roomStay和roomRates日期 |
| Room Type / Quantity | putBlockAllocation完整逐晚目标,清零旧日期/房型残留 | roomRates房型和分配到实际预订的numberOfUnits |
| Nights | 由日期推导,不另发固定晚数 | 同左 |
GROUP:postBlock / putBlock / getBlock,房量为putBlockAllocation / getBlockRoomRateGrid。FIT:postReservation / putReservation / getReservation。接口路径维持/api/v1/blocks、/api/v1/reservations及/{id}子资源。
Account是酒店的业务档案,不是登录账户,也不是SOURCE。现有运行清单按accountCode选择实际Profile ID;只用已配置编号,不把LIAN_TAI或显示名冒充Profile ID。getProfiles/getProfile/getProfileRelationships是此前核实的档案查找/核验接口;本次预订链复用配置档案关联,不额外新增按名称猜选档案的运行逻辑。
Market、Source取员工确认后的basic快照,再按当前酒店显式code映射转换;账户默认值不覆盖最终改选。已有映射直接使用;本地SOURCE别名company若未在运行清单配置对应酒店代码,会在生成写入前拒绝,不能猜为TA或虚构COMPANY码。此项是目录/连接配置,不是缺company接口。本次没有修改私有运行目录或补造未知映射。
## Meal与Note
前端已完成的Meal只读保持。服务端新确认重新加载受信Rate目录并派生早餐;客户端旧早餐/餐厅参数不能覆盖Rate。餐厅仅可保留目录兼容值,不再参与员工必填、独立Meal选择或酒店请求。重复确认保留冻结快照,不按后来目录重算。
酒店适配器逐字使用booking_note.text,不重新拼价格/早餐正文。GROUP新建现在发送Note;GROUP修改定位本系统过去成功发送的Note ID,仅改文本并核对其他备注及元数据保持。没有本系统来源记录时新增第一条业务Note,不拿现有相似文本当作本系统备注;新添后中断恢复仍只写一次。来源冲突、他人改写本系统旧Note或不完整回读会停止,不覆盖不明归属备注。空Note不构造写入。
GROUP修改前需要完整Profiles/Rates/Comments及房格回读;缺失完整集合不解释为空集合。尚未取得该酒店实际省略空集合的响应样例,故保持拒绝不完整响应。回读逐项验证账户/联系人、Market、Source、Rate、早餐、Note和日期房量;FIT增加账户/Market/Source的最终检查。任何必要字段未匹配都不能显示整单成功。
GROUP计划保存完整修改前头字段、Note ID和完整请求摘要,检查点升级为confirmed-fields-v3。旧计划不会按新字段语义静默恢复。FIT仍由冻结请求、原资料及现有持久计划保护。预览、逐字段结果和受控调用日志同步上述内容。
## 边界与验证
未新增公开Controller、权限、表或数据库迁移。保留现有酒店隔离、人工确认、租约、请求摘要、持久来源、回执及未知写入禁止自动重发的规则。TA、取消/转换、邮件附件边界不扩大。
- 后端全量2727项:2710通过、17既有跳过;随后新增2项并运行相关回归,最新测试报告合计2729项、0失败/错误、17跳过。
- 前端完整39文件409项通过;生产构建(含vue-tsc)通过。目标ESLint为0错误,ValueReview模板原有3条排版警告保留。
- Python模拟71项通过;本机Java装配以release17编译,合成目录及调用记录检查通过。
- 验证覆盖Note原文/其他备注保留、首次新增/重试、完整字段修改、错误和缺失回读、Rate早餐派生、历史确认、配置缺失、旧字段兼容、取消及类型转换回归。
- 不将本地合成测试称为Oracle沙箱页面验收;当前服务未加载本轮后端代码,未代用户确认卡片、重跑队列或操作酒店。
详细日志位于忽略目录.planning/confirmed-interface-implementation-20260918/。开始时保存的baseline用于区分其他任务已有修改,未重置既有工作区。
@@ -0,0 +1,42 @@
# CP32 每笔预订的预计接口与实际调用核对
## 用户目标与范围
用户希望在原工作台每笔预订下核对是否调用、选值是否正确,并在执行前看到系统预计调用哪些接口。本轮只补这个核对入口;不创建子智能体、不推送、不线上部署、不操作真实 Oracle,不修改邮件识别和员工确认规则。
## 交付行为
NEW / UPDATE 任务和对应预订记录可展开“预计调用的接口”。按当前 GROUP / FIT 类型显示办理顺序、接口名称、用途和前置条件,明确“流程预览,不代表已执行”。这是四条现有执行链的说明,不是已通过运行配置校验的请求,也不承诺固定调用次数。条件不满足、分页、逐笔预订和恢复都可能改变实际调用。
原办理面板下增加“接口核对 · 本机模拟酒店”:逐次显示查询或保存、接口代码、时间、HTTP 回执;展开后查看实际发送的业务字段与接口返回的核对字段。发送值来自已经序列化的请求,不从确认表单重构。HTTP 200 不等于业务完成,最终结果仍由现有查询核验决定。
缺少旧采集记录时明确提示,不能当作没有调用。实际检查的一笔旧 GROUP UPDATE 只在平台审计中有两次 searchBlocks / 200,未找到本机对象并停止后续写入;其历史请求参数未采集,未补造或自动重试。新执行的未找到、歧义、未能核实目标使用不同原因码。
## 实现与安全边界
- 正常 `OhipEdgeHttpAdapter` 默认使用空观察器;包内受信装配才能启用。观察器接收独立正文副本,不接收 HTTP 认证头,异常不改变业务响应或触发重发;采集之后仍重新检查调用截止时间。
- `OhipEdgeTraceValues` 只选择已知业务结构及字段,屏蔽凭据、原始邮件、附件字节、URL 和未知扩展;字段数、文本长度、深度有界,截断明确标注。
- 记录器和只读控制器位于 `src/test/local-fit-simulation/java`,不进入正常 Maven jar。只有本机专用装配与 `ohip.booking.trace-local-enabled=true` 才提供接口。私有目录 0700、逐调用文件 0600,原子替换;归属绑定 execution / 酒店 / task / card / confirmation version。
- GET `/api/reservation/order-tasks/{taskId}/cards/{cardId}/hotel-execution/calls` 使用原 `RESERVATION_TASK_READ`、当前酒店解析和原卡片归属校验,然后才读取本机文件。每页最多 50 条,最近在前。读取没有酒店查询、确认、续办或其他业务副作用。
- 前端切换任务取消旧请求并丢弃迟到结果,刷新重新建立连续分页;正式环境无测试路由时隐藏本机明细,不冒充正式审计。
## 验证
| 验证 | 结果 |
| --- | --- |
| Maven package | 2,672 项,零失败/错误,17 项条件跳过。 |
| 前端完整测试 | 37 个文件、347 项通过;类型检查通过。 |
| 相关 ESLint | 零错误;既有组件格式警告保留。 |
| 记录器及 HTTP 参数绑定 | 独立 LocalBookingTraceCheck 通过:白名单、权限先行、卡片/酒店/确认版本隔离、分页、路径校验、文件权限及 HTTP 200/403/404。 |
| 本机 FIT 新建页面确认 | 成功;93 次实际调用,包含 1 次 postProfile、2 次 postReservation、4 次上传及实际 GET。GC、BTQR、房型、Rate Code、Tour Code 发送字段已核对。 |
| 本机 FIT 修改页面确认 | 成功;175 次实际调用,包含 2 次搜索、2 次 putReservation、2 次上传、4 次旧附件删除和实际查询;新日期、房型房数和 Rate Code 已核对。 |
| 原工作台只读检查 | 5178 / 8082 新版本已加载;旧任务预览与历史空态正常;越权酒店 403、错误卡片 404。 |
| 原运行保留 | 原 44 个环境变量、启动设置、EML / Agent、身份权限、58 H2 / 24 PG 表的稳定内容摘要保持,无历史补办。继续使用原本机模拟酒店服务,未重建其数据。 |
初次隔离验证发现测试装配缺少反射参数名,以及局部异常未映射 HTTP 状态,均已修正并补 HTTP 绑定测试。刷新时合并不连续页的风险改为刷新重建首页,防止中间调用无法加载。完整前端测试发现新增多语言字段键不齐,补齐后 347 项全部通过。
私有证据位于工作区 `.planning/ohip-interface-review-20260915/cp32-execution-review/`,包含隔离调用摘录、测试结果、本机加载和双库摘要;不提交真实业务数据或凭据。启动包为 `server/var/local-replay/runtime/ohip-cp32-execution-review-20260917/th-hotel-local-simulation.jar`,SHA-256 `6c9b5dce2492b5446ad9be7db3d780f86c12df3f5de3588d83fa0241dda63bda`。
## 当前使用与后续边界
在原任务下展开“预计调用的接口”;新执行后在“接口核对”中展开实际调用,必要时加载更早记录。旧失败任务未自动续办,也没有为其制造缺失的模拟预订。真实 Oracle TA 字段与正式审计存储仍不在此次范围内;本机模拟成功不等于真实酒店验收。原 EML 入口保留,本轮未重新运行识别 Agent。
@@ -0,0 +1,61 @@
# FIT 新建 TA 条件写入:代码与测试交付
> 2026-09-18 后续更新:用户已授权更新本机服务,原8082及配套模拟接口现已加载并验证。交付的本机包附加类已修正为Java17,当前SHA256见release/manifest.json;普通平台包未安装。7封邮件的前置预订数据仍未装入。详见[本机更新证据](../../../.project-docs/50-evidence/topics/2026-09-18-local-service-update.md)。
以下为首次代码交付记录,运行状态以页首后续更新为准。2026-09-18。消费端代码已接入现有 `postReservation` / `getReservation`,无需平台新增封装;本轮未更新运行服务或操作平台沙箱。**写入映射依据官方API结构与数据字典组合推导,TA特殊类型是否落入沙箱页面的输入框尚待用户确认,不能把模拟回显当作页面验收。**
## 当前行为
| 情况 | 请求和完成判断 |
| --- | --- |
| GROUP NEW | 不发送TA,继续保留Block Name等原字段 |
| FIT NEW有Tour Code | 创建请求发送确认的Tour Code;逐笔回查唯一TA类型且值一致,才授予TA核验通过 |
| FIT NEW无Tour Code | 省略TA字段;不会因缺Tour而阻止新建,不生成TA核验项 |
| UPDATE | 不修改原预订TA;同类型新增房型仍沿用此前不传TA的范围 |
| GROUP→FIT | 实际按FIT NEW创建,使用最终Tour Code规则 |
缺失、空字符串、null、空白Tour均作为无值处理;非空值不自动改写或截断,超过Reservation说明中的50字符限制提前拒绝。Source Code=TA与TA Record Locator无关,保留原值。酒店附件上传仍取消。
## 请求结构与依据
Edge `POST /api/v1/reservations` 的原生正文内:
```json
{
"reservations": {
"reservation": [{
"externalReferences": [{
"id": "确认的Tour Code",
"idContext": "TA_RECORD_LOCATOR"
}]
}]
}
}
```
这里只展示本次增加的字段,其余创建必填内容仍由原转换器构造。无Tour时省略整个externalReferences;不传空数组或空值。不是把 `taRecordLocatorList` 查询参数塞进创建请求,也不改写成Custom Reference。
Oracle [RSV schema](https://github.com/oracle/hospitality-api-docs/blob/dd631fbd5d0fce74a7dbdf96b43f07ce587211f2/rest-api-specs/property/v1/rsv.json)提供创建和详情的externalReferences/id/idContext结构;[官方数据字典第718页](https://docs.oracle.com/en/industries/hospitality/opera-reporting-analytics/ddrna/SA_Definitions.pdf#page=718)把Travel Agent Locator对应为EXTERNAL_REFERENCE_TYPE=TA_RECORD_LOCATOR。两者组合是本次实现依据,官方公开材料仍没有直接的TA专用REST写入样例。平台公开0.11.0目录本轮再次确认两个操作存在。
## 实现与验证
新增窄范围TA转换器,实际创建与调用预览共用映射;回查按idContext查找唯一项,不假定第一项,不用其他类型同值替代。缺失、错误、重复类型阻止完整成功;确认值/酒店读回和调用摘要均能显示TA,其他外部参考号仍不进入调用展示。
修正此前隐藏的Tour必填条件及备注来源不能为空条件:仅允许无Tour的FIT NEW,酒店、任务/卡片/确认版本、来源邮件、正文摘要和调用锁仍严格一致;无Tour不能由此绕过UPDATE/附件归属。冻结创建计划使用null安全比较,新版本检查点拒绝旧计划静默续办。GROUP与既有FIT ID定位不改。
- 完整后端2714项:2697通过、17条件跳过、0失败/错误。
- 页面相关42项及类型检查通过;模拟器70项通过。
- 独立47参数场景(45成功、2预期零写拒绝)和3类取消故障符合预期;20笔创建携带TA专用类型,GROUP/UPDATE请求不携带。
- 无Tour的FIT真实Worker/H2/本地HTTP创建链成功;TA不一致时失败且不重复创建;官方固定版本schema切片校验通过。
全部为合成数据,无XML/EML上传。详细证据见[记录](../../../.project-docs/50-evidence/topics/2026-09-18-fit-new-conditional-ta.md)。
## 当前运行与用户下一步
当前原本机服务已经完成更新,不需要重复替换程序或重启。7封邮件中的修改/取消/转换需要先准备对应旧预订,再由用户上传及确认;本次只完成服务更新,没有导入这些预订。原本机模拟连接保留;使用平台沙箱的普通配置并未由本轮切换。
原交付包说明:更新包位于 `.planning/fit-new-ta-20260918/release/`:平台沙箱配置使用 `th-hotel-server-0.0.1-SNAPSHOT.jar`,原本机模拟装配使用 `th-hotel-local-simulation.jar`。`manifest.json`记录SHA256与尚未完成的页面核验。首次交付时仅生成文件;后续本机更新已完成,见页首。
历史安装参考:沿[原环境更新步骤](ohip-no-ta-local-test-20260918.md#用户接下来操作)使用这个新目录的包;继续使用原本机模拟酒店时,也需由用户更新匹配的模拟服务。不要用旧无TA包验收本次规则,不续办旧版本冻结任务。
服务更新后,以一笔有Tour的FIT新建、一笔无Tour的FIT新建和一笔GROUP新建核对。对有Tour的FIT,需同时查看Stay Details的TA输入框与同笔getReservation结果;若接口返回成功而输入框没有值,本映射不能算完成验收。无需为验证代码额外上传XML。
@@ -0,0 +1,118 @@
# FIT TA Record Locator:官方数据字典与现有接口候选映射
日期:2026-09-18。本次仅查官方资料和已有平台契约;没有修改业务代码、模拟器、运行服务或酒店数据。
## 结论
**暂未发现需要平台新增封装的独立接口。** 平台已有 `postReservation`、`putReservation`、`getReservation`、`searchHotelReservations`。本轮找到新的官方数据字典证据:Travel Agent Locator 存在于外部参考号中,其类型为 `TA_RECORD_LOCATOR`。因此,之前“没有任何等价字段证据”的说法需要收窄为:**底层数据映射已确认;OHIP 是否允许用此特殊类型写入、并如何返回,仍待直接契约说明或受控环境证据。**
此结论不改变业务定义:GROUP/FIT 的 TA 值均为 Tour Code / Block Name;不涉及附件。正式 FIT 和 GROUP→FIT 守卫仍保留,不能用下列推导候选直接放行。
## 新增官方证据
Oracle [OPERA Reporting and Analytics Subject Area Definitions](https://docs.oracle.com/en/industries/hospitality/opera-reporting-analytics/ddrna/SA_Definitions.pdf#page=718),PDF 第 718 页,Travel Agent Locator 行:
```text
EXTERNAL_REFERENCES.EXTERNAL_REFERENCE
WHERE EXTERNAL_REFERENCE_TYPE = 'TA_RECORD_LOCATOR'
```
同一行的报表字段为 `reservationgeneralDetails.TaRecordLocator`。已下载原始 PDF 并检查整页图像,确认不是搜索摘要误拼的表格行。官方[文档入口](https://docs.oracle.com/en/industries/hospitality/opera-reporting-analytics/ddrna/index.html)也指向此 PDF。
这证明数据存储关系,**不单独证明 Property REST 接口接受保留类型**,也不能把报表字段直接写进 Reservation 请求。
## 现有接口能承载的形状
Oracle 官方 [RSV 26.3 schema](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/rsv.json) 中:
- `hotelReservationType.externalReferences` 与 `hotelReservationInstructionType.externalReferences` 均引用 `externalReferencesType`。
- 数组元素 `externalReferenceType` 包含 `id`、`idContext`、可选 `idExtension`。
- 新建、修改、详情均可到达这组属性;schema 对 `idContext` 没有列举 `TA_RECORD_LOCATOR`,也没有声明特殊类型的写入规则。
| 操作 | Oracle 原生方法/路径 | 已证明的普通 External Reference 位置 |
| --- | --- | --- |
| 新建 `postReservation` | POST `/rsv/v1/hotels/{hotelId}/reservations` | `/reservations/reservation/0/externalReferences` |
| 修改 `putReservation` | PUT `/rsv/v1/hotels/{hotelId}/reservations/{reservationId}` | `/reservations/0/externalReferences` |
| 详情 `getReservation` | GET 同上 | 响应 `/reservations/reservation/0/externalReferences` |
| TA 搜索 `searchHotelReservations` | POST `/rsv/v1/hotels/{hotelId}/reservations/searches` | 请求 `/taRecordLocatorList`;这是已明确的 TA 搜索条件 |
结合数据字典,优先核实的候选片段如下。**这是依据两份官方资料推导的候选,不是 Oracle 发布的 TA 写入示例,也不是可直接执行的完整请求。**
```json
{
"externalReferences": [
{
"idContext": "TA_RECORD_LOCATOR",
"id": "<员工确认的 Tour Code / Block Name>"
}
]
}
```
候选读回方式是从详情的 `externalReferences` 数组中按 `idContext == "TA_RECORD_LOCATOR"` 找 `id`,不能默认它是数组第一项。详情查询支持 `fetchInstructions=Reservation`,但没有 `ExternalReferences` 这个 fetchInstructions 枚举;不要编造它。列表查询另有 `resvExternalReferencesToFetch`,是否允许该特殊类型也未获证,不能冒充详情查询参数。
已复核保存的 Edge 0.11.0 OpenAPI:新增/修改入口使用 `ConfirmedWrite`,承载 `OracleDocument`;普通外部参考号无需增加一个新的消费端 HTTP 路由。此为已发布契约检查,不等于授权、透传实现或酒店写入已验证。
## 还需核实的一个具体问题
可由用户转交平台/Oracle 接口负责人:
> Oracle 官方 Subject Area Definitions 第 718 页说明 Travel Agent Locator 是 EXTERNAL_REFERENCES 中 EXTERNAL_REFERENCE_TYPE='TA_RECORD_LOCATOR' 的 EXTERNAL_REFERENCE。请确认目标 OPERA Cloud 版本的 postReservation / putReservation 是否允许使用 externalReferences[{idContext:"TA_RECORD_LOCATOR",id:"Tour Code"}] 写入 Stay Details 的 TA Record Locator;getReservation 是否按同一 idContext 返回,需哪些 fetchInstructions?如该保留类型不能通过普通 externalReferences 写入,请给出正确 Operation ID、JSON 路径和版本依据。请同时核实覆盖已有 TA、保留其他 externalReferences 以及 Reservation Protection 的行为。
下一步应针对这一个候选补证,无需再重复泛搜字段名或重包相同新增/修改/查询接口。若使用环境验证,由用户操作测试酒店:先读取已有 TA 的预订确认返回位置,再验证新建和修改后的页面值、详情值及 TA 搜索一致;仅 HTTP 成功或返回普通 externalReferences 不足以证明写中了页面 TA。无需 XML/EML 上传。
## 本轮核查记录
- 官方仓库 main 仍为 `4bd129b455bc5e3ab0f900ac47983611e58659b4`,RSV `26.3.0.0`。
- 两个官方 Property Postman 集合复核,仍没有 TA 专用写入示例。45 个 Property v1 模块的扩展词形检索未找到 `TA_RECORD_LOCATOR` 常量;CRM 的 `travelAgentReferenceId` 明确属于 stay record,不能当作当前 Reservation 字段。
- 官方 GitHub issue 搜索 `locator`、`customReference` 均为零结果;不将此当作“不支持”的证明。
- PDF SHA-256:`d021bff8cf45457aaa05bf025e81f0605fce619db14ab218471f4defeb993e31`。
- 本机原始 PDF、718 页文本/图像、仓库 HEAD 和 issue 检索回执保存于 `.planning/fit-ta-official-recheck-20260918/`,该目录不作为公开项目附件提交。
- 未运行酒店业务测试;未更改正式 TA 守卫或模拟参数;没有创建子智能体。
## 同日继续核对:已明确普通写入支持,剩特殊类型验收
用户要求“继续核对”后,补查了 Oracle CRS 集成指南、版本说明和平台当前公开 OpenAPI:
1. Oracle [26.1 Resolved Issues](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.1/oprnc/c_resolved_issues.htm) 的 Bug `38561498 / HOPCS-84385` 明确描述:`post/putReservation` 对新增 external reference 的 `id` 或 `idContext` 长度非法的错误处理得到增强。[25.2 说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/25.2/oprnc/c_resolved_issues.htm) 的 `37430163` 也有同类说明。由此可直接确认普通外部参考号是这两个操作处理的写入数据,并非仅有一个返回结构;这些条目仍未提到 `TA_RECORD_LOCATOR` 特殊取值。
2. [CRS → OPERA Cloud 官方指南](https://docs.oracle.com/en/industries/hospitality/integration-platform/crsig/c_2_2_central_reservation_system_to_opera_cloud.htm) 确认预订新建/修改使用 `postReservation` / `putReservation`;OTA/PNR 编号应传入合适字段,但没有提供 TA 类型的 JSON 示例,不能把 OTA 编号说明替代 TA 证明。
3. 直接访问 `https://ohip.nianxx.cn/developer/v1/openapi` 成功,仍为 Edge `0.11.0`。四个所需操作均存在。`getReservation` 的根级 `x-query-allowlists` 明确允许 `fetchInstructions`,所以详情查询可沿现有路由提出 `Reservation` 查询要求,不用另包接口。
4. 本机候选平台代码 `b5e1a30650c66dc2bdea7e42d6d7d84a16d5382f` 的 `internal/ohip/operations.go`、`client.go` 中,Reservation 新增/修改将原始 JSON body 交给 HTTP 请求,详情保留原始 JSON response;没有看到 TA 专用字段删除逻辑。这只是本机候选 Client 层静态证据,**不能证明线上部署版本、完整平台链路或 Oracle 已接受该类型**。
本次公开 OpenAPI 原始回执保存在 `.planning/fit-ta-official-recheck-20260918/openapi-followup.json`,SHA-256 为 `19c2fd724b1e30e13e3d343419b09ede6f05ff30276596a280065116e194283c`;结构化摘要为 `platform-followup-summary.json`。一次 CLI 目录读取返回 transport 错误;随后直接 HTTPS 公共 OpenAPI 读取成功,未借用任何业务凭证。
### 下一步最小核对操作(由用户或平台同事执行)
先选一笔**页面 TA Record Locator 已有值**的测试 FIT,使用其 OPERA 内部 Reservation ID;不要把 Confirmation Number 当作内部 ID。
通过已有平台接口,只读查询:
```http
GET /api/v1/reservations/{ReservationID}?fetchInstructions=Reservation
```
平台内部对应 Oracle:
```http
GET /rsv/v1/hotels/{hotelId}/reservations/{ReservationID}?fetchInstructions=Reservation
```
脱敏后只需保留以下核对材料,不需要上传 XML/EML,也不需要完整客人资料:
- OPERA Cloud 版本、HTTP 状态和请求编号;
- 页面 TA 值(可替换为固定标记,返回中相同值也换成同一标记);
- Oracle 文档中的 `reservations.reservation[].externalReferences` 数组,保留字段名、`idContext` 和 `idExtension`;其他无关编号可脱敏。
判断规则:若找到 `idContext=TA_RECORD_LOCATOR` 且 `id` 与页面相同,可以关闭**读回映射**缺口;若页面有 TA 而详情没有该项,不能据此把 TA 当为空或认定不支持,应带请求编号询问 Oracle 特殊类型的返回方式。读回确认后再由用户在受控测试数据上验证新建和修改是否写中页面 TA、是否保留其他外部参考号;不自动修改现有预订、不用 `reservationNotification` 或保护覆盖开关试探。
本轮没有真实酒店调用或本地业务代码变更。普通外部参考号写入已得到更直接的官方佐证;TA 特殊类型的写入/读回仍未验收,正式 FIT/GROUP→FIT 继续阻断。
## 截图澄清后复核:验收对象是 Stay Details 输入框
用户再次明确:目标是截图中 **Stay Details → TA Record Locator** 的读写能力。GROUP/FIT 有 Tour Code / Block Name 时填写原值;无值时不提交,不因更新缺值主动清空已有值。这里讨论 `externalReferences` 仅为后台字段候选,不要求用户改用 External References 页面。
本轮补查 Oracle 26.3 [Updating Reservations](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_managing_reservations_editing_reservation_stay_details.htm) 和 [Managing Reservation External References](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_managing_reservations_external_references.htm):前者说明 Stay Details 的编辑保存流程,并把 External References 列为另一详情入口;后者说明该入口的 Type、ID、Leg。两页均没有说明 Stay Details 的 TA 输入框对应哪个 OHIP JSON 属性。26.3 [Resolved Issues](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/oprnc/c_resolved_issues.htm) 本次返回正文未检出 locator,也未取得 TA 写入示例。不能把这些补查记录表述为新的字段接通证据。
对已保存官方 RSV26.3 schema 的定向复核,TA 明示属性仍为两类搜索请求中的 `taRecordLocatorList`;`guestLocators` 系列明确处理 Guest Locator,不能替代 TA。`externalReferences` 候选仍需上述单笔查询补证。截图也不能单独证明存在一个名为 `TA_RECORD_LOCATOR` 的独立 REST 操作。
**交给用户/平台同事的最小动作:**选一笔已知页面 TA 值的测试 FIT,用其内部 Reservation ID 调用上述 GET;交回页面值、脱敏的 `externalReferences` 数组和请求编号。不要先新包接口、上传文件或修改预订。若返回与页面一致,只将读回标记为已确认;写入仍需目标版本官方说明或随后由用户操作的受控验证。可直接转交的精确问题已同步至[技术询问草稿](ohip-fit-ta-support-draft.md),尚未发送。
@@ -1,19 +1,29 @@
# FIT TA recorder 技术询问草稿(未发送)
# FIT TA Record Locator 技术询问草稿(未发送)
请平台或 Oracle 技术人员提供一个明确示例:用哪个接口、把团号放进哪个参数,才能写入酒店页面的 **TA Record Locator**;保存后通过哪个查询字段能读回同一个值。需要同时覆盖新建和修改。有 Tour Code 时原样填写;没有时不提交此字段。用户已截图确认界面位置为 Stay Details → TA Record Locator。现有平台已提供预订新建、修改、查询接口,因此请先判断是否可以直接使用,而非假定要新增接口。
以下为可供技术人员处理的英文版本,无真实酒店、客人或凭据内容。
2026-09-18 更新:已找到官方数据字典将 Travel Agent Locator 关联到 `EXTERNAL_REFERENCES.EXTERNAL_REFERENCE`,条件为 `EXTERNAL_REFERENCE_TYPE='TA_RECORD_LOCATOR'`。现在请优先确认下面这一候选,不需要重新泛搜所有接口。证据和单笔只读查询步骤见[专项核对](ohip-fit-ta-external-reference-evidence-20260918.md)。截图目标始终是 Stay Details 中的 TA 输入框。
# Draft — FIT TA Record Locator REST contract clarification
This request is prepared for review only; it has not been sent.
We need to populate the OPERA Cloud reservation UI field **TA Record Locator** with an agency booking reference, and verify the value through OHIP REST for both new and updated individual reservations. The same reference may identify multiple actual reservation IDs.
Reviewed on 2026-09-17: Oracle hospitality-api-docs, Property API 26.3.0.0, main commit 4bd129b455bc5e3ab0f900ac47983611e58659b4. rest-api-specs/property/v1/rsv.json is byte-identical to our previous dd631fbd revision. The middleware already passes Oracle JSON through postReservation/putReservation and returns getReservation documents; we are requesting the exact native mapping, not assuming a new middleware operation is needed.
Reviewed source: Oracle hospitality-api-docs, Property API 26.3.0.0, commit 4bd129b455bc5e3ab0f900ac47983611e58659b4, rest-api-specs/property/v1/rsv.json. The published middleware contract already exposes postReservation, putReservation, getReservation and searchHotelReservations with Oracle JSON documents. This contract review is not evidence that the deployed chain has successfully written the TA field.
The published searchHotelReservationsRequest defines taRecordLocatorList separately from customReference, GDS recordLocator and externalReferenceIds. We have not established the equivalent writable and readable property in the referenced createReservation, changeReservation or reservation schemas. The Block field reservationDetails.taRecordLocator is documented separately and is not assumed to apply to Reservation.
Oracle's [Subject Area Definitions, PDF page 718](https://docs.oracle.com/en/industries/hospitality/opera-reporting-analytics/ddrna/SA_Definitions.pdf#page=718) maps Travel Agent Locator to EXTERNAL_REFERENCES.EXTERNAL_REFERENCE where EXTERNAL_REFERENCE_TYPE = 'TA_RECORD_LOCATOR'. The RSV schema supports generic externalReferences entries containing id and idContext. Please confirm whether the following inferred candidate is supported for the **Stay Details → TA Record Locator** input in the target OPERA Cloud release:
```json
{"externalReferences":[{"idContext":"TA_RECORD_LOCATOR","id":"<Tour Code / Block Name>"}]}
```
This is a candidate fragment, not a verified request. Its generic container paths are `/reservations/reservation/0/externalReferences` for postReservation, `/reservations/0/externalReferences` for putReservation and `/reservations/reservation/0/externalReferences` in getReservation responses. If this context value is not supported, please provide the correct writable and readable mapping instead.
Please provide:
1. Supported REST operation and exact JSON path for creating the TA Record Locator on a new Reservation.
@@ -22,6 +32,6 @@ Please provide:
4. Relevant OPERA Controls, permissions and Reservation Protection behavior; restrictions on length/characters and using the same value on multiple reservations with one guest profile.
5. Minimal redacted create, update, exact-detail GET and TA-filtered search examples, plus applicable OPERA/OHIP version.
Please distinguish this field from External Reference, Custom Reference, the GDS Record Locator and the Reservation Guest Locator. A search parameter, business-event XML tag, or GraphQL field alone does not establish the required REST write/read mapping.
Please confirm the actual Stay Details input, rather than only the existence of a generic External Reference entry. Custom Reference, GDS Record Locator and Reservation Guest Locator must not be substituted. The database mapping, a search parameter, business-event XML tag or GraphQL field alone does not establish the required REST write/read mapping.
No real hotel identifiers, guest names, credentials or booking data are included in this request.
@@ -0,0 +1,81 @@
# 正式取消/类型转换接入与 FIT TA 查证
2026-09-18 新证据:[官方数据字典已明确 TA 对应类型 `TA_RECORD_LOCATOR` 的外部参考号](ohip-fit-ta-external-reference-evidence-20260918.md)。这更新下文“externalReferences 无等价证据”的旧结论;OHIP 对该特殊类型的写读支持仍待直接证据,正式守卫不变,无需先要求平台新增封装。
日期:2026-09-17。此文覆盖之前“正式取消/转换尚未装配”的状态。**本地代码接入不等于真实酒店已启用或验收**;没有更新运行服务、办理现有任务、修改凭证权限或写入 Oracle。
## 当前可交付范围
| 业务 | 正式入口代码 | 当前限制 |
| --- | --- | --- |
| 源 Allotment 取消 | 已接入 `CANCEL_ALLOTMENT`,查询 Block → 查关联客人 → 查允许的下一状态 → 更新状态 → 精确查回 | 部署须提供酒店真实取消规则;有关联客人或查询不完整时停止 |
| FIT → GROUP | 已接入 UPDATE 的类型识别;完整检查最终 GROUP 新建参数后,逐笔取消旧 FIT,再创建 Block、保存房量并核验 | 需显式开启类型转换;真实环境尚未验收 |
| GROUP → FIT | 编排入口已有,但正式入口仍在任何查询/取消之前阻断 | FIT TA 的真实写入、详情读取字段没有权威证据 |
| 同类型 GROUP 修改 | 仍走原 UPDATE,只改已确认范围,不要求新建用的 Rate/Note,不取消重建 | 既有日期/房量策略仍须验证 |
| FIT NEW / UPDATE | 原守卫保留 | 同一 FIT TA 缺口,不因配置标志而放行 |
邮件 Excel 仍用于解析/原件查看;本流程不读写酒店 Attachments。
## 需要哪些接口和参数
沿用平台 0.11.0 已发布契约,无新增消费端公共 HTTP 入口。这里的 `searchHotelReservations` 是平台实际 Operation ID,不能写成 `searchReservations`。
| 用途 | 平台方法与路径 | Oracle 参数/结果 |
| --- | --- | --- |
| 找旧团队 | POST `/api/v1/blocks/searches` (`searchBlocks`) | `hotelId`、`blockName=Tour`、`onlyPickupBlocks=false`;完整分页后精确 `getBlock` |
| 找旧 FIT | POST `/api/v1/reservations/searches` (`searchHotelReservations`) | `taRecordLocatorList=[Tour]`、`searchType=Any`、`similaritySearch=false`;完整分页后逐笔 `getReservation` |
| 核实团队无关联客人 | 同一 Reservation 搜索 | `blockIdList=[BlockId]`;严格要求完整结果且零条,不能静默连带取消客人 |
| 下一状态 | GET `/api/v1/blocks/next-status` (`getNextBlockStatus`) | query `currentStatus`;返回必须包含配置的取消状态 |
| 取消团队 | PUT `/api/v1/blocks/{id}/status` (`putBlockStatus`) | `verificationOnly=false`;`changeBlockStatus` 内 hotelId、Block id、currentBlockStatus、newBlockStatus、`cancellationDetails.cancellationCode.code`;HTTP 200 |
| 取消 FIT | POST `/api/v1/reservations/{id}/cancellations` (`postCancelReservation`) | `verificationOnly=false`、`reason.code`、单一 hotelId/Reservation id;HTTP 201 |
| 新 GROUP | 原 `postBlock`、`getBlock`、`putBlockAllocation`、`getBlockRoomRateGrid` | 复用现有完整新建/房量/查询链,不另造简化新建入口 |
取消的成功依据是当前精确详情返回取消状态。写入结果未知时不重发;详情仍未查实则停止新建。Block 取消不使用 override 或自动取消关联预订。
## 接入与恢复边界
- `OhipBookingRuntimeFactory` 已按部署策略装配 `OhipBookingChangeExecutionAdapter` 和持久取消调用日志,使用现有确认、数据源、租约和 worker。
- 首次类型转换把真实旧对象绑定与最终 NEW 准备检查点一起保存;不会留下只有酒店 scope、没有配置检查点的半成品。
- 原绑定、配置摘要、目标 NEW 路由、取消原因/状态均受续办核验。每次取消前重验最终 NEW 参数及检查点;配置变化会停止。
- 双类型搜索只排除**精确详情已证明取消**的历史对象。转换留下的旧记录不会阻挡同 Tour 后续普通修改;两种类型仍有当前对象时停止,交人工核对。未知/其他状态不擅自当不存在。
- 单独源 Allotment 取消保留已取消对象查询,支持原确认的恢复与完成核验;输入不要求编造 Account、Rate 或房量。
- 参数准备成功不能保证 Oracle 随后一定接受新建。取消已成功而新建失败时,保留已取消事实及同次确认的调用记录,由原续办处理,不能另造确认或自动恢复旧预订。
- 默认仍为 Pending;旧清单不自动开启取消/转换,配置也不授予应用权限。新增代码不迁移或重跑旧含附件任务。
2026-09-18 接续:已扩查全部 45 个 Property v1 模块,并补齐独立模拟参数;同类型 FIT 取消历史、Guest 准备顺序及无附件 GROUP UPDATE 的修复见[最新交付](ohip-parameter-simulation-closure-20260918.md)。此处真实 FIT TA 未决结论不变。
## FIT TA 的准确结论
业务含义已经明确:**TA Record Locator 填 Tour Code/Block Name**。问题是 Oracle REST 的写入与详情读回位置,目前不能把“有同名搜索条件”当作已确认写入字段。
本轮重新验证 Oracle 官方仓库 HEAD 仍为 `4bd129b455bc5e3ab0f900ac47983611e58659b4`,Property RSV `26.3.0.0`,完整文件树未截断;再检查 RSV schema、两个官方 Property Postman 集合及 Data API 定义:
| 证据 | 能证明什么 | 不能证明什么 |
| --- | --- | --- |
| [官方 RSV schema](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/rsv.json) 中 `taRecordLocatorList` | 可以按 TA 搜索 | 未找到 Reservation 创建/修改/精确详情的对应属性或等价映射 |
| [官方 Property Postman](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/postman-collections/property/oracle-hospitality-property.postman_collection.json) 与 [工作流样例](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/postman-collections/property/oracle-hospitality-property-workflows.postman_collection.json) | 可核实已有请求形状 | 两份全文未检出 `taRecordLocator` / `TA Record Locator`,不能给出 TA 写入样例 |
| [BookingsReservation GraphQL](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/graphql/data-apis/BookingsReservation.graphql#L3009) | 报表层存在 TA 字段 | 不是 Property REST 写入或 getReservation 的字段证明 |
| [TA Record Locator 控制](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.1/ocsuh/c_opera_controls_reservations.htm)、[26.2 保护字段说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.2/oprnc/c_feature_summary.htm) | 页面输入/搜索受酒店控制,可被设为保护字段 | 不提供 REST JSON 路径 |
`locators[].locatorText` 在官方定义中是 Guest Locator,`recordLocator` 搜索项是 GDS Record Locator;`customReference` 和通用 `reservationIdList` 尚无明确 TA 映射。2026-09-18 新数据字典证据已将 `externalReferences` 收窄为 `TA_RECORD_LOCATOR` 类型这一具体候选;其 REST 可写性、详情返回和保护行为仍待核实,不直接把候选或模拟字段填进正式请求。
**结论是公开证据仍不足,并非认定 Oracle 永远不支持。** 平台包好接口后,这个字段契约缺口仍需补齐;不能简单要求平台同事再做相同公开搜索。
## 后续操作由用户完成
本轮提供 [取消配置模板](ohip-runtime-cancellation.example.json),未替换任何运行清单。它是完整示例,包含占位值,不能原样启用。原 [四流程模板](ohip-runtime-manifest.example.json) 保持兼容。
1. 启用取消前,酒店提供真实 Reservation 取消原因、Block 取消原因、Block 取消状态、允许的原状态;两种原因分别配置,不能默认相同。管理员核实本应用所需 `blocks.read/write`、`reservations.read`;FIT→GROUP 另需 `reservations.write` 及对应已发布操作。只启用 Block 取消时不必额外授予 FIT 取消权限。
2. `cancellationPolicy` 保存上述值与核验记录 `verificationRef`;`CANCEL_ALLOTMENT` route 允许单独取消。`typeConversionsEnabled` 默认 false,完成目标 NEW/UPDATE 路由验证后才设 true。设置 true 仍不放开 GROUP→FIT。
3. FIT 字段建议把以下问题交 Oracle 支持/接口负责人,要求新证据,而不是重复公开检索:
> 请确认目标 OPERA Cloud 版本 Stay Details → TA Record Locator 的正式 OHIP 接口:postReservation / putReservation 的准确 JSON Pointer、getReservation 的读回 Pointer 与必要 fetchInstructions;若应使用其他接口,请提供 Operation ID、请求/响应样例及版本依据。该值是旅行社 Tour Code,不是 GDS Record Locator、Guest Locator 或普通 External Reference。还请说明 TA_RECORD_LOCATOR 与 Reservation Protection 控制对写入的影响。
若手头已有 TA 有值的测试 FIT,可先只读查询该 Reservation,交付脱敏的字段结构与已知 TA 值用于定位返回字段;这只能补读回证据,仍须官方依据或受控测试证明写入。不得提交 Cookie、Token 或完整客人资料。
4. 字段证据和真实配置齐备后,再由用户更新运行服务,在明确测试酒店/数据上验收。上传 EML、确认/续办、重启、数据准备和酒店修改均由用户操作。当前没有这些操作的完成记录。
## 本地验证
正式 Factory → Runtime → Worker → JDBC 日志 → localhost 酒店替身已覆盖:单独取消、FIT→GROUP、GROUP→FIT 提前阻断、同类型普通修改、已取消旧记录过滤、配置/权限缺失、配置中途变化、关联客人/不完整查询/下一状态禁止、未知取消不重发、取消回执丢失后精确查询恢复、重启不重复新建。全部使用临时 H2 与合成数据,绝非真实酒店验收。
最终检查数量和日志见[测试证据](../../../.project-docs/50-evidence/topics/2026-09-17-formal-cancellation-fit-ta.md)。
@@ -0,0 +1,33 @@
# CP33 本机酒店候选目录与联系人接口核对
## 已完成
用户确认现有房型/房价码正确,并提供其他候选及账户联系人截图。本轮只更新原本机测试运行装配,无代理、推送、发布或真实酒店调用。
本机执行目录复用已冻结的10 Room Type、21 Rate Code;截图/文字明确的17 Market、10 Source、2 Reservation Type、10 Payment、5 Block Status、1 Currency作为候选。其中用户文字的A=Contact原样保留;截图银行卡号不复制,截断的Rate Code不猜测、不改既有选价规则。候选目录的Company同名项保留作为选择干扰;旅行社与联系人分别匹配唯一的活动档案及所属账户。
目录实际姓名存于CP31/local-entry/private/hotel-catalog.json(0600),仓库catalog.example.json只含合成姓名。模拟编号沿用AGENT/CONTACT既有形式,绝非Oracle编号。目录在本机执行装配加载;未向Oracle配置环境写入,也未把本机目录读取伪装成Oracle API查询。现有工作台房型/房价码目录无需重建;其他候选不改变原工作台控件或识别算法。
新采集的接口核对字段保留实际code/id,在旁边显示“本机目录说明”。说明在记录产生时保存,历史记录不反向补造,读取不外呼。姓名说明来自私有目录,不是声称Oracle返回了姓名。41项相关前端测试、类型检查、针对性lint(零错误)通过。LocalBookingCatalogCheck验证10/21池、两账户、7类拒绝、两账户×GROUP/FIT四种请求转换,以及请求/响应说明持久化与旧记录读取;LocalBookingTraceCheck权限/归属/分页检查通过。本轮没有重复EML端到端验收。
## Account / Contact接口核对
依据[Oracle CRM 26.3官方契约](https://github.com/oracle/hospitality-api-docs/blob/main/rest-api-specs/property/v1/crm.json)及2026-09-17平台公开目录:
| 环节 | 契约与结论 |
| --- | --- |
| 查账户 | getProfiles,CRM profileType=Agent;UI显示Travel Agent,不用Company代替。按明确名称核对并取得内部profileId。 |
| 查联系人归属 | getProfiles查询Contact,fetchInstructions=PrimaryAccountInfo在官方枚举及平台允许参数内;返回profile.primaryAccountInfo可能提供其主账户profileId。需要真实响应核验、完整分页和唯一性,不能仅凭姓名绑定。 |
| 其他关系读取 | Oracle getProfile可带Relationship,独立getProfileRelationships也存在;平台当前getProfile query_names为空,独立关系接口catalog 404未封装。不能假定已支持;本轮不修改平台。 |
| GROUP写入 | blockProfiles.blockProfile分别用Agent和AgentContact角色、各自profileIdList;primary=true是团的关联属性,不能推断CRM档案全局Primary。 |
| FIT写入 | 旅行社关联角色TravelAgent;既定FIT流程未选择Contact。RSV没有AgentContact角色,不从GROUP截图擅自加入FIT。 |
因此账户搜索、联系人归属查询、写入团关联是不同步骤。当前本机采用显式模拟关联目录,真实Oracle自动发现及接口响应验证仍属于后续验收,不能宣布已完成。
## 原入口加载与恢复边界
原5178/8082已加载CP33专用包。正常Maven jar保持原摘要且不含本机目录类。先用原H2备份副本预启动,再由原launchd受控切换;实际参数、44项环境、cwd、身份权限全部保持,58张H2表和24张PG表内容摘要一致。原Edge/模拟酒店进程不变,39待确认仍在,无历史重入队。
本机候选代码集合变化进入既有checkpoint配置保护;新确认使用新目录。旧未完成办理不能绕过配置变更检查或自动续办。历史模拟酒店状态与原绑定保留。
本轮记录位于忽略目录cp33-hotel-catalog,含baseline、local-checks.log、runtime-verification.json、local-entry/prepared-runtime.json及managed-runtime-verification.json;私有凭据/备份不输出。活动测试包SHA256为32e1e1a6a3aa1d0d9de8dda0f50cf0c849ef3063107257772b9856462816c962。
## 下一步
用户在原工作台新导入EML并确认,查看本次实际请求和查询结果的代码/编号及目录说明。真实Oracle对接前,单独验证上述账户联系人查询返回和PrimaryAccountInfo路径,不把模拟成功当作真实环境验收。
@@ -0,0 +1,53 @@
# 无 TA 版本:本机测试交付
> 新版本已交付:[FIT NEW条件TA接入](ohip-fit-new-ta-20260918.md)。本页及原无TA包是历史版本;新包在 `.planning/fit-new-ta-20260918/release/`,特殊类型到沙箱页面对应尚待验证。
> 后续规则已变更:用户现要求 GROUP NEW不传TA;FIT NEW有Tour Code时传入TA Record Locator、无则不传。见[新CR](../requirements/CR-20260918-fit-new-conditional-ta.md)。本文及包体记录的是此前无TA版本,尚未实现新要求中的FIT有值写入,不应据此验收该项。
2026-09-18。**代码及独立模拟已完成;现有工作台尚未更新。下一步由用户更新测试服务,再上传邮件测试。** 平台接口连接的就是沙箱;本轮没有要求重新确认环境,也没有操作平台沙箱业务。
## 本次完成
| 流程 | 现在如何调用与定位 |
| --- | --- |
| GROUP 新建/修改 | 保留 Block Name、日期、账户联系人、已约定默认值及逐晚房量;GROUP 修改继续只改日期/房量。按 Block Name 定位后核对 Block ID/酒店。 |
| FIT 新建 | 客档创建/查询、预订创建/查询已接;保存实际 Oracle 预订编号。 |
| FIT 修改 | 从同酒店、同本地预订范围、同环境的最近成功办理结果取得完整编号,再逐笔调用 getReservation/putReservation;支持既定的房型新增/合并。 |
| 源 Allotment 取消 | 关联预订及下一状态检查、Block 取消、状态回查。 |
| FIT→GROUP、GROUP→FIT | 校验最终新建参数、取消原对象并核实,再创建新类型;使用上述保存编号定位 FIT。 |
GROUP/FIT 均不发送、清空、按 TA 搜索或要求核验 TA;没有改用其他参考号/备注替代。邮件及本地 Tour Code、GROUP Block Name 保留。酒店附件上传保持取消。Source Code 的 `TA` 是另一业务代码,正常保留。
没有新增需要平台封装的接口。正式接入工厂也已更新,但它仍使用受信的酒店/档案/取消配置和已有权限,测试代码不会自动替平台赋权。
## 模拟参数与结果
已造好 10 个房型、21 个 Rate Code、两组账户/联系人、Guest、Market/Source/Reservation Type/Payment/Currency、Initial/Actual 房量、状态和独立取消原因。GRPA3=BUALUANG、GRPA4=LEELA,均含早。参数文件是合成数据,不包含酒店真实档案 ID。
- 47 个参数场景符合预期:45 个完成,2 个因不能暗中取消多余 FIT 而零写入拒绝。
- 3 类取消故障符合预期,取消结果未知时不会重复发送。
- 1,034 条调用摘要没有 TA 请求字段、没有附件操作。
- 后端完整回归 2,706 项:2,689 通过、17 条件跳过、0 失败/错误;前端相关 41 项、类型检查、模拟器 67 项通过。
- 已确认 GROUP 修改保留酒店既有 TA;旧 TA 检查点不能静默续办。
最终合成验收位于 `.planning/no-ta-20260918/acceptance-6/`;`acceptance-result.json`、`summary.json`、`parameters/results.json`、`faults/results.json` 和调用目录可复核。临时服务已停止,临时连接文件已删除。模拟对象存在于这次独立验收的保存状态,**没有导入当前工作台或平台沙箱**。
## 用户接下来操作
1. **先更新测试后端。** 本机交付目录为 `.planning/no-ta-20260918/release/`。使用平台沙箱接口的正常运行配置,选 `th-hotel-server-0.0.1-SNAPSHOT.jar`;目前旧 CP33 本机模拟装配,选 `th-hotel-local-simulation.jar`。不要把本机模拟包用于平台沙箱配置。
2. **沿用相应启动配置,由用户替换程序路径并重启。** 本机原配置是 `/Users/chillishark/Library/LaunchAgents/com.chillishark.th-hotel-simple.backend-8082.plist`;其中旧包路径是 `server/var/local-replay/runtime/ohip-cp33-hotel-catalog-20260917/th-hotel-local-simulation.jar`。新本机包绝对路径为 `/Users/chillishark/Wyndham-RSVN0804/Wyndham-RSVN-0908/.planning/no-ta-20260918/release/th-hotel-local-simulation.jar`。本轮未修改该配置或原包。数据库、凭据及环境文件沿用原配置;不要以合成文件覆盖真实或既有档案配置。
3. **若继续使用旧 CP33 本机模拟酒店,还需同步模拟服务。** 原实例未加载取消/严格参数模拟的本轮版本;仅换后端包不足以验收全部流程。模拟代码、目录和独立启动说明位于 `server/src/test/local-fit-simulation/README.md`,严格参数入口是 `check_parameters.py`,目录是 `catalog.example.json`。它会启动完整独立链并在结束后停止;这不是当前工作台数据导入工具。当前工作台与新模拟实例绑定、目录/数据同步仍须由用户操作,不能把新空酒店直接搭配旧 FIT 编号使用。
4. **刷新页面,先测试全新 GROUP 和 FIT。** 确认成功后再测同笔修改;另建测试笔测 Allotment 取消及双向转换。首次使用新的 Tour Code,避免重复 NEW。查看“确认值、实际调用、酒店查询结果”是否一致。
5. **上传 EML/XML、员工确认和沙箱操作均由用户执行。** 原失败任务不要直接续办来测试新规则;旧检查点会因范围版本变化而拒绝。此轮没有上传文件,也没有点击原任务确认/继续。
交付目录的 `manifest.json` 保存包体及目录 SHA-256;普通包不含本机模拟装配。前端源代码已改并通过检查,现有开发页面刷新加载;若使用静态发布页面,则沿用原前端构建发布步骤。
## 本期限制
- **在 Oracle 手工创建、且本系统没有保存完整编号的旧 FIT,不能自动修改/取消/转换。** 暂停 TA 后不按姓名或备注猜匹配;也不开放随意填写编号的入口。先通过本系统成功新建的测试笔可验证后续流程。
- 同一范围最近一次办理失败、未完成或历史有歧义时,会停止,不跳过后选择更早成功记录。
- 原有多笔 FIT 不能通过减房隐式取消其中一笔;此情况仍提示人工处理。
- 极长 Note 的聚合显示超出原有 16,000 字符证据上限时,整单不会标为完全核验成功;没有借本次 TA 收尾放宽此界限。
- 本轮证明消费端参数接入和独立模拟结果,未代做现有页面邮件全链或平台沙箱业务验收。
完整开发证据见 [证据记录](../../../.project-docs/50-evidence/topics/2026-09-18-deferred-ta-local-test.md)。
@@ -0,0 +1,117 @@
# Oracle 业务参数与接口逐项核对
2026-09-18 最新创建规则:GROUP NEW不传TA Record Locator;FIT NEW有Tour Code时应传入TA Record Locator、没有时省略。业务规则和候选映射代码已接入:创建externalReferences以idContext=TA_RECORD_LOCATOR、id=Tour发送;映射依据官方结构+字典推导,沙箱页面落值待验,见[新CR](../requirements/CR-20260918-fit-new-conditional-ta.md)。查询接口 searchHotelReservations/taRecordLocatorList 已存在;不能用查询参数冒充创建写入字段。UPDATE的TA范围未改,FIT后续仍以系统保存的成功历史编号精确定位。
更新:2026-09-18。业务参数与字段已按公开 Oracle 契约及实际转换器逐项对照;平台公开目录复查为 184 项,消费端登记的 18 个非附件操作的方法、路径、查询参数白名单均匹配。此前“5 个核心接口尚未发布”已经关闭,不需要平台重复封装。配置查询接口已发布,但本应用的真实授权、酒店参数取值和真实酒店验收仍分开核实。
此前无TA版本的GROUP/FIT新建、修改、取消及双向转换已通过47参数/3故障;本次FIT NEW有Tour Code的候选映射已另行实现及重跑模拟,原生页面落值仍待验。原服务未更新,旧版记录见[无TA交付](ohip-no-ta-local-test-20260918.md)。
## 1. 业务范围先校正
| 任务 | 本次需要输入/修改的内容 |
| --- | --- |
| GROUP NEW | Tour、日期、Account/Contact、Rate、含早、Market/Source、GC、BTQR、TEN、THB、完整逐晚房型房数 |
| GROUP UPDATE | 按原约定修改日期、完整逐晚房型房数;保留原 Account/Contact、Rate、状态及其他新建默认值 |
| FIT NEW | Name/Country、共享 Guest、Travel Agent、日期、Adult、房型房数、Rate、Market/Source、GC、BTQR、Fixed Rate、Note;有Tour Code时写入TA Record Locator,无则省略(候选映射已接入,页面待验) |
| FIT UPDATE | 从已保存的成功历史编号查清实际 Reservation 集合,修改日期、房型房数、Rate、Note;本次同类型新增房型按已批准范围创建必要的新笔 |
| FIT ↔ GROUP | 逐个取消已确定的旧对象,再按最终类型的新建参数处理 |
| Allotment CANCEL | 查找源 Block、查询关联预订和允许的下一状态;以实际 Block ID、当前/取消状态及取消原因执行取消 |
此前口头把 GROUP 修改也概括为更新 Rate/Note 不准确。现有 CP22 和已确认流程没有该要求;GROUP Note 也未列入既定新建输入。FIT Note 保持员工确认原文;Note 中金额不转为手工房价。餐厅由 Rate Code 表达,不另写 BUALUANG/LEELA 字段;GROUP 另写含早布尔值。
## 2. GROUP 主资料
`postBlock` 新建根为 `blocks.blockInfo[].block`;`putBlock` 修改根为 `blocks[]`;`getBlock` 查询根为 `blocks.blockInfo[].block`。下表路径相对于各自单个 Block。原生接口分别为 POST `/blk/v1/hotels/{hotelId}/block`、PUT/GET `/blk/v1/hotels/{hotelId}/blocks/{blockId}`;Edge 对应 `/api/v1/blocks`、`/api/v1/blocks/{blockID}`。
| 业务参数 | Oracle 字段 | 使用范围 |
| --- | --- | --- |
| Tour / Block Name | `blockDetails.blockName` | 新建;修改前以 `searchBlocks` 的 `blockName` 查找并核对 |
| 入住 / 离店 | `blockDetails.timeSpan.startDate/endDate` | 新建、修改;按官方 Postman/指南使用 `YYYY-MM-DD`;schema 的 maxLength=8 矛盾保留记录 |
| Account / Contact 实际 ID | `blockProfiles.blockProfile[].profileIdList[].id`,配 `type=Profile` | 新建;Agent 与 AgentContact 两个关联 |
| 关联角色 / 主关联 | `blockProfiles.blockProfile[].blockProfileType`、`primary` | Agent / AgentContact,`primary=true`;不是修改 CRM 全局主账户 |
| Rate Code | `reservationDetails.ratePlanCode[].ratePlanCode`,`primary=true` | 新建;本轮同类型 GROUP UPDATE 不重设 |
| Breakfast Included | `reservationDetails.breakfast.breakfastIncluded` | 新建,使用已确认布尔值 |
| TA Record Locator | 本期不提交 | 暂停,不清空已有值 |
| Market=GTT | `blockDetails.marketCode.marketCode` | 新建 |
| Source=TA | `blockDetails.sourceOfSale.sourceCode.sourceCode` | 新建 |
| Reservation Type=GC | `blockDetails.reservationType.reservationType` | 新建 |
| Payment=BTQR | `blockDetails.paymentMethod.code` | 新建 |
| Block Status=TEN | `blockDetails.blockStatus.bookingStatus.status.code` | 新建;普通修改保留原状态 |
| Currency=THB | `blockDetails.currencyCode` | 新建 |
| Hotel / Block ID | `hotelId`;修改时 `blockIdList[].id/type` | 与路径及所选酒店一致,使用查询所得实际 ID |
上述主资料写入与查询接口均在当前公开 Edge 目录。历史GROUP TA字段由 [Oracle BLK 契约](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/blk.json)明确提供。
## 3. GROUP 每晚房型房数
主资料的 `putBlock` 不负责 Room Grid。写入用 `putBlockAllocation`:PUT `/blk/v1/hotels/{hotelId}/blocks/{blockId}/allocation`;查询用 `getBlockRoomRateGrid`:GET `/blk/v1/hotels/{hotelId}/blocks/{blockId}/roomRateGrid`。
| 参数 | `putBlockAllocation` 请求路径 |
| --- | --- |
| 酒店、Block | `criteria.hotelId`、`criteria.blockId.id/type` |
| 房型 | `criteria.allocationRoomTypes[].roomType` |
| Initial / Actual 类别 | `criteria.allocationRoomTypes[].allocationGridDates[].allocation`,本操作用 `INITIAL/ACTUAL` |
| 单个住宿日 | `criteria.allocationRoomTypes[].allocationGridDates[].roomAllocationInfo[].start/end`,逐日填写相同日期 |
| 该日房数 | 同一个 `roomAllocationInfo[]` 下 `inventory.onePerson/twoPerson/threePerson/fourPerson` |
以住宿夜为单位,不把离店日当住宿夜;输入完整目标数量,旧房型/旧日期需处理的格明确归零。未启用 occupancy split 时按官方流程用 onePerson;启用时需有明确分列数据。查询使用 `roomAllocationCriteria=Initial/Actual`,不能机械照抄写入枚举大小写;还需 startDate、numberOfDays、完整分页。依据实际状态 allowPickup 决定类别,不把所有修改强制当 TEN。
**写入和查询均已发布并接入**:Edge PUT `/api/v1/blocks/{blockID}/allocation`,GET `/api/v1/blocks/{blockID}/room-rate-grid`。Actual 分类与 occupancy split 是独立参数;模拟装配已纠正,不能把 allowPickup=true 当成人数分列开启。依据 [Oracle Room Grid 指南](https://docs.oracle.com/en/industries/hospitality/integration-platform/maeig/t_create_a_block_with_or_without_room_grid.htm)及 BLK 契约。
## 4. FIT 主资料、客档和 Note
`postReservation` 新建根为 `reservations.reservation[]`;`putReservation` 修改根为 `reservations[]`;`getReservation` 查询根为 `reservations.reservation[]`。下表相对于单个 Reservation。原生 POST `/rsv/v1/hotels/{hotelId}/reservations`,PUT/GET 加 `/{reservationId}`;Edge 对应 `/api/v1/reservations` 和 `/{reservationID}`。
| 业务参数 | Oracle 字段 | 适用/证据 |
| --- | --- | --- |
| 入住 / 离店 | `roomStay.arrivalDate/departureDate` | 新建、修改,date |
| 房价住宿区间 | `roomStay.roomRates[].start/end` | 新建、修改;当前实现 end 为离店前一日 |
| 房型 / 房数 | `roomStay.roomRates[].roomType/numberOfUnits` | 新建、修改;不同房型不能误当同一笔的日期分段 |
| Rate Code | `roomStay.roomRates[].ratePlanCode` | 新建、修改;不从 Note 取价格 |
| Adult=2 | `roomStay.guestCounts.adults` 及 `roomStay.roomRates[].guestCounts.adults` | 新建,两层保持一致 |
| Market=X / Source=TA | `roomStay.roomRates[].marketCode/sourceCode` | 新建 |
| Fixed Rate=true | `roomStay.roomRates[].fixedRate` | 新建明确设 true;当前修改保留酒店原值,实际重新定价行为另行验证 |
| Reservation Type=GC | `roomStay.guarantee.guaranteeCode` | 新建;不要写进 sourceCode |
| Payment=BTQR | `reservationPaymentMethods[].paymentMethod` | 新建 |
| 共用 Guest ID | `reservationGuests[].profileInfo.profileIdList[].id`,主客标记 `primary=true` | 新建;同次多笔引用同一实际客档 |
| Travel Agent ID | `reservationProfiles.reservationProfile[].profileIdList[].id` | 新建;角色 `reservationProfileType=TravelAgent`,FIT 不另加 GROUP 的 AgentContact |
| Note 原文 | `comments[].comment.text.value` | 新建、修改;修改另带已核实的 `comments[].id`,保留无关备注 |
| Hotel / Reservation ID | `hotelId`、修改时 `reservationIdList[].id/type` | 取实际酒店及预订 ID |
| Tour / TA Record Locator | FIT NEW有Tour Code时应写入,无则省略;候选创建字段externalReferences[].id/idContext已接入,页面待验 | 查询参数taRecordLocatorList已存在;不能当创建写入字段。按TA_RECORD_LOCATOR类型精确回查,错值/缺失/重复不通过 |
主资料新建、修改、查询和 `searchHotelReservations` 均在公开 Edge 目录。Note 正式 schema 是 `comments[]`,`putReservation` 内嵌 example 却有 `comments.commentInfo`,应保留这个契约差异,不能声称样例也已一致。依据 [Oracle RSV 契约](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/rsv.json)。
Name 与 Country 的独立建档接口已定位:`postProfile`,POST `/crm/v1/profiles`,字段为 `profileDetails.customer.personName[].surname`、`profileDetails.addresses.addressInfo[].address.country.code`,以及 `profileType=Guest`、`registeredProperty`。Country=CN,不代替 nationality;查询为 `getProfile`。Edge POST `/api/v1/profiles` 和 GET `/api/v1/profiles/{profileID}` 已发布且已接入。内嵌新档也有 Oracle schema,但本项目当前选用先建一次、后续共用的流程,不重复创建同次共享客档。
## 5. Account / Contact 的查询与写入是两步
| 目标 | 查询接口和参数 | 结果/写入 |
| --- | --- | --- |
| 旅行社账户 | `getProfiles`,`profileType=Agent`、`profileName`、`excludeInactive`,完整分页;也有 `searchProfiles` | 读取 `profileSummaries.profileInfo[].profileIdList[].id`,核对名称和类型 |
| 固定联系人及其主账户 | `getProfiles`,`profileType=Contact`、`profileName`、`fetchInstructions=PrimaryAccountInfo` | `profileSummaries.profileInfo[].profile.primaryAccountInfo.profileId.id`,比对已选 Account ID;联系人自身 ID 用于 GROUP 的 AgentContact |
| 其他关系路径 | Oracle 原生 `getProfileRelationships` 或 `getProfile` 的 Relationship fetch | `getProfileRelationships` 已发布为 GET `/api/v1/profiles/{profileID}/relationships`;`getProfile` 的可传参数仍按其独立白名单,不能猜加 fetchInstructions |
因此这一项的接口/字段已经找到了,剩余为实际关系数据验证;若联系人没有主账户关系,不得按名字猜归属。CRM 类型为 Agent,GROUP 关联角色 Agent,FIT 关联角色 TravelAgent,三个位置不能混用。依据 [Oracle CRM 契约](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/crm.json)。
## 6. 邮件 Excel 保留解析,取消 Oracle Attachments 上传
2026-09-17 用户取消“把本封邮件的 Excel 上传到对应的团队或 FIT 预订”。GROUP/FIT 新建、修改不再执行酒店附件上传、替换、查询核验或删除,也不再以 linkType、附件 ID、命名/描述及原件回查作为本流程前置或成功条件。邮件接收、原件查看和业务解析保留;TA现按最新NEW规则区分GROUP/FIT;邮件/本地Tour Code保留。既有附件研究仅作历史记录。执行代码已移除附件阶段及依赖;本轮另清除了 GROUP UPDATE 残留的非空来源附件条件。
## 7. 取消与类型转换
| 动作 | Oracle 原生接口 | 业务参数路径 |
| --- | --- | --- |
| 取消 FIT 原预订 | `postCancelReservation` POST `/rsv/v1/hotels/{hotelId}/reservations/{reservationId}/cancellations` | `reservations[].hotelId`、`reservationIdList[].id/type`、`reason.code`(可带 description)、`verificationOnly=false` |
| 查 Block 可转状态 | `getNextBlockStatus` GET `/blk/v1/blocks/status` | `hotelId`、`currentStatus`;这是业务允许的下一状态查询,不等同配置维护接口 |
| 取消 Block | `putBlockStatus` PUT `/blk/v1/hotels/{hotelId}/blocks/{blockId}/status` | `changeBlockStatus.hotelId`、`blockId.id/type`、`currentBlockStatus`、`newBlockStatus`、`cancellationDetails.cancellationCode.code`,根 `verificationOnly=false` |
| 取消后核对 | `getReservation` / `getBlock` | 按原实际 ID 查询状态;不能只根据写入 HTTP 返回证明当前已取消 |
三个取消/下一状态业务接口均已发布且接入正式 Factory/Runtime:POST `/api/v1/reservations/{reservationID}/cancellations`(201)、GET `/api/v1/blocks/next-status`、PUT `/api/v1/blocks/{blockID}/status`(200)。FIT 取消原因配置查询 `getCancellationCodes`、Block 取消原因 `getBlockCancellationReasons`、状态目录 `getBlockStatusCodes` 已在 Edge 目录;本轮没有读取酒店实际值。状态/原因的酒店代码是待获取的参数值,不是未知的字段位置。Allotment 取消还需查询关联 Reservation(搜索按实际 Block ID),不能顺带取消住客或 PM。
## 8. 第一层尚未关闭的项目
1. FIT NEW有Tour Code时的TA Record Locator写入恢复为本期需求,候选写入已接入并模拟验证,沙箱页面待验;查询接口已存在。GROUP NEW不传,FIT无Tour Code省略。缺本系统可信历史编号的旧FIT仍不自动修改/取消/转换。
2. 文档冲突已有实施依据:FIT Note 采用正式 schema 与官方 Postman 一致的 `comments[]`;GROUP 日期采用官方指南/Postman 的 `YYYY-MM-DD`。保留矛盾记录,真实运行行为另验,不再称字段未定。
3. 真实环境取值与验收:Account/Contact 实际 ID、可用代码、Block 房量控制、两种取消原因及状态需由受信配置提供。接口都已发布,这些是环境配置/验证事项,不是新增封装缺口。
完整映射已列明,本次已完成独立模拟参数和接口链验证。无TA当前状态和用户步骤见[最新交付](ohip-no-ta-local-test-20260918.md)。现有服务更新、文件上传及酒店操作由用户执行;没有向他人代发询问。
@@ -0,0 +1,62 @@
# 参数接口与独立模拟验收交付
> 历史验收记录:下文为暂停TA之前的实现。当前无TA代码与新一轮47参数/3故障已完成,以[无TA交付](ohip-no-ta-local-test-20260918.md)为准;平台接口连接沙箱,现有环境更新由用户执行。
日期:2026-09-18;接续 9 月 17 日无人值守要求。上传 XML/EML、操作已有任务和更新现有服务仍由用户完成。本轮只读查证公共资料、修改本地代码并运行独立合成参数测试,没有创建子智能体。
## 参数与接口现在到哪一层
[逐参数对照表](ohip-parameter-interface-mapping-20260917.md)已更新到实际发布状态,修正 `blockProfileType` 角色字段,并去除过期“接口尚未发布”结论。公共目录有 184 个操作;消费端登记的 18 个非附件操作,其方法、路由、查询参数白名单全部匹配。
| 业务 | 参数及接口接入 | 真实环境边界 |
| --- | --- | --- |
| GROUP 新建 | Block Name/TA、日期、Account/Contact、Rate、含早、GTT/TA/GC/BTQR/TEN/THB → `postBlock/getBlock`;完整逐晚房量 → `putBlockAllocation/getBlockRoomRateGrid` | 需真实酒店代码、档案和房量控制;本轮未启用 |
| GROUP 修改 | 先按 Tour 查当前 Block;只更新日期/TA/完整房量,保留 Rate、状态、Account 等 | Initial/Actual 与人数分列独立;改期真实行为待酒店验收 |
| FIT 新建 | 姓名/CN → `postProfile/getProfile`;共享 Guest、Agent、日期/房型房数、Rate、Fixed Rate、Adult/GC/BTQR、Note → `postReservation/getReservation` | **TA 正式写读字段未获证,正式入口保持阻断** |
| FIT 修改 | `searchHotelReservations/getReservation/putReservation`;同类型有界新增房型复用实际 Guest 和创建接口,备注按历史证据定位 | 同一 TA 缺口;不能把模拟成功称为真实 Oracle 成功 |
| 源 Allotment 取消 | 查 Block、关联预订、下一状态 → `putBlockStatus` → 精确状态回查 | 酒店须提供 Block 原因和取消状态;关联客人/PM 或不完整查询继续阻止 |
| FIT→GROUP | 完整检查最终 GROUP 参数后,逐笔 `postCancelReservation`,查实取消,再复用 GROUP 新建 | 正式编排已接;仍需真实环境配置/验收 |
| GROUP→FIT | 同次确认内取消 Block 后创建 FIT 的编排和模拟链已跑通 | 因真实 FIT TA 契约未齐,正式入口在取消原团之前阻断 |
Account/Contact 和代码查询所需的 `getProfiles/getProfileRelationships/getRoomTypes/getRatePlansByHotel/getMarketCodes/getSourceCodes/getGuaranteeCodes/getPaymentMethodsLOV` 已发布;取消配置 `getCancellationCodes/getBlockCancellationReasons/getBlockStatusCodes` 及 `getOperaSettings` 也已发布。本应用业务运行使用受信的酒店映射,不会每次按名字猜档案或把配置查询自动变成权限授予。这里没有新增让平台同事重复封装的清单。
## 测试参数已补齐
模拟目录 [catalog.example.json](../../../server/src/test/local-fit-simulation/catalog.example.json) 只含合成姓名和 ID。房型/Rate 直接使用冻结目录的 **10 个房型、21 个 Rate Code**,没有复制一套选价逻辑。GRPA3 含早/BUALUANG、GRPA4 含早/LEELA 保持原已确认规则;餐厅不另造 Oracle 写入字段。
- 两个 Account 各有 Agent、关联 Contact 和同名 Company 干扰项。GROUP 必须是 Agent/AgentContact,FIT 必须是 TravelAgent 关联到 Agent;跨账户 Contact、Company 冒充 Agent、无效 Guest ID 会被模拟酒店拒绝。
- Market、Source、Reservation Type、Payment、Currency、Room Type、Rate Code 都在模拟酒店真正校验,错误请求不能改变已存对象。之前只有 Java 展示目录,现在不再仅靠显示名称判断接通。
- 新增独立模拟取消原因 `SYN-FIT-CXL`、`SYN-BLOCK-CXL`,状态 `TEN/ACT/SYN-CANCEL`、下一状态规则、`occupancy_split_enabled=false`。它们不是目标酒店真实值,不能复制到正式配置。
- Initial/Actual 两份网格独立保存,查询按 `roomAllocationCriteria` 精确筛选。未启用人数分列时总量使用 onePerson;Actual 不代表必须拆成人数列。开启分列的原生模拟校验独立覆盖,应用层仍要求明确完整四列,不能猜分配。
- 模拟 TA 专用属性 `__localSimulationTaRecordLocator` 明确保留为测试协议;正式 Adapter 没有采用这个字段。
- 已取消历史仍出现在模拟搜索里,应用必须根据精确详情过滤;不能靠模拟酒店偷偷隐藏旧记录让测试通过。
隔离参数验收不读取 Excel,也不上传 XML/EML。旧纯协议单元测试仍可不加载完整目录;**完整接口验收必须以 `--catalog` 启用严格参数目录**,缺失新 `simulation` 配置就拒绝启动。现有私有目录/运行服务没有被替换。
## 完整测试发现并修复的问题
1. GROUP UPDATE 残留“来源附件必须非空”的条件已去除,无附件也能按确认参数更新。邮件原件的接收/查看和历史附件审计不受影响。
2. 本地状态策略把 allowPickup 同时当作人数分列开关,导致 Actual 更新准备失败。现在独立读取分列配置,Initial/Actual 只决定网格类别。
3. 模拟网格查询忽略类别,Actual 查询混入 Initial 旧格。现在按请求返回真实对应类别,另一类不会被清零或改标签。
4. 同类型 FIT 的内层查询重新把已取消历史纳入目标;现在沿用外层精确识别结果,新增房型后的最终集合核验也排除详情已确认取消的旧记录。仍保留原确认、酒店、时间和租约校验。
5. GROUP→FIT 首次预检在原目标/检查点尚未由 Worker 保存时冻结 Guest 计划,触发 `GUEST_CONTEXT_CHANGED`。现在首次预检只返回准备结果,持久检查点存在后才冻结客档计划,没有放松仓库一致性校验。
保留既定拒绝边界:两笔旧 FIT 不能直接压成总共一间房并暗中取消一笔;本次把它列为预期拒绝并验证零写入。已支持的同住期、同 Guest、总数足以承接原编号的房型合并及有界新增房型继续运行。加床及其他未授权业务未扩展。
## FIT TA 官方查证结果
这轮检查了固定官方提交 `4bd129b455bc5e3ab0f900ac47983611e58659b4` 下 **全部 45 个 Property v1 模块**,不只查 RSV。BLK 文件下载重试失败后,使用已缓存文件并核实 Git blob 摘要与该提交完全一致。
- [BLK](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/blk.json) / BLK Async 有 Block 的 `reservationDetails.taRecordLocator`。
- [RSV](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/rsv.json) 的 `taRecordLocatorList` 是搜索条件;仍未找到 Reservation 创建/修改的相应属性。
- 新发现 [FOF 佣金模块](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/fof.json) `commissionDetailType.recordLocator` 描述为 TA Record locator。反向追踪到佣金查询/重算返回,未找到将其作为 Reservation TA 写入参数的接口。它不能替代 `getReservation` 的正式字段契约。
因此剩余准确缺口仍是:**FIT TA 的写入 JSON Pointer、精确读回 Pointer、所需 fetchInstructions/酒店控制及版本依据**。不是再缺一个已知待封装路径。给 Oracle/接口负责人的具体问题沿用[上一份交付](ohip-formal-cancellation-fit-ta-20260917.md),不重复要求平台同事搜索同一套公开文档。本轮未向外部发送消息。
## 复现与运行边界
运行器:[check_parameters.py](../../../server/src/test/local-fit-simulation/check_parameters.py)。它新建专属目录、临时 PostgreSQL、随机 loopback 端口的 Go Edge 与模拟酒店、独立 H2;完成或失败后停止自己创建的进程。不会启动工作台、上传文件、访问现有 5178/8082、使用真实凭证或初始化用户的测试数据。
命令及组件限制见[模拟工具说明](../../../server/src/test/local-fit-simulation/README.md)。这是应用参数与本地 HTTP/持久日志验证;取消入口在一次性 Go 副本中使用已有测试夹具,不是拿当前线上平台或真实 Oracle 写入做验收。公开目录匹配、隔离请求成功、实际应用授权、真实酒店接收是不同的证据。
最终数量、日志、失败定位与清理记录见[本轮证据](../../../.project-docs/50-evidence/topics/2026-09-18-parameter-simulation-closure.md)。用户之后仍负责文件上传和真实环境操作;本轮不需要用户补上传文件才能完成上述测试。
@@ -0,0 +1,96 @@
# 发给平台同事:酒店预订流程需补的 Oracle 接口
> **发布已确认(2026-09-17 后续复测)**:原地址成功返回目录及 OpenAPI **0.11.0/184 项**,下方 5 核心 + 3 建议只读均已发布,无需重复封装。下文 0.10.0 缺项说明保留为需求交付时的历史基线。真实酒店调用尚未验证,连接仍间歇失败且 /readyz 返回过 503;消费端发现的路由、取消 201、待回查响应适配差异已本地修正;酒店附件执行依赖也已移除,运行服务尚未加载。见[实现交付](ohip-published-contract-no-attachments-20260917.md)。见[最新核验](../../../.project-docs/50-evidence/topics/2026-09-17-platform-published-check.md)。
核对日期:2026-09-17。范围为 GROUP/FIT 新建、修改、类型转换及源 Allotment 取消,不含加床、真实酒店操作或邮件发送。
**最新业务范围(用户已取消酒店附件上传)**:GROUP/FIT 不再向 Oracle Attachments 上传或替换邮件 Excel;本轮不要求附件上传、清单、下载、删除接口,也不再追查附件 linkType。邮件 Excel 的接收、查看和业务解析继续保留。GROUP Block Name 与 GROUP/FIT TA Record Locator 仍按 Tour Code 填写。
**同日字段证据补充**:公开研究由消费项目 Agent 完成,见[公开证据复核](ohip-public-field-evidence-20260917.md)。FIT Note 已有 schema + 官方 Postman 支持 `comments[]`,GROUP 日期已有官方样例支持 `YYYY-MM-DD`。附件研究保留为历史依据,已退出当前需求。当前未关闭的字段证据主要是 FIT TA 写读路径,不再要求平台同事重复查同一套资料。
平台公开目录本次为 **0.10.0、176 项**。下方 5 个核心操作在 Oracle 原生契约中存在,但本次公开目录未列出。如平台已有未发布实现,请核实发布与目录暴露,不必重复开发。接口存在、应用授权和真实酒店业务验证分别验收。
Oracle 依据:Property API **26.3.0.0**,官方仓库 main 当前提交 `4bd129b455bc5e3ab0f900ac47983611e58659b4`;已比对下述模块及官方 Postman 样例与该提交一致。
## 一、请优先补这 5 个核心接口
以下路径全部是 **Oracle 原生路径**,不是平台 REST 路径。
| # | Operation ID | 方法与 Oracle 路径 | 当前业务用途 | 原生成功状态 |
| --- | --- | --- | --- | --- |
| 1 | `postProfile` | POST `/crm/v1/profiles` | FIT 先创建一次 Guest,全部关联预订共用实际 Profile ID | 201 |
| 2 | `putBlockAllocation` | PUT `/blk/v1/hotels/{hotelId}/blocks/{blockId}/allocation` | GROUP 新建/修改的完整逐晚房型房数 | 200 |
| 3 | `getNextBlockStatus` | GET `/blk/v1/blocks/status` | 按 Block 当前状态查询可转换状态 | 200 / 204 |
| 4 | `putBlockStatus` | PUT `/blk/v1/hotels/{hotelId}/blocks/{blockId}/status` | 取消原 GROUP 或源 Allotment | 200 |
| 5 | `postCancelReservation` | POST `/rsv/v1/hotels/{hotelId}/reservations/{reservationId}/cancellations` | FIT→GROUP 前逐笔取消原 Reservation | 201 |
来源:[CRM](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/crm.json)、[BLK](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/blk.json)、[RSV](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/rsv.json)。
### 入参与回查要求
| 操作 | 必须能表达的参数/行为 | 写后或查询结果核对 |
| --- | --- | --- |
| `postProfile` | 原生根 `profileDetails`;`profileType=Guest`、`registeredProperty`、`customer.personName[].surname`、`addresses.addressInfo[].address.country.code`;姓名原值,Country=CN | 返回实际 Profile ID,再用已有 `getProfile` 查姓名/Country/类型;不能用姓名充当 ID |
| `putBlockAllocation` | `criteria.hotelId`、`blockId.id/type`、`allocationRoomTypes[].roomType`、`allocationGridDates[].allocation=INITIAL/ACTUAL`、`roomAllocationInfo[].start/end`、`inventory.onePerson/twoPerson/threePerson/fourPerson`;支持零值、逐晚不同数量及多个房型 | 已有 `getBlockRoomRateGrid` 回查完整日期/房型范围;查询类别枚举是 `Initial/Actual`,大小写与写入不同;保留分页/完整性信息 |
| `getNextBlockStatus` | query `hotelId`、`currentStatus`;可选 `includeCateringStatus` | 返回下一状态集合及原生属性;不能把配置目录当作已经验证了本次状态转换 |
| `putBlockStatus` | `changeBlockStatus.hotelId`、`blockId.id/type`、`currentBlockStatus`、`newBlockStatus`、`cancellationDetails.cancellationCode.code`;根 `verificationOnly=false` | 已有 `getBlock` 查实际状态;拒绝擅自开启 cancelAllPMReservations、overbookAll 等扩大作用范围的字段 |
| `postCancelReservation` | `reservations[]` 每次仅一个实际目标,内含 `hotelId`、`reservationIdList[].id/type`;`reason.code`(可选 description);`verificationOnly=false` | 已有 `getReservation` 查取消状态,保留实际取消回执;部分原预订未取消时,不进入最终类型新建 |
GROUP 房量输入是绝对目标数量,不能被封装成“增减量”。`putBlock` 不能替代 Room Grid 写入;参考 [Oracle Room Grid 更新指南](https://docs.oracle.com/en/industries/hospitality/integration-platform/maeig/t_update_room_and_rate_grid.htm)。
### 建议保持的消费端路由形状
以下为本项目候选适配使用的路径,**不是声称平台已发布**。平台如采用不同路由或 envelope,请同时提供明确契约供消费端调整。
- `postProfile`:POST `/api/v1/profiles`。
- `putBlockAllocation`:PUT `/api/v1/blocks/{id}/allocation`。
- 取消:GET `/api/v1/blocks/{id}/next-status?currentStatus=...`;PUT `/api/v1/blocks/{id}/status`;POST `/api/v1/reservations/{id}/cancellations`。
已有确认写入 envelope、case/task/confirmation、酒店校验、幂等键和逐调用记录应继续沿用。
平台需同时提供更新后的 catalog/OpenAPI、query 白名单、请求/返回组件、能力组及读回操作。写入超时或回执丢失保留未知状态并查询核对,不能自动重发创建/取消;回查失败也不能抹去已知写入事实。保留原生状态码与空结果语义,尤其 FIT 取消原生是 201,不统一当作 200。
## 二、建议顺带补的 3 个只读接口
| 操作 | 原生路径/关键参数 | 用途与优先级 |
| --- | --- | --- |
| `getBlockPMReservations` | GET `/blk/v1/hotels/{hotelId}/blocks/{blockId}/postingMaster/reservations`,可选 `postingmaster` | 取消前单独识别 Posting Master,避免只查普通住客而漏掉 PM;建议随取消接口一起补 |
| `getOperaSettings` | GET `/ent/config/v1/settings`,query `hotelId`、`cROCode`、`parameterNameWildCard` | 获取影响房量分列、TA 等行为的实际 OPERA Controls;只读,不要求封装设置写入 |
| `getProfileRelationships` | GET `/crm/v1/profiles/{profileId}/relationships`,query `relationshipPrimaryProfile` | Contact 的 PrimaryAccountInfo 不足时读取准确关联;已有主账户查询足够时可暂缓。也可选择扩展已有 getProfile 的 Relationship fetch,提供可靠关系读法即可 |
来源:[Posting Master 官方流程](https://docs.oracle.com/en/industries/hospitality/integration-platform/maeig/t_create_block_posting_master.htm)、[ENT Config](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/entcfg.json)、CRM/BLK 契约。
`putBlockShift`(PUT `/blk/v1/hotels/{hotelId}/blocks/{blockId}/shifts`)仅作后备:它是整体移期,会涉及已有房格及相关日期;先核实普通日期更新对本次改期场景是否足够,不列为当前必包接口。FIT 增加另一房型也不直接等同 split,暂不额外要求拆分接口。
## 三、以下接口已有,不要重复包
`postBlock/putBlock/getBlock/searchBlocks`、`postReservation/putReservation/getReservation/searchHotelReservations`、`getBlockRoomRateGrid`、`getProfiles/searchProfiles/getProfile`、`getReservationAttachments` 已列入公开目录。
`getCancellationCodes`(FIT 取消原因)、`getBlockCancellationReasons`、`getBlockStatusCodes`、`getNextBlockStatusCodes` 也已有。注意 `getNextBlockStatusCodes` 是配置查询,参数为 configuredOnly/blockStatusCodes;它与业务 `getNextBlockStatus` 的 currentStatus 查询并非同一操作。
## 四、还需一起解决的字段和样例缺口
### A. FIT TA Record Locator(技术侧向 Oracle 查证)
新建/修改/查询 Reservation 接口都有,但我们仍没有可以证实的 TA 字段 JSON 路径。请提供带来源的新建请求、修改请求、详情查询响应及所需 fetchInstructions;业务值为 Tour Code。搜索 `taRecordLocatorList` 已有,不用新增一个同名搜索接口;也不能未经证据把 customReference 或 Guest locators 当成 TA。
Oracle 确认此 UI 功能受 `TA_RECORD_LOCATOR` Control 影响;26.2 还可纳入 Reservation Protection。需要同时核实目标环境是否允许编辑。该说明证明功能/控制存在,并不证明 REST 写入路径。[Oracle Control](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.1/ocsuh/c_opera_controls_reservations.htm)、[Reservation Protection 更新](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.2/oprnc/c_feature_summary.htm)。
### B. 已取消的酒店附件需求
用户明确取消把本封邮件 Excel 上传到 GROUP/FIT Attachments。以下原清单中的 5 个缺项撤回:`uploadFileAttachment`、`getFileAttachment`、`getBlockAttachments`、`deleteBlockAttachment`、`deleteReservationAttachment`。已有 `getReservationAttachments` 也不再作为本流程依赖。无需为本需求补封装、验证 linkType 或替换/删除酒店旧附件;这不要求删除平台已有通用能力。
### C. 官方 schema 与内嵌示例差异(已明确实现依据)
- FIT Note:正式 schema 与官方 Postman 新建/修改样例均支持 `comments[]`,据此实现;Swagger 内嵌 example 的 `comments.commentInfo` 保留为文档矛盾。按 ID 修改、读回原文与保留无关 Note 属下一层运行验证。
- GROUP 日期:官方指南及 Postman 支持 `YYYY-MM-DD`,据此实现;`format=date` 与 maxLength=8 的不一致保留为规范问题。缩期/移期后的房格行为仍需运行验证。
### D. 酒店参数值与权限
已有的房型、Rate、Account/Contact、GTT/X/TA/GC/BTQR/TEN/THB 业务选择不重新向员工询问。技术侧通过对应现有目录/查询核实目标酒店有效值、实际 Profile ID/关系及当前应用授权。补充取消原因、可用取消状态、allowPickup、occupancy split 的实际值;不使用本地合成值作为真实配置。
## 五、消费项目还需做的工作
平台发布并交付上面契约后,消费项目需要按最终封装接入、补齐实际字段映射并由用户操作测试。首次处理非本系统创建的旧预订,还需建立旧 Note 的可靠归属规则。这是应用接管逻辑,不能仅靠新增 Oracle 接口解决。现有候选代码仍包含附件执行及回显,需在后续实现阶段移除对应流程依赖与完成条件;本轮只修订需求和接口清单,未声称程序已改。
目前的独立模拟验证不代表目标 Oracle 验收,也不代表用户当前工作台已加载新代码。本材料只供平台同事评估/实施,未发送外部消息、未申请授权、未调用酒店业务或改动运行环境。
@@ -0,0 +1,40 @@
# 字段公开证据复核:哪些可确定,哪些仍缺证据
> 2026-09-17 后续本地适配已完成,附件已退出流程;FIT TA 仍缺精确写读证据,Business Events 的 TaRecordLocator 不能替代 REST 字段。见[最新交付](ohip-published-contract-no-attachments-20260917.md)。
> 范围更新(2026-09-17):用户已取消将邮件 Excel 上传至 GROUP/FIT Oracle Attachments。本文附件/linkType 部分仅保留为历史研究,不再构成当前待封装、待查证或验收缺口;FIT TA 写读路径仍需查证。当前范围见[平台清单](ohip-platform-gap-request-20260917.md)。
2026-09-17。用户指出平台同事同样只能查公开资料;这项研究由消费项目 Agent 完成,不把同一轮公开检索重复交给平台。只读官方规范、示例、指南、版本说明和官方仓库 issue 搜索,没有酒店调用或外部提问。
依据 Oracle 官方仓库提交 `4bd129b455bc5e3ab0f900ac47983611e58659b4`(Property API 26.3)。本机证据保存在 `.planning/public-field-evidence-20260917/`,不包含真实酒店记录。
## 结论
| 项目 | 公开证据与本轮结论 | 尚待验证的边界 |
| --- | --- | --- |
| FIT Note 数组结构 | **确定采用 `comments[]`**。正式 schema、官方 Postman 的新建及 `put Reservation` 示例一致;原生正文路径分别为 `reservations.reservation[].comments[].comment.text.value` 和 `reservations[].comments[].comment.text.value`。嵌入 Swagger 的 `comments.commentInfo` 是另一份矛盾示例,不再因此把整个字段标为未知。 | 本业务旧 Note 的准确 ID/归属,以及按 ID 修改不影响其他备注,仍需真实环境验证。 |
| GROUP 日期格式 | **确定采用 `YYYY-MM-DD`**。官方 Block 指南、模块 Postman、工作流 Postman 均使用该格式;与 schema 的 `format=date` 一致。`maxLength=8` 作为规范缺陷保留,不据此改成未获支持的紧凑日期。 | 修改日期后对旧房格/活动的实际作用属于流程验收,不再混入“日期字段没查清”。 |
| Reservation 附件 linkType | **官方样例明确使用 `Reservation`**,可作为 Reservation 关联类型的实现依据。样例场景是 eRegCard,调用同一个 `uploadFileAttachment`。 | 将该类型用于普通 Excel 是基于同一对象绑定语义的合理推断,尚不是 Excel 实测;公开 schema 未按扩展名规定不同 linkType。 |
| Block 附件 linkType | **仍未找到公开明确值**。上传 schema 仅定义字符串;本轮官方示例只有 Reservation/eRegCard 和 Guest/Profile image,没有 Block 上传样例。 | 不从 `Block`、`Allotment` 等业务名称猜值;需目标环境有效上传证据或 Oracle 明确说明。 |
| FIT TA Record Locator | **仍未找到公开可证实的 post/put/getReservation 写读路径**。RSV 的明确同名项只在搜索请求中;Data API 的 `taRecordLocator` 和 UI Control 证明概念存在,不能当作 REST 请求字段。 | 不改填 customReference、GDS recordLocator、Guest locators,不借其他查询产品冒充已完成写入。 |
## 可复查的官方出处
1. **FIT 修改 Note**:[官方 Postman 的 put Reservation](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/postman-collections/property/oracle-hospitality-property.postman_collection.json#L129076),目录 `Reservations (RSV) / Put Reservation (update) / put Reservation`。新建示例见[with a comment](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/postman-collections/property/oracle-hospitality-property.postman_collection.json#L128702)。正文均为数组;正式 [RSV schema](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/rsv.json) 也如此。
2. **GROUP 日期**:[Oracle 创建 Block 指南](https://docs.oracle.com/en/industries/hospitality/integration-platform/maeig/t_create_a_block_with_or_without_room_grid.htm) 的 postBlock 样例,`blockDetails.timeSpan` 使用带连字符日期;[官方模块 Postman](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/postman-collections/property/oracle-hospitality-property.postman_collection.json#L9920) 同样如此。
3. **Reservation 附件类型**:[官方 eRegCard 上传样例](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/postman-collections/property/oracle-hospitality-property.postman_collection.json#L23757),`Content Service (MED Config) / File Attachments / post File Attachments -> eRegCard`,linkType 为 Reservation。其隔壁 Profile image 示例为 Guest,不能用于推导 Block 值。
4. **上传结构**:[MED Config](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/medcfg.json) 中 fileToUpload.linkType 仅引用 stringLength200,没有枚举或按文件类型分支。
5. **TA 的证据边界**:[RSV](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/rsv.json) 有 taRecordLocatorList 搜索字段;[BookingsReservation Data API](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/graphql/data-apis/BookingsReservation.graphql#L3009) 有只读字段;[TA Control](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.1/ocsuh/c_opera_controls_reservations.htm) 说明界面输入/搜索功能。三者未建立 REST 写入和详情读回的等价路径。
进一步核对了官方模块/工作流 Postman、RSVASYNC、BookingReservationExtended,以及官方仓库针对 taRecordLocator/linkType/commentInfo 的公开 issue 搜索。没有找到能关闭后两项的新证据。检索未命中只表示本轮未找到,不能推断 Oracle 不支持。
## 剩余证据如何获得
如果后续公开资料仍缺失,平台同事重复同样检索也不能据此给出保证。剩下两项只能从新的权威材料或目标环境证据补齐:
- **FIT TA**:准确说明 postReservation、putReservation、getReservation 对应字段和必要 fetchInstructions,并提供同一测试预订写入/读回及界面 TA 一致的结果。
- **Block 附件**:提供 uploadFileAttachment 中确切 linkType,以已知测试 Block 为目标,上传后由 getBlockAttachments 与 getFileAttachment 证明父对象、文件和字节一致。
可向 Oracle 提交这两条具体契约问题,或由用户在指定测试环境操作并提供可核对结果;本轮不代执行、不发消息。仅观察 UI 保存请求可以提供线索,不能直接把内部 UI API 当成公开 OHIP API。
平台封装可先按既有正式 JSON 契约推进,不必等待 Note 数组和日期格式重复确认。未知 linkType 应作为受控待配置值保留,FIT TA 保护不解除;不能因为模拟器接受某值就升级为官方证据。
@@ -0,0 +1,46 @@
# 已发布接口接入与取消酒店附件:交付说明
日期:2026-09-17。当前完成的是本地代码和隔离测试;原工作台运行环境没有更新,未操作真实酒店。
## 本轮结果
| 项目 | 当前结论 |
| --- | --- |
| 平台缺口清单 | 已取得 0.11.0/184 项目录及完整 OpenAPI,5 个核心、3 个建议只读均已发布,无需重复封装。 |
| 下一 Block 状态 | 消费端改用 `GET /api/v1/blocks/next-status`,传 `currentStatus`,无需 Block ID;支持 `includeCateringStatus`。 |
| FIT 取消 | `POST /api/v1/reservations/{reservationID}/cancellations` 按原生 201 处理。 |
| 房量写入回查 | 接受平台 `pages/query/pagination_complete` 格式;分页完整不等于房量正确,仍独立查询并逐晚、逐房型核对。 |
| 已写入、待核验 | 原生 200/201 且 `pending_verification` 保留资源编号与原调用身份,保存为 ACCEPTED,不能算整单成功、不能重发。当前不会仅凭另一次数值相同的 GET 自动提升为完成;异常仍需核查。 |
| 酒店附件 | GROUP/FIT 新建和修改不要求 Excel、附件权限、linkType;不读酒店附件清单、不上传、不替换、不删除,也不生成附件执行步骤或完成条件。 |
| 邮件原件 | 来源邮件 Excel 解析、查看下载保留。历史酒店附件调用事实仍在审计中;没有删除任何历史酒店文件。 |
| FIT Note | 继续按原 Note ID 与历史写入来源核对,保留无关 Note;取消附件不放宽备注归属检查。 |
| 模拟工具 | 下一状态路径和取消 201 已同步;当前模拟装配去掉附件依赖,Java fixture 和临时 Go 副本编译通过。没有启动模拟服务。 |
参数规则保持:GROUP Block Name=Tour Code;GROUP/FIT TA Record Locator=Tour Code。无 Tour 时不提交 TA 字段。GRPA3 对应 BUALUANG、GRPA4 对应 LEELA,均含早餐,不再重复询问。
## 尚未关闭的项
1. **FIT TA Record Locator 的真实写入和详情回查字段**。RSV 搜索有 `taRecordLocatorList`;公开 schema、官方样例仍未建立对应写读路径。[Oracle 26.3 Business Events](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_be_reservation_module.htm) 的 `TaRecordLocator` 是事件字段,不能直接当 REST 请求字段。真实 FIT 入口继续拦截;模拟字段只用于本机测试。
2. **真实环境接线和验收**。没有验证本项目应用授权、真实酒店写入与回查。公开目录访问曾成功,但连接仍间歇超时且 `/readyz` 返回过 503。目录发布不等于酒店业务已办成。
3. **正式取消/类型转换入口**。底层取消适配与本机 Change 流程已实现;正常 RuntimeFactory 仍是四类新建/同类型修改入口,尚未装配 Change 路由和真实取消原因/状态策略。不得把本机合成策略带入生产。
4. **原工作台与七封 EML**。没有加载本轮代码、初始化原测试环境前态或重放邮件。此前 30 个合成前态 + 43 项确认快照矩阵是旧版隔离证据;本轮未重跑该链,不能沿用为无附件新版 EML 全链验收。
3 个建议只读接口(PM 清单、OPERA 设置、Profile 关系)已确认发布,尚未作为新的业务流程调用。PM 查询如后续使用必须 `postingmaster=false`,避免原生 GET 隐式创建 PM。
## 下一步由用户操作
按以下顺序验收,不一次混测全部任务:
1. 由你或平台同事加载本轮后台代码和本机模拟装配。当前服务没有更新;只刷新网页不会加载后台变化。模拟 Oracle 状态在内存,重建服务不能配合旧办理日志冒充续办;需要独立数据集及与之匹配的工作台记录。
2. 在原支持 EML 的工作台,由你上传一封包含 GROUP NEW 的邮件并确认。检查实际 Block 编号、日期、TA、Rate、Breakfast、逐晚房量;接口记录应无附件清单/上传/删除。
3. 同团做 GROUP UPDATE,确认日期、TA、完整房量改变,Rate/Note/Account 不被重置。
4. 再用模拟 FIT NEW、UPDATE 检查同一 Guest、实际 Reservation 编号、房型房数、Rate、TA 和原 Note;多个房型分别显示实际编号。真实 FIT 暂不执行第 4 步。
5. 最后验证取消及类型转换:旧对象取消查询确认后才创建新对象;源 Allotment 有关联客人或查询不完整时应停下。故障恢复由你点击原任务的继续处理,检查已成功的创建/修改没有重复。
旧版含 SAVE_ATTACHMENTS 步骤或旧配置摘要的任务会停止,不能删步骤、改日志后强行续办。先核对已发生的酒店操作与原任务;只有确认未产生重复风险的新测试数据才重新确认。
`server/src/test/local-fit-simulation/manage.py` 是关闭 EML Agent 的独立合成 FIT 启动器;它可检验模拟链,不能替代七封 EML 的原工作台验收。启动/停止方式见该目录 README。本轮没有替用户执行这些操作。
## 本地验证
最终结果见[本轮证据](../../../.project-docs/50-evidence/topics/2026-09-17-no-hotel-attachments.md)。所有自动检查使用临时数据库、本地 HTTP 或编译;没有真实酒店调用、运行服务重启、运行数据准备、凭据或权限修改。
@@ -0,0 +1,120 @@
{
"schemaVersion": 1,
"hotels": [
{
"localHotelId": "REPLACE_LOCAL_HOTEL",
"environmentRef": "REPLACE_TEST_ENVIRONMENT",
"externalHotelId": "REPLACE_ORACLE_HOTEL",
"baseUrl": "https://replace-edge-host.invalid",
"credentialEnvironment": "OHIP_BOOKING_TEST_API_KEY",
"codes": [
{
"type": "ROOM_TYPE",
"local": "REPLACE_ROOM",
"remote": "REPLACE_ROOM"
},
{
"type": "RATE_CODE",
"local": "REPLACE_RATE",
"remote": "REPLACE_RATE"
},
{
"type": "MARKET",
"local": "GTT",
"remote": "GTT"
},
{
"type": "SOURCE",
"local": "TA",
"remote": "TA"
},
{
"type": "BLOCK_STATUS",
"local": "TEN",
"remote": "TEN"
},
{
"type": "PAYMENT",
"local": "BTQR",
"remote": "BTQR"
},
{
"type": "RESERVATION_TYPE",
"local": "GC",
"remote": "GC"
},
{
"type": "CURRENCY",
"local": "THB",
"remote": "THB"
}
],
"accounts": [
{
"accountCode": "LIAN_TAI",
"profileId": "REPLACE_AGENT_ID",
"contactProfileId": "REPLACE_CONTACT_ID",
"blockRole": "Agent"
}
],
"operations": [
"postBlock",
"getBlock",
"searchBlocks",
"putBlock",
"putBlockAllocation",
"getBlockRoomRateGrid",
"searchHotelReservations",
"getReservation",
"getNextBlockStatus",
"putBlockStatus",
"postCancelReservation"
],
"capabilities": [
"blocks.read",
"blocks.write",
"reservations.read",
"reservations.write"
],
"flows": [
{
"flow": "GROUP_NEW",
"verificationRef": "REPLACE_AFTER_TARGET_VALIDATION",
"gridPolicy": {
"category": "INITIAL",
"occupancySplitEnabled": false
},
"dateChangeSequenceVerified": false,
"fitUpdateBehaviorVerified": false
},
{
"flow": "GROUP_UPDATE",
"verificationRef": "REPLACE_AFTER_TARGET_VALIDATION",
"gridPolicy": {
"category": "INITIAL",
"occupancySplitEnabled": false
},
"dateChangeSequenceVerified": false,
"fitUpdateBehaviorVerified": false
},
{
"flow": "CANCEL_ALLOTMENT",
"verificationRef": "REPLACE_AFTER_CANCELLATION_VALIDATION",
"gridPolicy": null,
"dateChangeSequenceVerified": false,
"fitUpdateBehaviorVerified": false
}
],
"cancellationPolicy": {
"verificationRef": "REPLACE_AFTER_CANCELLATION_VALIDATION",
"reservationReasonCode": "REPLACE_RESERVATION_REASON",
"blockReasonCode": "REPLACE_BLOCK_REASON",
"cancelledBlockStatus": "REPLACE_CANCELLED_STATUS",
"activeBlockStatuses": [
"REPLACE_ACTIVE_STATUS"
],
"typeConversionsEnabled": false
}
}
]
}
@@ -63,23 +63,16 @@
"searchBlocks",
"putBlock",
"putBlockAllocation",
"getBlockRoomRateGrid",
"getBlockAttachments",
"getFileAttachment",
"uploadFileAttachment",
"deleteBlockAttachment"
"getBlockRoomRateGrid"
],
"capabilities": [
"blocks.read",
"blocks.write",
"attachments.read",
"attachments.write"
"blocks.write"
],
"flows": [
{
"flow": "GROUP_NEW",
"verificationRef": "REPLACE_AFTER_TARGET_VALIDATION",
"attachmentLinkType": "REPLACE_VERIFIED_LINK_TYPE",
"gridPolicy": {
"category": "INITIAL",
"occupancySplitEnabled": false
@@ -90,7 +83,6 @@
{
"flow": "GROUP_UPDATE",
"verificationRef": "REPLACE_AFTER_TARGET_VALIDATION",
"attachmentLinkType": "REPLACE_VERIFIED_LINK_TYPE",
"gridPolicy": {
"category": "INITIAL",
"occupancySplitEnabled": false
@@ -0,0 +1,122 @@
# TA / Tour Code:扩大范围后的接口候选
日期:2026-09-18。用户授权扩大查证范围,把作用相近的接口也列出供业务确认;尚未批准用其他字段替换 Stay Details → TA Record Locator。本轮只查公开契约和官方说明,没有改业务代码、模拟器、权限或酒店数据,没有创建子智能体。
## 供业务确认的选择
| 选择 | 可以实现的业务作用 | Oracle 页面位置 / 与 TA 的关系 | 当前证据与限制 |
| --- | --- | --- | --- |
| A. 保留 TA Record Locator | Tour Code 写入指定 TA 输入框并按 TA 查找 | 用户截图中的原位置 | 数据字典有类型证据,普通 externalReferences 路径存在;特殊 `TA_RECORD_LOCATOR` 写读仍未直接证实 |
| B. Custom Reference Number(优先考虑的替代) | 保存团号、修改、读回、按团号找预订 | Stay Details 的另一个标准字段;不会因使用它就证明原 TA 已填写 | `customReference` 的写/读/搜索路径明确,最长50字符;酒店需确认此字段可用及页面是否已显示 |
| C. External References(普通外部参考号) | 按来源系统保存参考号、读回、按来源和编号查询 | External References 详情入口;普通类型不等于 TA | 使用酒店已有且可用的外部系统类型,不自行编造代码;已证实通用写读结构,酒店配置与重复编号行为待实际验证 |
| D. Reservation UDF / Flex Field(预订自定义字段) | 增设明确名为 Tour Code 的字段,存储和查询团号 | 通过 Page Composer 配置显示位置、标签;不等于原 TA | 写读结构明确,官方有界面搜索能力,REST有 `flexChar`;实际UDF编号及REST搜索字符串格式尚待确定,配置工作更多 |
选择 B/C/D 会改变业务字段位置与后续查找方式,需用户明确接受后才调整代码和模拟参数。原有 GROUP/FIT TA = Tour Code / Block Name 规则及正式守卫目前保持;有值原样填写,无值不生成、不因更新缺值主动清空旧值。
## 已核实的公共接口
重新下载 Oracle Property API 26.3 的 RSV、BLK、BLKASYNC,以及平台0.11.0公开OpenAPI。RSV与此前保存的26.3文件逐字节一致。这里“已存在”指公开契约存在,不代表用户应用权限、当前程序已采用该方案或真实酒店验收已通过。
| 操作 | Oracle 原生入口 | 现有平台入口 |
| --- | --- | --- |
| `postReservation` 新建 | POST `/rsv/v1/hotels/{hotelId}/reservations` | POST `/api/v1/reservations` |
| `putReservation` 修改 | PUT `/rsv/v1/hotels/{hotelId}/reservations/{reservationId}` | PUT `/api/v1/reservations/{reservationID}` |
| `getReservation` 详情 | GET 同上 | GET 同上 |
| `searchHotelReservations` 搜索 | POST `/rsv/v1/hotels/{hotelId}/reservations/searches` | POST `/api/v1/reservations/searches` |
| `getHotelReservations` 列表搜索 | GET `/rsv/v1/hotels/{hotelId}/reservations` | GET `/api/v1/reservations` |
| `getReservationByExtId` 按外部编号取详情 | GET `/rsv/v1/hotels/{hotelId}/externalSystems/{externalSystemCode}/reservations/{reservationExternalId}` | GET `/api/v1/external-systems/{externalSystemCode}/reservations/{reservationExternalID}` |
B/C/D 的基本存储、修改、详情和搜索均有现有入口,暂不需新增封装。平台 `getHotelReservations` 查询白名单包含 `customReference`、`externalReferenceIds`、`externalSystemCodes`、`taRecordLocatorList`、`flexChar`。POST搜索接收对应Oracle JSON对象。写入需使用既有 `ConfirmedWriteRequest`,原生请求置于 `payload`,不能省略确认上下文和幂等信息。
## B:Custom Reference 的参数已对清
官方 RSV 对 `customReference` 的定义是用于识别预订的自定义参考号,字符串,最多50字符。新建/修改和详情的容器不同:
| 用途 | Oracle JSON 位置 |
| --- | --- |
| 新建 | `/reservations/reservation/0/customReference` |
| 修改 | `/reservations/0/customReference` |
| 详情读回 | `/reservations/reservation/0/customReference` |
| POST搜索 | `/customReference` |
| GET列表搜索 | 查询参数 `customReference` |
以下仅为相关字段片段,不是完整预订请求,也未执行:
```json
{"customReference":"LT260517B"}
```
若用户接受此位置,可把员工确认的 Tour Code 写到该字段,并用相同值搜索,再按实际 Reservation ID、住期等条件核对;不能假定同一团号只对应一间房或一笔预订。历史 TA 数据不会因此自动出现在 Custom Reference 中,切换时需明确历史查询兼容方式,不能批量迁移或覆盖。
Oracle [Updating Reservations](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_managing_reservations_editing_reservation_stay_details.htm) 的 Stay Details 部分明确列出 Custom Reference Number,并说明可用它搜索预订。页面可见性受酒店定制影响。它可能已有用途:官方[渠道控制说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/c_opera_controls_channel_management.htm)中,`SET_RECORD_LOCATOR_CUSTOM_REFERENCE` 可将GDS/ADS编号填入Custom Reference。因此选择前需由酒店确认该字段没有被其他业务占用,不操作这些渠道开关来试探TA。
## C:普通 External References 的参数
新建、修改、详情分别在上述 B 容器下,将末尾字段换为 `externalReferences`;每项包含 `id`、`idContext`,可选 `idExtension`。RSV预订父类型说明限制 `id` 最多50字符、`idContext` 最多20字符。
```json
{
"externalReferences": [
{"idContext":"<酒店确认的现有类型>","id":"LT260517B"}
]
}
```
搜索使用 `externalReferenceIds` 和 `externalSystemCodes` 数组。详情按类型选择对应项,保留其他来源的参考号。`idContext` 不应直接填团号,也不自行创建外部系统类型。Oracle [External References 操作说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_managing_reservations_external_references.htm)明确其Type/ID/Leg及搜索用途;[外部系统配置说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_admin_interfaces_configuring_external_systems.htm)解释来源系统配置。
`getReservationByExtId` 是另一种预订寻址方式,不是新TA字段。同一Tour Code可能涉及多个实际预订,优先用列表搜索,再按内部Reservation ID操作;不能未经验证就把团号当成这个单笔详情路径的唯一编号。Oracle另有 `putReservationByExtId`,当前平台未发布该Operation ID,但普通 `putReservation` 已能按内部ID修改,不构成本方案的必补接口。
原TA候选 `idContext="TA_RECORD_LOCATOR"` 与普通类型分开判断,仍以[原TA专项证据](ohip-fit-ta-external-reference-evidence-20260918.md)中的限制为准。
## D:预订 UDF / Flex Field 的参数
新建、修改、详情分别在B的对应容器下使用:
```text
userDefinedFields.characterUDFs[].name
userDefinedFields.characterUDFs[].value
```
`characterUDFType` 推荐名称为 `UDFC01` 至 `UDFC40`;必须选择酒店确认可用的槽位,不直接占用第一个。Schema中的 `name` 最多20字符,`value` 最多2000字符;这不是酒店实际输入控件限制的保证。`alternateName` 不能代替酒店Page Composer配置,也不能认定写入它就会新增页面字段。
Oracle [Page Composer说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/c_getting_started_page_composer.htm)允许在预订Stay Details增加Flex Field、调整标签;[Flex搜索说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_getting_started_adding_flex_fields_to_search_panels.htm)说明到店、在住、离店高级搜索与结果列的配置。
RSV GET/POST搜索有 `flexChar` 字符串数组,平台已承载;但当前schema描述及两份官方Postman集合没有给出“字段名与值”如何编码在字符串中的明确示例。本方案不能将REST搜索格式标为已完全对清,也不编造 `UDFC01=...` 等格式。
## 其他相近接口:用途与封装状态
| 对象 / 接口 | 官方用途和参数 | 对本任务的判断 |
| --- | --- | --- |
| Block TA:`postBlock` / `putBlock` / `getBlock` | `reservationDetails.taRecordLocator`,最多50字符;新建/读回 `/blocks/blockInfo/0/block/reservationDetails/taRecordLocator`,修改 `/blocks/0/reservationDetails/taRecordLocator` | 真实明确的团队字段,平台已有;不能把Block头字段直接当作独立FIT的Stay Details字段 |
| `postRoomingList` / `putBlockReservations` | POST `/blk/v1/blocks/{blockId}/roomingList`;PUT `/blk/v1/blocks/{blockId}/reservations` | 团队下批量创建/修改。平台本次目录未发布这两项;schema仍使用通用预订对象,未发现可绕过TA映射缺口的专用预订TA属性。不能为填写FIT团号而引入Block关系和房量扣减 |
| Guest Locator:`getReservationLocators` / `postReservationLocators` / `changeReservationLocators` / `deleteReservationLocators` | `/rsv/v1/hotels/{hotelId}/reservations/{reservationId}/guestLocators`,修改/删除追加 `/{locatorId}`;核心 `locatorText`、起止日期/时间 | 记录客人在会议室等位置,供前台转接电话/递送;不是预订团号。平台仅有专用查询入口,不建议为本需求补包写入口 |
| GDS Record Locator | RSV搜索属性 `recordLocator` 明确描述为GDS编号 | 不等于TA或任意Tour Code;渠道控制可投影到Custom Reference或External Reference,不构成直接TA写入证明 |
| Reservation Notes / Comments | 预订备注可存团号说明,接口已有 | 可辅助员工查看,但不是结构化编号搜索的等价替代,不建议单独承载身份匹配 |
| Business Event / GraphQL TA 字段 | 事件/报表中的TA值 | 只能补充读取或事件观察证据,不提供当前FIT写入闭环 |
Guest Locator用途依据Oracle[26.3官方说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_managing_reservations_locators.htm),并由当前RSV26.3的Guest Locator类型与操作说明交叉核实。首次浏览工具取该页面失败;用户再次提供链接后,通过直接公开HTTPS取得HTTP200和完整正文,确认现行26.3仍是客人临时位置用途,原始HTML已保存在本轮忽略证据目录。团队房表约束及TA页面列依据[26.3 Rooming Lists说明](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/c_rooming_lists_group_rooming_lists_ch.htm),页面有TA列不能单独证明REST有同名写入属性。未要求或执行文件上传。
## 用户指定的 Locators 与 Notes 两页复核
用户提供的[Managing Reservation Notes](https://docs.oracle.com/en/industries/hospitality/opera-cloud/26.3/ocsuh/t_adding_notes_to_reservations.htm)明确允许一笔预订记录多条备注,在各预订状态下新增,并设置类型、内部标记、标题和通知区域。Notes可存放Tour Code供员工查看,属于另一显示位置;未取得通过备注内容直接按团号搜索预订的契约证据,不能因此替换TA匹配逻辑。
Notes通过现有 `postReservation` / `putReservation` 写入;通过 `getReservation?fetchInstructions=Comments` 读回。Oracle层新建数组路径为 `/reservations/reservation/0/comments`,修改为 `/reservations/0/comments`,详情返回为 `/reservations/reservation/0/comments`。条目内参数对应如下:
| Notes页面项 | Oracle条目参数 | 说明 |
| --- | --- | --- |
| Type | `comment.type` | 使用酒店实际备注类型代码 |
| Internal | `comment.internal` | 内部备注标记;手册说明一般不输出到信函/报表,具体报表例外另看其配置 |
| Title | `comment.commentTitle` | 备注标题 |
| Notification Area | `comment.notificationLocation` | 预订、在住、收银或通用区域;具体代码值需酒店目录/已证样例,不把页面显示词直接猜成代码 |
| Comment | `comment.text.value` | 备注正文 |
| 修改已有备注的身份 | `id` | 数组条目上的备注ID,不是 `comment` 内的ID |
本机FIT新建Converter已发送 `comments[].comment.text.value`,修改Converter还携带已有备注 `id`。这证明本地正文映射已实现,不证明类型、标题、Internal和通知区域都已显式配置,也不等于真实酒店已验收。页面说明正文最多2000个单字节字符;当前本地预订验证允许最多16000字符,RSV `formattedTextTextType.value` 没有对应maxLength。记录此差异供备注长度验收核对,不能把界面限制直接断言成已证实的REST硬限制,也不能用本地16000上限保证Oracle接受。本轮未改这些业务代码。
Locators的实际输入是起止日期、起止时间和Location文本,对应 `dateSpan`、`timeSpan`、`locatorText`;包含位置的文字虽然可自由填写,官方用途仍是电话/递送的临时位置指引。其独立增改查删接口与当前平台查询入口见上表,不为Tour Code用途补包或启用写入。
## 证据与下一步
官方Schema:[RSV](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/rsv.json)、[BLK](https://github.com/oracle/hospitality-api-docs/blob/4bd129b455bc5e3ab0f900ac47983611e58659b4/rest-api-specs/property/v1/blk.json)。平台:[公开OpenAPI](https://ohip.nianxx.cn/developer/v1/openapi)。本机原始文件、SHA256、结构化路径、平台入口对照保存于忽略目录 `.planning/fit-ta-alternatives-20260918/`。异步规范首次尝试了错误的 `/property/async/` 公开路径并得到404,随后按官方仓库树定位 `/property/v1/blkasync.json`,已成功取得26.3规范;不将404记作接口不存在。
待用户确认的产品选择是:必须写回原TA输入框,还是接受B/C/D的另一显示位置。优先请用户判断B是否符合酒店操作习惯、字段是否已被占用。确认后再编写相应映射、历史查询兼容和模拟;真实系统操作继续由用户执行。此轮没有实际预订调用,没有改变原守卫,也没有把公共契约检查当作真实字段验收。