---
schema: "hl-changelog/v2"
ticket: "5842"
title: "房务最终确认取消「必须每晚配齐」限制：部分配房/未确认行放行 + 新增未配/未确认晚号字段"
consumer: "admin"
change_type: "修改接口"
author: "wx(GIT)"
backend_status: "deployed"
gateway_status: "not_required"
frontend_status: "implemented"
frontend_owner: "mmg"
frontend_ref: "ddb3b823"
target_release: ""
verified_at: "2026-08-11"
status_note: "前端已实现(2026-08-11,mmg,ddb3b823)：orderDetailAdapter 透传 progress 新增 unarrangedDayNumbers/unconfirmedDayNumbers(互斥升序,缺省/非数组归一 []),新增纯函数 buildFinalizeConfirmContent 分列拼接二次确认文案;OrderDetailModal 最终确认按钮 @click 改 onFinalizeClick,两列表非空先弹 dialog.warning 二次确认(确认完成才 doFinalize)、均空保持直接确认;日期错位仍由后端 808183 硬拦+本地重拉清理入口不动。测试:adapter spec +3 透传 +4 文案纯函数用例,housekeeper 44 全绿,checkpoint 精确文件集全过。"
updated_at: "2026-08-11"
base: "dev-v3"
---

# 房务最终确认：取消「必须每晚配齐」限制（#5842）

> **服务**: hl-order-service-v3
> **PR**: #5860（已合并 dev-v3 并部署测试服）
> **日期**: 2026-08-11
> **背景**: 房务「最终确认」原要求每一晚都配房且全部已确认，否则按钮灰掉并提示「配房与当前行程不一致，请调整后再确认」。现场 26-7666（6 天 5 晚）配了 DAY1/2、DAY3/4/5 待配 → 房务无法收口。wx 要求取消该限制，改为二次确认。

---

## 变更接口

### 1. `GET /admin/house/orders/{orderId}` —— `actions.canFinalize` 放开条件

**旧行为**：只有「每晚都配齐且全部已确认」或「一晚都没配（客人全程自住）」两种情况可最终确认；**部分配房一律禁用**。

**新行为**：**部分配房、存在未确认配房行，均可最终确认**。

| 情况 | 旧 | 新 |
|---|---|---|
| 每晚配齐且已确认 | ✅ | ✅ 不变 |
| 一晚都没配 | ✅ | ✅ 不变 |
| **部分晚没配房** | ❌ 禁用 | ✅ **放开** |
| **配了但还没确认** | ❌ 禁用 | ✅ **放开** |
| 配房日期与当前行程错位 | ❌ 禁用 | ❌ **仍禁用** |

`disabledReason` 在放开后不再出现因部分配房产生的文案；日期错位时文案为「配房日期与当前行程不一致，请调整后再确认」。

> ⚠️ **日期错位仍然硬拦**：这是订单改期后残留的旧配房行，放行会把过期日期的房当成有效配房回传给定制师、直接坏数据（与 `pendingRescheduleAssignments` 互为兜底）。前端不要试图绕过。

### 2. `progress` 新增两个字段（供二次确认弹窗）

```json
"progress": {
  "arrangedCount": 1,
  "totalCount": 5,
  "unarrangedDayNumbers": [1, 2, 3, 5],    // 【新增】还没配房的晚号
  "unconfirmedDayNumbers": [4]             // 【新增】配了但还没确认的晚号
}
```

| 字段 | 类型 | 说明 |
|---|---|---|
| `unarrangedDayNumbers` | `List<Integer>` | 未配房的晚号，升序 |
| `unconfirmedDayNumbers` | `List<Integer>` | 已配房但 `confirmStatus != CONFIRMED` 的晚号，升序 |

两者**互斥**（同一晚不会同时出现在两个列表里），都为空数组时即配齐且全确认。

**为什么要加这两个字段**：`arrangedCount/totalCount` 只有数量，说不出「缺的是第 3、4、5 晚」，更完全无法表达「配了但酒店还没回复确认」——而这恰恰是本次放开的两类情况，二次确认文案必须分开说，否则房务不知道自己在确认什么。

---

## 前端要做

`canFinalize.enabled` 为 true 且 `unarrangedDayNumbers` 或 `unconfirmedDayNumbers` **非空**时，点「最终确认」先弹二次确认，文案把两类情况分开列，例如：

```
还有第 1、2、3、5 晚未配房，第 4 晚已配房但酒店尚未确认。
确认要完成配房吗？
```

两个列表都为空时（配齐且全确认）保持原有直接确认流程，不弹二次确认。

`disabledReason` 直接透传展示即可，不需要再针对「配房与当前行程不一致」做特殊分支——该文案现在只在真正的日期错位时出现。

---

## 验证证据

2026-08-11 测试服（网关 `https://api.test.1814.love:9443`，房务管理员 token）实测两个真实的部分配房订单：

| 订单 | 进度 | `unarrangedDayNumbers` | `unconfirmedDayNumbers` | `disabledReason` |
|---|---|---|---|---|
| 26-3420 | 1/5 | `[1,2,3,5]` | `[4]` | 仅本人持有的订单可最终确认（权限原因，非覆盖度） |
| 26-3281 | 1/2 | `[2]` | `[1]` | 同上 |

两单的 `disabledReason` 均已不再是「配房与当前行程不一致」，证明覆盖度拦截确已解除；字段互斥升序、数值与实际配房状态一致。

单测：`hl-order-service-v3` 全量 **586 类 / 7512 tests，0 failures 0 errors**，ArchTest 门禁 44 绿。核心逻辑通过**双向变异验证**：退回放开逻辑 → 7 个用例红（覆盖度层 2 / 读口 2 / 写口 3）；误把日期错位也放开 → 4 个用例红（含「未确认 + 旧日期」脏行场景）。
