--- schema: "hl-changelog/v2" ticket: "8030" title: "全团需求汇总逐日房间补酒店维度与住宿日期" consumer: "admin" author: "jw(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "not_required" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "9a14ad866b3ef6ac9acb0ca9ab9e9f0fe98f6559" target_release: "" verified_at: "2026-09-20" status_note: "全团需求汇总 GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary 的 dailyRoomBreakdown 纯增两项:stayDate(该晚住宿日期 = 团期出发日 + dayNumber - 1,团期无出发日时为 null)与 hotels[](该晚按酒店分组的用房,含 hotelId / hotelName / totalRoomCount / rooms[])。原有 rooms[](不分酒店的全天合计)与其余字段一个没动。这样团期「查看需求」页的「用房 · 汇总」表(Day N | 酒店 | 房型 | 所需间数)一个接口就能画完,不必逐户调订单调整快照去拼酒店,也不必等房务建完订房计划——后者在成团前根本没有数据。口径:与 rooms 同一次遍历的两个视角,任一天 Σhotels[].totalRoomCount == Σrooms[].totalRoomCount;每段只取首方案候选(候选是房控择一,累加全部会翻倍);取不到酒店的段归 hotelId=null 一条且恒排末位,间数不丢;酒店名优先取需求里落库的快照,缺失的按 ID 一次批量补,资源服务不可用时名称为 null、间数照常。 前端实证维持 not_required(mmg 2026-09-20):requirement-summary/dailyRoomBreakdown/stayDate(本端点义)/hotels 全仓零命中(同 #7925/#7937/#8023 第四次实证,汇总端点未接入);「用房·汇总」表属新功能排期,届时 stayDate null 按 Day N 展示、hotelName null 显「未知酒店」不隐藏行、全天合计直读 rooms[] 不跨酒店累加、hotelId 字符串。 2026-09-20 前端接入(mmg):「用房 · 汇总」建页随 hl-admin v2.1 交付——requirement-summary 经 getGroupRequirementSummary 接入「查看需求」页 RoomSummarySection:户数条(在团/需订房/已提交)+不一致只提示不扣减+Day N|酒店|房型|所需间数扁平表;stayDate null 按 Day N、hotelName null 显「未知酒店」不隐藏行、roomCategoryName null 回落编码、全天合计直读 rooms[] 禁跨酒店累加、hotelId null 组间数不丢;整团确认/按户打回后自动重拉。定向 Vitest 17/17,checkpoint 全绿。 2026-09-20 后端回填订正(mmg 复核):降级错误码 589574→589596(589574 是 #7932 保留位冲突),该码只在服务端降级日志产生、从不传播前端,前端零影响;TEST AC-1~11 回填全✓。frontmatter 维持 verified 不动。" updated_at: "2026-09-20" base: "dev-v3" --- # order-v3: 全团需求汇总逐日房间补酒店维度与住宿日期 > **服务**: hl-order-service-v3 (端口 8086) > **PR**: [#8040](https://git.1814.love:8443/wx/HL/pulls/8040)(主体)、[#8041](https://git.1814.love:8443/wx/HL/pulls/8041)(验收补丁:按酒店分组那份补房型中文名) > **Issue**: [#8030](https://git.1814.love:8443/wx/HL/issues/8030) > **日期**: 2026-09-20 > **影响范围**: 管理后台「团期订单 → 查看需求」页的「用房 · 汇总」表 --- ## ⚠️ 关键变化 - **纯增两项**:`dailyRoomBreakdown[].stayDate`、`dailyRoomBreakdown[].hotels[]`。既有字段一个没删没改,入参与路径不变。 - **一个接口画完整张表**:此前「酒店」这一列只能逐户调 `GET /v3/admin/order/{orderId}/adjustment/snapshot` 去拼,或等房务建完订房计划(`room-plans`,成团前为空)。 - **两个视角、同一批间数**:`hotels[]` 与 `rooms[]` 是同一次遍历的两个视角,任一天间数总和必然相等。 - **不改任何口径**:计入哪些户、哪些需求算数,全部沿用 #7925 / #8023 的既有判定。 --- ## 一、背景 原型「用房 · 汇总」要的是「Day N | 酒店 | 房型 | 所需间数」,而汇总此前只给房型与间数,酒店这一列没有数据源: | 想要的列 | 改前只能这么拿 | 问题 | |---|---|---| | 间数 + 房型 | `requirement-summary` | ✅ 有 | | 酒店 | 逐户 `adjustment/snapshot` | N 户调 N 次,且那是为「订单调整」设计的重接口 | | 酒店(房务实际订房) | `room-plans` | **要房务认领并建计划之后才有数据**,成团前为空 | | 住宿日期 | 无 | 只有 `dayNumber` | TEST 实证(团期 `jw测试1期`,招募中):`room-plans` 返回 `days: []`、`progress: NOT_STARTED` —— 运营恰恰要在成团前按这张表向酒店报数。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 全团需求汇总 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement-summary` | 修改 | `dailyRoomBreakdown[]` 纯增 `stayDate` 与 `hotels[]` | --- ## 三、接口详情 ### 1. 全团需求汇总 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary` **VO**: `Result` #### 使用场景 团期详情「查看需求」页的「用房 · 汇总」表:逐日列出住哪家酒店、什么房型、要几间,运营据此向酒店报数。 #### 入参 本次入参**不变**。 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期 ID | 团期不存在返回 589500 | #### 出参 | 字段 | 类型 | 说明 | |------|------|------| | dailyRoomBreakdown[].dayNumber | Integer | 第几天(不变) | | dailyRoomBreakdown[].stayDate | String(yyyy-MM-dd) | **新增**。该晚住宿日期 = 团期出发日 + dayNumber − 1;团期无出发日时为 `null`(不推算) | | dailyRoomBreakdown[].rooms[] | List | 该天各房型合计,**不分酒店**(不变) | | dailyRoomBreakdown[].hotels[] | List | **新增**。该天按酒店分组的用房 | | dailyRoomBreakdown[].hotels[].hotelId | String | 酒店 ID(字符串序列化防 JS 精度丢失);取不到酒店时为 `null` | | dailyRoomBreakdown[].hotels[].hotelName | String | 酒店名;资源服务不可用时为 `null` | | dailyRoomBreakdown[].hotels[].totalRoomCount | Integer | 该天该酒店合计间数 | | dailyRoomBreakdown[].hotels[].rooms[] | List | 该天该酒店各房型合计(结构同上面的 `rooms[]`) | | 其余既有字段 | — | 不变(无删除、无改名、无类型变化) | #### 请求示例 ```http GET /v3/admin/order/group-batch/2101506167098511362/requirement-summary Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "activeOrderCount": 3, "hotelNeededOrderCount": 2, "hotelSubmittedOrderCount": 2, "hotelFlagMismatchOrderCount": 1, "dailyRoomBreakdown": [ { "dayNumber": 1, "stayDate": "2026-09-27", "rooms": [{"roomCategory": "KING", "roomCategoryName": "豪华大床", "totalRoomCount": 3}], "hotels": [ { "hotelId": "2023714929877450753", "hotelName": "呼伦贝尔香格里拉大酒店", "totalRoomCount": 3, "rooms": [{"roomCategory": "KING", "roomCategoryName": "豪华大床", "totalRoomCount": 3}] } ] } ], "vehicleSeatSummary": [], "orderSpecialTags": [] }, "traceId": null, "success": true } ``` #### 空数据 / 降级响应 - 全团无计入需求时 `dailyRoomBreakdown` 为 `[]`(不是 null),不调房型字典、不调资源服务。 - 某天全部段都取不到酒店时,`hotels[]` 只有一条 `hotelId: null` 的记录,间数照常。 - 资源服务不可用时只有 `hotelName` 为 `null`,`hotelId`、间数与分组照常返回,接口不报错。 #### 错误响应 ```json { "code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "data": null, "success": false } ``` | code | 触发条件 | |------|----------| | 589507 | 当前角色没有 `group-batch:view`(本次不变) | | 589500 | 团期不存在(本次不变) | | 401 | 未登录(网关拦截) | 本次新增内部错误码 **589596**(资源服务住宿名称补全降级),**只在服务端降级日志里出现,不会传播给前端**。(初版误取 589574,该号是 #7932 的保留位,已于 2026-09-20 随 #8046 订正为 589596。) #### 业务边界 - **间数不变量**:任一天 `Σ hotels[].totalRoomCount == Σ rooms[].totalRoomCount`。两者是同一次遍历的两个视角,不是两套统计。 - **只取首方案**:一段有多个候选酒店时只计 `candidates[0]`。候选是房控择一、不是都要,累加全部会让采购分母翻倍。 - **取不到酒店的段**(没有候选、或候选未填酒店)归到 `hotelId: null` 一条,**不丢间数**;该条恒排在**最后**,即使它间数最多。 - **排序**:其余酒店按间数降序,同数按 hotelId 升序(稳定)。 - **酒店名来源**:优先取需求里落库的快照;快照缺的按 ID 一次批量补(不逐个查)。酒店改名后快照会旧,以补齐结果为准。 - **计入口径不变**:哪些户、哪些需求算数完全沿用 #7925 / #8023;客户自订晚仍整晚跳过。 --- ## 四、契约约束与正确调用方式 - 画「用房 · 汇总」表用 `hotels[]`;要「这一天一共几间」用 `rooms[]`。**不要**把 `hotels[]` 里的间数再跨酒店累加去当全天合计——直接读 `rooms[]` 即可,两者本就相等。 - `hotelId` 是**字符串**,不要用 JS 的 Number 解析。 - `hotelName` 可能为 `null`(资源服务降级 / 该酒店已删),前端按「未知酒店」展示即可,**不要**因此隐藏整行——间数仍然有效。 - `stayDate` 可能为 `null`(团期未排出发日),此时按 `Day N` 展示。 --- ## 六、边界行为 | 场景 | 行为 | |------|------| | 多户订同一家酒店 | 合并为一条,间数相加 | | 同一晚多户订不同酒店 | `hotels[]` 出多条,按间数降序 | | 一段有多个候选酒店 | 只计首方案,不重复计数 | | 段未填酒店 / hotelId 脏值 | 归 `hotelId: null` 一条并排末位,间数不丢 | | 客户自订晚 | 整晚跳过(不变) | | 团期无出发日 | `stayDate` 为 null,不推算 | | 资源服务不可用 | `hotelName` 为 null,其余照常 | --- ## 六.6、修改前后对比 ### 字段级对比 | 字段 | 改前 | 改后 | |------|------|------| | `dailyRoomBreakdown[].stayDate` | 无 | 新增,String(yyyy-MM-dd),可为 null | | `dailyRoomBreakdown[].hotels[]` | 无 | 新增,List | | `dailyRoomBreakdown[].rooms[]` | — | 不变 | | 其余既有字段 / 入参 / 路径 / 错误码 | — | 不变 | ### 行为级对比 | 场景 | 改前 | 改后 | |------|------|------| | 画「用房 · 汇总」表 | 逐户调 `adjustment/snapshot` 拼酒店,或等房务建订房计划 | 一个接口出数 | | 成团前查逐日酒店 | 无数据源(`room-plans` 为空) | 直接从各户已提交需求汇总 | | 逐日间数与房型 | 已有 | 逐字不变 | ## 六.7、影响评估 - **是否破坏向后兼容**: 是纯增字段,完全兼容;既有 `rooms[]` 数值逐字不变。 - **前端是否必须同步上线**: 否。不接入则页面保持现状。 - **回滚**: 回滚本 PR 即可,只读接口、无数据变更。 --- ## 七、不影响范围 - **仅影响**: 本接口 `dailyRoomBreakdown[]` 的两个新增字段。 - **零影响**: 计入户口径与三个户数、用车座位合计、各户特殊需求标签、整体确认预检、房务侧订房与分房、`room-plans`、`adjustment/snapshot`、数据库结构(只读接口,无表变更、无 Flyway)。 --- ## 八、测试环境已验证 TEST 环境(`hl-order-service-v3`,dev-v3,2026-09-20),团期 `jw测试1期`(`2101506167098511362`,出发 2026-09-27,6 晚)。 ``` AC-1 单户 6 晚逐晚不同酒店,hotels[] 与 hotelName 正确 ✓ AC-2 同天多户不同酒店 → 出多条并按间数降序;同一酒店合并且间数相加 ✓ AC-3 间数不变量 Σhotels == Σrooms:单测 + TEST 六天逐日实测 ✓ AC-4 stayDate = 出发日 + dayNumber − 1,跨月 09-30 → 10-01 正确 ✓ AC-5 字符串形态 hotelId 的存量 days JSON 正确解析(TEST 数据本就是字符串)✓ AC-6 候选缺失 / hotelId 为空 → 归「未指定」一条恒排末位,间数不丢 ✓ AC-7 酒店名:快照优先;快照置空后按 ID 批量补,实测补出正确名称 ✓ AC-8 既有字段零变化:三个户数 / rooms[] / vehicleSeatSummary / orderSpecialTags 与改前逐字一致 ✓ AC-9 客户自订晚仍不计入(该日由 3 间降为 2 间) ✓ AC-10 判权:低权限角色 1002/user → 589507;超管 200 ✓ AC-11 changelog 按「修改接口」推送(提交 af00e6f) ✓ ``` 订正(2026-09-20,#8046 回归发现):本单新增的降级错误码原取 589574,该号被 #7932 登记为保留位且有测试断言它必须空闲,#8030 当轮回归范围未覆盖该测试类故漏网。 现已改为 **589596**(见下方「五、错误码」与 PR [#8055](https://git.1814.love:8443/wx/HL/pulls/8055) 第一个提交)。 该码只在服务端降级路径产生,从不传播给前端,对前端无影响。 --- ## 十、相关文档 - 工单 [#8030](https://git.1814.love:8443/wx/HL/issues/8030) - 计入口径前置:[#7925](https://git.1814.love:8443/wx/HL/issues/7925)、[#8023](https://git.1814.love:8443/wx/HL/issues/8023) --- ## 关联 / 联系人 ### 链接 - **Issue**: [#8030](https://git.1814.love:8443/wx/HL/issues/8030) - **PR**: [#8040](https://git.1814.love:8443/wx/HL/pulls/8040)、[#8041](https://git.1814.love:8443/wx/HL/pulls/8041) ### 联系人 - **后端负责人**: @jw - **前端负责人**: @mmg