--- schema: "hl-changelog/v2" ticket: "8508" title: "调整配房(placement)已确认行同事务作废应付台账,有在途付款申请时新增 599602 拒绝" consumer: "admin" author: "wx(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "not_required" frontend_status: "not_required" frontend_owner: "" frontend_ref: "" target_release: "" verified_at: "" status_note: "PR #8575 已合并 dev-v3(合并提交 72baca1fd),测试服 order-v3 运行 99fb369ba8(含本单)。测试服实测:已确认行调整后原台账行作废、配房行回到询价中;再确认后生成一条新台账行,金额=结算价×新间数;调整后在询价中删除,无孤儿台账行。599602 拒绝、已付行红冲、无台账存量行、询价中阴性对照由单测覆盖:在途付款申请与已付状态需要财务域写入才能造出来,测试服不造。gateway_status=not_required:路径与方法未变。" updated_at: "2026-09-30" base: "dev-v3" --- # order-v3: 调整配房已确认行同事务作废应付台账,有在途付款申请时新增 599602 拒绝 > **存放目录**: > - 一期(v2,无 `order-v3` 标签的工单)→ `changelogs/{YYYY-MM}/` > - 二期(v3,`order-v3` 标签的工单)→ `changelogs-v2/{YYYY-MM}/` > > **服务**: hl-order-service-v3 > **PR**: #8575 > **Issue**: #8508 > **日期**: 2026-09-30 > **影响范围**: 管理后台「房务配房工作台」§2.3b 调整单条配房位置与资源接口,原配房行为已确认(CONFIRMED)且有在途付款申请的调整场景 --- ## ⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写) - `PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement` 新增一条失败分支:原配房行为「已确认(CONFIRMED)」,且该行的应付款台账有在途付款申请(`applied>0`)时,本次调整整体失败,返回 **599602**。此前这种情况会调整成功,但应付台账仍停在旧酒店旧价,产生台账与配房不一致。 - 除新增该错误码外,接口路径、方法、入参字段(`AssignmentPlacementUpdateReqVO`)、出参(`Result`)逐字节不变。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 调整单条配房位置与资源 | PUT | `/v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement` | 新增可能返回的错误码 | 原行已确认且应付台账有在途付款申请时返 599602 | --- ## 三、接口详情 ### 1. 调整单条配房位置与资源 `PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement` **VO**: `AssignmentPlacementUpdateReqVO → Void` #### 使用场景 房务对已存在的单条配房行原子调整目标晚次、酒店、房型和房间数(前端只传目标 dayNumber,入住日期由后端按当前订单行程推导)。本次改动不涉及入参/出参字段,只新增一条业务失败分支:原行为「已确认」且其应付款台账行有在途付款申请时,整次调整被拒绝。 #### 入参 路径参数 + Body(与改造前逐字节相同): | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | requirementId | Path | Long | ✅ | - | 当前生效住宿需求 ID | | id | Path | Long | ✅ | - | house_hotel_assignment 主键 | | dayNumber | Body | Integer | ✅ | ≥1 | 目标晚次(1=第1晚) | | hotelId | Body | Long | ✅ | - | 目标酒店 ID | | roomTypeId | Body | Long | ✅ | - | 目标房型 ID,须归属该 hotelId | | roomCategory | Body | String | ✅ | `@NotBlank` | 目标房型字典 code | | roomCount | Body | Integer | ✅ | ≥1 | 目标房间数 | | protoPrice | Body | BigDecimal | - | ≥0 | 协议价快照,不传按目标房型和目标入住日读取资源价格日历 | | settlementPrice | Body | BigDecimal | - | ≥0 | 结算价快照,不传同上兜底 | | settleType | Body | String | - | `cash\|sign\|company` | 支付方式快照 | | deductInventory | Body | Boolean | ✅ | - | 是否扣减资源库存 | | breakfast | Body | String | - | `INCLUDED\|EXCLUDED\|PENDING` | 早餐,不传保留原值 | | cancelProofFileIds | Body | List\ | - | 最多 9 个 | 原酒店取消凭证文件 ID | | cancelFee | Body | BigDecimal | - | ≥0,最多 2 位小数 | 原酒店取消费用 | | changeRemark | Body | String | - | ≤200 字 | 改配说明 | | syncProtocolPrice | Body | Boolean | - | 默认 false | 是否同步协议价到目标房型目标日期价格日历 | | syncSettlementPrice | Body | Boolean | - | 默认 false | 是否同步结算价 | | syncSettleType | Body | Boolean | - | 默认 false | 是否同步支付方式到目标酒店资源 | | remark | Body | String | - | - | 备注,不传保留原备注 | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | data | null | 无返回数据 | #### 请求示例 ```json { "dayNumber": 2, "hotelId": 200001, "roomTypeId": 300001, "roomCategory": "STANDARD", "roomCount": 2, "protoPrice": 320.00, "settlementPrice": 280.00, "settleType": "cash", "deductInventory": true, "syncProtocolPrice": false, "syncSettlementPrice": false, "syncSettleType": false, "remark": "改期后重新询房" } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": null, "success": true } ``` #### 空数据 / 降级响应 本接口无「空数据」概念(成功恒返回 `data: null`),无降级路径。 #### 错误响应 ```json { "code": 599602, "message": "应付款台账行已锁定", "data": null, "success": false } ``` | code | 触发条件 | |------|----------| | **599602(本次新增)** | 原配房行为已确认(CONFIRMED),且其应付款台账最新有效行有在途付款申请(`applied>0`) | (其余既有错误码——鉴权、参数校验、需求状态、库存迁移相关——均未变化,本次未改动,不在此重复列出。) #### 业务边界 - 触发条件只看原配房行的 `confirmStatus`:只有原行为 `CONFIRMED` 时才会查应付台账锁;原行为 `INQUIRING` 时零台账查询,行为与改造前完全一致。 - 599602 命中时**零写入**:库存迁移分两处闸——一次是预占目标库存之前的只读预检,一次是写库前事务内的兜底闸;命中任一处都不会预占新库存、不会改配房行、不会动台账;若预检已通过但事务内并发被占用而在兜底闸命中,已预占的新库存会被同步补偿释放。 - 原行已确认且已付(`paid>0`)不受本次新增拒绝影响:由财务域内部对该行做整行红冲(`CLOSED`),调整照常成功。 - 原行已确认但没有应付台账行(台账上线前确认的存量行):判存后跳过台账处理,调整照常成功,不报错。 - 调整成功后该行照旧退回询价中(`INQUIRING`,改造前既有行为不变);台账不在本次调整时重推,由下次单日确认按新酒店、新间数、新结算价重新推送。 --- ## 四、契约约束与正确调用方式(接口类必写) > 本节只写**后端接受/拒绝 payload 的规则**,不写 UI 渲染建议。 - 触发 599602 与请求体字段无关,完全由服务端读取的原配房行状态(`confirmStatus`)与其应付台账的 `applied` 值决定;前端无法通过改写请求体规避或复现这条错误,只能据响应处理。 - 判断失败必须读响应体 `code === 599602`(业务失败,HTTP 状态码仍是 200),不要判 HTTP 状态码。 - 该错误码不是全新码——删除、改价、清空三条既有写路径已经会返回同一个 599602;前端若已对这三条路径统一处理该码,调整配房无需额外新增分支即可覆盖。 --- ## 五、数据库行为(涉及写操作时必写) 本次不改变调整配房**成功**时原有的写入内容(配房行更新、库存迁移记账逐字节不变)。新增变化只发生在「原行已确认」这一分支: | 场景 | 改前 | 改后 | |------|------|------| | 原行已确认、有台账行、未付、无在途申请 | 台账行不处理,停留在旧酒店旧价 | 同事务作废该台账行(未付取消),配房行照常更新 | | 原行已确认、已付(`paid>0`)、无在途申请 | 台账行不处理 | 同事务由财务域整行红冲(`CLOSED`),配房行照常更新 | | 原行已确认、有在途付款申请(`applied>0`) | 调整成功,台账行不处理 | 整次调整失败(599602),配房行、库存、台账三者均不写入 | | 原行已确认、无台账行(存量行) | 调整成功 | 调整成功(无变化) | | 原行询价中(INQUIRING) | 调整成功,不涉及台账 | 调整成功,不涉及台账(无变化) | 被作废的台账行不在本次调整时重新推送;下一次该晚单日确认(confirmDay)时按新酒店、新间数、新结算价重新推送。 --- ## 六、边界行为 - 未登录 → 401(网关拦截) - 非房务角色 → 808090(原「房务组长只读监督 808091」已随 #8491 删除:全体房务可读全部配房单) - 配房行不存在,或 requirementId 与该行实际归属需求不一致 → 808120 - 需求不存在 / 已作废 / 不属于当前用户 / 未被抢单 → 808100/808113/808110/808116 - 订单已取消或异常处置中 → 808119 - 目标 dayNumber 超出应配晚数 → 808102 - 目标房型不属于目标酒店 → 808112 - 涉及库存迁移但原配房缺少可释放的持有日志 → 808126 - 团期子订单未成团 / 班期未建团 → 589552/589553 - **原行已确认且应付台账有在途付款申请(本次新增)→ 599602,HTTP 200,零写入** - 老数据兼容:请求/响应字段无增删,存量数据无需迁移 --- ## 六.5、枚举 / 数据字典(接口出现枚举时必写) `confirmStatus` 不是本接口的请求/响应字段,但它是**决定本次新增分支是否触发**的关键状态,前端可从既有的配房行读接口(如 `HouseOrderDetailRespVO` 里配房行的 `confirmStatus`/`confirmStatusLabel`,字段本身未变)预判会不会撞上 599602。 ### confirmStatus(配房行确认态,`HouseAssignmentConfirmStatus`) **所属字段**: 配房行 `confirmStatus`(既有字段,本次未变) | **类型**: `String` | 值 | 中文 | 说明 | |----|------|------| | `INQUIRING` | 询房中 | 调用本接口零台账查询,行为与改造前一致 | | `CONFIRMED` | 已确认 | 调用本接口会先查该行应付台账是否有在途付款申请,命中则 599602 | --- ## 六.6、修改前后对比(修改/删除类接口必写,新增跳过) ### 字段级对比 | 字段 | 改前 | 改后 | |------|------|------| | (无)| 请求体、响应体字段逐字节不变 | 同左 | ### 行为级对比 | 行为 | 改前 | 改后 | |------|------|------| | 原行已确认、有台账行、有在途付款申请时调整 | 调整成功,台账行不处理,停留在旧酒店旧价 | 整次调整失败,返回 599602,配房/库存/台账三者均不变 | | 原行已确认、有台账行、无在途申请(未付或已付)时调整 | 台账行不处理 | 同事务作废(未付取消 / 已付整行红冲),下次单日确认按新值重推 | | 原行已确认、无台账行的存量行调整 | 调整成功 | 调整成功(无变化) | | 原行询价中时调整 | 调整成功,不涉及台账 | 调整成功,不涉及台账(无变化) | | 请求/响应字段 | 不变 | 不变 | ## 六.7、影响评估(修改/删除类必写) - **是否破坏向后兼容**: 否——不改字段、不改既有错误码语义;新增一条此前不存在的失败路径(599602),且该码在删除/改价/清空三条既有路径中已经存在。 - **前端是否必须同步上线**: 若前端已对既有的删除/改价/清空三条路径统一处理 599602(按响应体 `code` 分支、非静默吞掉),调整配房无需新增代码即可覆盖;若尚未统一处理,需要补上。 - **前端 workaround 清理点**: 无。 --- ## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面) - **仅影响**: `PUT .../placement` 端点,原配房行为已确认(CONFIRMED)时的应付台账处理与新增失败分支 - **零影响**: - 原配房行为询价中(INQUIRING)时的调整(零台账查询,行为不变) - 已确认但无应付台账行的存量配房行调整(判存后跳过,成功不报错) - 已确认且已付(`paid>0`)的配房行调整(由财务域内部整行红冲,不新增拒绝) - 本接口的请求体、响应体字段结构 - 删除、改价、清空、转房、订单取消级联等既有路径对 599602 的处理逻辑(本次未改动这些路径) - hl-gateway 路由配置——端点路径、方法零变化 --- ## 八、测试环境已验证 测试服 order-v3 运行 `99fb369ba8` 及其后代 `ff68637542`(均含 PR #8575 合并提交 `72baca1fd`),全部用自造订单: - 已确认行调整(`PUT .../assignments/{id}/placement`,间数 1→2):`code=200`,配房行回到 `INQUIRING`,原台账行(seq=0)软删,改后无有效 NORMAL 行。 - 再确认该晚:配房行回到 `CONFIRMED`,台账新增 1 条有效行 seq=1,`payable=600.00`(300.00×2)。 - 换酒店(同一接口,改为同城另一家酒店,且两家酒店的供应商不同):`code=200`,配房行回到 `INQUIRING`,原台账行软删;再确认后台账有且只有 1 条有效行,`resource_id` 为新酒店,`supplier_id` 为新酒店的供应商(与原供应商不同),`payable=280.00`(280.00×1)。本条在 order-v3 `ff68637542` 上取证。 - 调整后在询价中删除(`DELETE /v3/admin/order/assignments/{id}`):只读孤儿判据 SQL 读数 0,无孤儿台账行。 以下分支由随 PR 提交的单测覆盖(`HouseAssignmentServiceTest`,`DisplayName` 均带 `#8508` 前缀;测试服不造「在途付款申请」「已付」状态,因为它们要在财务域写入): - `updatePlacement_payableLockedAtPreflight_throws599602BeforeInventoryDeduct`:预检命中在途申请 → 599602,库存迁移、配房行更新、台账作废均未调用。 - `updatePlacement_payableLockedAtFallback_throws599602AndCompensatesTargetInventory`:预检通过、事务内并发被占用 → 599602,新预占库存同步释放。 - `updatePlacement_confirmedPaidLine_removesLineAndMigratesInventory`:已付(`paid>0`)无在途申请 → 不拦,作废台账行一次;财务域据此整行红冲(`CLOSED`),再推时按 seq+1 生成新行(财务域既有单测 `PayablePushServiceTest` 覆盖)。 - `updatePlacement_confirmedWithoutPayableLine_skipsRemoveAndSucceeds`:已确认无台账行的存量行 → 调整照常成功。 - `updatePlacement_inquiringOriginal_skipsPayableQueries`:原行询价中 → 不做任何台账查询或作废。 --- ## 十、相关文档 - 关联 Issue: [wx/HL#8508](https://git.1814.love:8443/wx/HL/issues/8508) - 关联 PR: [wx/HL#8575](https://git.1814.love:8443/wx/HL/pulls/8575) ## 关联 / 联系人 ### 链接 - **Issue**: [#8508](https://git.1814.love:8443/wx/HL/issues/8508) - **PR**: [#8575](https://git.1814.love:8443/wx/HL/pulls/8575) - **Merge commit**: [72baca1fd](https://git.1814.love:8443/wx/HL/commit/72baca1fd) ### 联系人 - **后端负责人**: @wx