55 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 | 7322 | 团期整团抢单与抢单池分流:新增团期抢单池/我的团/接管四写口,普通抢单池排除团单逐户行 | admin | wx(GIT) | 新增接口 | deployed | not_required | verified | mmg | 745cd9b8 | 2026-09-10 | 本篇覆盖 PR #7436(合并提交 ab4118a01)。2026-09-10 已部署测试服(HEAD 6d41d6148,已验 ab4118a01 为其祖先),Flyway V20260910_301 执行成功,order_group_batch 三列 house_claimer_id/house_claimer_name/house_claimed_at 已落库。32 条验收项中 28 条已取证,含用 5 个真实登录账号过网关实测的归属与接管链路。后台菜单(sys_menu)的两行抢单池入口已由 PR #7467(squash 7f791a250,hl-user-service 的 V20260910_001)补上。⚠️ 部署两条:① #7322 的部署清单原本不含 hl-user-service,本次起必须带上;② 菜单树按 roleId 缓在 Redis(cache:menu:tree:role:{roleId},TTL 3600s),Flyway 直连 DB 不触发失效,部署后需 DEL cache:menu:tree:role:* 否则最长要等一小时。前端已交付(commit 745cd9b8):新增团期抢单池页(抢单池·团期+我的团两 Tab,含超管团级接管)与普通抢单池独立页,orders 页摘除 pool scope,BatchHero 接负责房务三字段;checkpoint 全绿。 | 2026-09-10 | dev-v3 |
团期模块:整团抢单与抢单池分流(新增团期抢单池/我的团/团级接管,普通池排除团单逐户行)
服务:
hl-order-service-v3PR: #7436 Issue: #7322 日期: 2026-09-10 影响范围: 房务管家「抢单池」体系——新增「抢单池·团期」与「我的团」两组共 5 个端点;改造既有「抢单池·普通」列表与逐户 claim/transfer 两个端点;团期详情追加 3 个只读字段
⚠️ 关键变化
给房务加「整团认领」:团期管理员整体确认需求后,该团以整团一条进入独立的「团期抢单池」,由一个房务整团认领;此前团单会把每一户拆成一行落进普通抢单池,被抢 N 次、落到 N 个房务手上——本次同步把团单逐户行从普通池移出。
- 新增 5 个端点(详见「三、接口详情」):团期抢单池列表、整团认领、整团释放、团级接管(超管专属,处理历史脏数据死结)、我的团(含组长/超管监督视图)。
GET /v3/admin/order/grab-pool/hotel-requirements(普通抢单池)行为变化:追加order_main.product_batch_id IS NULL过滤,团期子订单的逐户需求行不再出现在普通池,total同口径减少;productType=GROUP筛选仍接受但结果恒为空。POST /v3/admin/order/hotel-requirements/{requirementId}/claim(逐户抢单)行为变化:对团期子订单新增守卫,无条件拒绝,新增错误码 808650,零写入。POST /v3/admin/order/hotel-requirements/{requirementId}/transfer(逐户转单)行为变化:对团期子订单新增守卫——普通房务恒拒 808650;SUPER_ADMIN仅在该团尚未被整团认领(house_claimer_id IS NULL)时放行,用于把历史脏数据集中到一个在职房务名下,团已被整团认领后超管也拒。POST /v3/admin/order/hotel-requirements/{requirementId}/release(逐户释放)行为变化:⚠️ 与工单正文「口径与定案 #6」早期结论(release 不改)不同——2026-09-08 第六轮复审后追加了一道更严守卫:团期子订单若仍存在任意 active 逐户配房行(不限confirm_status,含询房中INQUIRING候选)→ 新增错误码 808662,零写入;非团单行为完全不变(仍是既有的「仅CONFIRMED阻塞释放」口径 808021)。GET /v3/admin/order/group-batch/{groupBatchId}(团期详情)追加 3 个只读字段:houseClaimerId/houseClaimerName/houseClaimedAt(均可空,未认领为null),供团期管理员侧展示「负责房务」。- 团级指针是团单房务归属的唯一真源,认领/接管不回写任何户级字段(不动
claimer_id/status/house_status/room_control_status,不 fire 户级状态机,不绑会话,不广播 SSE)——认领后户级room_control_status仍会显示「待配房」直到后续订房工单(#7325)把它置DONE,这是有意为之,不是缺陷。 - 无 SSE 广播:团期池/我的团页面需前端手动或定时刷新,不会像普通抢单池那样实时推送变更。
- 不新增权限码,5 个新端点走既有的「房务角色门」(
HouseWriteGuard,写口)与新增的读口角色门(ROOM_MANAGER/house_keeper_lead/SUPER_ADMIN,否则 808090),菜单种子(sys_menu)确实需要,已由独立的 hl-user-service FlywayV20260910_001__add_house_grab_pool_menus.sql(PR #7467)落地,不影响本篇端点契约。 - 数据库变更:
order_group_batch新增 3 列 + 1 索引(V20260910_301__order_group_batch_add_house_claimer.sql),无新表。
一、背景
团期管理员点「整体确认」后,RequirementService.dispatchGroupHotelRequirements 把每一户的 active 房需求 CAS 成 status=PENDING、claimer_id=NULL,而普通抢单池此前无任何团单排除条件——一个 30 户的团在普通池里是 30 行,会被抢 30 次、落到 N 个房务手上。「某个房务认领了整个团」这个概念此前在代码里不存在:GroupBatchDO.batchManagerId 语义是「团期管理员」不是房务,且全仓零写入。
本单给 order_group_batch 加三列认领指针,新建团期专属的抢单池/我的团/接管三个写口 + 两个读口,并把团单逐户行从普通池移出、给逐户 claim/transfer 加团单守卫,杜绝「一个团被多个房务分持」。历史脏数据(团单户已被逐户抢走)由「团级接管」端点(超管专属)兜底处理,测试库不存在生产存量(origin/main 上团期相关三个核心类全部零命中,二期功能尚未上线现网)。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 团期抢单池列表 | GET | /v3/admin/order/grab-pool/group-batches |
新增 | 整团一条,分页 + 多维筛选 |
| 2 | 整团认领 | POST | /v3/admin/order/grab-pool/group-batches/{groupBatchId}/claim |
新增 | CAS 先抢先得,户级零副作用 |
| 3 | 整团释放回池 | POST | /v3/admin/order/grab-pool/group-batches/{groupBatchId}/release |
新增 | 认领人本人或超管 |
| 4 | 团级接管 | POST | /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover |
新增 | 超管专属,处理历史脏数据死结 |
| 5 | 我的团 | GET | /v3/admin/order/grab-pool/my-claims/group-batches |
新增 | 含 scope=all 组长/超管监督视图 |
| 6 | 普通抢单池列表 | GET | /v3/admin/order/grab-pool/hotel-requirements |
行为修改 | 追加排除团单逐户行 |
| 7 | 逐户抢单 | POST | /v3/admin/order/hotel-requirements/{requirementId}/claim |
行为修改 | 新增 808650 团单守卫 |
| 8 | 逐户转单 | POST | /v3/admin/order/hotel-requirements/{requirementId}/transfer |
行为修改 | 新增 808650 团单守卫(超管有条件例外) |
| 9 | 逐户释放 | POST | /v3/admin/order/hotel-requirements/{requirementId}/release |
行为修改 | 团单户新增 808662 更严守卫(active 配房未清空不可释放),非团单不变 |
| 10 | 团期详情 | GET | /v3/admin/order/group-batch/{groupBatchId} |
行为修改 | 响应追加 3 个只读字段 |
三、接口详情
1. 团期抢单池列表 GET /v3/admin/order/grab-pool/group-batches
VO: HouseGroupGrabPoolPageReqVO → PageResult<HouseGroupGrabPoolItemRespVO>
使用场景
房务管家「抢单池·团期」页,展示未被认领、需求已整体确认、团期阶段在四态内的团(整团一条)。角色 ROOM_MANAGER / house_keeper_lead / SUPER_ADMIN,其它角色 808090。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| keyword | Query | String | 否 | ≤32 字 | batchNo/productName 模糊二选一 OR |
| productId | Query | Long | 否 | — | 产品精确过滤 |
| batchStatus | Query | String | 否 | 只接受 RESOURCE_PREPARING/MATERIAL_PREPARING/PENDING_DEPARTURE/TRAVELLING,非法值 400 |
团期阶段精确过滤 |
| departDateFrom / departDateTo | Query | LocalDate | 否 | yyyy-MM-dd |
出发日区间 |
| page | Query | Integer | 否 | ≥1,默认 1 | 页码 |
| pageSize | Query | Integer | 否 | 1-100,默认 20 | 每页条数 |
| sortBy | Query | String | 否 | departDate,asc(默认)/ createTime,desc |
排序 |
出参字段表 Result<PageResult<HouseGroupGrabPoolItemRespVO>>
| 字段 | 类型 | 说明 |
|---|---|---|
data.list[].groupBatchId |
String(Long ToString) | 团期主订单 ID |
data.list[].batchNo |
String | 运营团期号 |
data.list[].productId |
String(Long ToString) | 产品 ID |
data.list[].productName |
String | 产品名快照 |
data.list[].batchName |
String | 班期名快照 |
data.list[].batchLabel |
String | 第 N 期快照 |
data.list[].batchStatus |
String | 团期状态 code |
data.list[].batchStatusLabel |
String | 团期状态中文 |
data.list[].departDate / endDate / enrollDeadline |
LocalDate | 可空 |
data.list[].enrolledRooms / enrolledPeople |
Integer | 已报名房数 / 人数(持久计数器直取) |
data.list[].activeOrderCount |
Integer | 活跃子订单数(排除 CANCELLED) |
data.list[].hotelOrderCount |
Integer | 已放行到房务的需房户数(active 房需求 status ∈ {PENDING,PROCESSING,DONE} 的户数) |
data.list[].hotelReady |
Boolean | 团期酒店资源是否已就绪 |
data.list[].daysToDepart |
Integer | 今天到出发日天数,无出发日 null |
data.list[].urgencyLevel / urgencyLabel |
String | 紧急度 code/中文(NORMAL/URGENT/CRITICAL) |
data.list[].createTime |
LocalDateTime | 团期创建时间 |
(字段取自 HouseGroupGrabPoolItemRespVO.java:17-86。)
请求示例
GET /v3/admin/order/grab-pool/group-batches?page=1&pageSize=20&sortBy=departDate,asc
Authorization: Bearer <持房务三角色之一的管理端 token>
响应示例
{
"code": 200, "message": "成功", "success": true,
"data": {
"total": 1, "pages": 1, "current": 1, "size": 20,
"list": [
{ "groupBatchId": "1930000000000000001", "batchNo": "GB26060101", "productId": "1001",
"productName": "额吉的故乡", "batchName": "6月首发团", "batchLabel": "第3期",
"batchStatus": "RESOURCE_PREPARING", "batchStatusLabel": "资源准备中",
"departDate": "2026-06-15", "endDate": "2026-06-20", "enrollDeadline": "2026-06-10",
"enrolledRooms": 12, "enrolledPeople": 26, "activeOrderCount": 12, "hotelOrderCount": 10,
"hotelReady": false, "daysToDepart": 12, "urgencyLevel": "NORMAL", "urgencyLabel": "正常",
"createTime": "2026-05-01 10:00:00" }
]
}
}
(示例字段取自 VO 定义与 HouseGroupGrabService.toPoolItem 装配逻辑,非真实网关调用。)
空数据 / 降级响应
无符合条件的团时 list 为空数组、total=0,返回 200。0 需房户的团(全团客户自订酒店)同样进池,hotelOrderCount=0 明示,判定条件不含需房户数。
错误响应
{ "code": 808090, "message": "未登录或非房务角色,无权操作", "success": false, "data": null }
业务边界
- 入池判定 =
house_claimer_id IS NULL AND requirement_confirmed=1 AND batch_status IN (RESOURCE_PREPARING, MATERIAL_PREPARING, PENDING_DEPARTURE, TRAVELLING)(+ 未软删)。 - 读口角色门与写口不同:
house_keeper_lead(房务组长)在这里可以正常查看(三角色平权只读),只有写操作(认领/释放/接管)才会把组长挡在 808091。 - 无角色(系统态/内部调用/单测)放行,与
HouseWriteGuard同口径。
2. 整团认领 POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/claim
VO: 无请求体 → Result<Void>
使用场景
房务在团期抢单池卡片点击「认领」,CAS 先抢先得。成功后只写团级三列 + 团期时间线 + 通知事件,户级行零变化。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | 是 | 团期主订单 ID(雪花) | 前端应按 String 传递避免精度丢失 |
无请求体。
出参字段表 Result<Void>
| 字段 | 类型 | 说明 |
|---|---|---|
data |
null | 成功即 200,与逐户 claim 一致 |
请求示例
POST /v3/admin/order/grab-pool/group-batches/1930000000000000001/claim
Authorization: Bearer <持 ROOM_MANAGER 或 SUPER_ADMIN 的管理端 token>
响应示例
{ "code": 200, "message": "成功", "success": true, "data": null }
空数据 / 降级响应
无(写操作)。
错误响应
{ "code": 808654, "message": "该团有 2 户已被房务 王五 逐户抢单,请先释放后再整团认领", "success": false, "data": null }
| 码 | 符号 | 触发 |
|---|---|---|
| 808090 | NOT_LOGIN_OR_NOT_HOUSE_ROLE |
未登录 / 非房务角色 |
| 808091 | READ_ONLY_LEAD_FORBIDDEN |
房务组长(只读监督,不可写) |
| 589500 | GROUP_BATCH_NOT_FOUND |
团期不存在 |
| 808654 | GB_GRAB_HOUSEHOLD_CLAIMED_BY_OTHERS |
「该团有 {0} 户已被房务 {1} 逐户抢单,请先释放后再整团认领」——有户被别人逐户抢走 |
| 808659 | GB_GRAB_TAKEOVER_HAS_HOUSE_ASSIGNMENT |
「该团有 {0} 户存在逐户配房(首个订单 {1}),请先删除配房后再操作」——分流后剩余户存在 active 逐户配房行 |
| 808651 | GB_GRAB_BATCH_NOT_CLAIMABLE |
「该团期当前不可认领(需求未整体确认或团期阶段不允许)」——CAS 落空且未被他人认领 |
| 808652 | GB_GRAB_BATCH_ALREADY_CLAIMED |
「该团期已被其他房务认领」 |
| 808653 | GB_GRAB_BATCH_CLAIMED_BY_SELF |
「该团期已由您认领,请勿重复认领」 |
(错误码字面量与消息文案取自 HouseGroupBatchErrorCode.java:72-121。)
业务边界
- 顺序不可颠倒:脏数据预检(808654)→ 冻结名单分流(历史已 finalize 户不受影响)→ 无损前置(808659)→ 团级 CAS → 时间线 → afterCommit 通知(
HouseGroupGrabService.claim,HouseGroupGrabService.java:245-284)。 - 全部持有人都是本人时 808654 不触发(本人把「逐户抢的团」升级成整团认领)。
- 认领不回写户级
claimer_id/status/house_status/room_control_status,不 fire 户级状态机,不绑定会话,不广播 SSE——团级指针是唯一真源。 - 三个写口(claim/release/takeover)共用同一把团级
@Lock4j(name=HouseGroupBatchLockConstants.GROUP_BATCH_HOUSE_LOCK_NAME),配合 CAS 保证并发安全,与幂等注解(@Idempotent,5 秒防重复提交)叠加。
3. 整团释放回团期池 POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/release
VO: HouseGroupReleaseReqVO → Result<Void>
使用场景
认领人本人释放自己认领的团,或 SUPER_ADMIN 强制释放任意团(需附理由)。释放后该团若仍满足进池条件即重新出现在团期池。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | 是 | 团期主订单 ID | — |
reason |
Body | String | 否(超管必填≥10字) | ≤200 字 | 超管释放必填理由(trim 后 <10 字 → 808657);普通房务可空,空白兜底文案「释放回池」 |
请求体可为 null(@RequestBody(required=false))。
出参字段表 Result<Void>
| 字段 | 类型 | 说明 |
|---|---|---|
data |
null | 成功 200 |
请求示例
POST /v3/admin/order/grab-pool/group-batches/1930000000000000001/release
Authorization: Bearer <认领人本人 token>
{ "reason": "临时调休,交回团期池" }
响应示例
{ "code": 200, "message": "成功", "success": true, "data": null }
空数据 / 降级响应
无。
错误响应
{ "code": 808655, "message": "该团期不属于当前房务,无法释放", "success": false, "data": null }
| 码 | 符号 | 触发 |
|---|---|---|
| 808090 / 808091 | 角色门 | 同上 |
| 589500 | GROUP_BATCH_NOT_FOUND |
团期不存在 |
| 808656 | GB_GRAB_BATCH_NOT_CLAIMED |
「该团期尚未被认领,无需释放」 |
| 808655 | GB_GRAB_RELEASE_NOT_OWNER |
「该团期不属于当前房务,无法释放」——非认领人且非超管;或并发落空(CAS 时已被他人释放/接管) |
| 808657 | GB_GRAB_ADMIN_RELEASE_REASON_TOO_SHORT |
「超管操作原因长度不足 10 字」 |
| 808660 | GB_HOUSE_RELEASE_HAS_ROOM_PLAN |
「该团仍有 {0} 条未取消的订房计划,无法释放(请先处理订房计划或走接管)」——该团存在未取消的订房计划行(GroupBatchRoomPlanService.countActivePlans) |
业务边界
- 判定顺序:登录/角色门 → 团期存在 → 归属判定(本人/超管)→ reason 长度(超管)→ 已订房守卫 808660 → CAS 释放 → 时间线 → 通知(
HouseGroupGrabService.release,HouseGroupGrabService.java:296-341)。 - 808660 存在的理由:若无此守卫,把团释放成无主但计划行与已扣库存都还在,之后没有任何角色能继续处理;超管要强行换人须走「团级接管」而非「释放」。
- 释放后原认领人的历史轨迹只记团期时间线(
BATCH_HOUSE_RELEASE),不落回任何户级表。
4. 团级接管 POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover
VO: HouseGroupTakeoverReqVO → Result<HouseGroupTakeoverRespVO>
使用场景
超管专属。用于处理历史脏数据死结——当同一历史团的户被两个不同房务分别持有、且至少一户已有确认配房时,整团 claim(撞 808654)、持有人逐户 release(撞既有的 808021 已确认配房守卫)、逐户 transfer(撞本单新增的 808650)三条路同时封死,接管是唯一的无损出口。接管 = 覆盖式指定团期房务归属 + 清理该团历史遗留的户级抢单归属。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | 是 | 团期主订单 ID | — |
toUserId |
Body | Long | 是 | 须在 user-service 在职房务列表内,否则 808011 | 接管人 userId |
reason |
Body | String | 是 | trim 后 10-200 字 | 接管原因,写入团级时间线 |
出参字段表 Result<HouseGroupTakeoverRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
data.groupBatchId |
String(Long ToString) | 团期主订单 ID |
data.fromClaimerId |
String(Long ToString,可空) | 接管前的团级认领人(未认领时 null) |
data.toClaimerId |
String(Long ToString) | 接管后的团级认领人 |
data.toClaimerName |
String | 接管人真实姓名(user-service 解析失败时兜底 user-{id}) |
data.clearedOrderIds |
Array<String> | 本次被清掉户级 claimer_id 的子订单 ID 列表 |
data.legacyFinalizedOrderIds |
Array<String> | 冻结名单里的历史户(保留逐户配房,不迁移,见「业务边界」) |
data.skippedOrderIds |
Array<String> | 户级 CAS 并发落空、未清掉的子订单,需重跑或人工处理 |
(字段取自 HouseGroupTakeoverRespVO.java:16-46。)
请求示例
POST /v3/admin/order/grab-pool/group-batches/1930000000000000001/takeover
Authorization: Bearer <持 SUPER_ADMIN 的管理端 token>
{ "toUserId": 30002, "reason": "原认领房务离职,指派新房务接管该团" }
响应示例
{
"code": 200, "message": "成功", "success": true,
"data": {
"groupBatchId": "1930000000000000001", "fromClaimerId": "30001", "toClaimerId": "30002",
"toClaimerName": "李四", "clearedOrderIds": ["100", "101"],
"legacyFinalizedOrderIds": ["102"], "skippedOrderIds": []
}
}
(示例结构取自 HouseGroupTakeoverRespVO 字段定义与 HouseGroupGrabService.takeoverTx 装配逻辑,非真实网关调用。)
空数据 / 降级响应
无被清户/冻结户/落空户时对应数组为空列表 [],不是 null。
错误响应
{ "code": 808658, "message": "团期接管仅超级管理员可操作", "success": false, "data": null }
| 码 | 符号 | 触发 |
|---|---|---|
| 808090 / 808091 | 角色门 | 未登录/非房务角色/组长只读 |
| 808658 | GB_GRAB_TAKEOVER_NOT_SUPER_ADMIN |
「团期接管仅超级管理员可操作」——非超管调用 |
| 808657 | GB_GRAB_ADMIN_RELEASE_REASON_TOO_SHORT |
reason trim 后 <10 字 |
| 808011 | RECEIVER_NOT_FOUND |
「接收人不存在或已离职」——toUserId 不在在职房务列表 |
| 589500 | GROUP_BATCH_NOT_FOUND |
团期不存在 |
| 808659 | GB_GRAB_TAKEOVER_HAS_HOUSE_ASSIGNMENT |
分流后剩余户存在 active 逐户配房行,须先删配房 |
| 808651 | GB_GRAB_BATCH_NOT_CLAIMABLE |
团级覆盖 CAS 落空(团期已被软删等极端并发场景) |
业务边界
- 判定顺序(不可颠倒):超管判定 →
reason长度 → 接管人在职校验(Feign,事务外执行)→ 团期存在 → 冻结名单分流 → 无损前置(808659)→ 清剩余户级归属(CAS 落空计入skippedOrderIds,不抛错)→ 团级覆盖 CAS → 时间线(复用BATCH_HOUSE_CLAIM事件类型,靠extra.takeover=true区分)→ afterCommit 通知(HouseGroupGrabService.takeover/takeoverTx,HouseGroupGrabService.java:368-465)。 - 为什么接口是非事务外壳 + 事务内层两段:接管人姓名解析要调
houseStaffFeign.listHouseStaff(Feign),与写操作放在同一事务违反「事务内禁同步 Feign」(CODE_RULES §9);外壳(takeover)纯读完成超管判定/reason 校验/姓名解析后,经自注入代理调用真正的事务方法(takeoverTx)。对外契约(路径/入参/响应/错误码/执行顺序)不受这个内部拆分影响,前端无需关心。 - 冻结名单(
legacyFinalizedOrderIds)语义:接管时若某户在首次启用团期归属那一刻house_status=CONFIRMED(即已有实际住宿),该户会被冻结、保留其既有的逐户house_hotel_assignment配房,不迁移、不清claimer_id、不计入团期需求分母、不计入团期分房;此后该户即使重跑接管/认领也保持冻结(只读表,不重算)。 - 无损前置(808659)只对分流后的剩余户求值:真正冻结的历史户不受该守卫影响;判定用「是否存在 active 逐户配房行」(不限
confirm_status,含询房中候选),因为候选房同样占用已扣库存。 skippedOrderIds非空时是并发导致的部分成功(户级 CAS 落空但团级已覆盖成功),需要人工核对或重试清理这些户的残留claimer_id。- Feign 调用仍在 Controller 的团级
@Lock4j范围之内(有意如此,非漏网):把它挪到锁外只能挪进 Controller 做业务编排,违反分层铁律;持分布式锁期间卡住只让同团后续写入排队(锁有 30 秒过期兜底),比持 DB 事务卡住吊死连接池代价小得多。
5. 我的团 GET /v3/admin/order/grab-pool/my-claims/group-batches
VO: HouseMyGroupPageReqVO → HouseMyGroupPageRespVO
使用场景
当前房务查看自己已整团认领的团(scope=mine,默认);房务组长/超管可传 scope=all 查看全部已认领团(监督视图,行内带认领人)。不限团期阶段——已认领的团可能已流团/已结束,本单不自动释放指针,房务仍要看得见并手动释放。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| scope | Query | String | 否 | mine(默认)/ all |
普通房务传 all → 808092 |
| keyword | Query | String | 否 | ≤32 字 | batchNo/productName 模糊 |
| batchStatus | Query | String | 否 | 团期九态任一,非法值 400 | — |
| needsReconfirm | Query | Boolean | 否 | true=只看 requirement_confirmed=0 的团 |
待管理员重新确认 |
| departDateFrom / departDateTo | Query | LocalDate | 否 | — | 出发日区间 |
| claimedAtFrom / claimedAtTo | Query | LocalDateTime | 否 | yyyy-MM-dd'T'HH:mm:ss |
认领时间区间 |
| page / pageSize | Query | Integer | 否 | 默认 1/20,pageSize≤100 | — |
| sortBy | Query | String | 否 | claimedAt,desc(默认)/ departDate,asc |
— |
出参字段表 Result<HouseMyGroupPageRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
data.total |
Long | 当前筛选条件下的总数(DB count,任何情况下都准) |
data.stats.total |
Long | 该 scope 下已认领团数(不叠加列表筛选,只按 scope/认领人过滤) |
data.stats.needsReconfirm |
Long | 其中待管理员重新确认的团数;stats.total 超过扫描上限 500 时为 null,前端显示「—」 |
data.stats.cancelled |
Long | 其中已流团的团数;同口径超上限为 null |
data.list[] |
HouseMyGroupItemRespVO |
字段 = 团期池列表项全部字段(见「## 1.」出参)+ 下列 4 个 |
data.list[].houseClaimerId |
String(Long ToString) | 整团认领房务 adminId |
data.list[].houseClaimerName |
String | 整团认领房务姓名 |
data.list[].houseClaimedAt |
LocalDateTime | 整团认领时间 |
data.list[].requirementConfirmed |
Boolean | false 时前端应标「待管理员重新确认」 |
(字段取自 HouseMyGroupPageRespVO.java、HouseMyGroupItemRespVO.java、HouseMyGroupStatsVO.java。)
请求示例
GET /v3/admin/order/grab-pool/my-claims/group-batches?scope=mine&page=1&pageSize=20
Authorization: Bearer <房务 token>
响应示例
{
"code": 200, "message": "成功", "success": true,
"data": {
"total": 1,
"stats": { "total": 1, "needsReconfirm": 0, "cancelled": 0 },
"list": [
{ "groupBatchId": "1930000000000000001", "batchNo": "GB26060101", "productId": "1001",
"productName": "额吉的故乡", "batchStatus": "RESOURCE_PREPARING", "batchStatusLabel": "资源准备中",
"activeOrderCount": 12, "hotelOrderCount": 10, "hotelReady": false,
"houseClaimerId": "30001", "houseClaimerName": "张三",
"houseClaimedAt": "2026-06-01 10:00:00", "requirementConfirmed": true }
]
}
}
(部分字段省略以节省篇幅,完整字段见出参字段表;示例非真实网关调用。)
空数据 / 降级响应
未认领任何团时 list=[]、total=0、stats.total=0。stats.total 超过 500(扫描上限)时 stats.needsReconfirm/stats.cancelled 为 null(不是少算的数字),前端应显示「—」而不是 0——这是刻意设计,避免与 total 自相矛盾。
错误响应
{ "code": 808090, "message": "未登录或非房务角色,无权操作", "success": false, "data": null }
{ "code": 808092, "message": "无权查看全部房务订单(仅房务组长或超管可查看)", "success": false, "data": null }
业务边界
scope=all仅house_keeper_lead/SUPER_ADMIN可用,普通房务传all→ 808092(复用既有码,不是 808091「组长只读」,文案更贴切)。stats统计口径只按scope/认领人过滤,不叠加keyword/日期等列表筛选——头部数字是「我一共有多少团」,不随筛选跳动。
6. 普通抢单池列表(改造) GET /v3/admin/order/grab-pool/hotel-requirements
VO: HouseGrabPageReqVO → PageResult<HouseGrabPageItemRespVO>(本单不改任何入参/出参字段,只改查询条件)
使用场景
房务管家「抢单池·普通」页,逐户一条。本次改造后团期子订单的逐户需求行不再出现,只保留非团单的逐户需求。
入参字段表
无字段变化,沿用既有 HouseGrabPageReqVO:
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| keyword | Query | String | 否 | — | 订单号/团号/客人姓名/产品名 OR 模糊 |
| productType | Query | String | 否 | CORE/ROUTE/CUSTOM/GROUP |
传 GROUP 改后恒返空页(见业务边界) |
| productName | Query | String | 否 | — | 产品名模糊 |
| consultantId | Query | Long | 否 | — | 定制师精确 |
| guestName | Query | String | 否 | — | 客人姓名模糊 |
| departDateFrom / departDateTo | Query | LocalDate | 否 | — | 出发日区间 |
| page / pageSize | Query | Integer | 否 | 默认 1/20,pageSize≤100 | — |
| sortBy | Query | String | 否 | createTime,desc(默认)/ departDate,asc |
— |
出参字段表
无字段变化,沿用既有 HouseGrabPageItemRespVO:
| 字段 | 类型 | 说明 |
|---|---|---|
data.list[].id / orderId / orderNo / teamNo / guestName / personsDesc |
各自既有类型 | 需求/订单基本信息,本次不改 |
data.list[].productType / productName / productNo / route / departDate / nights / cities / totalAmount |
各自既有类型 | 产品/行程信息,本次不改 |
data.list[].consultantName / consultantId / consultantRemark |
各自既有类型 | 定制师信息,本次不改 |
data.list[].requirementNote / dispatchRemark / special / requirementVersion |
各自既有类型 | 需求备注/版本,本次不改 |
data.list[].urgencyLevel / urgencyLabel / daysToDepart / manualUrgent / createTime |
各自既有类型 | 紧急度/时间信息,本次不改 |
data.list[].isRework / reworkPrevClaimerName |
各自既有类型 | 返工标记,本次不改 |
(完整 29 个字段定义见 HouseGrabPageItemRespVO.java:28-119,本单零改动,此处不逐一展开类型。)
请求示例
GET /v3/admin/order/grab-pool/hotel-requirements?page=1&pageSize=20&sortBy=createTime,desc
Authorization: Bearer <房务 token>
响应示例
{
"code": 200, "message": "成功", "success": true,
"data": {
"total": 1, "pages": 1, "current": 1, "size": 20,
"list": [
{ "id": "7001", "orderId": "200", "orderNo": "HL2606000200", "teamNo": null,
"guestName": "王五", "productType": "CORE", "productName": "川西深度",
"departDate": "2026-06-10", "createTime": "2026-06-01 09:00:00" }
]
}
}
(部分字段省略以节省篇幅;完整结构见出参字段表,示例非真实网关调用。)
空数据 / 降级响应
无符合条件行时 list=[]、total=0,返回 200;本次改造后团期子订单逐户行不再计入 total,非团单场景的空数据行为与改前完全一致。
错误响应
{ "code": 400, "message": "参数校验失败", "success": false, "data": null }
无新增业务错误码,仅由全局参数校验处理器处理非法入参(如 pageSize > 100)。
业务边界
- 改前:返回全部
status=PENDING AND is_active=1 AND claimer_id IS NULL的行,含团期子订单逐户行。 - 改后:追加
order_main.product_batch_id IS NULL,团期子订单逐户行不再出现,total同口径减少。 productType=GROUP的筛选仍会被接受,但结果恒为空(团单只在团期池出现)——前端建议隐藏「产品类型」下拉里的GROUP选项,避免用户选中后困惑于「为什么筛出来是空的」。- 团单谓词用
product_batch_id IS NULL而不是product_type <> 'GROUP':团期产品但尚未归到任何团期(product_batch_id为空)的订单没有团期管理员确认链,若按product_type排除会让这类单在两个池里都不出现。
7. 逐户抢单(改造) POST /v3/admin/order/hotel-requirements/{requirementId}/claim
VO: 无请求体 → Result<Void>(本单不改请求/响应结构,只在业务逻辑最前面加一道团单守卫)
使用场景
房务对非团期子订单逐户抢单,行为不变;对团期子订单新增拦截,引导房务改用团期抢单池整团认领。
入参字段表
无变化:
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
requirementId |
Path | Long | 是 | 用房需求 ID | 与改前完全一致 |
出参字段表
无变化:
| 字段 | 类型 | 说明 |
|---|---|---|
data |
null | 成功 200,与改前完全一致 |
请求示例
POST /v3/admin/order/hotel-requirements/7002/claim
Authorization: Bearer <房务 token>
响应示例
{ "code": 200, "message": "成功", "success": true, "data": null }
空数据 / 降级响应
无(写操作),与改前一致。
错误响应
{ "code": 808650, "message": "团期订单不支持逐户抢单/转单,请到团期抢单池整团认领", "success": false, "data": null }
| 码 | 符号 | 触发 |
|---|---|---|
| 808090 / 808091 | 角色门 | 现状不变 |
| 808002 | REQUIREMENT_NOT_FOUND |
现状不变 |
| 808004 | ORDER_CANCELLED |
现状不变 |
| 808003 | REQUIREMENT_CLAIMED_BY_SELF |
现状不变 |
| 808001 | REQUIREMENT_ALREADY_CLAIMED |
现状不变 |
| 808650 | GB_GRAB_ORDER_IS_GROUP |
新增:需求所属订单 product_batch_id != null,无条件拒,零写入 |
业务边界
- 改前:对团单户 CAS 成功即逐户抢走。
- 改后:CAS 之前先判团单,团单户零写入直接抛 808650;非团单行为完全不变。
- 守卫放在 CAS 之前而非之后:即使抛错回滚,CAS 之后再判也已经真写过一次并多一次锁竞争。
8. 逐户转单(改造) POST /v3/admin/order/hotel-requirements/{requirementId}/transfer
VO: HouseTransferReqVO → Result<Void>(本单不改请求/响应结构,只在既有流程中插入一道团单守卫)
使用场景
超管指派或房务转单,非团单行为不变;对团期子订单新增有条件拦截(三分支,见业务边界)。
入参字段表
无变化:
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
requirementId |
Path | Long | 是 | 用房需求 ID | 与改前完全一致 |
toUserId |
Body | Long(JSON String) | 是 | 接收人房务 ID | 与改前完全一致 |
reason |
Body | String | 否(超管≥10字) | ≤200 字 | 与改前完全一致 |
skipUpperLimit |
Body | Boolean | 否 | 历史字段,仅审计留痕 | 与改前完全一致 |
出参字段表
无变化:
| 字段 | 类型 | 说明 |
|---|---|---|
data |
null | 成功 200,与改前完全一致 |
请求示例
POST /v3/admin/order/hotel-requirements/7002/transfer
Authorization: Bearer <SUPER_ADMIN token>
{ "toUserId": "30002", "reason": "原房务请假,临时指派" }
响应示例
{ "code": 200, "message": "成功", "success": true, "data": null }
空数据 / 降级响应
无(写操作),与改前一致。
错误响应
{ "code": 808650, "message": "团期订单不支持逐户抢单/转单,请到团期抢单池整团认领", "success": false, "data": null }
现状 808010/808011/808013/808014/808016/808001/808002/808090/808091/808930(状态机)均不变;新增 808650(GB_GRAB_ORDER_IS_GROUP)。
业务边界
- 改前:团单户可被逐户转单。
- 改后(三分支):
- 普通房务对团单户调用 → 恒 808650;
SUPER_ADMIN对团单户调用,且该团order_group_batch.house_claimer_id IS NULL(尚未被整团认领)→ 放行(用于把存量脏数据集中到一个在职房务名下);SUPER_ADMIN对团单户调用,但该团已被整团认领 → 同样 808650。
- 团期行查不到(
order_group_batch尚未 lazy 建行)时按「未被整团认领」处理放行——拒绝会把历史脏数据唯一的无损出口封死。 - 判定顺序:查到 active 需求后立即判团单归属,早于持有人校验(第 3 步)与转单次数上限判定。
9. 逐户释放(改造) POST /v3/admin/order/hotel-requirements/{requirementId}/release
VO: HouseReleaseReqVO → Result<Void>(本单不改请求/响应结构,只在既有守卫链后追加一道团单专属守卫)
使用场景
房务释放自己抢到的需求回普通抢单池;对团期子订单追加更严格的前置检查,防止释放后候选房与已扣库存无人能继续处理。
⚠️ 与工单正文「口径与定案 #6」的早期结论不同:正文最初写「release、close:不改」,但 2026-09-08 第六轮复审后给团单户追加了本节的 808662 守卫;close 端点确认未改动。
入参字段表
无变化:
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
requirementId |
Path | Long | 是 | 用房需求 ID | 与改前完全一致 |
reason |
Body | String | 否 | ≤200 字 | 与改前完全一致,@RequestBody(required=false) |
出参字段表
无变化:
| 字段 | 类型 | 说明 |
|---|---|---|
data |
null | 成功 200,与改前完全一致 |
请求示例
POST /v3/admin/order/hotel-requirements/7002/release
Authorization: Bearer <认领人本人 token>
{ "reason": "客户改期,暂时放弃" }
响应示例
{ "code": 200, "message": "成功", "success": true, "data": null }
空数据 / 降级响应
无(写操作),与改前一致。
错误响应
{ "code": 808662, "message": "该团单户仍有 2 条配房(含询房中候选),请先删除配房后再释放", "success": false, "data": null }
| 码 | 符号 | 触发 |
|---|---|---|
| 808090 / 808091 | 角色门 | 现状不变 |
| 808002 | REQUIREMENT_NOT_FOUND |
现状不变 |
| 808020 | RELEASE_NOT_OWNER |
现状不变 |
| 808021 | RELEASE_HAS_CONFIRMED_ASSIGNMENT |
现状不变(仅数 CONFIRMED,全部订单通用) |
| 808662 | GB_HOUSE_RELEASE_HAS_ACTIVE_ASSIGNMENT |
新增,仅团期子订单:存在 active 配房行(含 INQUIRING 候选,不限 confirm_status),零写入 |
业务边界
- 改前:团单户释放判据与普通单完全一致,只看
RELEASE_HAS_CONFIRMED_ASSIGNMENT(808021,仅数CONFIRMED状态的配房)。 - 改后:普通单判据不变;团期子订单在既有 808021 判定之后,额外追加「active 配房行(不限
confirm_status,含询房中候选)不为 0 → 808662」的更严守卫。 - 为什么团单不能沿用「仅
CONFIRMED阻塞」这条既有规则:换酒店场景会 reopen 需求、软删旧确认行、插入INQUIRING候选且库存已真实扣减,此时CONFIRMED计数为 0、808021 不拦;若放行释放,claimer_id被清空后,候选房与已占库存将无法被任何角色继续处理——delete/clear/finalize 会被HouseClaimGuard以 808116 拒绝(该守卫无超管免检分支),逐户 claim 被 808650 拒绝,逐户 transfer 的 CAS 条件(claimer非空且status='PROCESSING')在释放后两者都不满足,团级takeover不处理这类冻结旧户,团级release又会被同团新户的订房计划以 808660 拒绝——形成无人能处理的死结,故必须在释放前一步就拦住。 close(标记异常完成)端点本次未改动,仅release追加了本条守卫。
10. 团期详情(改造:响应追加三字段) GET /v3/admin/order/group-batch/{groupBatchId}
VO: 无请求体 → GroupBatchDetailRespVO(现有字段全部不变,仅追加 3 个只读字段)
使用场景
团期管理员侧团期详情页,展示「负责房务」;也是 #7328(房务↔团期管理员会话)取会话对端的依据。
入参字段表
无变化:
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | 是 | 团期主订单 ID | 与改前完全一致 |
出参字段表(仅列追加部分,其余既有字段本次不改动)
| 字段 | 类型 | 说明 |
|---|---|---|
data.houseClaimerId |
String(Long ToString,可空) | 整团认领房务 adminId(未认领 null) |
data.houseClaimerName |
String(可空) | 整团认领房务姓名(未认领 null) |
data.houseClaimedAt |
LocalDateTime(可空) | 整团认领时间(未认领 null) |
(取自 GroupBatchDetailRespVO.java:190-204;与 batchManagerId「团期管理员」是两个不同的人,全站契约写死不启用 batchManagerId 表示房务,不可混用。)
请求示例
GET /v3/admin/order/group-batch/1930000000000000001
Authorization: Bearer <团期管理员 token>
响应示例
{
"code": 200, "message": "成功", "success": true,
"data": {
"groupBatchId": "1930000000000000001",
"houseClaimerId": "30001", "houseClaimerName": "张三", "houseClaimedAt": "2026-06-01 10:00:00"
}
}
(仅展示本次新增的三字段,既有字段(数十个)省略以节省篇幅,完整既有结构不受本次改动影响。)
空数据 / 降级响应
该团未被整团认领时,追加的三字段均为 null,其余既有字段的空数据行为不受影响。
错误响应
{ "code": 589500, "message": "团期不存在", "success": false, "data": null }
无变化,沿用既有 589500。
业务边界
- 三字段权限现状不变(沿用团期详情既有权限),本次仅多返回三个字段,不改变任何鉴权逻辑。
四、契约约束与正确调用方式
团期抢单池写口(claim / release / takeover)
| 场景 | 结果 |
|---|---|
✅ ROOM_MANAGER/SUPER_ADMIN 认领未被占用且已整体确认的团 |
200 |
❌ house_keeper_lead 调 claim/release/takeover |
808091(只读监督) |
| ❌ 非房务角色调 claim/release/takeover | 808090 |
| ❌ 普通房务调 takeover | 808658(仅超管) |
| ❌ 该团有户被别人逐户抢走时整团 claim | 808654 |
| ❌ 该团有户存在 active 逐户配房行时 claim/takeover | 808659 |
| ❌ 释放时该团仍有未取消订房计划 | 808660 |
普通池与逐户 claim/transfer
| 场景 | 结果 |
|---|---|
| 团期子订单的逐户需求行 | 不再出现在普通抢单池列表 |
| 对团期子订单逐户 claim | 808650,零写入 |
| 普通房务对团期子订单逐户 transfer | 808650 |
| 超管对未被整团认领的团期子订单逐户 transfer | 放行(存量脏数据集中用) |
| 超管对已被整团认领的团期子订单逐户 transfer | 808650 |
| 逐户 close 对团单户 | 不变 |
| 逐户 release 对团单户,且无 active 配房行 | 放行(保留作为历史脏数据清理出口) |
逐户 release 对团单户,但仍有 active 配房行(含 INQUIRING 候选) |
808662(新增,非团单不受影响,仍是既有 808021 口径) |
- 「我的接单」列表中历史上被逐户抢走的团单户仍会出现(脏数据可见性即清理入口,
GET /v3/admin/order/grab-pool/my-claims/hotel本单不改)。 - 转单候选「在跟单数」统计(
countInProgressHotelClaimsByClaimer)不计团级认领,这是已知口径差,前端展示候选人工作量时应注意。
切换状态时的必要动作
- 无状态机字段切换;团级三列(
house_claimer_id/house_claimer_name/house_claimed_at)是简单 CAS 覆盖,不经状态机。
五、数据库行为
order_group_batch新增 3 列 + 1 索引(V20260910_301__order_group_batch_add_house_claimer.sql):house_claimer_id BIGINT NULL(整团认领房务 adminId,NULL=未认领)house_claimer_name VARCHAR(64) NULL(认领房务姓名快照)house_claimed_at DATETIME NULL(认领时间)KEY idx_house_claimer_id (house_claimer_id)- 版本号说明:正文原规划
V20260908_301,因#7323的V20260908_311已先合入并在测试服执行过,Flyway 按乱序拒绝更低版本号,故实际落地为V20260910_301。
- 无新表;
house_operation_log、order_hotel_requirement本次均不写入改动。 - 认领 / 释放 / 接管均写团期时间线(
group_batch_status_log,事件类型BATCH_HOUSE_CLAIM/BATCH_HOUSE_RELEASE,DATA 类),时间线写入失败仅记 WARN 日志、不回滚主事务。 - 认领 / 释放 / 接管不写入任何户级表(
order_hotel_requirement、house_hotel_assignment等)——团级接管例外:会 CAS 清理非冻结名单户的order_hotel_requirement.claimer_id(详见「三、4」业务边界)。 - 团级写口 afterCommit 发通知中心事件(
GROUP_BATCH_HOUSE_CLAIMED/GROUP_BATCH_HOUSE_RELEASED),notification_event_config未配置时 dispatcher 只记日志,不影响主链路,不阻塞接口响应。
六、边界行为
- 0 需房户的团(全团客户自订酒店)仍会进入团期抢单池,
hotelOrderCount=0明示,房务据此自行跳过。 - 团期无出发日(
depart_date可空)时,按出发日排序的分页里NULL行的页内位置由 Service 内存处理挪到本页末尾,不做跨页重排。 - 认领后管理员对该团任一户打回需求,会清掉团级
requirement_confirmed标记(但不清认领指针):「我的团」里该团requirementConfirmed=false,前端应标「待管理员重新确认」;团级计划行/分房行校验本单不涉及,留给后续工单补充。 - 「我的团」头部统计
needsReconfirm/cancelled在stats.total > 500时返回null(不是少算的数字),前端应显示「—」。 house_operation_log与逐户「我的接单」列表不受团级认领影响,历史被逐户抢走的团单户仍在原列表可见,作为清理入口。- 权限服务或房务员工列表 Feign(团级接管的接管人姓名解析)异常时按兜底名
user-{id}处理并跳过在职校验;接收人列表非空但查无该人时才会明确拒 808011。 - 极窄边界场景(不属于本篇 10 个端点,供排查参考):冻结名单内的历史户(首次启用团期归属时已有确认配房)被
release清空房务归属后,若定制师对该户提交新版本用房需求(既有的upsertHotelRequirement端点,走「已抢/已配房后的需求调整」分支),后端会 fail-closed 拒绝并返 808661(GB_HOUSE_LEGACY_RESUBMIT_NO_OWNER),提示需先清空候选配房再重提,避免出现「旧房仍占库存却无人能处理」。该端点请求/响应契约本身未变,仅新增这一条极窄场景下的业务错误。
六.5、枚举 / 数据字典
错误码新增清单(HouseGroupBatchErrorCode.java,段位 808600-808699 内本单占用 808650-808662)
| 码 | 符号 | 消息 |
|---|---|---|
| 808650 | GB_GRAB_ORDER_IS_GROUP |
团期订单不支持逐户抢单/转单,请到团期抢单池整团认领 |
| 808651 | GB_GRAB_BATCH_NOT_CLAIMABLE |
该团期当前不可认领(需求未整体确认或团期阶段不允许) |
| 808652 | GB_GRAB_BATCH_ALREADY_CLAIMED |
该团期已被其他房务认领 |
| 808653 | GB_GRAB_BATCH_CLAIMED_BY_SELF |
该团期已由您认领,请勿重复认领 |
| 808654 | GB_GRAB_HOUSEHOLD_CLAIMED_BY_OTHERS |
该团有 {0} 户已被房务 {1} 逐户抢单,请先释放后再整团认领 |
| 808655 | GB_GRAB_RELEASE_NOT_OWNER |
该团期不属于当前房务,无法释放 |
| 808656 | GB_GRAB_BATCH_NOT_CLAIMED |
该团期尚未被认领,无需释放 |
| 808657 | GB_GRAB_ADMIN_RELEASE_REASON_TOO_SHORT |
超管操作原因长度不足 10 字(释放/接管共用) |
| 808658 | GB_GRAB_TAKEOVER_NOT_SUPER_ADMIN |
团期接管仅超级管理员可操作 |
| 808659 | GB_GRAB_TAKEOVER_HAS_HOUSE_ASSIGNMENT |
该团有 {0} 户存在逐户配房(首个订单 {1}),请先删除配房后再操作(claim 与 takeover 共用) |
| 808660 | GB_HOUSE_RELEASE_HAS_ROOM_PLAN |
该团仍有 {0} 条未取消的订房计划,无法释放(请先处理订房计划或走接管) |
| 808661 | GB_HOUSE_LEGACY_RESUBMIT_NO_OWNER |
该户历史配房仍在但已无房务归属,请先由房务或超管清空候选配房后再重提需求——触发于既有用房需求提交端点(RequirementService.upsertHotelRequirement,非本篇新增/改造的 10 个端点之一),场景是冻结名单内的历史户被 release 后房务归属清空、定制师又对其重提新版本需求,RequirementService.java:895-904 |
| 808662 | GB_HOUSE_RELEASE_HAS_ACTIVE_ASSIGNMENT |
该团单户仍有 {0} 条配房(含询房中候选),请先删除配房后再释放——触发于本篇「### 9. 逐户释放(改造)」 |
(取自 HouseGroupBatchErrorCode.java:72-150。⚠️ 与工单正文「口径与定案 #6」早期表述不同——808661/808662 均已在本次合并的 PR #7436 内实际接线并可触发,不是预留给后续工单的空常量:808662 挂在本篇改造的逐户释放端点上;808661 挂在既有的用房需求提交端点(未改请求/响应契约,只是新增了一条极窄场景下的业务错误,未纳入本篇「三、接口详情」的 10 个端点,因为该端点本身不属于房务抢单池体系、且触发条件极窄——仅冻结名单内旧户被 release 后又被重提新版本需求时命中)。)
batchStatus(团期抢单池筛选,HouseGroupGrabPoolPageReqVO.batchStatus)
| 值 | 含义 |
|---|---|
RESOURCE_PREPARING |
资源准备中 |
MATERIAL_PREPARING |
材料准备中 |
PENDING_DEPARTURE |
待出发 |
TRAVELLING |
行程中 |
(仅这四态可入池;「我的团」的 batchStatus 筛选接受团期完整九态,因为已认领的团可能已流团/已结束。)
七、不影响范围
- 「我的接单」(逐户)、待办、日历、详情、组长监督视图等既有房务工作台功能不变——团级认领不回写户级,这些视图读的都是户级数据。
- 团期需求确认/打回(
group-batch:demand:confirm)不受影响,仍是团进池的唯一触发条件之一。 - 逐户
close端点不改。逐户release端点对非团单行为完全不改;对团单户继续可用(作为历史脏数据清理出口),但新增了 808662 更严守卫,见「三、9.」——这一点与工单正文早期「release 不改」的表述不同,以本篇为准。 - 「我的接单」
GET /v3/admin/order/grab-pool/my-claims/hotel不改。 - 房务日历不展示抢单池订单,本单排除条件不影响日历。
- 无 SSE 广播改动:团期池变更不推送实时事件,前端仍需沿用既有的普通池 SSE(不受影响)或手动/定时刷新团期池。
- CODE_RULES 不改;网关路由零改动(
/v3/admin/order/**已通配)。
八、测试环境已验证
当前状态:代码已合入 dev-v3(HEAD 353f4b2d6),测试服部署与网关实测待管理者安排,部署后回写本节与 frontmatter。
已完成的验证(单测/编译层面,不等价于网关实测):
- 单测覆盖(
HouseGroupGrabServiceTest,节选,完整清单见工单 #7322「测试要求」):认领成功写时间线、CAS 落空按持有人分类返 808652/808653/808651、脏数据户返 808654 且零写入、未登录返 808090、组长返 808091、释放非本人/未认领/超管理由过短分别返 808655/808656/808657、scope=all非组长返 808092。 HouseGrabServiceImplTest追加:团单户逐户 claim 返 808650 且零 CAS、团单户逐户 transfer 返 808650、团单户逐户 release 在无 active 配房行时仍放行、有 active 配房行(含INQUIRING候选)时返 808662。- IT(Testcontainers/H2):
selectGrabPoolPageWithJoin排除团单户后total与真实剩余行数一致;GroupBatchMapper新增 default 方法(selectHouseGrabPoolPage/selectHouseClaimedPage/casHouseClaim/casHouseRelease/casHouseTakeover)覆盖四态过滤、并发 CAS 落空场景。 - ArchTest:
HouseModuleBoundaryArchTest、MapperBoundaryArchTest、RedLineArchTest、HouseErrorCodeRangeTest(新增段位 808650-808662 全部登记)均绿。
待补(部署后由管理者执行并回填):网关实测 5 个新端点 + 4 个改造端点的全部错误码路径、Flyway 执行结果(SHOW COLUMNS/SHOW INDEX 核对)、角色矩阵、菜单种子口径确认结果(见「十、相关文档」)。
十、相关文档
- 团期看板权限地基 #6902、团期需求确认/打回 #7210:本单角色门不复用这两处的平台权限码体系,走既有的
HouseWriteGuard房务角色门。 - 团期房务批次后续工单:
#7323(订房地基/扣减来源分支)、#7324(房务团期看板/订房计划 CRUD,读本单GroupBatchDetailRespVO新三字段、提供countActivePlans供本单 808660 使用)、#7325/#7326(按日确认与自动分房、分房微调)、#7327(双源巡检契约)、#7328(房务↔团期管理员会话,参与人取本单house_claimer_id)。 - 旧拟议契约
docs/group/团期模块接口文档-v2.0.htmlGB-ADM-016(POST /v3/admin/order/group-batch/{groupBatchId}/hotel-requirements/claim,逐户批量薄编排)已被本单替代,不采用该旧契约。 - 已确认(口径与定案 #11):房务管家菜单确由
sys_menu驱动(该目录menu_id=2064956343462092801下已有 4 个既有子菜单:订单列表 / 日历视图 / 待处理 / 月度对账),故需种子行;已由 PR #7467 落地:「抢单池·普通」/housekeeper/grab-pool、「抢单池·团期」/housekeeper/grab-pool-group,sort_order5/6 接在既有四项之后,授ROOM_MANAGER/house_keeper_lead/SUPER_ADMIN(不授 ADMIN:团期读口有角色门,ADMIN 调必得 808090)。
关联 / 联系人
链接
联系人
- 后端负责人: wx
- 待确认对象(前端 hl-ui): mmg——房务管家需新增「抢单池·团期」「我的团」两个页面(含团级接管的超管专属入口);普通抢单池「产品类型」筛选下拉建议隐藏
GROUP选项;团期详情页展示「负责房务」三字段;无 SSE,页面需手动或定时刷新;⚠️ 菜单路由请确认——两个页面是「抢单池·普通」与「抢单池·团期」,「我的团」是团期页内的一个 Tab、不是独立页面(本行先前写成两个页面与工单两处口径不符,2026-09-10 订正)。迁移已先行落地(/housekeeper/grab-pool与/housekeeper/grab-pool-group,按同目录既有四行的风格)——理由:hl-ui 找不到组件会回落「开发中」页、不白屏不报错,有一个落到「开发中」的菜单项,好过没有菜单项。若实际路由不同,回一句我补一个迁移覆写(menu_id不变、无副作用);sort_order想调也一并说。