# 【后端已修 + 前端待改·管理后台】配房需求回显修复：段×候选结构 + 晚数 + 候选≠分住 (#4220)

> **后端 PR**: #4220（已合 dev-v3 + 已部署测试服 + API 实测通过） | **服务**: hl-order-service-v3 | **更新**: 2026-06-22
> **前端负责人**: @mmg —— 本文后半「前端待改」是回显 bug，需前端配合改渲染。

## 1. 背景（前端反馈的回显 bug）

3 天 2 晚的订单，配房回显出现两类错误：
- **订单详情「住宿安排」**：2 晚的行程显示成「第 1-2 晚 / 第 3-4 晚」（共 4 晚）。
- **抢单池「配房行程」**：同一段里房控择一的 2 个候选酒店，被标成「同晚分住 1/2、2/2」。

排查（直查测试库 + 调真实 API）定论：**需求数据本身正确**（2 天、每天 1 段、每段 2 候选）。根因有二——后端缺数据 + 前端渲染口径错。

**核心语义（务必对齐）**：一晚的用房 = `segments[]`（分住段，**多段才是同晚分住，房数相加**）；一个 segment 内 = `candidates[]`（**房控择一的候选，房数/预算不叠加**）。**候选 ≠ 分住**。本例每天只有 1 段、段内 2 候选 → 是「2 个候选择一」，不是「2 晚」也不是「2 个分住」。

## 2. 后端已修（#4220，数据现已就绪）

两个真实后端缺陷已修复并部署：

| # | 缺陷 | 修复 |
|---|------|------|
| A | 候选 `hotelId` 恒为 `null`（JSON 里 hotelId 是字符串，后端旧解析只认数字跳过了）→ 点名高亮/跳转酒店失效 | 统一解析 String/Number，hotelId 现正确返回 |
| B | 订单详情 `/itinerary` 的 `requirement.days[]` 只有扁平 `hotels[]`、无 `hotelName`、无 `segments` → 前端无法区分候选/分住、无法展示完整语句 | 新增 `days[].segments[]`（段×候选）+ `hotels[].hotelName`，对齐房务侧 |

**实测确认**（`GET /v3/admin/order/{id}/itinerary` 已返回）：
- `days[].hotels[].hotelId` / `hotelName` 非 null；
- 新增 `days[].segments[]`：每段含 `roomCategory`/`roomCategoryLabel`/`roomCount`/`budget`/`remark` + `candidates[]`（`hotelId`/`hotelName`/`remark`）。
- 房务侧 `GET /admin/house/orders/{id}` 的 `requirement.current.days[]` 本就有 `segments[]`（同结构），hotelId 现也修复。

## 3. 前端待改（回显修复）

### 3.1 订单详情「住宿安排」（行程安排 Tab，源 `/v3/admin/order/{id}/itinerary`）

- **晚数标签用 `days[].dayNumber`**（第 1 晚 / 第 2 晚）。**严禁把同一天的多个 `hotels[]`/候选条目当成多晚累加** —— 这正是「2 晚显示成第 1-2 / 3-4 晚」的根因（day1 的 2 个候选被当成第 1、2 晚，day2 的 2 候选被当第 3、4 晚）。
- **改用新增的 `days[].segments[]` 渲染**：一个 segment 一行（房型 × `roomCount` 间 × `budget` 预算/晚），段内 `candidates[]` 作为「候选酒店（择一）」列表展示（带 `hotelName`）。仅当某天 `segments.length > 1` 才是真·同晚分住（房数相加）。
- **完整结构化语句**（解决「语句不全」）：每晚 = 房型(`roomCategoryLabel` 优先，降级 `roomCategory`) + `roomCount` 间 + `budget` 预算/晚 + 候选酒店名（`candidates[].hotelName`）；整单级 = `specialTags`（诉求）+ `remark`（备注）。
- 旧 `hotels[]` 扁平字段后端**加性保留**（不破坏现有渲染），但建议迁移到 `segments[]`。
- **「已回配」徽章**：仅当 `assignments[]` 非空才显示。当前需求 `status=PENDING`、`assignments=[]` 时仍显示「已回配」属误导，应改为按实配存在性判定。

### 3.2 抢单池 / 房务详情「配房行程」（源 `/admin/house/orders/{id}` → `requirement.current.days[]`）

- **抢单池预览（未抢单）不显示需求酒店名**，只显示行程：`dayNumber` / `stayDate` / `city` / 房型 / 间数 / 预算即可（按反馈「只显示行程就行」）。
- 若需展示候选：用 `days[].segments[].candidates[]`，标「**候选 X/N（房控择一）**」，**不要标「同晚分住 X/N」**。「同晚分住」只适用于 `segments.length > 1` 的场景（同一晚住多家、房数相加）。

### 3.3 「内部留言」tab 展示定制师需求的结构化语句（前端只读卡片）

需求：房务在「内部留言」tab 能直接看到定制师提出的需求（结构化语句）。**目标格式**（wx 确认）：

```
第 1 晚 · 1间 普通标间 · ¥380.00/晚
第 2 晚 · 1间 俄式标准房 · ¥280.00/晚
诉求：安静楼层、高楼层、景观房、无烟房、含早餐、可加床、含双早、可拆分双床、相邻房间
备注：客人怕吵，尽量安排安静房间，要求高
```

- 在「内部留言」tab 顶部加一张**只读「定制师需求」卡片**，用上述格式渲染。**数据源同 §3.1**（`requirement.days[]`：晚=`dayNumber`、间数=段 `roomCount`、房型=`roomCategoryLabel`/`roomCategory`、预算=段 `budget`；诉求=`specialTags`；备注=`remark`）。一段一行，晚数严格按 `dayNumber`。
- 这是**只读展示**，复用 §3.1 的渲染逻辑即可，**不需要新建聊天消息 / 不动站内信系统**（与「给定制师留言」的真实消息流分开）。
- 注：若产品后续要「定制师提交需求时自动往内部留言**发一条系统消息**」（而非只读卡片），那属站内信/聊天链路（当前 deferred 等新原型），需 wx 另定，不在本次。

## 4. 影响 / 兼容

- 后端纯加性（新增 `segments`/`hotelName`，保留 `hotels`），不破坏现有前端；hotelId 由恒 null 变为真值（修复，非破坏）。
- 前端改完后回显应为：本例 = 第 1 晚（普通标间×1，¥380，候选：香格里拉/嘉世豪择一）、第 2 晚（俄式标准房×1，¥280，候选：瓦西里民宿/香格里拉择一）。

## 5. 关联

- **后端 PR**: [#4220](https://git.1814.love:8443/wx/HL/pulls/4220)（前端反馈驱动，无关联 Issue）
- **后端负责人**: @wx ｜ **前端**: @mmg
- 「内部留言显示定制师需求」=§3.3 前端只读卡片（不动站内信系统）；仅「提交需求自动发系统消息」变体涉 deferred 聊天，wx 另定。
