# 房务管家(订单详情 / 抢单池)渲染缺口 + 抢单门控(#4237,后端 PR #4239) > 变更类型:🐛 后端数据已齐全 → 前端渲染对齐 + 交互门控 > 端类型:房务管家(housekeeper)前端(订单详情弹窗 + 抢单池列表) > 日期:2026-06-22 > 工单:#4237 | 服务:hl-order-service-v3(数据已就绪) --- ## 总览 wx 评审房务管家发现多处「前端渲染不全 / 交互缺门控」。**后端数据基本都齐全**(段×候选完整结构、productName、claim 信息),下列 1-3 项前端按完整结构渲染即可,**可立即接**。另有进度条 6 态 + 留言计数两项后端在改(PR #4239,合并部署后出字段更新版)。 ## 1. 抢单前隐藏「联系定制师 / 给定制师留言」(可立即接) **规则**:抢单后才能和定制师聊天(聊天 = 留言)。订单还在抢单池(未被本房务抢单)时,**不显示**「联系定制师」「给定制师留言」入口(订单详情顶部两个按钮 + 内部留言区的「给定制师留言」)。 **前端**:按订单详情的 `claim.isMine`(未抢单为 false)门控——未抢单 → 隐藏/禁用这些入口;抢单后 → 显示。 ## 2. 抢单池列表补「产品名」(可立即接) **现状**:列表只显挡位名(如"舒适·N晚"),无产品名。后端 pool VO 已含 `productName`(如"测试核心产品-单档-固定比例")。 **前端**:列表行补显 `productName`(产品名 + 挡位,别只显挡位名)。 ## 3.「定制师需求」按完整段×候选结构渲染(可立即接) **现状**:① 内部留言 tab 的「定制师需求」② 配房行程行 —— 都只显 `第N晚 · X间 · 房型 · ¥价 · 备注` 的简化视图,**漏候选酒店 + 各候选备注 + 段备注**(例:DAY1 实有候选 呼伦贝尔香格里拉(备注777555) / 海拉尔嘉世豪(备注77777222),现只显一个备注、无酒店)。 **后端已就绪**:`requirement.current.days[].segments[]` = `{roomCategory, roomCount, budget, remark(段备注), candidates:[{hotelId, hotelName, remark(候选备注)}]}`(房务侧 RequirementSnapshot,#4128 起完整;配房行程的 DayDetail 同样含 segments / hotels)。 **前端**:按完整段×候选渲染——每段 `房型 · X间 · ¥预算 · 候选(择一):酒店A(候选备注..) / 酒店B(候选备注..) · 段备注`,对齐 admin 侧(#4220)。内部留言「定制师需求」与配房行程行都补全。 **✅ 实测复证(本单 HL20260620152849917,房务侧端点 `GET /admin/house/orders/{orderId}` → `data.requirement.current.days[]`)**:候选酒店字段后端**已返齐**,房务侧与管理后台定制师侧『行程安排 → 住宿安排』**同源同数据**,前端照渲即可一致: ``` DAY1 普通标间 ×1 ¥380:candidates = [呼伦贝尔香格里拉大酒店(remark 777555), 海拉尔嘉世豪酒店(remark 77777222)] DAY2 俄式标准房 ×1 ¥280:candidates = [恩和瓦西里民宿, 呼伦贝尔香格里拉大酒店] ``` > 取值:每段 `segments[].candidates[].hotelName`(+ `.remark` 候选备注);兼容字段 `days[].hotels[]` 同数据。**验收目标 = 房务管家「内部留言→定制师需求」渲染与管理后台定制师侧「住宿安排」逐段逐候选一致**。 ## 4-5. 进度条 6 态 + 内部留言计数(✅ PR #4239 已合并部署测试服 + API 实测验证,可接) - **进度条**:原 4 步硬编码「接单/配房中/待最终确认/已最终确认」+ 待配房单误显「配房中」。后端已改由房务侧 6 态驱动,字段契约: - `progress.steps[].label` 校正为固定 4 步「待配房 / 配房中 / 待最终确认 / 已确认」,`completed` 按是否越过该步。 - `progress.currentStep`:待配房=1 / 配房中(含询房中)=2 / 待最终确认=3 / 已确认=4。 - **新增** `progress.houseStatus`(6 态枚举 name:`PENDING_CLAIM`/`CLAIMING`/`IN_INQUIRY`/`PENDING_FINALIZE`/`CONFIRMED`/`EXCEPTION`)+ `progress.houseStatusLabel`(中文,含 4 步未单列的 **询房中 / 异常**)。**前端进度条按这两个权威字段渲染,别再用旧硬标签**。 - 实测(抢单池单 `GET /admin/house/orders/{id}`):`houseStatus=PENDING_CLAIM, houseStatusLabel=待配房, currentStep=1, steps[0].completed=false`。 - **内部留言计数**:`tabCounts.messageCount` 由恒 0 改为计入定制师需求(=1)。实测 `messageCount=1`。 - **H 留言空态对齐(前端)**:既然口径是「定制师需求算 1 条留言」(messageCount=1),内部留言区**别再显「该订单暂无内部留言」**(与 count=1 自相矛盾,且该文案是前端写死,后端不返)。正确:把「定制师需求」当作**第 1 条留言**展示;真正空态只针对**抢单后**房务↔定制师聊天那部分——抢单前聊天关闭,可显「抢单后可与定制师沟通」之类引导而非笼统「暂无内部留言」。 - **H2 抢单池「抢单后可与定制师沟通」门控保持不变(与定制师留言不冲突)**:定制师在抢单前可**单向留言**,这些留言由后端压在 pending、**房务抢单那刻自动整批送达抢到的人**(带未读角标 + SSE)。房务在抢单池里未接单、无身份,先不聊是对的;**抢单后**这些留言会出现在房务↔定制师聊天里。聊天气泡的「已读/留言/已送达」显示口径见 **`22_4223` §10/§11**(后端原无「已读」字段,抢单前标「留言」;#4289 已补真已读 `readByPeer`,抢单后 true=「已读」/false=「已送达」,**别写死「已读」**)。 ## 8.「内部留言」框显示定制师留言(✅ #4288 已上线测试服 + API 实测,可接) **现状(你 Image #28 反馈「有留言没显示」)**:房务侧订单详情「内部留言」tab 只显「定制师需求」+「抢单后可与定制师沟通」占位,**定制师抢单前发的留言不显示**(你 Image #29 定制师侧已显 2 条留言,房务侧却看不到)。 **后端已补(实测 `GET /admin/house/orders/{orderId}`)**:详情 **新增顶层 `messages` 列表**(抢单前定制师留言),`tabCounts.messageCount` 由恒 1 改为 **`1(定制师需求) + N(留言)`**。**抢单池/未抢单也返**——房务在池里浏览即见定制师留了什么话。 **字段契约**: ``` data.messages: [ { messageId(String雪花), senderName, senderRole, content, sentAt("yyyy-MM-dd HH:mm:ss"), msgType } ] data.tabCounts.messageCount = 1 + messages.length ``` **实测**(订单 2068234602970828802,抢单池态):`messageCount=3`,`messages`=[「你好 测试消息」14:05、「确认单发我一下」14:32],与定制师侧 Image #29 一致。 **前端**:「内部留言」框 = 第 1 条「定制师需求」(沿用现有 requirement 渲染)+ 下面按 `messages` 逐条渲染留言(`senderName` + `content` + `sentAt`)。`senderRole` 抢单前留言可能为 null(恒定制师,按「定制师」渲染即可)。抢单后这些留言并入房务↔定制师聊天,气泡已读口径见 `22_4223 §11`。 ## 6. 操作日志「操作人」别打整串 JSON,渲染 `operator.name`(前端 bug,可立即接) **现状**(订单详情弹窗 → 操作日志 Tab):操作人显示成 `by { "userId": "2037350531801993218", "name": "腰苏图" }` —— 把后端返回的**结构化 `operator` 对象**整个 `JSON.stringify` 打了出来。 **后端正确**(实测 `GET /v3/admin/house/orders/{orderId}/operation-log` → `data.records[]`):每条 `operator` 是结构化对象 `{ userId, name }`,`operator.name` 已是干净姓名「腰苏图」: ```json { "opType": "REQUIREMENT_SUBMIT", "opTypeLabel": "定制师提交需求", "summary": "定制师提交房型需求 v1", "operator": { "userId": "2037350531801993218", "name": "腰苏图" } } ``` **前端**:操作人只渲染 `operator.name`(→「腰苏图」/「by 腰苏图」),**别 `JSON.stringify(operator)`、别直接把对象插进模板**。`operator.userId` 仅用于跳详情/排查,不展示。其余照常:`summary` 后端已拼好直接显示、`opTypeLabel` 当中文徽标、`time` 当时间。 ## 7. 配房行程逐日城市改显区县(✅ 后端 #4264 已上线,前端无需改动) **现状/变更**:配房行程 Tab 每天行的城市原来显**市级**(呼伦贝尔市),现后端改为返**区县**(海拉尔区/陈巴尔虎旗)。来源 = 订单产品快照 `dismissalPlace.districtName`(下单冻结)→ 资源酒店 `district` 兜底 → 市级 city 兜底。 **字段契约**:`itinerary[].cityName` = **区县**(解析得到时;否则回退市级,绝不空);`itinerary[].cityCode` = **市级**(向后兼容,若前端某处按市级筛选/分组用这个)。 **前端**:继续渲染 `cityName` 即自动得区县,**无需改动**。实测(`GET /admin/house/orders/{orderId}`):DAY1 cityName=海拉尔区、DAY2 cityName=陈巴尔虎旗,cityCode 均=呼伦贝尔市。 --- ## 说明(非 bug) - 抢单池列表「呼伦贝尔市」= 后端按候选酒店 hotelId 解析中文城市去重回填(#3172),非已下线的手填城市字段。是否保留待产品定。