文件
hl-api-changelog/changelogs-v2/2026-09/11_7325_团期房务按日订房确认H7-H8-预检-新增接口-管理后台.md
T
API Changelog Bot和Claude Fable 5.1 65d786ea87 docs(changelog): #7325 团期房务按日订房确认 H7/H8/confirm-check(已部署实测 dfd5a8f6f)
3 个新端点、8 个新错误码、808616 删除、808602 日期上限改为 endDate-1。
已部署测试服并经网关实测,Flyway V20260910_302 success=1。

起草时「GET /confirm-check 任何后台角色都能调」写错,已更正:
读端点虽不标 @HouseWriteGuarded,但在编排层入口直接调
HouseReadGuard.assertHouseReadPermission(),非房务角色同样 808090。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 10:37:49 +08:00

31 KiB
原始文件 Blame 文件历史

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 7325 团期房务按日订房确认:3 个新端点(H7·H8·confirm-check);8 个新错误码;808616 删除;808602 日期上限改为 endDate-1 admin wx(GIT) 新增接口 deployed verified pending mmg 2026-09-11 2026-09-11 已部署测试服并经网关实测(squash dfd5a8f6f,PR #7497)。部署面只有 hl-order-service-v3 一个服务(两条独立路径核过:53 个改动文件全在该模块;对 hl-common / hl-gateway / 其它服务的命中数 = 0),未改 hl-common-*,无消费方需连带滚。Flyway V20260910_302 在测试库执行成功(flyway_schema_history 版本 20260910.302 success=1)。⚠️ 本篇起草时写错过一条并已更正:原写「GET /confirm-check 任何后台角色都能调」——不成立。读端点虽然不标 @HouseWriteGuarded、不走拦截器,但在编排层入口直接调 HouseReadGuard.assertHouseReadPermission()(GroupBatchRoomDayConfirmManager.java:489),非房务角色同样 808090。错因是只枚举了「注解+拦截器」一条机制就下了否定结论;2026-09-11 测试服实测(test_admin/CUSTOMIZER 打 GET 得 808090)与源码调用点双向坐实后已在「关键变化 1」更正。网关零改动:hl-gateway 自 2026-09-07 21:53 起未重启(jar mtime 与进程 lstart 两条路径核过),三个新端点均返回业务层响应而非路由未命中,证明既有 /v3/admin/** 通配已覆盖。⚠️ 尚未验证:H7/H8 真实确认业务逻辑未跑通——取证用的团期处于未认领态,POST 先撞 808612,需先造「整团抢单→提交需求→确认基线」数据链才能触达,本轮未做,如实标为未验证而不写「正常」。 2026-09-11 dev-v3

团期房务按日订房确认(H7·H8 + 只读预检)

服务: hl-order-service-v3 PR: #7497 Issue: #7325 日期: 2026-09-11 影响范围: 管理后台房务团期页面新增「按日确认」与「整团确认」功能,及确认前的零副作用预检


⚠️ 关键变化

#7325 是 #7324 房务团期看板(H1-H6)的后续交付,新增三个确认端点。三处易漏内容:

1. 三个端点都有角色门,GET /confirm-check 也不例外

两条门是两套不同机制,别只看注解:

  • 两个 POST:注解 @HouseWriteGuarded + 拦截器,在 @Idempotent 与 @Lock4j 之前执行,非房务角色根本进不了幂等窗口;
  • GET /confirm-check:不走拦截器,但在编排层入口直接调 HouseReadGuard.assertHouseReadPermission()(GroupBatchRoomDayConfirmManager.java:489)——一样会拒。
角色 两个 POST 写口 GET /confirm-check
ROOM_MANAGER(房务管理员) 放行 放行
SUPER_ADMIN(超管) 放行 放行
house_keeper_lead(房务组长) 808091(只读监督,不可写) 放行
其它后台角色(定制师 / 运营 / 客服 / 财务 / 素材管理员) 808090 808090

🔴 前端两个要点:

  1. 房务组长看得到预检、点不了确认。 按「能看见就能点」渲染,组长点确认会拿到 808091;该文案可直接展示。
  2. 非房务角色连预检都调不到,返回的是 808090(业务码,HTTP 仍 200),不是空数据。别把 808090 渲染成「暂无差额」——那会让定制师以为团期没问题。

⚠️ 本条曾写错,2026-09-11 更正。 起草时写的是「GET 任何后台角色都能调(不标 @HouseWriteGuarded)」。 错因是只枚举了「注解 + 拦截器」一条机制就下了否定结论,没查到读门是编排层直接调用的第二套机制。 测试服实测(test_admin / CUSTOMIZER 打 GET 得 808090)与源码调用点双向坐实后更正。 读写两套门分开的理由写在 HouseReadGuard 的 javadoc 里:组长写门必须拒(808091)、读门必须放行, 否则组长连看板都打不开,监督无从谈起。

2. 808616 已删除,前端可移除兜底分支

GB_ROOM_PLAN_CONFIRMED_REVISION_UNAVAILABLE(808616)是 #7324 为「已确认行改删连锁尚未接入」留的占位码。#7325 实现了连锁能力,该码零调用、按 CODE_RULES「0 调用即删」随实现消失。前端若有该码兜底分支可以移除——它不会再返回。

3. 808602 日期上限改为 endDate - 1(右端开区间)

入住日合法性校验: stayDate ∈ [departDate, endDate)。结束日是离店日、不产生间夜,所以 stayDate == endDate 判越界。前端日期选择器的可选上限应是 endDate - 1。


一、背景

#7325 交付两块能力补齐团期房务确认链路:

  1. 预检(零写入):GET /confirm-check 逐日展示订房间数 vs 已确认需求的差额表、阻塞名单(越界户/无基线户)、整团能否确认的判定。
  2. 按日确认:POST /days/{stayDate}/confirm 单日确认订房,触发库存扣减、计划行翻已确认、分房重算、子订单完成标记。
  3. 整团确认:POST /confirm 先整团预检(零写入),通过后按日期升序逐日执行,支持幂等重跑(已处理日跳过)。

三个端点都标 @HouseWriteGuarded 进行角色门控(H7·H8,#7324 的 H4-H6 已标)。只读预检额外不标该注解,让组长也能看到差额表做监督。

新增错误码 8 个落在 HouseGroupBatchErrorCode 的 808604-808610 / 808630 / 808631 / 808643 九段(注:先占号后实现,有占位有真实)。同时删除 808616,更新既有 589560 的参数含义。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 按日确认订房 POST /v3/admin/house/group-batches/{groupBatchId}/room-plans/days/{stayDate}/confirm 新增接口 逐日执行,扣库存 + 翻态 + 重算分房 + 完成标记,返 3 个 ID 列表 + 警告
2 整团确认订房 POST /v3/admin/house/group-batches/{groupBatchId}/room-plans/confirm 新增接口 先整团预检,再按日期升序逐日执行;支持幂等重跑与部分成功
3 订房确认预检 GET /v3/admin/house/group-batches/{groupBatchId}/room-plans/confirm-check 新增接口 零副作用,返回逐日差额表、阻塞名单、整团能否确认;可选按单日过滤

三、接口详情

1. 按日确认订房 POST /v3/admin/house/group-batches/{groupBatchId}/room-plans/days/{stayDate}/confirm

VO: 无请求体 → GroupBatchRoomDayConfirmRespVO

使用场景

房务在日历上逐日打开某日的确认弹窗,点击「确认本日订房」后调用该端点。服务端原子性地扣库存、翻计划行状态、重算该日分房并检查子订单完成标记,返回处理细节(本次确认了哪些行、之前就确认的哪些行、真正扣了库存的哪些行)与告警。

入参

字段 位置 类型 必填 约束 说明
groupBatchId Path Long 是 雪花 ID 团期主订单 ID
stayDate Path LocalDate(ISO) 是 yyyy-MM-dd 格式 入住日,须落在团期 [departDate, endDate) 内

出参字段表 Result<GroupBatchRoomDayConfirmRespVO>

字段 类型 说明
groupBatchId String(雪花 ID,ToStringSerializer) 团期主订单 ID
stayDate String 入住日(yyyy-MM-dd)
confirmedPlanIds List<Long>(元素 ToStringSerializer) 本次由待确认(PENDING)翻成已确认(CONFIRMED)的计划行 ID
skippedPlanIds List<Long> 本次之前已确认的计划行 ID(未重复扣库存,已确认状态无变化)
deductedPlanIds List<Long> 本次真实扣减了库存的计划行 ID(deduct_inventory=true 且非幂等短路)
allocationCount Integer 本日 diff-apply 之后的 active 分房行数
doneOrderIds List<Long> 本次被置为已完成(DONE)的子订单 ID
hotelReady Boolean 本次结束后团期的配房完成标志(全日全户已分房 ∧ 无阻塞)
warnings List<GroupBatchRoomConfirmWarningVO> 告警清单(确认已成功,但有事实需关注)

GroupBatchRoomConfirmWarningVO 结构:

字段 类型 说明
code String 告警码,可能值:BASELINE_INACTIVE(该户待新版需求确认)、DAY_OUT_OF_RANGE(该户某晚越界)、MANUAL_OVERFLOW(人工分房超出边界)、ALLOC_SHORTAGE(该户该房型缺房)、ALLOC_LEFTOVER(计划行有房未分)、ALLOC_SPLIT_HOTEL(该户被分到多家酒店)
orderId Long(可空,ToStringSerializer) 相关子订单 ID,无具体户时为空
message String 面向房务的说明文案

请求示例

POST /v3/admin/house/group-batches/20260610001/room-plans/days/2026-06-12/confirm

响应示例

{
  "code": 200,
  "success": true,
  "data": {
    "groupBatchId": "20260610001",
    "stayDate": "2026-06-12",
    "confirmedPlanIds": ["500001", "500002"],
    "skippedPlanIds": [],
    "deductedPlanIds": ["500001", "500002"],
    "allocationCount": 3,
    "doneOrderIds": ["900001"],
    "hotelReady": false,
    "warnings": [
      {
        "code": "BASELINE_INACTIVE",
        "orderId": "900002",
        "message": "订单900002有新版本需求待管理员确认,该户未置为已完成"
      }
    ]
  }
}

空数据 / 降级响应

  • 无空态:入参是团期 ID + 日期,必有对应团期则有响应。若该日零计划行或全已确认(幂等重跑),三个 ID 列表可能为空。

错误响应

{ "code": 808602, "message": "入住日 2026-06-15 不在团期出行区间内", "success": false }
{ "code": 808604, "message": "订房计划行已被确认或版本已变化,请刷新后重试", "success": false }
{ "code": 808605, "message": "酒店 示例酒店 房型 标间 库存不足(需 5 间),请调整订房计划或更换房型", "success": false }
{ "code": 808607, "message": "示例酒店 的 标间 订房 3 间 / 已确认需求 5 间(共 2 项不等,详见预检)", "success": false }
{ "code": 808609, "message": "入住日 2026-06-12 没有可确认的订房计划", "success": false }
{ "code": 808610, "message": "入住日 2026-06-12 订房 35 间,超过班期最大房间数 30", "success": false }
{ "code": 808630, "message": "团期确认采用预占失败(酒店 示例酒店 房型 标间),请稍后重试或等待对账任务收口", "success": false }
{ "code": 808643, "message": "该团有 2 户的分房已过时(首个订单 ORD202606120001),请先重算分房后再确认", "success": false }
{ "code": 808090, "message": "无权操作(仅房务角色可操作)", "success": false }
{ "code": 808091, "message": "无权操作(仅房务管理员或超管可操作)", "success": false }

业务边界

  • 幂等窗口:按「团期 ID + 入住日」组合,3 秒租约。同日多次提交返回「处理中」。
  • 已发生间夜冻结:该日入住日早于操作当日且计划行状态已为 CONFIRMED 时,整个团期状态必须是 TRAVELLING/TRIP_FINISHED/REVIEWING/CANCELLED 之一,否则 808690。
  • 部分成功回滚:若某行扣库存失败(808605),本次已扣成功的行必须逐条归还,整事务回滚、零副作用。

2. 整团确认订房 POST /v3/admin/house/group-batches/{groupBatchId}/room-plans/confirm

VO: 无请求体 → GroupBatchRoomConfirmAllRespVO

使用场景

房务打开整团视图,点击「整团确认订房」,服务端先做整团预检(零写入),检查所有可执行日(非越界日)是否等量 + 不超班期 + 有基线;预检通过后按日期升序逐日执行,支持幂等重跑(已处理日自动跳过)。

入参

字段 位置 类型 必填 约束 说明
groupBatchId Path Long 是 雪花 ID 团期主订单 ID

出参字段表 Result<GroupBatchRoomConfirmAllRespVO>

字段 类型 说明
groupBatchId String(雪花 ID,ToStringSerializer) 团期主订单 ID
confirmedDates List<String> 本次新确认的入住日列表(yyyy-MM-dd);前 k−1 日成功后第 k 日失败时只返前 k−1 日,重跑会跳过
skippedDates List<String> 本次之前已全部确认的入住日(幂等重跑时返回)
emptyDemand Boolean true 表示全团无需房户或每户每晚都自订,已直接置配房完成;false 表示有需房户需逐日确认
doneOrderIds List<Long>(元素 ToStringSerializer) 本次累计被置为已完成的子订单 ID
hotelReady Boolean 本次结束后团期的配房完成标志
warnings List<GroupBatchRoomConfirmWarningVO> 逐日告警并集

请求示例

POST /v3/admin/house/group-batches/20260610001/room-plans/confirm

响应示例(正常确认)

{
  "code": 200,
  "success": true,
  "data": {
    "groupBatchId": "20260610001",
    "confirmedDates": ["2026-06-12", "2026-06-13"],
    "skippedDates": [],
    "emptyDemand": false,
    "doneOrderIds": ["900001", "900002"],
    "hotelReady": true,
    "warnings": []
  }
}

响应示例(全团无需订房豁免出口)

{
  "code": 200,
  "success": true,
  "data": {
    "groupBatchId": "20260610001",
    "confirmedDates": [],
    "skippedDates": [],
    "emptyDemand": true,
    "doneOrderIds": [],
    "hotelReady": true,
    "warnings": []
  }
}

空数据 / 降级响应

  • 全团无需房或每户自订时直接返 emptyDemand=true,不再逐日处理。
  • 第 k 日失败时已确认的日期在 confirmedDates,未处理的日期不返回(不是返回空,而是整体返回成功日期 + 错误响应)。

错误响应

{ "code": 808607, "message": "示例酒店 的 标间 订房 3 间 / 已确认需求 5 间(共 2 项不等,详见预检)", "success": false }
{ "code": 808610, "message": "入住日 2026-06-12 订房 35 间,超过班期最大房间数 30", "success": false }
{ "code": 808631, "message": "本团仍有 3 户住宿需求未填全或未落在团期区间内,不能按「无需订房」置配房完成", "success": false }
{ "code": 808090, "message": "无权操作(仅房务角色可操作)", "success": false }
{ "code": 808091, "message": "无权操作(仅房务管理员或超管可操作)", "success": false }

业务边界

  • 幂等窗口:按「团期 ID + 'ALL'」组合,3 秒租约。
  • 两步预检:第一步全局检查(有基线、阶段允许);第二步逐日检查(每日等量 + 不超班期)。全通过才进执行。
  • 豁免出口:全团无需房或每户自订时直接置 hotelReady=true,跳过逐日等量校验与越界户一票否决。
  • 部分成功:若第 k 日失败,前 k−1 日已确认状态保留、不回滚,confirmedDates 只返前 k−1 日,重跑会自动跳过。

3. 订房确认预检 GET /v3/admin/house/group-batches/{groupBatchId}/room-plans/confirm-check

VO: GroupBatchRoomConfirmCheckReqVO(GET 直接绑定) → GroupBatchRoomConfirmCheckRespVO

使用场景

在确认前查看「哪一天哪个房型差几间」的完整差额表,以及阻塞名单(越界户/无基线户)。支持按单日过滤,也支持不传日期查看有需求或有计划行的全部日。零副作用,但同样有角色门——非房务角色返 808090,房务组长放行(见「关键变化 1」)。

入参字段表

GroupBatchRoomConfirmCheckReqVO(GET 参数直接绑定):

字段 位置 类型 必填 约束 说明
groupBatchId Path Long 是 雪花 ID 团期主订单 ID
stayDate Query LocalDate(ISO) 否 yyyy-MM-dd;不传则返回「有需求或有计划行」的全部日 仅查看某一天的差额;确认弹窗按日打开时传,看板整团预检时不传

出参字段表 Result<GroupBatchRoomConfirmCheckRespVO>

字段 类型 说明
groupBatchId String(雪花 ID,ToStringSerializer) 团期主订单 ID
batchStatus String 团期主状态(如 TRAVELLING)
stageAllowed Boolean 团期阶段是否允许确认(REVIEWING/RECRUITING/SETTLED/CANCELLED 不允许)
baselineExists Boolean 是否存在任一户已确认的需求基线
hotelReady Boolean 当前配房完成标志
maxRooms Integer 班期最大房间数(null 或 0 表示不限)
ready Boolean 整团是否可确认(全部可执行日都等量 ∧ 阶段允许 ∧ 有基线);注:ready=true 不等于配房完成标志能置上(还需无阻塞)
blockedByOutOfRange Boolean 是否被越界户/无基线户阻塞(房务自己解决不了,需团期管理员改期/重新确认需求/置为不需配房)
days List<DayCheck> 逐日预检
noBaselineOrders List<NoBaselineOrder> 需房但无已确认基线的户
outOfRangeOrders List<OutOfRangeOrder> 越界或出发日缺失的户(阻塞名单,不是剔除名单)

DayCheck 子结构(逐日明细):

字段 类型 说明
stayDate String(yyyy-MM-dd) 入住日
planStatus String 该日计划行状态:NONE(无计划)/ PENDING(全待确认)/ PARTIAL(混合)/ CONFIRMED(全已确认)
plannedTotal Integer 该日订房总间数
demandedTotal Integer 该日已确认需求总间数
exceedsMaxRooms Boolean 该日订房总数是否超过班期最大房间数
demand Map<String, Integer> 该日各房型大类的需求间数(键=房型大类,值=间数);含越界户落在本格的需求
mismatch List<CategoryMismatch> 逐房型大类的差额(仅列 diff≠0 的行)
stale Boolean 该日存在的分房记录基线与当前基线不一致(基线被重新确认过)
dayReady Boolean 该日是否就绪(等量 ∧ 不超班期);越界日恒为 false
outOfBatchRange Boolean 该日落在团期区间之外(越界户那一格,订不了也确认不了)

CategoryMismatch 子结构(某日某房型的差额):

字段 类型 说明
roomCategory String 房型大类(如 STANDARD)
planned Integer 订房间数
demanded Integer 已确认需求间数
diff Integer 差额 = 订房 − 需求;正数多订、负数少订

NoBaselineOrder 子结构(无基线户):

字段 类型 说明
orderId Long(ToStringSerializer) 子订单 ID
orderNo String 订单号
reason String 原因:NOT_SUBMITTED(未提过)/ PENDING_REVIEW(待确认)/ REJECTED(被打回)

OutOfRangeOrder 子结构(越界或日期缺失户):

字段 类型 说明
orderId Long(ToStringSerializer) 子订单 ID
orderNo String 订单号
departDate String(可空,yyyy-MM-dd) 该户出发日(缺失为空)
reason String 原因:OUT_OF_RANGE(出发日外)/ DATE_MISSING(缺失日期)

请求示例

GET /v3/admin/house/group-batches/20260610001/room-plans/confirm-check
GET /v3/admin/house/group-batches/20260610001/room-plans/confirm-check?stayDate=2026-06-12

响应示例(有需求不等量)

{
  "code": 200,
  "success": true,
  "data": {
    "groupBatchId": "20260610001",
    "batchStatus": "TRAVELLING",
    "stageAllowed": true,
    "baselineExists": true,
    "hotelReady": false,
    "maxRooms": 30,
    "ready": false,
    "blockedByOutOfRange": false,
    "days": [
      {
        "stayDate": "2026-06-12",
        "planStatus": "PENDING",
        "plannedTotal": 3,
        "demandedTotal": 5,
        "exceedsMaxRooms": false,
        "demand": { "STANDARD": 5 },
        "mismatch": [
          {
            "roomCategory": "STANDARD",
            "planned": 3,
            "demanded": 5,
            "diff": -2
          }
        ],
        "stale": false,
        "dayReady": false,
        "outOfBatchRange": false
      }
    ],
    "noBaselineOrders": [],
    "outOfRangeOrders": []
  }
}

响应示例(被越界户阻塞)

{
  "code": 200,
  "success": true,
  "data": {
    "groupBatchId": "20260610001",
    "batchStatus": "TRAVELLING",
    "stageAllowed": true,
    "baselineExists": true,
    "hotelReady": false,
    "maxRooms": null,
    "ready": false,
    "blockedByOutOfRange": true,
    "days": [],
    "noBaselineOrders": [],
    "outOfRangeOrders": [
      {
        "orderId": "900003",
        "orderNo": "ORD202606120003",
        "departDate": "2026-06-08",
        "reason": "OUT_OF_RANGE"
      }
    ]
  }
}

空数据 / 降级响应

  • days[] / noBaselineOrders[] / outOfRangeOrders[] 均可为空数组(团尚无需求提交或全部已就绪)。
  • 单日查询(传了 stayDate)时,若查询日不在「有需求或有计划行」的日期范围内,days 返回空数组。

错误响应

{ "code": 589500, "message": "团期不存在", "success": false }

业务边界

  • 零副作用:不更新任何状态,不消耗幂等令牌,即便并发调用也互不影响。
  • 阶段允许:RECRUITING / REVIEWING / SETTLED / CANCELLED 团期返 stageAllowed=false,但预检仍继续返回差额表(前端可用于诊断)。
  • 与整团确认的异同:
    • 相同:都检查阶段、基线、等量、班期上限、阻塞情况。
    • 不同:预检只读不扣库存,整团确认还会顺手补检「豁免出口」(全团无需房时直接置完成)。

四、契约约束与正确调用方式

✅ 正确 / ❌ 错误调用顺序

场景 调用顺序
✅ 逐日确认工作流 先调 GET /confirm-check 查看差额表 → 房务调整订房 → 再调 POST /days/{stayDate}/confirm 逐日确认
✅ 整团一键确认 先调 GET /confirm-check 检查整团是否 ready=true → 再调 POST /confirm 一并执行
✅ 幂等重跑 整团确认失败后(第 k 日失败),修复第 k 日问题 → 再调 POST /confirm 一次,前 k−1 日自动跳过
❌ 不看差额直接调用 POST 房务无法预知会因 808607 / 808610 失败,应先看 confirm-check
❌ 用旧的 ready 状态去执行 预检与执行之间可能有新的需求确认或订房改动,不能信时间轴之外的 ready;前端应在执行前再确认一遍

角色与权限拦截顺序

步骤 拦截点 返回码
1 @HouseWriteGuarded 检查(两个 POST 才有) 808090(非房务)/ 808091(组长只读)
2 @Idempotent 幂等窗(三个端点都有) 「处理中」文案
3 业务逻辑 808602 / 808604 / 808605 / 等

非房务角色在第 1 步就被挡下、压根进不了幂等窗口。


五、数据库行为

操作 group_batch_room_plan 表 group_batch_room_allocation 表 group_batch 表
POST /days/{stayDate}/confirm 成功 匹配行 plan_status 改为 CONFIRMED;version 不变 逐日 diff-apply 生成新分房或调整存量分房(若自动分房上线) 可能改 hotel_ready=true(全日全户已分房 ∧ 无阻塞)
POST /confirm 成功 全部匹配行 plan_status 改为 CONFIRMED 逐日全量重算分房 最终置 hotel_ready=true 或保持 false(有阻塞)
GET /confirm-check 成功 无变化 无变化 无变化
库存扣减 在按日确认时按幂等键 gbrp-{planId}-v{version} 与 Feign 到 resource 扣库存(成功后落 group_batch_room_plan_deduct_log) — —

六、边界行为

  • 并发确认:多个房务同时确认不同日期,由团期级 @Lock4j 序列化(30 秒租约)。
  • 已发生间夜:确认端点对已发生的间夜(stayDate < today ∧ plan_status = CONFIRMED)逐行返 808690,直接拒不执行。
  • 阶段限制:RECRUITING / REVIEWING / SETTLED / CANCELLED 团期调确认端点返 808600;预检仍然可调(只读)。
  • 部分成功回滚:某行失败时已扣的库存必须同事务归还(afterCommit 不执行),确保「计划行状态 vs 库存持有 log」对账平衡。

六.5、枚举 / 数据字典

计划行状态(GroupBatchRoomPlanDO.planStatus)

所属字段: days[].planStatus 及 GroupBatchRoomPlanRespVO.planStatus | 类型: String

值 中文 说明
NONE 无 该日没有计划行
PENDING 待确认 该日全部计划行状态都是 PENDING
PARTIAL 混合 该日既有 PENDING 也有 CONFIRMED 计划行
CONFIRMED 已确认 该日全部计划行状态都是 CONFIRMED

告警码(GroupBatchRoomConfirmWarningVO.code)

所属字段: GroupBatchRoomDayConfirmRespVO.warnings[].code 及 GroupBatchRoomConfirmAllRespVO.warnings[].code | 类型: String

值 中文 说明
BASELINE_INACTIVE 待新版需求确认 该户有新版本需求待管理员确认,本次未置为已完成
DAY_OUT_OF_RANGE 越界告警 该户某晚换算后落在团期区间外;需求已计入分母、该户进阻塞名单,整团配房完成置不上
MANUAL_OVERFLOW 人工分房超出 人工分房行超出计划行间数或该户需求,本次未自动调整
ALLOC_SHORTAGE 缺房告警 该户该房型还欠房;首次确认不会出现,它来自重确认 / 人工微调 / 改删连锁重算
ALLOC_LEFTOVER 多房告警 该计划行还有房没分出去(订了但没人住)
ALLOC_SPLIT_HOTEL 跨酒店分房 该户同一晚被分到了不止一家酒店(算法精确匹配房型大类、不看酒店)

无基线原因(GroupBatchRoomConfirmCheckRespVO.noBaselineOrders[].reason)

所属字段: noBaselineOrders[].reason | 类型: String

值 中文 说明
NOT_SUBMITTED 未提交 该户从未提交过需求
PENDING_REVIEW 待确认 提过但最新版本待管理员确认
REJECTED 被打回 最新版本被打回,无有效基线

越界原因(GroupBatchRoomConfirmCheckRespVO.outOfRangeOrders[].reason)

所属字段: outOfRangeOrders[].reason | 类型: String

值 中文 说明
OUT_OF_RANGE 出发日外 该户出发日落在团期 [departDate, endDate) 之外
DATE_MISSING 日期缺失 该户出发日为空,无法判定是否在团期内

七、不影响范围

  • 仅影响: 管理后台房务团期页面的确认弹窗与看板视图
  • 零影响:
    • C 端行程相关接口
    • 订单创建、详情查看
    • 既有配房、退改、核单流程
    • 其他后台模块

八、测试环境已验证

部署:2026-09-11 10:12:31 部署 hl-order-service-v3 到测试服(dev-v3 @ dfd5a8f6f,双实例滚动)。

验的是什么 证据
进程 LISTEN *:8086、LISTEN *:8186 两实例
Nacos 两实例 "healthy":true,"enabled":true
跑的确是 dfd5a8f6f 两条独立路径:①服务器 git rev-parse HEAD = dfd5a8f6ffb0e768548e787fe1b8cb276ccf86fc;②jar mtime 10:12:31 与 .deployed 登记一致,两实例 lstart 均晚于 jar mtime
Flyway flyway_schema_history 版本 20260910.302 success=1(10:12:38);SHOW COLUMNS 列序为 order_id → hotel_id → room_type_id → room_count,与 AFTER 定义相符
启动异常 两实例日志 `ERROR

网关实测(走网关,非直连服务端口):

端点 角色 业务码 说明
GET /confirm-check house_keeper_lead 200 正常返回预检体(baselineExists:false / ready:false / noBaselineOrders[].reason=NOT_SUBMITTED)
GET /confirm-check CUSTOMIZER 808090 读门生效(更正了本篇起草时的错误口径)
GET /confirm-check ROOM_MANAGER(非该团认领人) 808612 团未被认领
POST /days/{stayDate}/confirm CUSTOMIZER 808090 —
POST /days/{stayDate}/confirm house_keeper_lead 808091 只读监督角色
POST /confirm CUSTOMIZER 808090 —
POST /confirm house_keeper_lead 808091 —

三个端点全部返回业务层响应而非网关路由未命中,证明既有 /v3/admin/** 通配已覆盖新路径。 网关零改动另有独立证据:hl-gateway 自 2026-09-07 21:53 起未重启(jar mtime 与进程 lstart 两条路径核过),与本次改动无交集。

被拒的两次 POST 零副作用已用 DB 复核:group_batch_room_plan 该团行数 = 0,order_group_batch.update_time 未变。

单元测试:合并前全量 Tests run: 9925, Failures: 0, Errors: 0, Skipped: 7,BUILD SUCCESS。

Flyway 迁移验证:新增 GroupBatchRoomAllocationFlywayMySqlIT 在真实 MySQL 8.0.33 容器上验过 V20260910_302 含存量行的 ALTER(先插一行存量再跑迁移,断言两列 NOT NULL 回填 0 且落在 order_id 之后,2 个用例绿)。

🔴 尚未验证,如实标注:H7 / H8 的真实确认业务逻辑没有跑通。 取证用的团期处于「未被房务认领」态,POST 先撞 808612 就返回了,触达不到确认逻辑本身。 要跑通需要先造「整团抢单 → 提交需求 → 管理员确认基线」这条数据链路,本轮没做—— 所以本节证明的是可达性 + 鉴权 + 迁移 + 启动,不是 H7/H8 的业务正确性。 后者目前只有单测覆盖。前端联调时若遇到与预期不符的确认行为,请直接反馈,不要假定后端已实测过。

单元测试: 合并前全量 Tests run: 9925, Failures: 0, Errors: 0, Skipped: 7,BUILD SUCCESS。

Flyway 迁移验证: 新增 GroupBatchRoomAllocationFlywayMySqlIT 在真实 MySQL 8.0.33 容器上验过 V20260910_302 含存量行的 ALTER TABLE group_batch_room_allocation ADD COLUMN hotel_id / room_type_id(2 个用例绿)。


九、相关历史 PR

PR Issue 说明 是否仍有效
#7465 #7324 房务团期看板 H1-H6 + 整团释放(前一期确认前置工作) ✅ 有效
本 PR #xxxx #7325 按日确认 H7 + 整团确认 H8 + 只读预检 ✅ 最新

十、相关文档

  • 后续计划: 分房与微调(#7326 H9-H11,定案中),按日确认分房重算全景(#7327 巡检)
  • 错误码管理: 见 HouseGroupBatchErrorCode.java 文档注释,段位 808600-808699,本单占 808604-808610 / 808630 / 808631 / 808643

关联 / 联系人

链接

  • Issue: #7325
  • PR: #xxxx
  • Merge commit: (合并后回填)

联系人

  • 后端负责人: @wx