# 【修改·管理后台】配房/选酒店流程评审 11 点收口(候选房型富化 + 段×候选 + 池内快照 + 特殊诉求字典) > 涉及 PR:#4126 / #4127 / #4135 / #4136 / #4148 / #4149(hl-order-service-v3,区县另涉 hl-resource-service) > 影响页面:**订单详情 → 调整订单 → 酒店安排**(配房需求表单)+ 其中的**「选择酒店」弹窗** > 状态:已合并 dev-v3、已部署测试服双实例、候选接口已网关 API 实测通过 --- ## ⚠️ 关键说明(先读) 1. 这是定制师「提交配房需求」表单 +「选择酒店」弹窗的一轮产品评审收口,共 11 个点,前后端都要改。 2. 两处契约变化,前端必改: - **配房需求提交体** `HotelRequirementReqVO.days` 结构升级为「**段 × 候选**」(见 §三-2)。 - **选酒店候选接口** `GET /v3/admin/hotel-candidates` 出参**富化**(真实房型/城市/区县/池内,见 §三-1)。 3. **房型下拉不再用通用字典**,改由「所选酒店的真实房型」驱动;**不选酒店不能选房型**。 4. 金额字段(协议价/标价/预算)均为 **String**(后端 @JsonSerialize),前端按字符串收。 --- ## 一、配房需求表单改动 ### 点1 删「当晚备注」 - 每晚的「当晚备注(当晚整体安排说明)」**整条去掉**(输入框删除)。 - 后端 `DayHotelReq.remark` 字段已移除;行程 Tab 的 `requirementDays[].remark` 也随之废弃(新单恒空,前端别再展示当晚备注)。 - **保留**「该酒店备注」(迁为每个候选酒店的备注,见点8 `CandidateHotel.remark`)。 ### 点2 删「城市/区域」输入(纯前端) - 每晚那个「城市/区域(如 拉萨市区)」输入框**删掉**——它本就没进后端,且与「选择酒店」弹窗自带的搜索框重复。 ### 点3 特殊诉求改字典(多选 + 允许自定义) - 「特殊诉求」从自由文本标签改为**绑定字典** `house_special_demand`,前端做**多选**,**仍允许回车加自定义**。 - 字典加载:`GET /admin/dict/data/house_special_demand` → `[{dictValue, dictLabel}]`(当前值:安静楼层/高楼层/景观房/无烟房/含早餐/含双早/可加床/可拆分双床/相邻房间/远离电梯,运营可在字典管理增删)。 - 提交字段不变,仍是 `specialTags: List`(存字典 value,自定义则存原文)。 ### 点8 酒店支持「段 × 候选」(提交体结构升级,重点) 两个**独立**维度(产品确认都要): - **分住**:同一晚住两家(房数相加)—— 一晚有多个 **段(segment)**。 - **候选**:定制师对某段提多家心仪酒店,**房控后台择一**回配 —— 一段有多个 **候选(candidate)**。 UI: - 每晚可「+ 加一段不同酒店(同一晚分住)」= 加 segment(每段独立填 房数/房型/预算/段备注)。 - 每段的「酒店」框支持**多选**候选酒店(房控择一);每个候选可填该酒店备注。 - 候选酒店会自动进候选接口的「定制师点名」高亮(`isConsultantRecommended`)。 --- ## 二、选择酒店弹窗 + 房型 ### 点4 协议价按真实房型 / 点6 可用房按真实房型 / 点7 房型按真实酒店 - 候选接口现返回每家酒店的**真实房型列表** `roomTypes[]`(来自 resource 价格日历/库存)。 - 前端: - **房型下拉**改由所选酒店的 `roomTypes[]` 驱动(`name`/`bedType`/`maxOccupancy`),**不选酒店则房型不可选**;不再用通用 `room_category` 字典。 - **协议价**显示所选房型的 `roomTypes[].protocolPrice`(不是酒店级起步价 `protoPrice`)。 - **可用房**显示所选房型的 `roomTypes[].available`(+ `inventoryStatus` AVAILABLE/FULL/CLOSED)。 - 注:测试酒店需 resource 侧配了价格日历+库存才有 `roomTypes` 数据(无则该酒店 roomTypes 为空/价 null)。 ### 点5 城市 / 点10 区县 - 候选项新增 `city`(城市,如 呼伦贝尔市)+ `district`(区/县,如 满洲里市)。 - 选酒店列表/候选展示加「城市」「区/县」两列。 ### 点9+11 池内徽章:按行程天数 + 从订单产品快照取 - 「池内」徽章 `isPoolMatch` 语义修正为:**该酒店在「订单产品快照」里当天当档(order.tierSeq)的产品酒店池中**。 - 按**行程天数**:第1天只标第1天产品行程的酒店,第2天标第2天的。 - 从**订单产品快照冻结值**取(非 live 产品配置)——产品下单后被改不影响已下单订单的池内判定。 - 前端无需改调用方式,照常按 `dayNumber` 查候选,`isPoolMatch`/`poolMatchBadge` 直接用。 --- ## 三、接口契约 ### 1. 候选接口出参(新增字段) `GET /v3/admin/hotel-candidates?orderId=&dayNumber=&roomCategory=&roomCount=&keyword=&limit=` 出参 `data.candidates[]`(`HotelCandidateRespVO.Candidate`)**新增**: | 字段 | 类型 | 说明 | |------|------|------| | `city` | String | 酒店所在城市(如 呼伦贝尔市) | | `district` | String | 酒店所在区/县(如 满洲里市) | | `roomTypes` | List | 该酒店当日真实房型列表(见下) | `roomTypes[]`(`RoomTypeOption`): | 字段 | 类型 | 说明 | |------|------|------| | `roomTypeId` | String | 房型 ID | | `name` | String | 房型名称(如 普通标间) | | `bedType` | String | 床型(如 TWIN_BED) | | `maxOccupancy` | Integer | 最大入住人数 | | `available` | Integer | 今日可用房数 | | `stock` | Integer | 总库存 | | `protocolPrice` | String | 协议价(金额字符串) | | `basePrice` | String | 标价 | | `inventoryStatus` | String | AVAILABLE / FULL / CLOSED | `isPoolMatch`(既有字段,语义改为按天+快照)、`protoPrice`/`matchedRoomType*`(既有,向后兼容保留)不变。 ### 2. 配房需求提交体(结构升级) `HotelRequirementReqVO`(提交配房需求 / 调整订单 hotelRequirement.days 同构): ``` { "days": [ { "dayNumber": 1, "segments": [ // ← 原 hotels[] 改为 segments[](分住段) { "roomCount": 2, "roomCategory": "TWIN", "budget": "600.00", // 可空(后端反查协议价覆盖) "remark": "段备注", "candidates": [ // ← 候选酒店列表(房控择一) { "hotelId": 80001, "hotelName": "拉萨西藏宾馆", "remark": "靠湖一侧" }, { "hotelId": 80002, "hotelName": "山景酒店", "remark": "含双早" } ] } ] } ], "specialTags": ["安静楼层", "含早餐"], // 字典 house_special_demand 的 value(允许自定义) "remark": "整单备注" } ``` - **去掉**:`days[].remark`(当晚备注)、`days[].hotels[].source`(死字段)。 - 后端兼容旧 `hotels[]` 结构读取(存量订单不报错),但**新提交请用 `segments[].candidates[]`**。 ### 3. 特殊诉求字典 `GET /admin/dict/data/house_special_demand` → `data: [{dictValue, dictLabel, ...}]`。 --- ## 四、curl 实测(候选接口,2026-06-20 测试服) ``` GET /v3/admin/hotel-candidates?orderId=2068235380959682562&dayNumber=1&limit=3 → 200 data.candidates[0]: hotelName=呼伦贝尔香格里拉大酒店 city=呼伦贝尔市 district=满洲里市 isPoolMatch=true roomTypes=[{name:普通标间, bedType:TWIN_BED, available:20, protocolPrice:"280.00", inventoryStatus:AVAILABLE}, ...] data.candidates[1]: 海拉尔嘉世豪酒店 district=海拉尔区 isPoolMatch=false ``` --- 如对契约/字段有疑问随时找后端(王骁)。