diff --git a/changelogs-v2/2026-09/20_8030_全团需求汇总逐日房间补酒店维度与住宿日期-修改接口-管理后台.md b/changelogs-v2/2026-09/20_8030_全团需求汇总逐日房间补酒店维度与住宿日期-修改接口-管理后台.md new file mode 100644 index 00000000..b6cfa838 --- /dev/null +++ b/changelogs-v2/2026-09/20_8030_全团需求汇总逐日房间补酒店维度与住宿日期-修改接口-管理后台.md @@ -0,0 +1,248 @@ +--- +schema: "hl-changelog/v2" +ticket: "8030" +title: "全团需求汇总逐日房间补酒店维度与住宿日期" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "not_required" +frontend_status: "pending" +frontend_owner: "mmg" +frontend_ref: "" +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、间数照常。" +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 | 未登录(网关拦截) | + +本次新增内部错误码 589574(资源服务住宿名称补全降级),**只在服务端降级日志里出现,不会传播给前端**。 + +#### 业务边界 + +- **间数不变量**:任一天 `Σ 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 后回填实测结果(工单 #8030 AC-1~AC-11) +``` + +--- + +## 十、相关文档 + +- 工单 [#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