36 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 | 7326 | 团期分房重算/人工微调:重判后不平的已完成户退回处理中,时间线新增 reopenedOrderIds | admin | wx(GIT) | 修改接口 | deployed | not_required | verified | mmg | 917b1848abed2657ac4aea5a3f88c87b6b36e4d3 | 2026-09-15 | PR #7689(Issue #7326 AC-8)已 squash 合并 dev-v3(合并提交 b57413ab8)。2026-09-15 代码已确认部署在测试服(deploy-status.sh 实测),并对 GET status-logs 做了真实网关取证,证实 extra.reopenedOrderIds 字段真实存在(恒为数组)。backend_status 记 deployed:代码已部署且字段契约已实测。⚠️ 如实标注未覆盖范围:取证样本恰好都是空数组,未采到非空(真的退回)场景——该场景目前只有单测覆盖,非测试服端到端验证,详见正文「八」节。gateway_status=not_required:本次涉及的三个端点均为已有路由(/v3/admin/house/group-batches/**、/v3/admin/order/group-batch/**),未新增/修改任何路径或方法。frontend_status=pending:待前端确认「人工微调/重算分房提交后,之前已标记完成的户可能被静默退回处理中」这件事是否需要在结果弹窗提示,或在待配房列表/订单详情页对这类回退做特殊展示,故不定为 not_required。 前端已闭环(917b1848):BoardDetailModal onAllocSaved 在 hotelReady=false 时补查 status-logs 取最新 H10/H11 记录 extra.reopenedOrderIds,非空则结果弹窗点名退回户数并升 warning,补查失败不阻塞;StatusLogsPanel extraText 补「退回处理中 N 户」展示(空数组/历史缺键兜住);spec 共 +4 例。 | 2026-09-15 | dev-v3 |
团期房务: 分房重算/人工微调把重判不平的已完成户退回处理中
存放目录:
- 一期(v2,无
order-v3标签的工单)→changelogs/{YYYY-MM}/- 二期(v3,
order-v3标签的工单)→changelogs-v2/{YYYY-MM}/服务: hl-order-service-v3 (端口 8086) PR: #7689(Issue #7326 AC-8) Issue: #7326 日期: 2026-09-15 影响范围: 管理后台团期「人工微调分房」「重算分房」两个写口新增的副作用(已完成户可能被退回处理中);团期状态流水(时间线)接口的
extra新增reopenedOrderIds字段
⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写)
- 本次变了什么:H10(人工微调分房,
POST /v3/admin/house/group-batches/{groupBatchId}/allocations)与 H11(重算分房,POST .../allocations/rebuild)在判定「团级配房完成标志判不出就绪、需要置回 false」这一分支时(resetWhenNotReady=true),新增了一个户级动作:把「此前已经是住宿已完成(house_status=CONFIRMED且需求status=DONE),但按最新分房重判后又不平」的户,CAS 退回处理中——需求status从DONE回退到PROCESSING,house_status改写为PENDING_CLAIM,订单flow_status尝试从PENDING_CONFIRMCAS 到RESOURCE_PREPARING,并重新同步一次该订单的房务待办(GroupBatchRoomDayConfirmManager.java:707-744、:833-843;GroupBatchRoomLifecycleManager.java:547-559)。 - 前端/调用方以前以为的是什么:H10/H11 只会影响「团级」的
hotelReady之类聚合标志;某个户一旦被标记为「住宿已完成」,除非房务在这户自己的详情页手动操作,否则它的状态不会被别的团级操作(人工微调另一天、或重算另一晚)连带改动。 - 实际现在是什么:只要本次 H10/H11 调用判不出团级就绪(
resetWhenNotReady=true分支被触发),凡是重判后不平的已完成户都会被一并静默退回处理中;H10/H11 自身的直接响应体(GroupBatchRoomAllocationRebuildRespVO)不携带这批被退回的户名单——调用方必须另外读团期状态流水接口(GET /v3/admin/order/group-batch/{groupBatchId}/status-logs),在eventType=BATCH_ROOM_ALLOC_MANUAL/BATCH_ROOM_ALLOC_REBUILD的记录里读extra.reopenedOrderIds才能知道具体是哪些子订单(GroupBatchRoomAllocationManager.java:250-258、:329-338)。不读时间线,前端只能看到该户重新出现在待配房列表/订单详情的房务进度条回退到「待配房」,却不知道原因。
一、背景(选填)
Issue #7326 AC-8:H10/H11 共用的 GroupBatchRoomDayConfirmManager#settleHouseholdsAndHotelReady 此前只做团级 resetHotelReady(把团级 hotelReady 置回 false),户级的 DONE + house_status=CONFIRMED 原样留着不动(GroupBatchRoomDayConfirmManager.java:728,2026-09-15 对照 origin/dev-v3 核实更新,2026-09-14 起草时的 708-712 因中间插入 #7700/#7459 等提交已漂移约 +20 行;javadoc 原话:「同一个假信号的户级版本」)。这会导致:核单侧依赖 house_status=CONFIRMED/需求 status=DONE 的户级闸门继续放行,待办也已经从房务列表里消失,但这户实际上已经因为重算/人工微调而少了房,两边状态不一致。
本次改动让 resetWhenNotReady=true 时,团级 resetHotelReady 与户级 reopenUnbalancedHouseholds 两个反向动作成对执行(同一个开关,不新增第二个开关;理由见 GroupBatchRoomDayConfirmManager.java:713-716 javadoc:拆成两个参数就是同一条判据两份实现,容易两条链路各改各的、对「这个团安排好了没有」给出矛盾答案)。
H7/H8(另外两个会触发 settleHouseholdsAndHotelReady 的入口,见 GroupBatchRoomDayConfirmManager.java:233、:346)传的固定是 resetWhenNotReady=false——它们只会让事实变好(确认计划行、扣库存),不会触发户级回退,本次改动不影响 H7/H8 这两个入口的行为。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 人工微调分房 | POST | /v3/admin/house/group-batches/{groupBatchId}/allocations |
副作用扩大 | 判不出团级就绪时,重判后不平的已完成户被 CAS 退回处理中;时间线 extra 新增 reopenedOrderIds |
| 2 | 重算分房 | POST | /v3/admin/house/group-batches/{groupBatchId}/allocations/rebuild |
副作用扩大 | 同上 |
| 3 | 团期状态流水(时间线) | GET | /v3/admin/order/group-batch/{groupBatchId}/status-logs |
响应内容新增字段 | eventType=BATCH_ROOM_ALLOC_MANUAL/BATCH_ROOM_ALLOC_REBUILD 两类记录的 extra 新增 reopenedOrderIds(数组,恒不为 null,可为空数组) |
三、接口详情
三个端点的请求参数、错误码均未变化;H10/H11 的直接响应体字段也未变化,只是多了一个不反映在响应体里的副作用;端点 3 的响应体新增一个 extra 子字段。以下逐接口自包含描述。
1. 人工微调分房 POST /v3/admin/house/group-batches/{groupBatchId}/allocations
VO: GroupBatchRoomAllocationSaveReqVO → Result<GroupBatchRoomAllocationRebuildRespVO>
使用场景
房务在「团期分房」页面对某几条已确认计划行提交人工分房覆盖时调用(H10)。本次改动后,若提交这次微调导致判不出团级就绪,且团内存在此前已标记「住宿已完成」但按新分房结果不平的户,这些户会被自动退回处理中——这是本接口触发的副作用,不在直接响应体里体现,前端需要另外查询时间线(见本文件端点 3)或该户的订单详情才能感知。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| items | Body | Array<Object> | ❌ | 与 clearPlanIds 至少一个非空;最多 500 行 | 覆盖后的人工分房行集合(本次未改) |
| items[].planId | Body | String(雪花 ID) | ✅ | 须属本团、未软删且已确认 | 订房计划行 ID |
| items[].orderId | Body | String(雪花 ID) | ✅ | 须为本团在团子订单 | 分给哪一户 |
| items[].roomCount | Body | Integer | ✅ | 1-99 | 占用间数 |
| items[].roomGroupNo | Body | String | ❌ | 形如 F1~F999,不填按 F1 | 家庭分组号 |
| items[].travelerCount | Body | Integer | ❌ | 1-9 | 该房入住人数 |
| items[].bedType | Body | String | ❌ | single/double/twin/family,非法 808131 | 床型 code |
| items[].remark | Body | String | ❌ | ≤256 字 | 备注 |
| clearPlanIds | Body | Array<String>(雪花 ID) | ❌ | 与 items 至少一个非空;最多 200 条 | 这些计划行下的人工分房全部软删(回到纯自动分房) |
(以上入参字段结构本次未改,见 GroupBatchRoomAllocationSaveReqVO.java、GroupBatchRoomAllocationItemReqVO.java)
出参 Result<GroupBatchRoomAllocationRebuildRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
| data.groupBatchId | String(雪花 ID) | 团期主订单 ID |
| data.force | Boolean | 本次是否重置了人工分房(H10 恒 false) |
| data.balanced | Boolean | 本次范围内所有日都对平、且无过时户、无阻塞户 |
| data.hotelReady | Boolean | 后置判定之后团期的配房完成标志;本次起:若有已完成户被退回处理中,通常伴随此值为 false |
| data.days[] | Array<Object> | 本次处理过的入住日结果(leftover/shortage/manualConflicts/outOfRange 等,字段结构本次未改) |
| data.skippedDays[] | Array<Object> | 未处理的入住日(计划行未确认) |
| data.staleOrderIdsBefore[] | Array<String> | 本次开始前判定为分房过时的户 |
| data.replacedAllocIds[] | Array<String> | 本次被软删的分房行 ID |
| data.warnings[] | Array<Object> | 户完成联动与团级判定过程中产生的告警 |
⚠️ 本次改动不在这里新增字段:被退回处理中的户 ID 列表不在 GroupBatchRoomAllocationRebuildRespVO 里,只能从本文件端点 3(时间线)的 extra.reopenedOrderIds 读到。
请求示例
{
"items": [
{ "planId": "880101", "orderId": "70001", "roomCount": 1, "roomGroupNo": "F1" }
],
"clearPlanIds": []
}
响应示例
以下取自 GroupBatchRoomAllocationManagerTest#saveManual_householdsReopened_writesThemIntoTimeline 测试夹具(团期 groupBatchId=9001,提交人工分房时触发户 orderId=70002 被判不平并退回),不是测试服抓包报文;响应体本身的字段结构与本次改动前逐字节相同:
{
"code": 200,
"message": "成功",
"data": {
"groupBatchId": "9001",
"force": false,
"balanced": false,
"hotelReady": false,
"days": [],
"skippedDays": [],
"staleOrderIdsBefore": [],
"replacedAllocIds": [],
"warnings": []
},
"success": true
}
空数据 / 降级响应
items/clearPlanIds 均传空数组时按 100001 拒绝(既有校验,本次未改,见「四、契约约束」);本接口不存在「有效提交但返回空数据」的形态——只要通过校验,days 至少包含受影响的入住日。
错误响应
计划行未确认或不属本团(既有错误码,本次未改):
{
"code": 808601,
"message": "计划行不存在或未确认",
"data": null,
"success": false
}
受影响户存在过时分房时整单拒绝(既有错误码,本次未改):
{
"code": 808643,
"message": "存在分房过时的户,请先重算",
"data": null,
"success": false
}
业务边界
- 鉴权:
@HouseWriteGuarded,非房务角色在幂等/锁窗口之前即被拦截;团期须处于可配房阶段,否则assertConfirmableStage拒绝(既有逻辑,本次未改)。 - 团期级锁:
@Lock4j(house-groupbatch-lock),同团期并发提交串行化,本次未改。 - 新增副作用只在
resetWhenNotReady=true分支触发:本端点内部固定传true(GroupBatchRoomAllocationManager.java:392起的settleAndFinish私有方法),即 H10 每次调用都参与户级回退判定,不是可选行为,前端不能假设「只微调了没关系的一天就不会影响别的已完成户」。 - 只退真的从 DONE 被 CAS 掉的户:若某户虽然「不平」但此前并不是完成态(
status≠DONE),CAS 直接返回 0 行,reopenedOrderIds不计入它,也不会有任何写操作发生在它身上(GroupBatchRoomLifecycleManager.java:601-613reopenHousehold方法体,2026-09-15 核实更新,2026-09-14 起草时的 551-553 已漂移约 +50 行)。 - 全自订户不受影响:全自订户走的是
completeSelfBookedHouseholds独立判定路径,不进入evaluateHouseholds的返回集合,因而结构性地碰不到本次新增的退回逻辑(GroupBatchRoomDayConfirmManager.java:826-829)。
2. 重算分房 POST /v3/admin/house/group-batches/{groupBatchId}/allocations/rebuild
VO: GroupBatchRoomAllocationRebuildReqVO → Result<GroupBatchRoomAllocationRebuildRespVO>
使用场景
团期管理员重新确认需求后,房务在「团期分房」页面点击「重算分房」调用(H11)。可指定 stayDate 只重算某一天,或不传重算整团全部已确认日;force=true 会先把范围内的人工分房整片软删。本次改动后,重算若导致某个已完成户不再平,同样会被自动退回处理中。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| force | Body | Boolean | ❌ | 默认 false | 是否先重置全部人工分房 |
| stayDate | Body | String(日期,yyyy-MM-dd) |
❌ | 不传 = 整团全部已确认日 | 只重算某一入住日 |
| reason | Body | String | force=true 时 ✅ |
≤256 字 | 重置原因,写进团级时间线 |
(以上入参字段结构本次未改,见 GroupBatchRoomAllocationRebuildReqVO.java)
出参 Result<GroupBatchRoomAllocationRebuildRespVO>
字段结构与端点 1 完全相同(两端点共用同一个 GroupBatchRoomAllocationRebuildRespVO),差异只在于 force/days 的取值来源不同;reopenedOrderIds 同样不出现在这里。
| 字段 | 类型 | 说明 |
|---|---|---|
| data.groupBatchId | String(雪花 ID) | 团期主订单 ID |
| data.force | Boolean | 本次是否重置了人工分房(随请求 force 回显) |
| data.balanced | Boolean | 本次范围内所有日都对平、且无过时户、无阻塞户 |
| data.hotelReady | Boolean | 后置判定之后团期的配房完成标志;本次起:若有已完成户被退回处理中,通常伴随此值为 false |
| data.days[] | Array<Object> | 本次处理过的入住日结果(字段结构本次未改) |
| data.skippedDays[] | Array<Object> | 未处理的入住日(计划行未确认) |
| data.staleOrderIdsBefore[] | Array<String> | 本次开始前判定为分房过时的户 |
| data.replacedAllocIds[] | Array<String> | 本次被软删的分房行 ID |
| data.warnings[] | Array<Object> | 户完成联动与团级判定过程中产生的告警 |
请求示例
{
"force": false,
"stayDate": "2026-06-12"
}
响应示例
以下取自 GroupBatchRoomAllocationManagerTest#rebuild_householdsReopened_writesThemIntoTimeline 测试夹具(团期 groupBatchId=9001,重算导致户 orderId=70002 判不平并退回),不是测试服抓包报文:
{
"code": 200,
"message": "成功",
"data": {
"groupBatchId": "9001",
"force": false,
"balanced": false,
"hotelReady": false,
"days": [],
"skippedDays": [],
"staleOrderIdsBefore": [],
"replacedAllocIds": [],
"warnings": []
},
"success": true
}
空数据 / 降级响应
范围内无已确认计划行时拒绝(既有错误码 808644,本次未改);未确认的日进 skippedDays,不算错误。
错误响应
范围内无已确认计划行(既有错误码,本次未改):
{
"code": 808644,
"message": "范围内无已确认计划行,无法重算",
"data": null,
"success": false
}
force=true 但未填 reason(既有校验,本次未改):
{
"code": 100001,
"message": "reason 不能为空",
"data": null,
"success": false
}
业务边界
- 鉴权/锁:同端点 1。
- 新增副作用同样固定触发:本端点内部同样固定传
resetWhenNotReady=true(GroupBatchRoomAllocationManager.java:329起),force=false(默认档,只重跑自动分房、保留人工分房)与force=true(先软删人工分房再重来)两档都会触发户级回退判定,不因force取值而豁免。 stayDate只影响本次重算的范围,不影响户级回退的判定范围:reopenUnbalancedHouseholds对全团重新判定平衡(evaluateHouseholds判的是每户全部基线晚,不按stayDate收窄),因此即使只重算了某一天,如果这户在另一天早就不平,本次也会一并把它退回处理中(GroupBatchRoomDayConfirmManager.java:821-823javadoc)。- 只退真的从 DONE 被 CAS 掉的户:同端点 1。
3. 团期状态流水(时间线) GET /v3/admin/order/group-batch/{groupBatchId}/status-logs
VO: Result<List<GroupBatchStatusLogItemVO>>(无请求体,仅路径参数)
使用场景
管理后台团期详情页查看操作历史/时间线时调用,按 changedAt 升序返回该团期全部流水,不分页。本次起,H10/H11 触发的 BATCH_ROOM_ALLOC_MANUAL/BATCH_ROOM_ALLOC_REBUILD 两类记录的 extra 多一个 reopenedOrderIds 字段,前端可用它在时间线条目上提示「本次操作把 N 户退回了处理中」。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | - | 团期主订单 ID |
出参 Result<List<GroupBatchStatusLogItemVO>>
| 字段 | 类型 | 说明 |
|---|---|---|
| data[].logId | String(雪花 ID) | 流水 ID |
| data[].groupBatchId | String(雪花 ID) | 团期 ID |
| data[].changeType | String | STATUS/DATA;本次涉及的两类事件恒为 DATA |
| data[].eventType | String | 事件类型值,本次涉及 BATCH_ROOM_ALLOC_MANUAL/BATCH_ROOM_ALLOC_REBUILD |
| data[].eventTypeName | String | 事件类型中文标签,如「房务人工微调分房」「房务重算分房」 |
| data[].content | String | 展示文本,如「房务人工微调分房(1 行,涉及 1 天)」 |
| data[].extra | Object/null | 附加快照(已从 JSON 字符串解析为对象);本次起,BATCH_ROOM_ALLOC_MANUAL/BATCH_ROOM_ALLOC_REBUILD 两类记录新增 extra.reopenedOrderIds 字段 |
| data[].extra.reopenedOrderIds | Array<Number> | 新增字段。本次操作真被 CAS 退回处理中的子订单 ID 数组;恒为数组(不会是 null),CAS 未命中任何户时为 [] |
| data[].changedAt | String(时间) | 变更时间 |
(operatorType/operatorId/operatorName/reason 等既有字段本次未改,未逐一列出)
请求示例
GET /v3/admin/order/group-batch/9001/status-logs
Authorization: Bearer <token>
响应示例
以下取自 GroupBatchRoomAllocationManagerTest#rebuild_householdsReopened_writesThemIntoTimeline 测试夹具中对 extra 的断言(该用例只断言 extra map 的内容,未经过完整的 GroupBatchStatusLogItemVO 序列化链路;logId/changedAt 等字段为示意值,不是取自该用例):
{
"code": 200,
"message": "成功",
"data": [
{
"logId": "90501",
"groupBatchId": "9001",
"changeType": "DATA",
"eventType": "BATCH_ROOM_ALLOC_REBUILD",
"eventTypeName": "房务重算分房",
"fromStatus": "RESOURCE_PREPARING",
"fromStatusName": "资源准备中",
"toStatus": "RESOURCE_PREPARING",
"toStatusName": "资源准备中",
"content": "房务重算分房(1 天,force=false)",
"reason": null,
"operatorType": "ADMIN",
"operatorId": "1001",
"operatorName": "小呼",
"extra": {
"force": false,
"reason": null,
"stayDate": "2026-06-12",
"unbalancedDays": ["2026-06-12"],
"manualReset": 0,
"reopenedOrderIds": [70002]
},
"changedAt": "2026-09-14 10:21:33"
}
],
"success": true
}
未触发任何户级回退时,同一 extra 里 reopenedOrderIds 为空数组(取自 rebuild_manualExceedsNewDemand_keepsManualAndReportsConflict 等既有用例默认桩 SettleResult(List.of(), List.of(), false)):
{ "extra": { "reopenedOrderIds": [] } }
空数据 / 降级响应
该团期从未发生过任何生命周期事件(理论场景,团期一旦创建即有 BATCH_GROUP 建团记录)时返回空数组:
{ "code": 200, "message": "成功", "data": [], "success": true }
extra 解析失败或该条记录本身无附加数据时,extra 为 null,不是空对象(既有降级规则,本次未改,见 GroupBatchStatusLogItemVO.java:17-19 javadoc)。
错误响应
groupBatchId 指向不存在的团期(既有错误码,本次未改):
{
"code": 808001,
"message": "团期不存在",
"data": null,
"success": false
}
业务边界
- 鉴权:走网关统一鉴权,未登录 401;本端点不带额外角色限制(与改动前一致)。
reopenedOrderIds只对BATCH_ROOM_ALLOC_MANUAL/BATCH_ROOM_ALLOC_REBUILD两类事件出现:其余事件类型(如BATCH_GROUP、BATCH_HOUSE_CLAIM)的extra结构不受本次改动影响。- 该字段只反映「CAS 真命中」的户:调用方不应据此反推「团内还有多少户不平」——不平但本来就不是完成态的户不会出现在这里(不属于「退回」,因为它们从未处于「已完成」态)。
- 写入是尽力而为、不阻断主链路:时间线写入走
recordHouseTimelineQuietly降级包装,DB 类异常之外的失败只记日志不抛出;但注意它与主事务共用一个事务边界,DB 类异常仍会导致整个 H10/H11 调用连带回滚(GroupBatchService.java:346-360(origin 行号)javadoc 明确写了这条边界)。 - 只读:本端点不提供任何写口。
四、契约约束与正确调用方式(接口类必写)
本节只写后端接受/拒绝 payload 的规则与调用后必须做的动作,不写 UI 渲染建议。
✅ 正确 / ❌ 错误 调用方式对照
| 场景 | 说明 |
|---|---|
✅ 提交 H10/H11 后,若 hotelReady=false,前端另外查一次时间线确认是否有户被退回 |
reopenedOrderIds 只在时间线里,响应体的 hotelReady=false 只说明团级判不出就绪,不说明是不是有已完成户被动了状态 |
✅ 展示某户的房务进度条前,以 GET /admin/house/orders/{orderId} 返回的 progress.houseStatus 为准 |
该户是否被退回处理中,最终权威落在这个字段上,不要用前端本地缓存的「已完成」状态继续渲染 |
| ❌ 认为「H10/H11 返回 200 且响应体没有报错字段」就等于「除了本次显式提交的行,其他户状态不受影响」 | 已完成户被退回是静默副作用,不在响应体里报告,200 不代表没有连带影响 |
❌ 把 extra.reopenedOrderIds 缺失当成「没有户被退回」 |
该字段本次才新增;改动前产生的历史时间线记录(changedAt 早于本次上线)不会有这个字段,前端按字段缺失兜底为「未知」而不是「一定为空」 |
切换状态时的必要动作
被退回处理中的户,其需求会重新出现在房务「待配房」相关列表/抢单池、待办也会重新生成(orderTodoSyncContract.syncForOrder(orderId, SYNC_REASON_REOPEN),实现在 GroupBatchRoomLifecycleManager.java:611,2026-09-15 核实更新;⚠️ 2026-09-14 起草时把这个内部原因常量的字面值错写成了 "REOPEN",源码实际值是 "group room plan revised"——该值只用于待办同步的内部 reason 参数,不出现在任何对外响应字段里,故本节此前的错误不影响契约本身,只更正引用准确性)。前端若维护了「已完成」本地状态缓存或已勾选/已处理的 UI 状态,收到时间线里的 reopenedOrderIds 非空后应主动刷新这些户的展示,不要依赖用户手动刷新页面。
五、数据库行为(涉及写操作时必写)
| 前置条件 | order_hotel_requirement.status 列 |
order_hotel_requirement.house_status 列 |
子订单 order_main.flow_status 列 |
|---|---|---|---|
该户当前 status=DONE,重判后不平,CAS 命中 |
DONE → PROCESSING(CAS,仅当前恰为 DONE 时生效) |
→ PENDING_CLAIM |
仅当前恰为 PENDING_CONFIRM 时 CAS → RESOURCE_PREPARING,否则原样不动(CAS 结果不影响 reopenedOrderIds 是否计入该户) |
该户当前 status≠DONE(已经不是完成态) |
不变(CAS 0 行) | 不变 | 不变 |
该户重判后仍平(balanced=true) |
不变 | 不变 | 不变 |
幂等/并发:reopenHousehold 的第一条语句就是 requirement.status 的 DONE-only CAS,非完成态或已被并发操作改动的户会在这一步返回 0 行,后续 house_status/flow_status/待办同步三个写操作都不会执行(RequirementService.java:3560-3566(origin 行号,未在本轮复核);GroupBatchRoomLifecycleManager.java:601-613,2026-09-15 核实更新)。
六、边界行为
- 未登录 → 401(网关拦截)
- 团期不存在 → 808001(端点 3)/ 已有错误码(端点 1/2 建团期不存在时的行为本次未改)
- 计划行未确认/不属本团 → 808601(既有,未改)
- 存在过时分房 → 808643(既有,未改)
- 范围内无已确认计划行 → 808644(既有,未改)
- 户重判后不平但本来就不是完成态 → 不产生任何写操作,不出现在
reopenedOrderIds里,不是错误 - 全自订户 → 结构性不参与本次新增的判定,不受影响
- 时间线写入失败(非 DB 类异常)→ 降级为只记日志,H10/H11 主链路不受影响;DB 类异常仍会导致整次调用回滚
六.5、枚举 / 数据字典(接口出现枚举时必写)
以下枚举不是本文件端点 1/2/3 直接返回的字段,但是理解「已完成户被退回处理中」这一行为对页面展示影响的必要背景——它们出现在另一个未被本次改动的既有端点 GET /admin/house/orders/{orderId}(房务侧订单详情聚合,§2.0)的 data.itinerary.progress.houseStatus 字段上;本次改动只是让部分订单的这个字段值出现回退,字段本身与端点契约均未变。
houseStatus(com.hulalv.house.statemachine.HouseStateEnum)
所属字段: HouseOrderDetailRespVO.Itinerary.Progress.houseStatus(GET /admin/house/orders/{orderId},非本次变更接口) | 类型: String
| 值 | 中文 | 说明 |
|---|---|---|
PENDING_CLAIM |
待配房 | 本次改动后,被退回处理中的户会落到这个值(HouseStateEnum.java:34) |
CLAIMING |
抢单中 | 本次未涉及 |
PENDING_FINALIZE |
待核实 | 本次未涉及 |
CONFIRMED |
已完成 | 被退回前的原值(HouseStateEnum.java:48) |
EXCEPTION |
异常 | 本次未涉及 |
flow_status(com.hulalv.order.core.enums.OrderFlowStatus,节选)
所属字段: 子订单 order_main.flow_status(内部字段,无对外只读端点直接暴露该原始枚举值;本次列出仅为解释 CAS 的判定条件) | 类型: String
| 值 | 中文 | 说明 |
|---|---|---|
PENDING_CONFIRM |
待确认 | 被退回时 CAS 的判定条件:仅当子订单当前恰为此值,回退才会把它 CAS 成 RESOURCE_PREPARING(OrderFlowStatus.java:28) |
RESOURCE_PREPARING |
资源准备 | CAS 命中后的目标值(OrderFlowStatus.java:27) |
六.6、修改前后对比(修改/删除类接口必写,新增跳过)
字段级对比
| 字段 | 改前 | 改后 |
|---|---|---|
H10/H11 直接响应体(GroupBatchRoomAllocationRebuildRespVO) |
字段结构不变 | 字段结构完全不变——本次不改响应体任何字段 |
status-logs 响应 extra(BATCH_ROOM_ALLOC_MANUAL/BATCH_ROOM_ALLOC_REBUILD) |
无 reopenedOrderIds 字段 |
新增 reopenedOrderIds: Array<Number>,恒为数组(不为 null) |
已完成户的 house_status(经 GET /admin/house/orders/{orderId}) |
一旦 CONFIRMED,不会因为别的户的分房调整而改变 |
团级 H10/H11 判不出团级就绪时,重判不平的已完成户可能被静默改回 PENDING_CLAIM |
行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
H10/H11 判不出团级就绪时(resetWhenNotReady=true 分支) |
只置回团级 hotelReady;户级 DONE/CONFIRMED 原样保留 |
团级置回 + 重判不平的已完成户 CAS 退回处理中(需求 status/house_status/子订单 flow_status/待办同步四件事) |
| 房务待配房列表/抢单池 | 已完成户不会出现 | 被退回处理中的户会重新出现(orderTodoSyncContract.syncForOrder 触发待办重同步) |
| 时间线可追溯性 | H10/H11 的时间线记录只报告受影响日/受影响计划行,不报告户级状态回退 | 新增 extra.reopenedOrderIds,可精确定位是哪些子订单被退回 |
H7/H8(resetWhenNotReady=false 的两个入口) |
不触发户级回退 | 仍不触发,本次改动对它们零影响 |
六.7、影响评估(修改/删除类必写)
- 是否破坏向后兼容:否。三个端点的路径、方法、请求参数、错误码均未变化;H10/H11 响应体字段结构未变;端点 3 只新增一个此前不存在的可选字段(
extra.reopenedOrderIds),旧前端忽略未知字段不受影响。 - 前端是否必须同步上线:不同步上线不会导致接口调用失败,但存在一个真实的展示风险——已完成户可能被静默退回,若前端在人工微调/重算分房的结果弹窗、以及待配房相关列表页对此毫无提示,运营/房务可能不知道「为什么这户又出现在待办里了」。建议前端读取端点 3 的
extra.reopenedOrderIds并在结果里提示。 - 前端 workaround 清理点:无(此前没有相关字段可供 workaround)。
七、不影响范围(显式声明, 帮前端/QA 缩小排查面)
- 仅影响:H10(人工微调)、H11(重算分房)判不出团级就绪分支下的已完成户状态;团期状态流水接口
BATCH_ROOM_ALLOC_MANUAL/BATCH_ROOM_ALLOC_REBUILD两类记录的extra。 - 零影响:
- H9 分房总览(只读,
GET /v3/admin/house/group-batches/{groupBatchId}/allocations)——零副作用,未调用settleHouseholdsAndHotelReady - H7/H8(
resetWhenNotReady=false的两个既有入口) - 全自订户的完成态判定
- 团级
hotelReady/resetHotelReady本身的既有语义与既有判定逻辑 - 月度对账、核单结算等其他房务只读端点(另见
changelogs-v2/2026-09/14_7327_月度对账并入团期计划与分房行-修改接口-管理后台.md) - 团期车务(fleet)域
- H9 分房总览(只读,
八、测试环境已验证
部署与实测状态:修复本身(PR #7689,合并提交 b57413ab8)已合入 dev-v3 并是测试服当前部署基线(hl-order-service-v3 2026-09-15 13:12 部署于 6a7b43a17,经 deploy-status.sh 实测确认)的祖先,代码已在测试服跑着。2026-09-15 对 GET /v3/admin/order/group-batch/{groupBatchId}/status-logs 做了真实网关取证(账号 1001,只读 GET),证实了 extra.reopenedOrderIds 字段真实存在于线上响应,但取证用的团期(groupBatchId=2099692512066105346,另一会话当天为工单 #7327 AC-24/25 造的真实夹具)当时只有单户、且该户处于单户团自动回填场景,本轮 7 条 BATCH_ROOM_ALLOC_MANUAL 记录的 extra.reopenedOrderIds 均为空数组——证实了字段契约(恒为数组、不为 null),但没有证实非空场景(即真的有已完成户被退回处理中的那条主线)。这不是负面证据,单测已覆盖非空场景,backend_status 仍记 deployed(代码已部署、字段契约已实测),但「真的有户被退回处理中」这一核心场景请前端联调时按「单测覆盖、未端到端验证」对待,待专项造数(多户团、其中一户先置 DONE 再改分房让它不平)后补测。以下先列本机自动化单测断言,不是测试服抓包:
GroupBatchRoomDayConfirmManagerTest(origin/dev-v3,新增 4 例):
settleHouseholdsAndHotelReady_unbalancedDoneHousehold_reopenedWithItsOwnBasis
→ 重判不平的已完成户被退回,reopenedOrderIds=[ORDER_ID],doneOrderIds 不含该户 ✓
settleHouseholdsAndHotelReady_unbalancedNotDoneHousehold_notListedAsReopened
→ 不平但原非完成态的户,CAS 落空,不进回退名单(verify(reopenHousehold) 正向对照) ✓
settleHouseholdsAndHotelReady_mixedBalance_reopensOnlyTheUnbalancedHousehold
→ 同一次调用,已平户走完成、不平户走回退,两条路互斥 ✓
settleHouseholdsAndHotelReady_unbalancedDoneHouseholdButResetNotRequested_neverReopens
→ resetWhenNotReady=false(H7/H8 语义)时,同样的不平事实不触发任何回退 ✓
GroupBatchRoomAllocationManagerTest(origin/dev-v3,新增 2 例):
rebuild_householdsReopened_writesThemIntoTimeline
→ H11 时间线 extra.reopenedOrderIds=[OTHER_ORDER_ID=70002] ✓
saveManual_householdsReopened_writesThemIntoTimeline
→ H10 时间线同样落 extra.reopenedOrderIds=[OTHER_ORDER_ID=70002] ✓
全量(PR 描述自报):Tests run: 81, Failures: 0, Errors: 0(GroupBatchRoomAllocationManagerTest=49 + GroupBatchRoomDayConfirmManagerTest=32)
2026-09-15 测试服真实取证(账号 1001,只读 GET,证实字段契约,未证实非空场景):
GET /v3/admin/order/group-batch/2099692512066105346/status-logs → 200
→ 共 16 条流水,其中 7 条 eventType=BATCH_ROOM_ALLOC_MANUAL
→ 逐条 extra 均含 reopenedOrderIds 字段,7 条全部为空数组 []
→ 例:{"stayDates": ["2027-01-25"], "planIds": ["2099692682136743937"], "itemCount": 2,
"clearedPlanIds": [], "reopenedOrderIds": []}
→ 该团期是当天另一会话为工单 #7327 AC-24/25 造的真实夹具(单户团),字段存在但本次未观察到
非空样本;非空场景(真的有已完成户被退回)目前仍只有单测覆盖
网关:本次无新增/修改路径,三个端点沿用既有路由。测试服部署已确认(见上),extra.reopenedOrderIds 字段的存在性已实测,但「非空、真的退回了户」这一核心场景仍待专项造数验证,建议按此专项补测。
十、相关文档
- 关联 Issue: wx/HL#7326
- 关联 PR: wx/HL#7689
- 同一 Issue 下与本次相关的既有背景:H10/H11 的完整契约、错误码见工单 #7326 正文
- 团期状态流水端点的通用契约背景:
changelogs-v2/2026-09/14_7327_月度对账并入团期计划与分房行-修改接口-管理后台.md(引用了同一个GroupBatchStatusLogItemVO) - 同一 Issue 下 #7325 的另一改动(confirm-check 预检行粒度变化 + 旧 finalize 拒绝越界户,PR #7700,已合并):
changelogs-v2/2026-09/15_7325_越界户预检列出末晚与旧finalize拒绝越界户-修改接口-管理后台.md
关联 / 联系人
链接
联系人
- 后端负责人: @wx