20 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 8023 | 需求汇总与确认预检不再只认 needs_hotel 标记位 + 提交住宿需求时就地纠正该标记 | admin | jw(GIT) | 修改接口 | deployed | verified | verified | mmg | 9a14ad866b3ef6ac9acb0ca9ab9e9f0fe98f6559 | 2026-09-20 | 后端已合并 dev-v3(df66ec357)并部署 TEST,网关实测 AC-1~AC-8 全通过。一处口径放宽 + 一个纯新增出参 + 一处写侧副作用。口径:全团需求汇总 GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary 的 dailyRoomBreakdown 与整体确认预检 GET .../requirement/confirm-check,此前都只认订单上的 needs_hotel 标记位,导致「标记说不需要住宿、定制师却已提交完整需求」的户被整户静默丢弃——间数不进汇总(页面用房表空白)、确认时也不放行给房务(需求填了没人配房);现在计入条件改为「标记为真 或 已提交有效需求」,打回态仍不计。新增出参 hotelFlagMismatchOrderCount 把这种不一致显式暴露。写侧:PUT /v3/admin/order/{id}/hotel-requirement 提交成功后,若该单 needs_hotel 不为真则就地置 1(单向 0→1,同事务,已为真时不写)——该标记原先只在创单时按产品有没有配酒店派生一次、之后全仓无写通道,存量错单改不回来。既有字段一个没删没改,入参与路径不变。 前端实证维持 not_required(mmg 2026-09-20):requirement-summary/dailyRoomBreakdown/hotelNeededOrderCount/hotelFlagMismatchOrderCount 全仓零命中(汇总端点未接入,同 #7925/#7937 结论);confirm-check 已消费(orderV2GroupBatch.js)但出参结构不变,ready/missing 直渲自动受益;hotel-requirement 标记纠正为服务端同事务副作用、响应不变,零感知。 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 | dev-v3 |
order-v3: 需求汇总/预检不再只认 needs_hotel,提交需求时就地纠正该标记
服务: hl-order-service-v3 (端口 8086) PR: #8028 Issue: #8023 日期: 2026-09-20 影响范围: 管理后台「团期订单 → 查看需求」页的用房汇总、整体确认预检;定制师提交住宿需求的副作用
⚠️ 关键变化
- 不再只认标记位:汇总与预检的计入条件从「
needs_hotel=1」放宽为「标记为真 或 已提交有效需求」。需求是事实,标记只是创单时的推测,两者冲突时以事实为准。 - 新增出参
hotelFlagMismatchOrderCount:「标记为假却已提交有效需求」的户数,把这种不一致显式暴露出来,不再静默吞掉。 hotelNeededOrderCount口径同步放宽:改为「标记为真的户 ∪ 已提交有效需求的户」,保证hotelSubmittedOrderCount ≤ hotelNeededOrderCount恒成立。- 提交住宿需求会就地纠正标记:
needs_hotel不为真时置 1,单向、同事务、已为真时不写。 - 打回态仍不计:#7925 立的那条判定原样保留,不受本次放宽影响。
一、背景
needs_hotel 只在创建订单那一刻由「产品有没有配酒店 / 有没有默认房数」派生一次,之后全仓没有任何写通道(OrderCreateTransactionExecutor.deriveNeedsHotel)。产品当时没配酒店、后来补配了,或定制师按客户实际情况提了住宿需求,这个标记都改不回来。
而汇总与预检都按它筛户,于是出现了自相矛盾的一屏:子订单列表显示该户「待审核」,需求汇总却说这个团「0 户需要住宿」,用房表全空。
TEST 实证(团期 jw测试1期):三户 needs_hotel=0,其中一户已提交 6 晚 12 间的 PENDING_REVIEW 需求,改前 dailyRoomBreakdown 为空数组。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 全团需求汇总 | GET | /v3/admin/order/group-batch/{groupBatchId}/requirement-summary |
修改 | 计入户口径放宽为「标记为真或已提交有效需求」;新增 1 个出参 |
| 2 | 整体确认预检 | GET | /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check |
修改 | 取户口径同步放宽,字段结构不变 |
| 3 | 提交住宿需求 | PUT | /v3/admin/order/{id}/hotel-requirement |
修改 | 新增副作用:提交成功后就地纠正 needs_hotel |
三、接口详情
1. 全团需求汇总 GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary
VO: Result<GroupRequirementSummaryRespVO>
使用场景
团期详情「查看需求」页顶部的「用房 · 汇总」表:全团逐日要订几间、什么房型,运营据此向酒店报数。
入参
本次入参不变。
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | 团期 ID | 团期不存在返回 589500 |
出参
| 字段 | 类型 | 说明 |
|---|---|---|
| activeOrderCount | Integer | 在团户数(不变) |
| hotelRequirementCount | Integer | 有效房需求条数(不变;不看标记位也不看打回状态) |
| hotelNeededOrderCount | Integer | 口径放宽:标记为真的户 ∪ 已提交有效需求的户 |
| hotelSubmittedOrderCount | Integer | 口径放宽:不再要求标记为真,只看需求是否有效 |
| hotelFlagMismatchOrderCount | Integer | 新增。标记不为真却已提交有效需求的户数,恒 ≥0 |
| vehicleRequirementCount | Integer | 有效用车需求条数(不变) |
| dailyRoomBreakdown | List | 逐日房间明细(计入户集合本次放宽) |
| dailyRoomBreakdown[].dayNumber | Integer | 第几天(不变) |
| dailyRoomBreakdown[].rooms[].roomCategory | String | 房型编码(不变) |
| dailyRoomBreakdown[].rooms[].roomCategoryName | String | 房型中文名(不变,#7925 引入) |
| dailyRoomBreakdown[].rooms[].totalRoomCount | Integer | 该天该房型合计(不变,数值因放宽可能变大) |
| vehicleSeatSummary | List | 车型座位合计(不变) |
| orderSpecialTags | List | 各户特殊需求标签(不变) |
请求示例
GET /v3/admin/order/group-batch/2101506167098511362/requirement-summary
Authorization: Bearer <token>
响应示例
{
"code": 200,
"message": "成功",
"data": {
"activeOrderCount": 3,
"hotelRequirementCount": 1,
"hotelNeededOrderCount": 1,
"hotelSubmittedOrderCount": 1,
"hotelFlagMismatchOrderCount": 1,
"vehicleRequirementCount": 0,
"dailyRoomBreakdown": [
{"dayNumber": 1, "rooms": [{"roomCategory": "KING", "roomCategoryName": "豪华大床", "totalRoomCount": 2}]}
],
"vehicleSeatSummary": [],
"orderSpecialTags": []
},
"traceId": null,
"success": true
}
空数据 / 降级响应
全团无有效需求且无标记为真的户时,三个户数均为 0、dailyRoomBreakdown 为 [](不是 null),hotelFlagMismatchOrderCount 为 0,且不调房型字典。房型字典不可用时只有 roomCategoryName 为 null,编码与房间数照常返回,接口不报错。
错误响应
{ "code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "data": null, "success": false }
| code | 触发条件 |
|---|---|
| 589507 | 当前角色没有 group-batch:view(本次不变) |
| 589500 | 团期不存在(本次不变) |
| 401 | 未登录(网关拦截) |
本次无新增错误码。
业务边界
- 计入的户:标记为真 或 已提交有效需求(有效 =
PENDING_REVIEW/PENDING/PROCESSING/DONE)。 - 打回态仍不计:退回定制师、退回管理员两种状态不是「有效需求」,不能靠它把户拉进计入集合。
- 客户自订晚整晚跳过,不进采购分母(不变)。
- 户范围仍是「在团」口径(含已完成的户),与预检刻意不同(#7316)。
hotelFlagMismatchOrderCount仅用于提示与订正,不要用它去扣减间数——那批户的间数已经正常计入。
2. 整体确认预检 GET /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check
VO: Result<GroupBatchRequirementCheckRespVO>
使用场景
团期管理员点「确认需求」之前的缺失预检;确认时用同一份判定决定放行哪些户的需求给房务。
入参
本次入参不变。
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | 团期 ID | 团期不存在返回 589500 |
出参
字段结构不变(ready / missing[] / checkedResourceTypes 等一个没动)。变的是取户口径。
| 字段 | 类型 | 说明 |
|---|---|---|
| ready | Boolean | 是否可确认(不变) |
| missing | List | 缺失清单(结构不变;新增「标记为假但已提交」的户参与判定后,其缺项会如实出现在这里) |
| checkedResourceTypes | List | 本次检查的资源类型(不变) |
请求示例
GET /v3/admin/order/group-batch/2101506167098511362/requirement/confirm-check
Authorization: Bearer <token>
响应示例
{
"code": 200,
"message": "成功",
"data": {
"groupBatchId": "2101506167098511362",
"batchStatus": "RECRUITING",
"batchStatusName": "招募中",
"ready": false,
"missing": [],
"checkedResourceTypes": ["HOTEL", "VEHICLE"]
},
"traceId": null,
"success": true
}
空数据 / 降级响应
无需订房户时 missing 为 [](不是 null),ready 仍按车侧结果给出,行为不变。团内一户都没有时同样返回空清单而非报错。
错误响应
{ "code": 589500, "message": "团期不存在", "data": null, "success": false }
| code | 触发条件 |
|---|---|
| 589500 | 团期不存在(本次不变) |
| 589507 | 无团期权限(本次不变) |
本次无新增错误码。
业务边界
- 为什么必须跟着汇总一起放宽:只放宽汇总会变成「间数算进汇总了、确认时却不放行给房务」——需求填了、汇总也算了、房务收不到,比改前更难排查。
- 放宽后这些户也参与「房型行填齐」「晚数与行程一致」等校验,填不全会如实报缺,这是应有的行为。
- 打回态的户仍不参与,避免逼管理员对一份正在返工的需求反复确认。
3. 提交住宿需求 PUT /v3/admin/order/{id}/hotel-requirement
VO: Result<HotelRequirementRespVO>
使用场景
定制师逐晚填报住宿需求并提交。
入参
本次入参不变。
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| id | Path | Long | ✅ | 订单 ID | 订单不存在返回 582001 |
| days | Body | List | ✅ | 逐晚需求 | 结构不变 |
出参
本次出参不变,列出以便自包含。
| 字段 | 类型 | 说明 |
|---|---|---|
| requirementId | Long(String) | 本次写入的需求 ID(不变) |
| version | Integer | 需求版本号(不变) |
| status | String | 需求状态,团单为 PENDING_REVIEW、核心单为 PENDING(不变) |
| isActive | Boolean | 是否当前生效版本(不变) |
| branchTaken | String | 版本分支 INIT_SUBMIT / PENDING_EDIT / DONE_ADJUST(不变) |
| previousVersion | Integer | 仅 DONE_ADJUST 分支返回上一版本号(不变) |
请求示例
PUT /v3/admin/order/2101506167043985410/hotel-requirement
Authorization: Bearer <token>
Content-Type: application/json
{"days": [{"dayNumber": 1, "segments": [{"roomCount": 2, "candidates": [{"hotelId": "2023714929877450753", "rooms": [{"roomTypeId": "3002000000000000013", "roomCategory": "KING", "roomCount": 2}]}]}]}]}
响应示例
{
"code": 200,
"message": "成功",
"data": {"requirementId": "2101558489333784578", "version": 1, "status": "PENDING_REVIEW", "isActive": true, "branchTaken": "INIT_SUBMIT"},
"traceId": null,
"success": true
}
空数据 / 降级响应
不适用:本端点要么写入成功返回完整 HotelRequirementRespVO,要么抛业务异常,不存在空响应。响应体本次一个字段都没改,标记纠正是服务端副作用,不体现在返回值里——调用方若要确认标记已纠正,读汇总接口的 hotelFlagMismatchOrderCount。
错误响应
{ "code": 582017, "message": "订单状态不允许提交需求", "data": null, "success": false }
| code | 触发条件 |
|---|---|
| 582017 | 订单非定制中(本次不变) |
| 582099 | 团单房型行未填齐(本次不变) |
| 582016 | 房间数必须大于 0(本次不变) |
本次无新增错误码。
业务边界
- 新增副作用:提交成功后若该单
needs_hotel不为真,则置 1,与需求写入同一事务——分开提交会留下「需求写进去了、标记没纠正」的中间态,而那正是本单要修的不一致。 - 单向 0→1:不提供反向置 0。反向意味着「这户不要住宿了」,那是退需求的业务动作,有自己的链路与副作用,不由提交需求顺带做掉。
- 已为真时不写:避免每次提交都产生一行无意义的行变更。
- 提交被守卫拒绝时零写入:标记也不动。
- 为 null 的历史行与为假同等对待,同样置位。
四、契约约束与正确调用方式
- 前端不需要改代码即可受益:既有字段一个没变,部署后用房汇总直接出数。
- 若要展示不一致提示,读
hotelFlagMismatchOrderCount:大于 0 说明存在创单时被判定不需要住宿、事后却提了需求的存量户,其间数已照常计入,该字段只用于提示与订正,不要用它去扣减间数。
五、数据库行为
只写外部可观察行为:
| 动作 | 外部可观察结果 |
|---|---|
| 提交住宿需求且该单此前「不需要住宿」 | 该单从此被视为需要住宿:其间数进入全团用房汇总、该户参与确认预检、确认时其需求被放行给房务 |
| 提交住宿需求且该单本就「需要住宿」 | 无额外变化(不产生多余的行变更) |
| 提交被守卫拒绝(如订单非定制中) | 零变化:需求没写入,「是否需要住宿」也不变 |
| 读接口(汇总 / 预检) | 只读,无任何写入 |
- 纠正与需求写入在同一事务:需求回滚则纠正一并回滚,不会出现「需求没提上去、标记却变了」。
- 单向:只会从「不需要住宿」变成「需要住宿」,反向不会发生。
- 无表结构变更、无 Flyway 脚本。
六、边界行为
| 场景 | 行为 |
|---|---|
| 标记为真 + 已提交 | 计入,hotelFlagMismatchOrderCount 不 +1 |
| 标记为假 + 已提交(有效态) | 计入,hotelFlagMismatchOrderCount +1 |
| 标记为假 + 需求被打回 | 不计入,不报不一致 |
| 标记为假 + 无任何需求 | 不计入,不报不一致(真的不需要住宿) |
| 标记为 null(历史行) | 与为假同等对待;提交需求时同样置位 |
六.6、修改前后对比
字段级对比
| 字段 | 改前 | 改后 |
|---|---|---|
hotelFlagMismatchOrderCount |
无 | 新增,Integer |
hotelNeededOrderCount |
仅 needsHotel=true 的户数 |
标记为真的户 ∪ 已提交有效需求的户 |
hotelSubmittedOrderCount |
需标记为真且已提交 | 只看需求是否有效 |
| 其余既有字段 | — | 不变(无删除、无改名、无类型变化) |
| 入参 / 路径 / 错误码 | — | 不变 |
行为级对比
| 场景 | 改前 | 改后 |
|---|---|---|
| 标记为假、已提交完整需求的户 | 整户丢弃:间数不进汇总、确认时不放行房务 | 间数计入、参与预检、确认时放行 |
| 汇总与子订单列表是否自洽 | 列表显示「待审核」、汇总说「0 户需住宿」 | 两处一致 |
提交住宿需求后的 needs_hotel |
保持创单时的值,永远改不回来 | 不为真则就地置 1 |
| 打回态需求 | 不计入 | 不计入(不变) |
六.7、影响评估
- 是否破坏向后兼容: 字段层面兼容(只增不删);数值层面每日房间合计可能变大——这正是本次要修的漏算。
- 前端是否必须同步上线: 否。
- 回滚: 回滚本 PR 即可。注意写侧已置位的
needs_hotel不会随回滚还原,但那些户本就应为 1,保留更正确。
七、不影响范围
- 仅影响: 上述三个端点的取户口径、一个新增出参、提交需求的标记纠正副作用。
- 零影响: 用车需求与座位合计、各户特殊需求标签、房务侧订房与分房逻辑、数据库结构(无表变更、无 Flyway)。
八、测试环境已验证
被测版本:hl-order-service-v3 = dev-v3 df66ec357(2026-09-20 15:2x 部署)。构建身份探针:部署后接口开始返回新字段 hotelFlagMismatchOrderCount。
验收团期 jw测试1期(2101506167098511362),在团 3 户。
AC-1 标记为假但已提交 → 计入
张三 HL20260920105808925(6 晚 12 间 PENDING_REVIEW)needs_hotel 改回 0
needed=1 submitted=1 mismatch=1
D1 豪华大床×2 | D2 标间×1+豪华大床×1 | D3 标间×2 | D4 豪华大床×2 | D5 豪华大床×2 | D6 豪华房×1+标间×1
改前同一数据 dailyRoomBreakdown = [] ✓
AC-2 标记为假且无需求(王五/李玉)→ 不计入、不报不一致 ✓
AC-3 标记为真的既有场景:六天逐日间数与标记为 0 时逐字相同,mismatch=0 ✓
AC-4 mismatch:标记1+已提交→0;标记0+已提交→1;无不一致时为 0 不是 null ✓
AC-5 写侧自动置位(真调提交接口,非 mock)
PUT /v3/admin/order/2101507276118626306/hotel-requirement(6 晚)→ 200
提交前 needs_hotel=0 → 提交后 needs_hotel=1
汇总立即计入该户,逐日每晚 +1:needed=2 submitted=2 mismatch=1 ✓
AC-6 存量排查(只出数):TEST 上不一致户 21、涉及团期 10
按状态 PENDING_REVIEW 16 / PENDING 5;改前这 21 户间数一条都没进过汇总 ✓
生产侧按既有口径不查(无任意 SQL 通道),不作阻塞
AC-7 判权:ROOM_MANAGER / VEHICLE_MANAGER → 589507;
GROUP_BATCH_MANAGER / SUPER_ADMIN → 200(超管短路放行,故用低权限角色验) ✓
未做:写侧 WARN 留痕日志未在 TEST 取证(order-v3 日志窗口只有 200 行、SQL 刷屏会冲掉证据),该分支由 4 条单测覆盖;未跑 order-v3 全量单测,四域定向回归 4717 用例全绿。