docs(changelog): order-v3 团期用房新增子订单订房记录接口(#8046);回填 #8030 实测并订正错误码
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
#8046 新增接口条目(含 TEST 真实响应、AC-1~AC-11 实测读数、地域字段该用 district 而非 city 的实测依据)。 同时补两处 #8030 的遗留: - 「八、测试环境已验证」当时留着「待部署后回填」的占位没填,现按当轮实测回填; - 正文里的降级错误码 589574 订正为 589596(589574 是 #7932 的保留位)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -150,7 +150,7 @@ Authorization: Bearer <token>
|
|||||||
| 589500 | 团期不存在(本次不变) |
|
| 589500 | 团期不存在(本次不变) |
|
||||||
| 401 | 未登录(网关拦截) |
|
| 401 | 未登录(网关拦截) |
|
||||||
|
|
||||||
本次新增内部错误码 589574(资源服务住宿名称补全降级),**只在服务端降级日志里出现,不会传播给前端**。
|
本次新增内部错误码 **589596**(资源服务住宿名称补全降级),**只在服务端降级日志里出现,不会传播给前端**。(初版误取 589574,该号是 #7932 的保留位,已于 2026-09-20 随 #8046 订正为 589596。)
|
||||||
|
|
||||||
#### 业务边界
|
#### 业务边界
|
||||||
|
|
||||||
@@ -222,10 +222,28 @@ Authorization: Bearer <token>
|
|||||||
|
|
||||||
## 八、测试环境已验证
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
TEST 环境(`hl-order-service-v3`,dev-v3,2026-09-20),团期 `jw测试1期`(`2101506167098511362`,出发 2026-09-27,6 晚)。
|
||||||
|
|
||||||
```
|
```
|
||||||
待部署 TEST 后回填实测结果(工单 #8030 AC-1~AC-11)
|
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) 第一个提交)。
|
||||||
|
该码只在服务端降级路径产生,从不传播给前端,对前端无影响。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 十、相关文档
|
## 十、相关文档
|
||||||
|
|||||||
@@ -0,0 +1,414 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8046"
|
||||||
|
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/hotel-households,一次返回该团期下所有需订房子订单的逐晚填报明细——每户一张卡(团号 / 联系人 / 人数 / 定制师 / 需求状态 / 配房需求 / 特殊需求标签 / 打回原因)+ 逐晚(住宿日期 / 酒店 / 城市 + 区县 / 房型 / 间数;页面地点那列请用 district 而非 city)。此前这块数据只能逐户调 GET /v3/admin/order/{orderId}/itinerary,N 户 N 次。两块的数字关系是硬约束:汇总就是把子订单记录汇总出来的,requirement-summary 的逐日间数 == 本接口里 countedInSummary=true 那些户的逐日加总,一间不差;判据与逐晚间数都与汇总走同一份单源(RequirementStatus.isSummaryCounted 与 HotelRequirementDaysNormalizer),由单测守护。但本列表比汇总宽:被打回正在改的户也会列出来并标 countedInSummary=false(否则管理员一打回,那户就从页面消失,没法跟进谁还没改回来),前端应灰显并标注「打回中 · 未计入汇总」。客户自订晚同理——汇总整晚跳过,本接口仍列出该晚并置 customerSelfBooked=true、hotels 为空数组,避免卡片上「第 N 晚凭空消失」。requirement-summary 一字未改。"
|
||||||
|
updated_at: "2026-09-20"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# order-v3: 团期用房新增子订单订房记录接口
|
||||||
|
|
||||||
|
> **服务**: hl-order-service-v3 (端口 8086)
|
||||||
|
> **PR**: [#8055](https://git.1814.love:8443/wx/HL/pulls/8055)(主体)、[#8057](https://git.1814.love:8443/wx/HL/pulls/8057)(补 district)、[#8059](https://git.1814.love:8443/wx/HL/pulls/8059)(打回户改看最新版本行)、[#8060](https://git.1814.love:8443/wx/HL/pulls/8060)(AC-11 固化)
|
||||||
|
> **Issue**: [#8046](https://git.1814.love:8443/wx/HL/issues/8046)
|
||||||
|
> **日期**: 2026-09-20
|
||||||
|
> **影响范围**: 管理后台「团期订单 → 查看需求」页的「用房」板块下半部分(子订单订房记录)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
- **纯新增一个只读端点**,既有接口一个没动(`requirement-summary` 逐字未改)。
|
||||||
|
- **汇总 == Σ子订单**:`requirement-summary` 的逐日间数严格等于本接口里 `countedInSummary=true` 那些户的逐日加总。两处走同一份状态判据与同一个 days 归一器,不是各算各的。
|
||||||
|
- **本列表比汇总宽**:被打回正在改的户也列出来,标 `countedInSummary=false`。前端要灰显并标注「打回中 · 未计入汇总」,否则用户会觉得两块数字对不上。
|
||||||
|
- **客户自订晚保留**:汇总整晚跳过,本接口列出该晚并标记,`hotels` 为空数组。
|
||||||
|
- **地域是反查来的**:days JSON 里不存 city/district,按 hotelId 批量查资源库得到;取不到为 `null`,不影响间数。**页面地点那一列用 `district` 而不是 `city`**(见出参表说明)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、背景
|
||||||
|
|
||||||
|
原型「用房」板块是上下两块:
|
||||||
|
|
||||||
|
| 块 | 内容 | 改前 |
|
||||||
|
|---|---|---|
|
||||||
|
| 上:汇总 | Day N \| 酒店 \| 房型 \| 所需间数 | ✅ `requirement-summary`(#8030 已交付) |
|
||||||
|
| 下:子订单订房记录 | 每户一张卡 + 逐晚(第N晚 / 日期 / 城市·酒店 / 间数 / 房型)+ 配房需求 + 打回 | ❌ **无接口** |
|
||||||
|
|
||||||
|
下块此前只能逐户调 `GET /v3/admin/order/{orderId}/itinerary`,N 户就是 N 次请求。
|
||||||
|
|
||||||
|
最接近的现有端点 `GET /v3/admin/house/group-batches/{groupBatchId}/room-requirements` 三处对不上:结构是 day-major(第N晚 → 各户)而原型要 order-major(每户 → 逐晚);每户每晚只有 `roomCategory` + `roomCount`,**酒店在上游归一的「段视图」那步就被丢掉了**;另缺定制师姓名、真实房型名、需求级配房备注。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 团期子订单订房记录 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement/hotel-households` | 新增 | 一次返回该团期下所有需订房子订单的逐晚填报明细 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. 团期子订单订房记录 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement/hotel-households`
|
||||||
|
|
||||||
|
**VO**: `Result<GroupHotelHouseholdsRespVO>`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期详情「查看需求」页「用房」板块的下半部分:逐户展示定制师填报了什么,团期管理员据此审核或打回。与上半部分的汇总表并列展示,两者各调各的接口。
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | Path | Long | ✅ | 团期 ID | 团期不存在返回 589500 |
|
||||||
|
|
||||||
|
#### 出参
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| groupBatchId | String | 团期 ID(字符串序列化防 JS 精度丢失) |
|
||||||
|
| departDate | String(yyyy-MM-dd) | 团期出发日;未定时为 `null`,此时各晚 `stayDate` 也全为 `null` |
|
||||||
|
| householdCount | Integer | 本列表户数(= 需订房户数,与汇总的 `hotelNeededOrderCount` 同口径) |
|
||||||
|
| countedHouseholdCount | Integer | 其中计入汇总间数的户数;小于 `householdCount` 时,差值就是正被打回的户 |
|
||||||
|
| households[] | List | 子订单订房记录,按 `orderNo` 升序 |
|
||||||
|
| households[].orderId | String | 子订单 ID |
|
||||||
|
| households[].orderNo | String | 子订单团号 |
|
||||||
|
| households[].customerName | String | 主联系人姓名 |
|
||||||
|
| households[].participantCount | Integer | 出行人数(成人+儿童+小童+婴儿,走订单域单源公式) |
|
||||||
|
| households[].consultantId | String | 定制师 ID;未指派为 `null` |
|
||||||
|
| households[].consultantName | String | 定制师姓名;未指派为 `null` |
|
||||||
|
| households[].status | String | 需求状态码(`PENDING_REVIEW` / `PENDING` / `PROCESSING` / `DONE` / `REJECTED_TO_CONSULTANT` / `REJECTED_TO_ADMIN`);该户尚未提交需求时为 `null` |
|
||||||
|
| households[].statusName | String | 状态中文名;状态为空或未知值时为 `null` |
|
||||||
|
| households[].countedInSummary | Boolean | **该户是否计入了汇总间数**。四个计入态为 `true`,两个打回态为 `false`,未提交需求为 `false` |
|
||||||
|
| households[].remark | String | 配房需求自由文本(如「希望安排有窗房间」);未填为 `null` |
|
||||||
|
| households[].specialTags | List\<String\> | 特殊需求标签(`house_special_demand` 字典,如 连通房 / 同层相邻);未填为 `[]` |
|
||||||
|
| households[].returnRemark | String | 打回原因;未被打回过为 `null` |
|
||||||
|
| households[].returnedAt | String(yyyy-MM-dd HH:mm:ss) | 打回时间;未被打回过为 `null` |
|
||||||
|
| households[].days[] | List | 逐晚明细,按 `dayNumber` 升序;未提交需求时为 `[]` |
|
||||||
|
| households[].days[].dayNumber | Integer | 第几晚(从 1 起) |
|
||||||
|
| households[].days[].stayDate | String(yyyy-MM-dd) | 住宿日期 = 出发日 + dayNumber − 1;团期无出发日为 `null` |
|
||||||
|
| households[].days[].customerSelfBooked | Boolean | 该晚是否客户自订。为 `true` 时 `hotels` 为空数组且不计入汇总间数,**但该晚仍会列出** |
|
||||||
|
| households[].days[].hotels[] | List | 该晚的住宿(一般 1 家;「分住」时多家;自订晚为 `[]`) |
|
||||||
|
| households[].days[].hotels[].hotelId | String | 酒店 ID;该段没有候选或未填酒店时为 `null`(间数照常不丢) |
|
||||||
|
| households[].days[].hotels[].hotelName | String | 酒店名:优先取需求里落库的快照,快照缺的按 ID 批量补;资源服务不可用时为 `null` |
|
||||||
|
| households[].days[].hotels[].city | String | 酒店所在**地级市**中文名(如「呼伦贝尔市」),按 hotelId 反查资源库;未维护或资源服务不可用时为 `null` |
|
||||||
|
| households[].days[].hotels[].district | String | 酒店所在**区县**中文名(如「海拉尔区」「额尔古纳市」)。**页面上「海拉尔 · 海堂酒店」这一列请用本字段**,不要用 `city`——TEST 实测全库酒店的 `city` 几乎清一色「呼伦贝尔市」(额尔古纳 14 家、海拉尔 7 家、满洲里 6 家全在同一个 city 下),拿它渲染同一团期每晚都显示同一个地级市,区分不出地点 |
|
||||||
|
| households[].days[].hotels[].totalRoomCount | Integer | 该晚该酒店合计间数 |
|
||||||
|
| households[].days[].hotels[].rooms[] | List | 该晚该酒店的房型行(定制师填的原貌,不做合并) |
|
||||||
|
| households[].days[].hotels[].rooms[].roomTypeId | String | 房型 ID;未填为 `null` |
|
||||||
|
| households[].days[].hotels[].rooms[].roomTypeName | String | 房型名称,定制师填的真实房型;未填为 `null` |
|
||||||
|
| households[].days[].hotels[].rooms[].roomCategory | String | 房型大类编码(`room_category` 字典);缺失时为「未知」 |
|
||||||
|
| households[].days[].hotels[].rooms[].roomCategoryName | String | 房型大类中文名;字典无此编码或不可用时为 `null` |
|
||||||
|
| households[].days[].hotels[].rooms[].roomCount | Integer | 间数 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2101506167098511362/requirement/hotel-households
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"groupBatchId": "2101506167098511362",
|
||||||
|
"departDate": "2026-09-27",
|
||||||
|
"householdCount": 2,
|
||||||
|
"countedHouseholdCount": 2,
|
||||||
|
"households": [
|
||||||
|
{
|
||||||
|
"orderId": "2101506167043985410",
|
||||||
|
"orderNo": "HL20260920105808925",
|
||||||
|
"customerName": "张三",
|
||||||
|
"participantCount": 6,
|
||||||
|
"consultantId": "2083457702519873537",
|
||||||
|
"consultantName": "金卫",
|
||||||
|
"status": "PENDING_REVIEW",
|
||||||
|
"statusName": "待审核",
|
||||||
|
"countedInSummary": true,
|
||||||
|
"remark": "",
|
||||||
|
"specialTags": [],
|
||||||
|
"returnRemark": null,
|
||||||
|
"returnedAt": null,
|
||||||
|
"days": [
|
||||||
|
{
|
||||||
|
"dayNumber": 1,
|
||||||
|
"stayDate": "2026-09-27",
|
||||||
|
"customerSelfBooked": false,
|
||||||
|
"hotels": [
|
||||||
|
{
|
||||||
|
"hotelId": "2023714929877450753",
|
||||||
|
"hotelName": "呼伦贝尔香格里拉大酒店",
|
||||||
|
"city": "呼伦贝尔市",
|
||||||
|
"district": "满洲里市",
|
||||||
|
"totalRoomCount": 2,
|
||||||
|
"rooms": [
|
||||||
|
{
|
||||||
|
"roomTypeId": "3002000000000000013",
|
||||||
|
"roomTypeName": "大床房",
|
||||||
|
"roomCategory": "KING",
|
||||||
|
"roomCategoryName": "豪华大床",
|
||||||
|
"roomCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"roomTypeId": "3002000000000000013",
|
||||||
|
"roomTypeName": "大床房",
|
||||||
|
"roomCategory": "KING",
|
||||||
|
"roomCategoryName": "豪华大床",
|
||||||
|
"roomCount": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dayNumber": 2,
|
||||||
|
"stayDate": "2026-09-28",
|
||||||
|
"customerSelfBooked": false,
|
||||||
|
"hotels": [
|
||||||
|
{
|
||||||
|
"hotelId": "3001000000000000005",
|
||||||
|
"hotelName": "额尔古纳白桦大酒店",
|
||||||
|
"city": "呼伦贝尔市",
|
||||||
|
"district": "额尔古纳市",
|
||||||
|
"totalRoomCount": 2,
|
||||||
|
"rooms": [
|
||||||
|
{
|
||||||
|
"roomTypeId": "3002000000000000005",
|
||||||
|
"roomTypeName": "主题标间",
|
||||||
|
"roomCategory": "STANDARD",
|
||||||
|
"roomCategoryName": "标间",
|
||||||
|
"roomCount": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"roomTypeId": "3002000000000000005",
|
||||||
|
"roomTypeName": "主题标间",
|
||||||
|
"roomCategory": "KING",
|
||||||
|
"roomCategoryName": "豪华大床",
|
||||||
|
"roomCount": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"orderId": "2101507276118626306",
|
||||||
|
"orderNo": "HL20260920110233355",
|
||||||
|
"customerName": "王五",
|
||||||
|
"participantCount": 2,
|
||||||
|
"consultantId": "2083457702519873537",
|
||||||
|
"consultantName": "金卫",
|
||||||
|
"status": "PENDING_REVIEW",
|
||||||
|
"statusName": "待审核",
|
||||||
|
"countedInSummary": true,
|
||||||
|
"remark": null,
|
||||||
|
"specialTags": [],
|
||||||
|
"returnRemark": null,
|
||||||
|
"returnedAt": null,
|
||||||
|
"days": [
|
||||||
|
{
|
||||||
|
"dayNumber": 1,
|
||||||
|
"stayDate": "2026-09-27",
|
||||||
|
"customerSelfBooked": false,
|
||||||
|
"hotels": [
|
||||||
|
{
|
||||||
|
"hotelId": "3001000000000000005",
|
||||||
|
"hotelName": "额尔古纳白桦大酒店",
|
||||||
|
"city": "呼伦贝尔市",
|
||||||
|
"district": "额尔古纳市",
|
||||||
|
"totalRoomCount": 1,
|
||||||
|
"rooms": [
|
||||||
|
{
|
||||||
|
"roomTypeId": "3002000000000000005",
|
||||||
|
"roomTypeName": "主题标间",
|
||||||
|
"roomCategory": "STANDARD",
|
||||||
|
"roomCategoryName": "标间",
|
||||||
|
"roomCount": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 上面是 TEST 环境 2026-09-20 的真实读数(团期 `jw测试1期`),为控制篇幅只保留了每户前 1–2 晚。
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
团期下没有需订房子订单时:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"groupBatchId": "2101506167098511362",
|
||||||
|
"departDate": "2026-09-27",
|
||||||
|
"householdCount": 0,
|
||||||
|
"countedHouseholdCount": 0,
|
||||||
|
"households": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
资源服务不可用时**不报错**:`hotelName` 回落到需求里的快照名(快照也没有才为 `null`)、`city` 与 `district` 为 `null`,间数与酒店 ID 照常返回。字典不可用时 `roomCategoryName` 为 `null`,`roomCategory` 编码与间数照常。某户 days JSON 损坏时该户 `days` 为 `[]`,**其它户不受影响**。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 589507,
|
||||||
|
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| code | 触发条件 |
|
||||||
|
|---|---|
|
||||||
|
| 589507 | 当前角色没有 `group-batch:view` |
|
||||||
|
| 589500 | 团期不存在 |
|
||||||
|
| 401 | 未登录(网关拦截) |
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- **汇总 == Σ子订单(硬约束)**:`requirement-summary` 的 `dailyRoomBreakdown[].rooms[].totalRoomCount` 逐天等于本接口 `countedInSummary=true` 各户该晚各酒店 `rooms[].roomCount` 之和。两处用同一个状态判据(`RequirementStatus.isSummaryCounted`)和同一个 days 归一器,单测有「明细聚合回去必等于汇总视角」的不变量守护。
|
||||||
|
- **列表比汇总宽**:被打回(`REJECTED_TO_CONSULTANT` / `REJECTED_TO_ADMIN`)的户仍出现在 `households[]`,`countedInSummary=false`。这是刻意的——汇总排除打回态是因为作废草稿的间数会让「全团要几间」随一份正在改的需求浮动(#7925);而明细若也排除,管理员一打回那户就从页面消失,没法跟进。
|
||||||
|
- **打回户的 `status` / `returnRemark` / `days` 照常返回**:打回是*原地把需求行改成 `REJECTED_*` 并失活*,该户此时**没有任何 active 行**。本端点对这种户回退到最新一版历史记录取展示信息,所以前端能拿到「被谁打回、什么原因、原先填的是哪几晚」。而 `countedInSummary` 仍只认 active 行,与汇总严格同源。
|
||||||
|
- **打回户与从未提交户可区分**:前者 `status=REJECTED_*` 且 `days` 非空,后者 `status=null` 且 `days` 为 `[]`。
|
||||||
|
- **哪些户进列表**:`needsHotel=true` 的户 ∪ 已提交有效需求的户(#8023 同口径)。标记为需订房但一份需求都没提交的户**也会列出**,`status` 为 `null`、`days` 为 `[]`——这正是管理员要催的人。
|
||||||
|
- **只取首方案**:一段有多个候选酒店时只取 `candidates[0]`,与汇总同口径(候选是房控择一,累加全部会翻倍)。
|
||||||
|
- **分住**:同一晚拆多段住多家时 `hotels[]` 出多条;多段指向同一家酒店会合并成一条。
|
||||||
|
- **排序**:`households[]` 按 `orderNo` 升序(`orderNo` 为空的排最后);`days[]` 按 `dayNumber` 升序。
|
||||||
|
- **上限**:单次最多返回 500 户,超出截断并打 warn。团期正常几十户,超过说明数据异常。
|
||||||
|
- **性能**:房型中文名与酒店名/城市都对整份结果**一次性批量**补齐,不在逐户逐晚的循环里打字典或 Feign。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式
|
||||||
|
|
||||||
|
- 本接口与 `requirement-summary` **并列调用**,不要用其中一个推算另一个:汇总不含逐户信息,本接口不含车辆/特殊需求全景。
|
||||||
|
- 前端渲染两块时,若发现数字对不上,先看 `householdCount` 与 `countedHouseholdCount` 的差值——差值就是被打回的户数,属预期行为而非 bug。
|
||||||
|
- `orderId` / `consultantId` / `groupBatchId` / `hotelId` 均为字符串序列化,**不要**当数字解析。
|
||||||
|
- 打回动作仍走既有端点(`POST /v3/admin/order/{id}/hotel-requirement/reject` 单户、`POST .../group-batch/{id}/requirement/reject` 批量),本次未改。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
| 场景 | 行为 |
|
||||||
|
|---|---|
|
||||||
|
| 户被打回 | 仍在列表里,`countedInSummary=false`,带出 `status=REJECTED_*` / `returnRemark` / `returnedAt`,以及原先填的逐晚明细 |
|
||||||
|
| 户被打回后重新提交 | 回到 `countedInSummary=true`,打回痕迹清空(TEST 实测) |
|
||||||
|
| 户标记需订房但未提交需求 | 仍在列表里,`status` 为 null、`days` 为 `[]`、`countedInSummary=false` |
|
||||||
|
| 户 needsHotel=false 但已提交有效需求 | 在列表里(#8023 口径),`countedInSummary=true` |
|
||||||
|
| 户既没标记也没有效需求 | 不进列表 |
|
||||||
|
| 客户自订晚 | 该晚仍列出,`customerSelfBooked=true`、`hotels` 为 `[]` |
|
||||||
|
| 同一晚分住多家 | `hotels[]` 出多条,间数不丢 |
|
||||||
|
| 同一晚多段同一家 | 合并成一条 |
|
||||||
|
| 段未填酒店 | `hotelId` 为 `null` 的一条,间数不丢 |
|
||||||
|
| 团期无出发日 | 所有 `stayDate` 为 `null`,不推算 |
|
||||||
|
| 资源服务不可用 | `city` / `district` 为 `null`,`hotelName` 回落快照名 |
|
||||||
|
| 字典不可用 | `roomCategoryName` 为 `null`,编码与间数照常 |
|
||||||
|
| 某户 days JSON 损坏 | 该户 `days` 为 `[]`,其它户不受影响 |
|
||||||
|
| 团期下无在团子订单 | `households` 为 `[]`,不发起需求与资源服务调用 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- **是否破坏向后兼容**: 纯新增端点,无任何既有接口改动。
|
||||||
|
- **前端是否必须同步上线**: 否。不接入则「用房」板块保持现状(只有汇总表)。
|
||||||
|
- **回滚**: 回滚本 PR 即可,只读接口、无数据变更、无表结构变更。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、不影响范围
|
||||||
|
|
||||||
|
- `GET .../requirement-summary`(汇总)**一字未改**,既有字段与数值逐字不变。
|
||||||
|
- `room-plans` / `allocations`(房务订房计划与分房结果)未动。
|
||||||
|
- 住宿需求提交契约未动(**没有**往 days JSON 加 city 字段,城市一律由 hotelId 反查)。
|
||||||
|
- 未改 hl-resource-service。
|
||||||
|
- 打回 / 审核 / 确认相关端点未动。
|
||||||
|
- 无数据库表结构变更,无 Flyway 迁移。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
TEST 环境(`hl-order-service-v3`,dev-v3,2026-09-20),团期 `jw测试1期`(`2101506167098511362`,出发 2026-09-27,6 晚,3 个子订单 / 2 户需订房)。
|
||||||
|
|
||||||
|
```
|
||||||
|
AC-1 单户多晚多酒店:张三 6 晚跨 3 家酒店,逐晚 hotels[] 与 rooms[] 正确 ✓
|
||||||
|
AC-2 多户 + orderNo 升序 + 联系人/人数/定制师(金卫) 与订单表一致 ✓
|
||||||
|
AC-3 交叉对账(程序化,非目测):
|
||||||
|
逐日总数 Day1-6 汇总 3/3/3/3/3/3 == Σ子订单 3/3/3/3/3/3 ✓
|
||||||
|
逐日×酒店×大类 三级全等 ✓
|
||||||
|
AC-4 打回王五后四件事同时成立:
|
||||||
|
① 仍在列表里;② status=REJECTED_TO_CONSULTANT / statusName=驳回;
|
||||||
|
③ returnRemark 与 returnedAt 带出,原填 6 晚明细仍可见;
|
||||||
|
④ 汇总 Day1-6 由 3 降为 2,且降后 AC-3 等式仍全等 ✓
|
||||||
|
打回后重新提交 → countedInSummary 回到 true,打回痕迹清空 ✓
|
||||||
|
AC-5 客户自订晚:第5晚仍列出、customerSelfBooked=true、hotels=[],
|
||||||
|
汇总该日由 3 降为 2 ✓
|
||||||
|
AC-6 未填酒店的段:hotelId=null 一条,间数不丢,不变量仍成立 ✓
|
||||||
|
AC-7 酒店名:快照优先;快照缺名的按 ID 批量补出正确名称 ✓
|
||||||
|
AC-8 stayDate = 出发日 + dayNumber − 1,跨月 09-30 → 10-01 正确 ✓
|
||||||
|
AC-9 房型:roomTypeName(大床房/主题标间)与 roomCategoryName(豪华大床/
|
||||||
|
标间)均正确译出,单测显式打桩避免空洞断言 ✓
|
||||||
|
AC-10 判权:低权限角色 1002/user → 589507 被拦;超管 200 ✓
|
||||||
|
AC-11 批量加载:整份结果字典与资源服务各只调一次,去重后仅传 3 个 hotelId ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
地域字段实测:`city` 全部是「呼伦贝尔市」,`district` 分别是「满洲里市 / 额尔古纳市 / 海拉尔区」——**页面地点那一列必须用 `district`**。
|
||||||
|
|
||||||
|
单测:`GroupBatchHotelHouseholdServiceTest` 19/19、`HotelRequirementDaysNormalizerTest` 76/76(含「明细聚合回去必等于汇总视角」不变量)、相关回归 194/194。order-v3 全量 A 组 8334 用例 / B 组 3995 用例,3 个失败均非本次引入并逐个做过基线对照(`FinanceFeignContextMockInventoryTest` 基线同样红、`GroupBatchAuditVersionCasMySqlIT` 并发 flaky 单跑 10/10 绿、`MapperBoundaryArchTest` 已知基线红 #7989)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- 工单 [#8046](https://git.1814.love:8443/wx/HL/issues/8046)
|
||||||
|
- [#8030](https://git.1814.love:8443/wx/HL/issues/8030) 用房汇总(本接口的姊妹接口,同一板块的上半部分)
|
||||||
|
- [#8023](https://git.1814.love:8443/wx/HL/issues/8023) 需订房户判据(本接口沿用同一套 `countsTowardHotel` 口径)
|
||||||
|
- [#7925](https://git.1814.love:8443/wx/HL/issues/7925) 汇总计入状态集合(本次收口为 `RequirementStatus.isSummaryCounted` 单源)
|
||||||
|
- [#7316](https://git.1814.love:8443/wx/HL/issues/7316) `requirement-summary` 判权接 `group-batch:view`(本接口沿用)
|
||||||
|
- [#7324](https://git.1814.love:8443/wx/HL/issues/7324) 「团期看板要看全部版本而不是 active 行」——打回后无 active 行这一条的出处
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
### 链接
|
||||||
|
|
||||||
|
- **Issue**: [#8046](https://git.1814.love:8443/wx/HL/issues/8046)
|
||||||
|
- **PR**: [#8055](https://git.1814.love:8443/wx/HL/pulls/8055)、[#8057](https://git.1814.love:8443/wx/HL/pulls/8057)、[#8059](https://git.1814.love:8443/wx/HL/pulls/8059)、[#8060](https://git.1814.love:8443/wx/HL/pulls/8060)
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- **后端负责人**: @jw
|
||||||
|
- **前端负责人**: @mmg
|
||||||
在新工单中引用
屏蔽一个用户