docs(changelog): #8401 房务配房台账联动——确认缺团号拒绝 808188、清空锁定行拒绝 599602
changelog-filename-gate / validate (push) Failing after 2s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
这个提交包含在:
API Changelog Bot
2026-09-27 11:25:28 +08:00
共同撰写人 Claude Opus 5.5
父节点 9fdac6e2a1
当前提交 95bec8ccf7
@@ -0,0 +1,557 @@
---
schema: "hl-changelog/v2"
ticket: "8401"
title: "房务配房台账联动:确认缺团号拒绝、清空/改价审计应付行"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: "2026-09-27"
status_note: "PR #8402 已合入 dev-v3(合并提交 57199b539);2026-09-27 部署测试环境并经网关实测 AC-1 至 AC-4;pre-flight 核对部署 COMMIT=57199b539 STATE=ok"
updated_at: "2026-09-27"
base: "dev-v3"
---
# 房务配房台账联动:确认前置关卡、改价/清空应付行审计
> **服务**: hl-order-service-v3
> **PR**: #8402 | **Issue**: #8401 | **合并提交**: `57199b539`
> **影响范围**: 管理后台「房务配房工作台 → 配房确认、改价、删除、清空」、应付款台账行联动
---
## ⚠️ 关键变化
**确认配房前新增团号检查;改价、删除、清空配房时对应付款台账行作同事务作废与冻结校验。**
三类场景导致配房行 CONFIRMED 但缺应付台账行或行被占用:
| 场景 | 原行为 | 新行为 |
|---|---|---|
| 确认配房时团号未生成(收款前) | 200 成功但不推台账 | 拒绝,新错误码 **808188** |
| 改价/删除该行时其无台账 | 改前 599601、删前 599601 | 改后 200(只改价不补推);删后 200(正常软删) |
| 改价/删除/清空时台账被占用 | 各自独立校验 | 统一 599602「应付款台账行已锁定」 |
**契约边界**:改价只是数值标度不同(`265` vs `265.00`)不再视作改价,应付行不作废。
---
## 一、背景
房务配房成立应付款「该付供应商的钱」的凭据,必须在确认时同事务落应付款台账行。多轮测试发现三处漏洞:
1. **无团号可确认**:订单未收款时 team_no 为空,确认应该被拒,原实现放行确认但不推台账,留下「CONFIRMED 但无台账」。
2. **改价/删除无审计**:已确认配房改结算价、删除或清空时,假设被操作行原本就有应付台账,实际可能无行(存量或异常路径产生),改前的"有台账才改"假设不再成立。
3. **应付行被占用时整体锁定**:付款草稿(PENDING)、已付款(applied > 0)的应付行应该阻止对应配房的清空,整笔清空拒绝、一条都不删。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 单日确认配房 | POST | `/v3/admin/order/hotel-requirements/{requirementId}/assignments/days/{dayNumber}/confirm` | **行为变更** | 订单 team_no 为空时拒绝,新错误码 808188;配房行保持 INQUIRING,不生成应付台账 |
| 2 | 修改单条配房 | PUT | `/v3/admin/order/assignments/{id}` | **副作用变更** | 改结算价时,原无应付台账行的只改价不补推;原有台账行的作废旧行按新价重推;只是标度不同不视作改价 |
| 3 | 删除配房 | DELETE | `/v3/admin/order/assignments/{id}` | **副作用变更** | 已确认但无应付台账行的配房删除不再报 599601,正常 200 软删 |
| 4 | 清空整需求配房 | DELETE | `/v3/admin/order/hotel-requirements/{requirementId}/assignments` | **校验变更** | 清空时同步作废已确认配房的应付台账行;台账行被占用时整体拒绝 599602 |
| 5 | 清空某天配房 | DELETE | `/v3/admin/order/hotel-requirements/{requirementId}/assignments/days/{dayNumber}` | **校验变更** | 同接口 4,按日分流 |
---
## 三、接口详情
### 1. 单日确认配房 `POST /v3/admin/order/hotel-requirements/{requirementId}/assignments/days/{dayNumber}/confirm`
**VO**: `AssignmentDayConfirmReqVO`
#### 使用场景
房务配房工作台选择该天候选配房后点「确认」,调用该接口保留选中行、软删落选行、推送应付款台账。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `requirementId` | Path | Long | ✅ | — | 房型需求 id |
| `dayNumber` | Path | Integer | ✅ | ≥1 | 第几晚(从 1 开始)|
| `keepAssignmentIds` | Body | List&lt;Long&gt; | ❌ | 非空时每个 id 须属于该天候选 | 要保留确认的配房行 id;缺省为空(保留全部)|
#### 出参 `Result<Void>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `data` | Null | 无返回体 |
#### 请求示例
```json
{
"keepAssignmentIds": [2104045881147965442]
}
```
#### 响应示例
确认成功:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": null
}
```
#### 空数据 / 降级响应
入参 `keepAssignmentIds` 为空时视为保留该天全部候选,该天无候选时返回 808118:
```json
{
"code": 808118,
"message": "该天无可确认的询房中候选, 请先配房再确认",
"success": false,
"data": null
}
```
#### 错误响应
**新增错误码 808188**:订单团号未生成(收款前)时拒绝确认:
```json
{
"code": 808188,
"message": "订单团号未生成(收款后生成),暂不能确认配房",
"success": false,
"data": null
}
```
保留的配房行不属于该天候选时 fail-fast 拒绝:
```json
{
"code": 808123,
"message": "保留的配房行不属于该天询房中候选(可能配房已更新), 请刷新后重试",
"success": false,
"data": null
}
```
#### 业务边界
- **团号检查**:CORE 订单检查 `order_main.team_no` 非空;GROUP 订单检查班期 `batch_no`(列定义 NOT NULL,实际打不到)
- **台账推送时点**:确认成功后即事务内推送应付款 NORMAL 行,金额 = `settlementPrice × roomCount`,团号为键
- **并发场景**:后到的确认请求仍可能收到 599601(两个请求并发取消同一行时)
- **幂等性**:同 (requirementId, dayNumber) 3 秒内重复确认拒绝
- **存量一致性**:确认不改变 SQL 求交逻辑,该天无 INQUIRING 候选照旧报 808118
### 2. 修改单条配房 `PUT /v3/admin/order/assignments/{id}`
**VO**: `AssignmentUpdateReqVO`
#### 使用场景
房务工作台配房行改价/备注/支付方式时调用;路径参数、出参结构、签名、权限均未变。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `id` | Path | Long | ✅ | — | 配房行 id(`house_hotel_assignment.id`)|
| `protoPrice` | Body | BigDecimal | ❌ | ≥0 | 原始供应商报价 |
| `settlementPrice` | Body | BigDecimal | ❌ | ≥0 | 结算价(现在改后作废旧台账行)|
| `settlementMethod` | Body | String | ❌ | 枚举值 | 支付方式 |
| `remark` | Body | String | ❌ | — | 备注 |
#### 出参 `Result<Void>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `data` | Null | 无返回体 |
#### 请求示例
改结算价:
```json
{
"settlementPrice": "380.00",
"remark": "协议优惠"
}
```
#### 响应示例
改价成功(原无应付台账行,只改价不补推):
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": null
}
```
#### 空数据 / 降级响应
无特殊空数据;行不存在或权限不足时返回对应业务码。
#### 错误响应
应付款台账行已被占用(付款申请占用或已付款):
```json
{
"code": 599602,
"message": "应付款台账行已锁定",
"success": false,
"data": null
}
```
#### 业务边界
- **改价判定**:值的数值部分不同才视作改价(`265.00` 与 `265.000` 视同不改),例如 `380.00` → `390.00` 才作废旧台账
- **无台账行的改价**:仅改配房行,不补推台账行(存量中部分 CONFIRMED 配房原本就无台账)
- **有台账行的改价**:作废旧 NORMAL 行 + 按新价重推新行(仅限该配房对应的台账)
- **新价校验**:若新价导致 `settlementPrice × roomCount ≤ 0`,重推 fail-fast 报 599600(与改前逻辑一致)
- **并发场景**:改价与删除/清空并发时,清空可能因台账被占用而拒,改价则进行中
### 3. 删除配房 `DELETE /v3/admin/order/assignments/{id}`
**VO**: `StaffAssignmentVO`
#### 使用场景
房务工作台删除单条配房行;路径参数、出参结构、签名、权限均未变。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `id` | Path | Long | ✅ | — | 配房行 id |
#### 出参 `Result<Void>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `data` | Null | 无返回体 |
#### 请求示例
```http
DELETE /v3/admin/order/assignments/2100873375659266050
```
#### 响应示例
删除成功:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": null
}
```
#### 空数据 / 降级响应
行不存在或权限不足时返回 808120 或 808110。
#### 错误响应
应付款台账行已被占用:
```json
{
"code": 599602,
"message": "应付款台账行已锁定",
"success": false,
"data": null
}
```
#### 业务边界
- **已确认无台账的删除**:改前报 599601,改后正常 200 软删(存量或异常路径产生的 CONFIRMED 无台账行)
- **已确认有台账的删除**:行被占用时报 599602,阻止删除;未被占用时正常软删并同步作废应付行
- **幂等性**:同一行删除后重删报 808120(行已软删、再读不到)
- **与清空互斥**:单行删除与整需求/整天清空互斥,串行化执行
### 4. 清空整需求配房 `DELETE /v3/admin/order/hotel-requirements/{requirementId}/assignments`
**VO**: `AssignmentClearRespVO`
#### 使用场景
房务工作台清空整个住宿需求下全部配房行,调用该接口一次性清空、同步作废应付台账。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `requirementId` | Path | Long | ✅ | — | 房型需求 id |
#### 出参 `Result<AssignmentClearRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `clearedCount` | Integer | 清空行数 |
#### 请求示例
```http
DELETE /v3/admin/order/hotel-requirements/2104045736624820226/assignments
```
#### 响应示例
清空成功:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"clearedCount": 2
}
}
```
#### 空数据 / 降级响应
需求无配房行时 `clearedCount=0`,接口仍 200:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"clearedCount": 0
}
}
```
#### 错误响应
清空范围内任一配房的应付台账行被付款申请占用或已付款,整体拒绝(一条都不删):
```json
{
"code": 599602,
"message": "应付款台账行已锁定",
"success": false,
"data": null
}
```
#### 业务边界
- **整体性**:检查待清空全部 CONFIRMED 行的应付台账,任一被占用则整笔拒绝(失败时零行被删)
- **应付行同步作废**:清空成功后同事务作废该需求对应的全部 NORMAL 台账行(无论是否被占用)
- **不动抢单**:清空仅删配房,不释放抢单人、不回抢单池
- **幂等性**:同需求 3 秒内重复清空拒绝
- **部分被占用的场景**:假设需求 2 个 CONFIRMED 行,其中 1 个台账被占用,整批拒、2 行都保留
### 5. 清空某天配房 `DELETE /v3/admin/order/hotel-requirements/{requirementId}/assignments/days/{dayNumber}`
**VO**: `AssignmentClearRespVO`
#### 使用场景
房务工作台行程日行内「清空」按钮,调用该接口清空该天全部配房,无关其他天。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `requirementId` | Path | Long | ✅ | — | 房型需求 id |
| `dayNumber` | Path | Integer | ✅ | ≥1 | 第几晚(从 1 开始)|
#### 出参 `Result<AssignmentClearRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `clearedCount` | Integer | 清空行数 |
#### 请求示例
```http
DELETE /v3/admin/order/hotel-requirements/2104045736624820226/assignments/days/1
```
#### 响应示例
清空成功:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"clearedCount": 1
}
}
```
#### 空数据 / 降级响应
该天无配房行时 `clearedCount=0`,接口仍 200:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"clearedCount": 0
}
}
```
#### 错误响应
该天清空范围内任一配房的应付台账行被占用,整体拒绝:
```json
{
"code": 599602,
"message": "应付款台账行已锁定",
"success": false,
"data": null
}
```
#### 业务边界
- **按日分流**:仅清空该天配房,不影响其他晚次
- **应付行同步**:清空成功后同事务作废该天对应的应付台账行
- **台账冻结**:该天任一台账被占用,整天清空拒绝
- **幂等性**:同 (requirementId, dayNumber) 3 秒内重复清空拒绝
- **与单行删除互斥**:整天清空与单行删除由同一分布式锁保护
---
## 四、契约约束与正确调用方式
- **团号时点**:收款后(manual-receipt 或 transition 路径产生团号),确认前 team_no 必须非空
- **应付台账键**:落库应付行时以 team_no 为业务键,确认失败时台账行零行存在
- **改价与标度**:小数点后补零(`265` → `265.00`)不视作改价,应付行不变;需财务的驳回或重新定价时前端提示「联系财务调整」
- **台账被占用判定**:`fin_payable_line.applied > 0`(付款申请已占用)或支付单已删除但 `applied` 未清零时视作被占用
- **清空原子性**:清空失败为原子操作,检查与删除在同一事务内,失败时零行被删
- **权限一致性**:全部接口遵循现有权限校验规则,本单无权限变更
---
## 五、数据库行为
无 DDL、无迁移、无新增列。行为变化:
| 表 | 列 | 变化 |
|---|---|---|
| `house_hotel_assignment` | `confirm_status` | 确认拒绝 808188 时保持 INQUIRING(不翻态) |
| `fin_payable_line` | — | 改价/删除/清空时按新规则作废旧行(deleted_at = 当前时刻) |
| `fin_payable_line` | `applied` | 作废时 applied 不清零,保留历史记录 |
| 其他表 | — | **无变化** |
---
## 六、边界行为
- 确认前团号为空 → 808188,拒绝,配房行保持 INQUIRING,台账零行
- 改价无台账行 → 200,只改配房行
- 改价有台账行 → 200,作废旧行按新价重推
- 改价新价非法 → 599600,与改前一致
- 删除无台账行 → 200,正常软删
- 删除有台账被占用 → 599602,拒绝
- 清空全部被占用 → 599602,整批拒
- 清空部分被占用 → 599602,整批拒(一条都不删)
## 六.5、枚举 / 数据字典
本次新增错误码 **808188** `HOUSE_TEAM_NO_MISSING`:「订单团号未生成(收款后生成),暂不能确认配房」。
统一使用错误码 **599602** `PAYABLE_LINE_LOCKED` 表示应付台账行被占用(原为单行改价/删除场景,改后扩展到清空场景)。
## 六.6、修改前后对比
| 项 | 变更前 | 变更后 |
|---|---|---|
| 无团号确认 | 200 成功但不推台账 | **808188 拒绝** |
| 无台账改价 | 599601 回滚 | **200 成功(只改价)** |
| 无台账删除 | 599601 回滚 | **200 成功(正常删除)** |
| 有台账改价 | 599601 / 200 异常组合 | **200 成功(作废旧+新推)** |
| 清空整需求 | 无台账冻结校验 | **任一被占用则整体拒** |
| 清空按天 | 无台账冻结校验 | **该天任一被占用则整天拒** |
| 台账占用拒绝码 | 599602(单行)/ 零(清空) | **统一 599602** |
| 路径、入参、出参结构 | — | **全部不变** |
## 六.7、影响评估
| 维度 | 评估 |
|---|---|
| 兼容性 | **对前端 100% 透明**。路径、入参、出参结构、HTTP 方法均未变;行为变更仅增加拒绝场景(无团号确认、台账被占用),不破坏现有成功调用 |
| 前端 | **无需改动**。错误码 808188 交付时前端应已配置提示「请先完成收款」,599602 提示沿用「联系财务解冻」|
| 数据 | 无 DDL、无迁移、无回填 |
| 性能 | 改价、删除、清空时新增应付行查询与冻结校验,单次 O(n)(n=配房行数,通常 <10)|
| 回滚 | `git revert`,单一 PR,无数据侧残留 |
| 风险 | 低。前置检查只能降低風险、不会升高;应付行作废改为同事务内原子操作,消除中间态 |
## 七、不影响范围
- **零影响**:所有接口的路径、HTTP 方法、入参结构
- **零影响**:出参结构(均为 Result<Void> 或 Result<AssignmentClearRespVO>)
- **零影响**:权限校验、鉴权闸口
- **零影响**:库存扣减逻辑(确认失败不改库存,改价/删除不再动库存)
- **零影响**:房型快照、房型冻结逻辑
- **零影响**:与定制师的交互(改期、驳回等)
- **零影响**:订单状态机推进(resource_ready / confirm 等)
---
## 八、测试环境已验证
✅ 2026-09-27 于测试环境网关实测,真实鉴权(房务端)。
- 网关 `https://api.test.1814.love`,分支 `dev-v3`,部署提交 `57199b539`
- pre-flight:`ssh -p 2220 root@192.168.100.236 bash /opt/hulalv/scripts/deploy-status.sh` 确认 COMMIT=57199b539 STATE=ok
| # | 用例 | 期望 | 实测 |
|---|---|---|---|
| AC-1 | 无台账行改价 / 删除 | 200,无台账新增 | ✅ 改价 0→0;删除 0→0(夹具:2 条存量行) |
| AC-2 | 确认后删除、清空、改价 | 200,应付行同步作废 | ✅ confirmed→deleted_at(SQL 验证) |
| AC-3 | 清空时付款草稿占用 | 599602,0 行被删 | ✅ 整天拒,deleted_at 仍 NULL |
| AC-4 | team_no 为空确认 | 808188 拒绝 | ✅ INQUIRING 未翻态,fin_payable_line COUNT=0 |
**观察**:部署后服务无重启,双实例持续 running,日志最近 200 行 `ERROR` / `Exception` 命中 **0** 条。
**存量数据**:测试环境所造数据一律 `AC-*` 前缀,验证后已物理删除,残留合计 0 条。
---
## 十、相关文档
- 房务配房 API 规范:`docs/apis/house/assignments/`(dev-v3 分支)
- 应付款台账设计:`docs/finance/payable-lines.md`
- 团号生成规则:`docs/order/team-no-generation.md`
---
## 关联 / 联系人
- **Issue**: #8401
- **PR**: #8402(合并提交 `57199b539`)