文件
hl-api-changelog/changelogs-v2/2026-09/26_8385_团期订房计划建守卫与释放文案改进-修改接口-管理后台.md
T
Mimingguang bcfe323b60
changelog-filename-gate / validate (push) Failing after 1s
docs(changelog): 回写 #8385~#8390 前端 not_required(6 条 grep 实证零改动)
88caa74 房务接口审计批次:#8385 订房计划守卫/#8386 住宿驳回加门+删车务口/#8387 旧房间分配三口下线/#8388 回执认领校验+读门/#8389 家庭维度写口下线/#8390 16 读端点补门+菜单撤授。前端按钮后端字段驱动+错误码拦截器透 message+被删端点零调用/入口仅房务角色页,6 条均 not_required;#8387/#8389 后端已标,余 4 条翻 not_required,owner/ref 留空,status_note 引号内追充实证。
2026-09-27 09:55:25 +08:00

374 行
15 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "8385"
title: "团期订房计划建守卫:超管未认领团上禁建(808612),释放文案改为可操作指引"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "not_required"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "超管在未被房务整团认领的团期上新建订房计划时返回 808612(复用已有码);释放端点的 808660 文案改为可操作的指引(删除计划、联系超管接管)。后端改动已合入 dev-v3 并部署测试服,808612 新守卫与 808660 新文案均已网关实测。前端实证(mmg 2026-09-27):room-plans 写口错误码拦截器透 message,808612/808660 常量已存在新文案自动上屏;超管无未认领团直接排房入口(看板仅 isMine 走团级接管),零改动 not_required。"
updated_at: "2026-09-26"
base: "dev-v3"
---
# order-v3:团期订房计划守卫与释放指引改进(管理后台)
**服务**: hl-order-service-v3
**PR**: #8395
**Issue**: #8385
---
## ⚠️ 关键变化
1. **新增入口守卫**:超管(SUPER_ADMIN)在**未被房务认领的团期**上调用 `POST /v3/admin/house/group-batches/{groupBatchId}/room-plans` 时,返回 **808612**「该团期尚未被房务整团认领」,不落库。
- 背景:超管本可在无人认领的团上直接建计划,导致"团没人管、但计划和库存都在"的状态。释放时房务因为 808660 无法释放,只剩逐条删计划这一条路。
- 约束:超管要在团上排房,必须先 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover` 指派给某个房务,再建计划。
2. **改进释放文案**:端点 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/release` 的错误码 808660 文案改为:
「该团仍有 {0} 条未取消的订房计划,无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管」
- 改前文案提到的「走接管」对房务不可用(接管只有超管能做)。改后提供两条可行出口:删除计划(房务自助)/ 联系超管接管(超管权限)。
---
## 一、背景
团期整团认领的唯一真实指针是 `order_group_batch.house_claimer_id`。超管出于"人离职、团转手"的清理需要被允许越过认领校验,但在一个**没人认领、也不需要清理**的团上直接建计划,不属于这类处置。
释放时的 808660 守卫堵住了"有计划+无认领人"的释放口,文案则指向一个房务无法执行的操作(接管),导致房务被无谓地卡住。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 提交订房计划 | POST | `/v3/admin/house/group-batches/{groupBatchId}/room-plans` | 新增入口守卫 | 超管未认领团上返回 808612 |
| 2 | 释放认领 | POST | `/v3/admin/order/grab-pool/group-batches/{groupBatchId}/release` | 错误码文案改进 | 808660 文案改为可操作指引 |
网关无改动(既有 `/v3/admin/house/` 与 `/v3/admin/order/` 前缀均可达);返回码、触发条件、已认领团的行为全部不变。
---
## 三、接口详情
### 1. 提交订房计划 `POST /v3/admin/house/group-batches/{groupBatchId}/room-plans`
**VO**: `GroupBatchRoomPlanSaveReqVO → Result<List<GroupBatchRoomPlanRespVO>>`
#### 使用场景
房务或超管在团期上提交多条订房计划(房型、房数、价格、结算方式、库存扣减等)。超管在未认领的团上调用时新增拒绝,改后只能通过先 takeover 指派给房务的方式排房。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | 是 | — | 团期 ID |
| items | Body | List | 是 | ≤200 条 | 订房计划行 |
| items[].stayDate | Body | LocalDate | 是 | 格式 `yyyy-MM-dd` | 入住日期 |
| items[].hotelId | Body | Long | 是 | — | 酒店 ID |
| items[].roomTypeId | Body | Long | 是 | — | 房型 ID |
| items[].roomCount | Body | Integer | 是 | ≥1 | 房间数 |
| items[].roomCategory | Body | String | 否 | ≤32 | 房型大类 code;服务端以 resource 权威值覆盖,仅用于前端回显 |
| items[].protoPrice | Body | BigDecimal | 否 | ≥0 | 协议价快照;不传按「日历价 → 酒店协议价」兜底 |
| items[].settlementPrice | Body | BigDecimal | 否 | ≥0 | 结算价快照;不传按「日历价 → 协议价」兜底 |
| items[].settleType | Body | String | 否 | `cash` \| `sign` \| `company` | 结算方式;不传取酒店资源配置 |
| items[].deductInventory | Body | Boolean | 否 | 默认 true | 是否扣库存 |
| items[].remark | Body | String | 否 | ≤512 | 备注 |
| items[].version | Body | Integer | 否 | — | 乐观锁版本;本端点(批量新建)不校验,只有单行修改端点必填并校验 |
| items[].replaceReason | Body | String | 否 | ≤256 | 替换原因;本端点不使用,仅单行修改端点在语义为"删旧建新"时使用 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| planId | String | 计划行 ID(雪花 ID,序列化为字符串) |
| groupBatchId | String | 团期 ID(雪花 ID,序列化为字符串) |
| stayDate | LocalDate | 入住日期 |
| hotelId / hotelName | String / String | 酒店 ID(序列化为字符串)及名称 |
| roomTypeId / roomTypeName | String / String | 房型 ID(序列化为字符串)及名称 |
| roomCount | Integer | 房间数 |
| protoPrice | String | 协议价快照,可为 null(BigDecimal,序列化为字符串) |
| settlementPrice | String | 结算价快照,可为 null(BigDecimal,序列化为字符串) |
| settleType | String | 结算方式快照,可为 null |
| planStatus | String | 状态码(PENDING / CONFIRMED) |
| version | Integer | 乐观锁版本号 |
#### 请求示例
```http
POST /v3/admin/house/group-batches/2099918391610314754/room-plans HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <admin token>
Content-Type: application/json
{
"items": [
{
"stayDate": "2026-10-01",
"hotelId": 1001,
"roomTypeId": 5001,
"roomCount": 2,
"settlementPrice": 450.00,
"deductInventory": true,
"remark": "标准间"
}
]
}
```
#### 响应示例
**成功(已认领团)**:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": [
{
"planId": "2103794613192208385",
"groupBatchId": "2099918391610314754",
"stayDate": "2026-10-01",
"hotelId": "1001",
"hotelName": "丽思卡尔顿",
"roomTypeId": "5001",
"roomTypeName": "豪华标间",
"roomCount": 2,
"settlementPrice": "450.00",
"planStatus": "PENDING",
"version": 1,
"createTime": "2026-09-26 14:30:00"
}
]
}
```
**失败(未认领团,超管)**:
```json
{
"code": 808612,
"message": "该团期尚未被房务整团认领",
"success": false,
"data": null
}
```
#### 空数据 / 降级响应
- 参数校验失败(日期格式错、房间数 ≤0):HTTP 200,`code=400`,message 含具体字段文案;不落库。
#### 错误响应
```json
{
"code": 808612,
"message": "该团期尚未被房务整团认领",
"success": false,
"data": null
}
```
**其他错误码**:
- **808090**:未登录或非房务角色,无权操作。
- **808091**:房务组长为只读监督角色,无权执行该操作(HOUSE_KEEPER_LEAD)。
- **808613**:该团期由其他房务认领,无权操作。
- **808600**:团期当前阶段不允许修改订房计划(订房计划仅在配置阶段可改)。
- **808614**:该入住日在酒店的该房型上已有订房计划,请改用修改单行。
- **808611**:团期出发日或结束日缺失,无法录入订房计划。
- **808602**:入住日不在团期出行区间内。
- **808603**:订房间数必须大于 0。
- **808691**:无法确定房型大类(取权威房型数据失败),请稍后重试。
- **808112**:房型不属于该酒店。
- **100502**:3 秒幂等窗口内重复提交,「订房计划提交处理中,请勿重复提交」。
#### 业务边界
- **整团认领**:认领人本人或超管可提交;非认领房务提交返回 808613;团未认领(`house_claimer_id IS NULL`)时所有人(含超管)返回 808612。
- **超管排房出口**:超管要在未认领的团上排房,必须先 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover` 指派给房务,再来提交计划。
- **库存扣减**:`deductInventory=true` 时,CONFIRMED 状态的计划会扣掉 `resource_hotel_room_inventory` 对应房型该日的可用房数;PENDING 阶段不扣。
- **日期范围**:入住日期必须在团期的出发日期与结束日期之间。
---
### 2. 释放认领 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/release`
**VO**: `HouseGroupReleaseReqVO → Result<Void>`
#### 使用场景
房务释放所有团期认领,团回到抢单池状态(`house_claimer_id → NULL`)。房务有订房计划待处理时返回 808660,文案告知删除计划或联系超管接管两条出口。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | 是 | — | 团期 ID |
| reason | Body | String | 否 | ≤200 字 | 释放原因;超管必填且不少于 10 字 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| — | null | 成功返回 null |
#### 请求示例
```http
POST /v3/admin/order/grab-pool/group-batches/2099918391610314754/release HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
Content-Type: application/json
{
"reason": "已完成配房"
}
```
#### 响应示例
**成功**:
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": null
}
```
**失败(有计划待删)**:
```json
{
"code": 808660,
"message": "该团仍有 4 条未取消的订房计划,无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管",
"success": false,
"data": null
}
```
#### 空数据 / 降级响应
不适用。
#### 错误响应
```json
{
"code": 808660,
"message": "该团仍有 4 条未取消的订房计划,无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管",
"success": false,
"data": null
}
```
**其他错误码**:
- **808655**:该团期不属于当前房务,无法释放;非认领人释放时触发(超管不受此限),CAS 并发失败(读到时归本人、提交时已被超管释放/接管)也报此码。
- **808656**:该团期尚未被认领,无需释放。
- **808657**:超管操作原因长度不足 10 字(房务释放时 reason 可为空)。
- **808090**:未登录或非房务角色,无权操作。
- **808091**:房务组长为只读监督角色,无权执行该操作(HOUSE_KEEPER_LEAD)。
#### 业务边界
- **释放前置**:须先删光全部未取消的订房计划(PENDING + CONFIRMED)才能释放;所有角色(含超管)受 808660 约束,release 本身不碰计划行。
- **幂等**:幂等键按团(`groupBatchId`),窗口 5 秒;窗口内重复提交返回 100502「整团释放处理中,请勿重复提交」。
---
## 四、契约约束与正确调用方式
| 场景 | 调用方法 | 说明 |
|------|---------|------|
| ✅ 超管先排房再指派 | 先 `POST /v3/admin/order/grab-pool/group-batches/{id}/takeover` 指派给房务 → 再 `POST .../room-plans` 提交 | takeover 让团被指定房务认领,之后超管仍可在上面操作 |
| ✅ 房务提交计划 | `POST /v3/admin/house/group-batches/{id}/room-plans` | 认领的房务可随时提交,RESOURCE_PREPARING 阶段有效 |
| ✅ 房务释放有计划 | 先 `DELETE /v3/admin/house/group-batches/{id}/room-plans/{planId}` 逐条删 → 再 `POST .../release` | 删除由 `HouseGroupBatchClaimGuard` 控制,认领人可删(含他人建的),释放成功返回 200 |
| ❌ 超管在未认领团直接建计划 | — | 返回 808612,需先 takeover 指派 |
| ❌ 有计划待删时释放 | — | 返回 808660,不释放;先删后释 |
---
## 五、数据库行为
无表结构变更、无 Flyway 迁移。
---
## 六、边界行为
| 场景 | 行为 |
|---|---|
| 超管 POST room-plans on 未认领团 | 返回 808612,`group_batch_room_plan` 零新增 |
| 超管 takeover 后 POST room-plans | 返回 200,计划行正常落库 |
| 房务释放、仍有 PENDING 计划 | 返回 808660,计数含 PENDING |
| 房务删掉全部计划、再释放 | 返回 200,团回到池里 |
| 超管释放、仍有计划 | 同样返回 808660(不分角色) |
## 六.6、修改前后对比
| 维度 | 改前 | 改后 |
|---|---|---|
| 超管在未认领团上 POST room-plans | 返回 200,计划落库,团仍无主 | 返回 808612,不落库 |
| 释放时有计划、808660 文案 | 「无法释放(请先处理订房计划或走接管)」 | 「无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管」 |
## 六.7、影响评估
- **兼容性**:hl-ui 在 `origin/v2.1` 上房务页面无调用 `takeover`(该接口仍在,超管直接用也可以);超管排房流程可能需要调整为"先指派再排"。
- **前端要动的**:房务页面若有"直接排房"的超管入口,需提示"未认领团先指派再排";释放时若遇到 808660,按改后文案提示房务删除计划。
- **数据影响**:无;本单仅加守卫,不改历史数据。
- **其它服务**:product-v2、fleet、user-service 无改动。
---
## 七、不影响范围
- 已认领团的超管操作(确认、分房、delete 等)全部不动。
- 更新/删除/确认计划的守卫不动(仍走 `assertWritableByCurrentUser`,超管仍可清理)。
- 其他团期相关接口(认领、接管、整团确认等)无改动。
---
## 八、测试环境已验证
部署:测试环境 order-v3 `1f65d7894`(含 e2313790b)。网关 `api.test.1814.love:9443`。
| # | 场景 | 实测结果 |
|---|---|---|
| 1 | 超管 POST 未认领团(RESOURCE_PREPARING) | code 808612,计划行零新增 |
| 2 | 该团由超管 takeover 给某房务后,超管再 POST | code 200 |
| 3 | 超管 DELETE 上一步新增的计划行 | code 200,`releasedLogId=null` |
| 4 | 该房务 release | code 200,团回到无主 |
| 5 | 另一认领房务逐条 DELETE 自己团的 4 条 PENDING 计划 | 全部 200(`releasedLogId=null`) |
| 6 | 上一步之后 release | code 200 |
| 7 | 超管对他人认领团的存量计划行 DELETE(62 行) | 全部 200 |
| 8 | 认领房务在团上挂 1 条 PENDING 计划时 release | code 808660,message「该团仍有 1 条未取消的订房计划,无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管」,与源码逐字一致 |
| 9 | 删掉该计划后再 release | code 200,团回到无主 |
---
## 十、相关文档
- Issue:https://git.1814.love/wx/HL/issues/8385
- PR:https://git.1814.love/wx/HL/pulls/8395
- takeover API:`POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover`
- 同批单号:#8386、#8387、#8388、#8389、#8390(房务接口审计批次)
---
## 关联 / 联系人
### 联系人
- **后端负责人**: @wx