docs(changelog): #7925 全团需求汇总房间合计对齐预检判定 + 补房型中文名与需房/已提交户数(修改接口)
changelog-filename-gate / validate (push) Failing after 1s

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
jw
2026-09-20 09:52:45 +08:00
共同撰写人 Claude Opus 5
父节点 afc2699499
当前提交 8d644fc8f3
@@ -0,0 +1,266 @@
---
schema: "hl-changelog/v2"
ticket: "7925"
title: "全团需求汇总:房间合计只计需房且非打回的户 + 新增需房/已提交户数与房型中文名"
consumer: "admin"
author: "jw(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: "mmg"
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "全团需求汇总 GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary 有一处行为变更 + 三个纯新增出参。行为:dailyRoomBreakdown 每日房间合计此前把「已改成不需要订房」的户和「被打回重改中」的户也算进去,房数虚高;现在只计 needsHotel=true 且状态非打回的户,与整体确认前的缺失预检口径一致。新增出参 hotelNeededOrderCount(需房户数)、hotelSubmittedOrderCount(需房且已提交的户数,即计入合计的户数)、dailyRoomBreakdown[].rooms[].roomCategoryName(房型中文名,字典无此编码时为 null)。既有字段一个没删没改。户范围保持「在团」口径(含已完成的户),与预检刻意不同,见第四节。后端已合并 dev-v3(20465f21e)并部署 TEST,网关实测通过(工单 #7925 AC-2~AC-5)。"
updated_at: "2026-09-20"
base: "dev-v3"
---
# order-v3: 全团需求汇总房间合计对齐预检判定 + 补房型中文名与需房/已提交户数
> **存放目录**: `changelogs-v2/{YYYY-MM}/`(管理后台,二期 order-v3)
>
> **服务**: hl-order-service-v3 (端口 8086)
> **PR**: [#7927](https://git.1814.love:8443/wx/HL/pulls/7927)
> **Issue**: [#7925](https://git.1814.love:8443/wx/HL/issues/7925)
> **日期**: 2026-09-20
> **影响范围**: 团期详情「查看需求」页的全团需求汇总
---
## ⚠️ 关键变化
1. **每日房间合计的数会变小(变准)**:此前把「已改成不需要订房」的户、「被打回正在重改」的户也算进每日合计,房数虚高;现在只算需要订房且非打回的户。
2. **新增三个出参**(纯增量,不接入也不影响现有功能):`hotelNeededOrderCount`、`hotelSubmittedOrderCount`、`dailyRoomBreakdown[].rooms[].roomCategoryName`。
3. **既有字段全部保留、语义不变**;入参、路径、错误码都没变。
4. `roomCategory` 一直是**编码**(如 `STANDARD`),本次只是补了它的中文名字段,编码本身原样返回。
---
## 一、背景
「查看需求」页的每日房间合计与「整体确认」前的缺失预检各算各的:汇总把全部有效房需求都加起来,预检只认「需要订房且没被打回」的那些。于是页面显示要 8 间、预检却按 5 户算,运营无法判断到底该订几间。本次把汇总的计数口径对齐预检,并补上两个户数字段,让「几户需要订房 / 其中几户已提交」在页面上可见。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 全团需求汇总 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement-summary` | 修改 | 每日房间合计改为只计需房且非打回的户;新增 3 个出参 |
---
## 三、接口详情
### 1. 全团需求汇总 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary`
**VO**: `Result<GroupRequirementSummaryRespVO>`
#### 使用场景
团期详情「查看需求」页:展示全团逐日要订几间房、大巴座位合计、各户的特殊需求标签。运营据此向酒店/车队报数。
#### 入参
本次入参**不变**。
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | 团期 ID | 团期不存在返回 589500 |
#### 出参
| 字段 | 类型 | 说明 |
|------|------|------|
| activeOrderCount | Integer | 在团户数(不变) |
| hotelRequirementCount | Integer | 有效房需求条数(不变。**注意**:它不看 needsHotel、也不看打回状态,和下面两个户数不是一回事) |
| hotelNeededOrderCount | Integer | **新增**。在团户中「需要订房」的户数 |
| hotelSubmittedOrderCount | Integer | **新增**。需要订房**且**需求非打回的户数,即真正计入每日合计的户数 |
| vehicleRequirementCount | Integer | 有效用车需求条数(不变) |
| dailyRoomBreakdown | List | 逐日房间明细(**合计口径本次变更**) |
| dailyRoomBreakdown[].dayNumber | Integer | 第几天(不变) |
| dailyRoomBreakdown[].rooms[].roomCategory | String | 房型**编码**,如 `STANDARD`(不变) |
| dailyRoomBreakdown[].rooms[].roomCategoryName | String | **新增**。房型中文名;字典里没有该编码、或字典不可用时为 `null` |
| dailyRoomBreakdown[].rooms[].totalRoomCount | Integer | 该天该房型的房间合计(不变,但数值口径变准) |
| vehicleSeatSummary | List | 车型座位合计(不变;`vehicleType` 仍是编码,中文名见 #7937) |
| orderSpecialTags | List | 各户特殊需求标签(不变,仍取全部有效需求) |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2099927193172815873/requirement-summary
Authorization: Bearer <token>
```
#### 响应示例
(测试服真实响应,2026-09-20,5 户各需 1 间标间、住 2 晚)
```json
{
"code": 200,
"message": "成功",
"data": {
"activeOrderCount": 5,
"hotelRequirementCount": 5,
"hotelNeededOrderCount": 5,
"hotelSubmittedOrderCount": 5,
"vehicleRequirementCount": 1,
"dailyRoomBreakdown": [
{ "dayNumber": 1, "rooms": [ { "roomCategory": "STANDARD", "roomCategoryName": "标间", "totalRoomCount": 5 } ] },
{ "dayNumber": 2, "rooms": [ { "roomCategory": "STANDARD", "roomCategoryName": "标间", "totalRoomCount": 5 } ] }
],
"vehicleSeatSummary": [ { "vehicleType": "suv", "totalSeats": 5, "totalCount": 1 } ],
"orderSpecialTags": []
},
"success": true
}
```
#### 空数据 / 降级响应
全团都不需要订房时(测试服真实响应,该团 1 户且已置为不需订房):房间相关全为空/0,接口照常 200,且不会去查字典。
```json
{
"code": 200,
"message": "成功",
"data": {
"activeOrderCount": 1,
"hotelRequirementCount": 0,
"hotelNeededOrderCount": 0,
"hotelSubmittedOrderCount": 0,
"vehicleRequirementCount": 1,
"dailyRoomBreakdown": [],
"vehicleSeatSummary": [ { "vehicleType": "suv", "totalSeats": 5, "totalCount": 1 } ],
"orderSpecialTags": []
},
"success": true
}
```
字典服务不可用或某编码不在字典里时,只有 `roomCategoryName` 为 `null`,`roomCategory` 与房间数照常返回,接口不报错。
#### 错误响应
```json
{ "code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "data": null, "success": false }
```
| code | 触发条件 |
|------|----------|
| 589507 | 当前角色没有 `group-batch:view`(本次不变) |
| 589500 | 团期不存在(本次不变) |
| 401 | 未登录(网关拦截) |
本次无新增错误码。
#### 业务边界
- **计入每日合计的户**:需要订房(`needsHotel=true`)且需求状态非打回(打回态为退回定制师、退回管理员两种)。
- **户范围**:与此前一致的「在团」口径,**包含已完成的户**;整体确认前的缺失预检不含已完成的户,两者刻意不同——汇总回答「全团一共要几间」,预检回答「确认前还缺谁」。团内没有已完成的户时,`hotelNeededOrderCount` 与预检检查的户数相同。
- `hotelRequirementCount` 是**需求条数**,不看 needsHotel 与打回状态,和两个新户数字段口径不同,不要混用。
- `orderSpecialTags` 不受本次过滤影响,仍取全部有效需求。
---
## 四、契约约束与正确调用方式
| 场景 | 做法 |
|------|------|
| ✅ 页面显示「几户需要订房 / 其中几户已提交」 | 用 `hotelNeededOrderCount` / `hotelSubmittedOrderCount` |
| ✅ 房型显示中文 | 优先 `roomCategoryName`,为 null 时回落显示 `roomCategory` 编码,别显示空白 |
| ✅ 与「整体确认」预检对照 | 两者户范围不同(汇总含已完成的户),页面若要并排展示需注明 |
| ❌ 把 `hotelRequirementCount` 当作「需要订房的户数」 | 它是需求条数,不看 needsHotel 与打回状态 |
| ❌ 前端自己维护房型编码→中文映射 | 会与后端字典漂移,直接用 `roomCategoryName` |
---
## 六、边界行为
- 户需求被打回(退回定制师 / 退回管理员)→ 不计入每日合计,但仍计入 `hotelNeededOrderCount`(它只看需不需要订房)。
- 户被改成不需要订房 → 既不计入合计,也不计入两个新户数。
- 全团无人需要订房 → `dailyRoomBreakdown` 为空数组、两个新户数为 0,且不查字典。
- 房型编码不在字典里 → 该项 `roomCategoryName` 为 null,编码与房间数照常返回(单测 `summary_fillsRoomCategoryNameFromDict` 用 `LOFT` 钉住)。
---
## 六.6、修改前后对比
### 字段级对比
| 字段 | 改前 | 改后 |
|------|------|------|
| `hotelNeededOrderCount` | 无 | 新增,Integer |
| `hotelSubmittedOrderCount` | 无 | 新增,Integer |
| `dailyRoomBreakdown[].rooms[].roomCategoryName` | 无 | 新增,String,可为 null |
| 其余既有字段 | — | 不变(无删除、无改名、无类型变化) |
| 入参 / 路径 / 错误码 | — | 不变 |
### 行为级对比
| 场景 | 改前 | 改后 |
|------|------|------|
| 户已改为不需要订房,但历史需求记录还在 | 计入每日合计(房数虚高) | 不计入 |
| 户需求被打回、正在重改 | 计入每日合计 | 不计入 |
| 需要订房且未被打回的户 | 计入 | 计入(不变) |
| 与预检的关系 | 两边计数口径不同 | 「哪份需求算数」已对齐,仅户范围仍按设计不同 |
## 六.7、影响评估
- **是否破坏向后兼容**: 字段层面兼容(只增不删);**数值层面**每日房间合计可能变小——这正是本次要修的虚高。
- **前端是否必须同步上线**: 否。新字段不接入也不影响现有页面;接入后才能显示户数与房型中文名。
- **回滚**: 回滚 PR #7927 即可,无数据变更(只读接口)。
---
## 七、不影响范围
- **仅影响**: 本接口的每日房间合计口径与三个新增出参。
- **零影响**: 整体确认前的缺失预检、整体确认写操作、房务侧订房与分房、用车需求与座位合计、各户特殊需求标签、数据库结构(只读接口,无表变更)。
---
## 八、测试环境已验证
被测版本:hl-order-service-v3 = dev-v3 `20465f21e`(2026-09-20 09:45 部署,双实例 running)。
```
团期 2099927193172815873(5 户,各 1 间标间 × 2 天)
逐日合计 接口 {1:{STANDARD:5}, 2:{STANDARD:5}}
手算 {1:{STANDARD:5}, 2:{STANDARD:5}}(逐户取其需求明细相加) ✓
hotelNeededOrderCount=5 / hotelSubmittedOrderCount=5,与逐户手算一致 ✓
roomCategoryName:STANDARD → 标间 ✓
团期 2099919607530762241(1 户,已置为不需订房)
dailyRoomBreakdown=[],两个户数均为 0,接口 200 ✓
判权(自签 token 按角色打真实网关)
团期管理员 GROUP_BATCH_MANAGER → 200;定制师 CUSTOMIZER → 200(#7949 已授码)
房务 ROOM_MANAGER / 车务 VEHICLE_MANAGER → 589507;超管 → 200(短路放行) ✓
```
本地:`GroupBatchRequirementSummaryTest` 13/13 通过,含 `summary_countsOnlyHotelNeededAndNonRejected`(六户六形态精确合计)、`summary_fillsRoomCategoryNameFromDict`(字典命中与缺值 null)、`summary_noHotelNeeded_emptyRoomsAndNoDictCall`。
> 说明:本次逐日合计的「手算」取自各户需求明细接口(`GET /v3/admin/order/{orderId}/adjustment/snapshot` 的 `hotelRequirement.days`,与库内存的是同一份 JSON),因取证机器当时连不上测试库。
---
## 十、相关文档
- 关联 Issue: [wx/HL#7925](https://git.1814.love:8443/wx/HL/issues/7925)
- 关联 PR: [wx/HL#7927](https://git.1814.love:8443/wx/HL/pulls/7927)
- 车型编码的同类问题(`vehicleSeatSummary[].vehicleType` 无中文名)见 [wx/HL#7937](https://git.1814.love:8443/wx/HL/issues/7937)
## 关联 / 联系人
### 链接
- **Issue**: [#7925](https://git.1814.love:8443/wx/HL/issues/7925)
- **PR**: [#7927](https://git.1814.love:8443/wx/HL/pulls/7927)
- **Merge commit**: [20465f21e](https://git.1814.love:8443/wx/HL/commit/20465f21e)
### 联系人
- **后端负责人**: @jw
- **前端负责人**: @mmg