# 【行为变更·管理后台】订单调整保留配房与房务驳回定制师待办（#4907）

> 2026-07-16 最终后端状态：零配房最终确认动作契约已由 PR #5014 补齐并合入 `dev-v3`。最新代码、部署、网关 API、DB/库存和日志证据均通过；前端页面实现属于独立交付，不作为后端工单关单门禁。

> 服务：`hl-order-service-v3`
>
> 生效分支：`dev-v3`
>
> 接口结构：新增“单条配房晚次与资源原子调整”接口；其余沿用订单调整、房务详情、最终确认和房务驳回现有接口
>
> 后端状态：既有 PR [#4915](https://git.1814.love:8443/wx/HL/pulls/4915)、[#4925](https://git.1814.love:8443/wx/HL/pulls/4925)、[#4926](https://git.1814.love:8443/wx/HL/pulls/4926)、[#4928](https://git.1814.love:8443/wx/HL/pulls/4928)、[#4931](https://git.1814.love:8443/wx/HL/pulls/4931) 与最新 PR [#5014](https://git.1814.love:8443/wx/HL/pulls/5014) 均已合并；最终验证基线 `dev-v3@d54435af7`，测试环境部署任务 `86d9bf11` 成功，`8086/8186` 双实例 UP
>
> 联调证据：2026-07-16 部署后重新使用隔离订单执行接口面、缺口流程、订单日志和调整闭环四组探针，全部通过；最终调整报告 `D:/work2/HL-v3/.tmp/house-adjustment-flow-probe-20260716-184129.json` 为 `ok=true`

## 0. 2026-07-12 追加：返工标签唯一口径与前端未完成项

### 0.1 返工订单不能再展示“刚抢单待配房”

后端已修复待办聚合口径：同一需求存在归属当前房务的 OPEN `REQUIREMENT_ADJUSTED` 时，`需求变更重配` 是主待办，后端不会再为该需求派生 `PENDING_ARRANGE`。

- `GET /v3/admin/order/todos`：该订单 `todoTypes[]` 不再包含 `PENDING_ARRANGE`。
- 按 `todoType=PENDING_ARRANGE` 筛选：返工订单不会进入结果。
- `GET /admin/profile/dashboard`：对应 `todoCards[].todoType` 为 `REQUIREMENT_ADJUSTED`，文案为“需求变更重配”。
- `HOTEL_REPLY_TIMEOUT`、`UNREAD_CHAT` 等独立提醒仍可与返工标签共存，前端不得把它们误删。

前端必须直接按后端 `todoTypes[]` / `todoCards[].todoType` 渲染，禁止因为 `houseStatus=CLAIMING` 再自行追加“刚抢单待配房”。

测试环境订单 `HL20260712140748356` 网关实测结果：

```json
{
  "todoTypes": ["REQUIREMENT_ADJUSTED", "HOTEL_REPLY_TIMEOUT"],
  "pendingArrangeFacetRows": 0,
  "dashboardTodoType": "REQUIREMENT_ADJUSTED",
  "dashboardTodoLabel": "需求变更重配"
}
```

### 0.2 当前页面仍缺“调整配房”入口

目前房务详情仍只读展示 `stayDate`，只有“替换/配房/询房”等旧动作，无法把既有配房调整到当前行程的另一个有效晚次。前端需要在每条已有配房旁增加“调整”入口，完整调用下文 §1 的 placement 接口；不能只改页面显示，也不能组合“新增目标晚 + 删除原晚”。

### 0.3 返工待办生命周期与刷新规则

`REQUIREMENT_ADJUSTED` 不是永久状态标签。前端每次业务动作成功后必须重新请求待办和房务详情，不能用旧页面缓存继续展示：

| 业务动作 | 后端结果 | 前端处理 |
|---|---|---|
| 同一需求版本重复触发调整事件 | 幂等复用原待办，不新增重复标签 | 保留一条 `需求变更重配` |
| 连续调整产生新需求版本 | 旧版本返工待办转 `RESOLVED`，只保留当前版本 OPEN 待办 | 刷新后仍只显示一条当前返工标签 |
| 当前需求最终确认 | 当前 `REQUIREMENT_ADJUSTED` 自动 `RESOLVED` | 从未完成列表和工作台卡片移除 |
| 房务驳回住宿需求 | 房务返工待办自动 `RESOLVED`，订单侧为原定制师产生 `ASSIGN_ROOM` 待办 | 房务端移除返工标签，不展示定制师待办 |
| 订单取消 | 房务返工待办自动 `RESOLVED`；有配房时可能产生 `REFUND`，退款成功后该待办可立即变为 `RESOLVED` | 不得假设取消后一定存在 OPEN `REFUND` |

待办接口的当前契约：

```http
GET /v3/admin/order/todos?scope=mine&status=OPEN&page=1&pageSize=20
```

- 一订单一行，完整标签来自 `list[].todoTypes[]`；顶层 `todoType/title` 只是主标签兼容字段。
- `REQUIREMENT_ADJUSTED` 与同一需求的 `PENDING_ARRANGE` 后端已互斥，`stats.PENDING_ARRANGE` 同口径排除。
- 前端禁止根据 `houseStatus=CLAIMING` 自行补“刚抢单待配房”。
- `HOTEL_REPLY_TIMEOUT`、`UNREAD_CHAT` 可与返工标签并存，不能因为互斥普通新抢标签而一并隐藏。
- §1 的目标晚次选择和“调整”入口属于前端实现清单；前端完成后独立验收，不再用于控制后端 Issue #4907 状态。

### 0.4 首页、待办列表、订单详情必须保持一致

前端需要同时验收以下三处，不能只修其中一个页面：

1. 房务首页：直接按 `GET /admin/profile/dashboard` 返回的 `todoCards[].todoType` / `todoLabel` 渲染。
2. 房务待办列表：直接按 `GET /v3/admin/order/todos` 返回的 `list[].todoTypes[]` 渲染完整标签，顶层 `todoType/title` 只作为主标签兼容字段。
3. 房务订单详情：业务动作成功后重新加载房务详情，并同步刷新首页和待办列表；不得沿用进入详情前的旧标签或旧计数。

同一订单在三处页面必须遵守同一口径：

- 存在 OPEN `REQUIREMENT_ADJUSTED` 时，主标签只能是“需求变更重配”，不得再显示“刚抢单待配房”。
- 最终确认、房务驳回或订单取消成功后，前端必须重新请求接口；对应返工待办已关闭时，三处均不得继续显示旧标签。
- `HOTEL_REPLY_TIMEOUT`、`UNREAD_CHAT` 等独立提醒可以与返工主标签共存，但不得自行拼接同需求的 `PENDING_ARRANGE`。
- 调整配房只能提交目标 `dayNumber`，不得自行提交或计算 `stayDate`；入住日期由后端根据当前行程晚次推导。

前端独立验收建议包含同一订单在“房务首页、待办列表、订单详情”三处的刷新后截图，并证明标签、数量和当前业务状态一致；该证据用于前端交付验收，不反向重开已完成的后端 Issue。

## 1. 订单调整后的房务口径

定制师通过以下接口修改行程天数、出发日期或出行人数：

```http
POST /v3/admin/order/{orderId}/adjustment/submit
```

后端现在按最新行程重新对齐住宿需求逐晚结构。既有配房不会被自动删除；真正改出发日期时，仍在新行程晚数范围内的配房会按 `dayNumber` 自动平移入住日期：

| 调整项 | 前端应展示的结果 |
|---|---|
| 增加行程天数 | 原晚次配房保持；新增夜显示待配房 |
| 减少行程天数 | 需求夜数减少；多余旧配房继续显示，等待房务人工清空/替换 |
| 修改出发日期 | `stayDate = 新出发日期 + dayNumber - 1`；酒店、房型、房间数、价格、扣库存选择和备注原样保留；配房退回询房中重新确认 |
| 同时改日期和天数 | 新行程范围内的配房自动平移；新增夜为空待配；超出新晚数的旧配房不自动删除 |
| 修改出行人数 | 原配房继续显示；完整配房回到待最终确认，不完整则回配房中 |

新增夜允许空候选占位，`roomCount` 未填或为 `0` 均可提交；负数仍会被后端拒绝。前端不要为了绕过校验自行填造假的间数。

### 改期库存原子性

- `deductInventory=false`：只平移订单配房快照，不操作资源库存。
- `deductInventory=true`：后端先逐晚预占新日期库存；全部成功后才修改订单、行程、需求和配房日期，并释放旧日期库存。
- 任意新日期库存不足或日历缺失：整次订单调整返回 `808901`；订单日期、需求版本、配房和旧库存全部保持原样，已临时预占的新日期库存自动补偿。
- 前端收到 `808901` 时只提示库存不足，不得将本地表单当作已保存。

### 房务人工修改单条配房

当前前端仅在 `ItineraryPanel.vue` 只读显示日期，并通过整晚 `POST assignments` 做“配房/替换”。这不能安全实现“把 6 月 1 日的既有配房移动到 6 月 5 日”：若前端组合调用新增目标行和删除原行，中间任一步失败都会造成配房或库存半套。

请为每条既有配房增加“调整”入口，统一调用以下原子接口：

```http
PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{assignmentId}/placement
Content-Type: application/json

{
  "dayNumber": 1,
  "hotelId": "200001",
  "roomTypeId": "300001",
  "roomCategory": "DELUXE",
  "roomCount": 2,
  "protoPrice": "520.00",
  "settlementPrice": "480.00",
  "settleType": "sign",
  "deductInventory": true,
  "syncProtocolPrice": false,
  "syncSettlementPrice": false,
  "syncSettleType": false,
  "remark": "改期后重新询房"
}
```

成功响应：

```json
{
  "code": 200,
  "message": "成功",
  "data": null,
  "success": true
}
```

字段规则：

| 字段 | 必填 | 说明 |
|---|---|---|
| `dayNumber` | 是 | 目标晚次，从当前详情 `days[]` 选择。必须按当前行程映射：若出发日已从 6 月 1 日改为 6 月 5 日，则 6 月 5 日是当前第 1 晚，传 `1`，不是按“日期中的 5”传 `5` |
| `hotelId` | 是 | 目标酒店 ID，字符串透传 |
| `roomTypeId` | 是 | 目标房型 ID，字符串透传 |
| `roomCategory` | 是 | 房型字典 code |
| `roomCount` | 是 | 目标房间数，必须大于 0 |
| `deductInventory` | 是 | 是否扣系统库存；缺失返回 `808124` |
| `protoPrice` / `settlementPrice` | 否 | 不传时按目标房型和目标入住日读取资源价格日历；金额按字符串提交 |
| `settleType` | 否 | `cash` / `sign` / `company` |
| 三个 `sync*` | 否 | 仅用户明确选择同步时传 `true` |
| `remark` | 否 | 不传时保留原备注 |

前端交互要求：

1. 目标日期不是自由输入框。下拉选项来自当前详情的有效住宿晚次，展示“第 N 晚 / 日期 / 城市”，提交其 `dayNumber`；不要提交 `stayDate`。
   如果订单已改期但某条历史配房仍显示 6 月 1 日，当前行程第 1 晚已是 6 月 5 日，则房务选择“第 1 晚 / 6 月 5 日”提交；后端会把同一配房 ID 的 `stayDate` 更正为 6 月 5 日。
2. 同一弹窗必须允许同时修改目标晚次、酒店、房型、房间数、是否扣库存、协议价、结算价、支付方式和备注。
3. 后端按当前 `order_itinerary_day` 推导并保存 `stayDate`。旧配房 ID 不变，成功后该行退回 `INQUIRING`，前端关闭弹窗并重新加载详情。
4. 扣库存场景由后端先占目标库存、提交后释放原库存；失败时原配房和原库存不变。前端禁止再组合调用“目标晚新增 + 原晚删除”。
5. 现有整晚 `POST /v3/admin/order/hotel-requirements/{requirementId}/assignments` 继续用于新增候选和同晚整组对账，不替代本接口的跨晚次原子移动。

主要错误码：

| code | 处理 |
|---|---|
| `808102` | 目标晚次已不在当前行程，刷新详情后重选 |
| `808117` | 目标晚已有同酒店、同房型、同库存口径配房，提示合并房间数 |
| `808124` | 未选择是否扣库存 |
| `808125` | 配房或行程被并发修改，刷新详情后重试 |
| `808126` | 原配房声明扣库存但缺少可释放的持有日志；禁止继续调整，需先核对库存 |
| `808130` | 目标房间数小于已录入的家庭分房条数 |
| `808901` | 目标日期资源库存不足；保留原页面数据 |

## 2. 最终确认

```http
POST /admin/house/assignments/requirements/{requirementId}/finalize
```

- 没有任何配房时允许最终确认，用于客人自行解决住宿的场景。
- 一旦存在任意配房，所有 active 配房必须已确认，并按晚次精确覆盖当前行程住宿日期。
- 缺夜、多余夜、日期不匹配或存在未确认配房返回：

```json
{
  "code": 808181,
  "message": "已有配房与当前行程不一致，请先补配、替换或清空",
  "data": null
}
```

前端收到该错误后保留当前配房数据，提示房务逐日处理；不得清空页面状态或隐藏超出新行程的旧配房。

### 2.1 零配房动作契约

详情接口与最终确认写接口现已使用同一业务口径：

- 当前生效需求由当前房务持有。
- `houseStatus=CLAIMING`。
- 没有任何配房记录。
- 没有未闭环询房。

满足以上条件时，房务详情返回：

```json
{
  "actions": {
    "canFinalize": {
      "enabled": true
    }
  }
}
```

以下场景仍保持禁用：存在部分配房、存在未闭环询房、非当前持有人、非当前生效需求或需求已经完成。最终确认成功后必须重新加载详情和待办；后端会关闭相关待办且不会创建配房或变更库存。

## 3. 房务驳回后的定制师待办

```http
POST /v3/admin/order/{orderId}/hotel-requirement/supplier-reject
Content-Type: application/json

{
  "returnRemark": "酒店无法满足当前房型，请重新确认需求"
}
```

驳回成功后，后端会为原定制师显式创建一条待办：

```json
{
  "todoType": "ASSIGN_ROOM",
  "todoLabel": "房型需求 · 已打回",
  "status": "PENDING",
  "assigneeRoleKey": "CUSTOMIZER",
  "relatedBizType": "HOTEL_REQUIREMENT",
  "relatedBizId": "2075933198734303233"
}
```

该待办与订单当前是否仍处于“补出行人资料”阶段无关。定制师待办页按现有待办接口正常展示并跳转订单调整即可。

## 4. 前端验收

1. 增天后原配房不消失，新增夜可单独配房；不再出现 `roomCount 必须 > 0`。
2. 改期后原配房按晚次自动平移日期，酒店、房型、房间数和价格快照不变，状态退回询房中。
3. 改期同时增减天数时，新范围内配房平移、新增夜为空、超范围旧配房保留。
4. 房务可把单条既有配房原子移动到另一当前有效晚次，并同时修改酒店、房型、房间数和库存口径；日期始终与目标晚次一致。
5. 扣库存配房改期成功时旧库存释放、新库存扣减；任一晚不足时整单不变。
6. 减天后的多余旧配房继续显示，最终确认被后端阻断，直到人工清空/替换。
7. 修改人数后已有配房保持，房务重新最终确认。
8. 零配房订单允许最终确认。
9. 房务驳回后，原定制师能看到“房型需求 · 已打回”待办。
10. 历史需求和旧配房均继续展示，不得只渲染当前需求而丢失历史证据。
11. 需求变更返工订单只展示 `需求变更重配` 主标签，不得再从 `CLAIMING` 自行补出 `刚抢单待配房`；询房超时、未读消息等独立标签照常展示。
12. 每条已有配房均提供“调整”入口，可选择当前有效晚次并同时修改酒店、房型、房间数和库存口径；改期后从 6 月 1 日调整到当前第 1 晚 6 月 5 日时提交 `dayNumber=1`。
13. 同一返工订单在房务首页、待办列表、订单详情三处的主标签、数量和当前状态一致；动作成功后均使用重新请求的数据，不使用本地旧缓存补标签。
14. 前端提交单条配房调整时只提交 `dayNumber`，不得提交 `stayDate`；完成前端适配时提供三处页面刷新后的同订单截图。

## 5. 后端验证证据

- PR：`wx/HL#4910/#4911/#4913/#4915/#4925/#4926/#4928/#4931/#5014`
- 最终验证基线：`dev-v3@d54435af7`
- 模块全量：5609 项测试，0 failure，0 error，15 skipped，`BUILD SUCCESS`
- 最新测试环境部署任务：`86d9bf11`，`8086/8186` 双实例均 UP
- 网关接口面：68/68 通过，报告 `D:/work2/HL-v3/.tmp/house-api-surface-probe-20260716-182908.json`
- 缺口流程：3/3 通过，报告 `D:/work2/HL-v3/.tmp/house-api-gap-flow-probe-20260716-183121.json`
- 订单日志：3/3 通过，报告 `D:/work2/HL-v3/.tmp/house-order-log-flow-probe-20260716-183224.json`
- 调整闭环：4/4 通过，报告 `D:/work2/HL-v3/.tmp/house-adjustment-flow-probe-20260716-184129.json`
- 实测通过：增晚、减晚、人数变化、连续调整新旧待办替代、改期+增晚、跨晚次原子调整、酒店/房型/房间数替换、入住日期对齐、人工清空、零配房最终确认、供应商驳回、订单取消、库存成功迁移、库存不足整单回滚
- 返工标签：`REQUIREMENT_ADJUSTED` 压制同需求 `PENDING_ARRANGE`；最终确认、供应商驳回、订单取消后返工待办均正确关闭

