docs(changelog): #8711 房务转房招募中放开退给酒店,源/目标团期阶段栅栏分码 808327/808323,列表新增 transferAllowed/transferBlockedReason
changelog-filename-gate / validate (push) Failing after 2s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
这个提交包含在:
API Changelog Bot
2026-10-02 17:14:32 +08:00
共同撰写人 Claude Opus 5.5
父节点 3d0a3d7757
当前提交 e66936fc5a
@@ -0,0 +1,563 @@
---
schema: "hl-changelog/v2"
ticket: "8711"
title: "房务转房与退给酒店:招募中放开,源团期阶段栅栏按动作分码(#8711)"
consumer: "admin"
author: "wx(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "团期招募中时,「向酒店取消」放开、「转出」保持拒绝。源团期阶段不符时错误码统一用 808327 并随文案标出阶段。列表行新增两字段标示当前行能否转出,前端据此控制转出按钮与提示。"
updated_at: "2026-10-02"
base: "dev-v3"
---
# 房务:转房与取消,招募中场景分权,源团期栅栏细化(管理后台)
> **服务**: hl-order-service-v3
> **关联 Issue**: #8711
> **关联 PR**: #8730
> **部署状态**: 测试服验证通过(commit 704ecdd887)
> **影响范围**: 房务控制台「退团转房」页面(I-17/I-20/I-21 三端点)
---
## ⚠️ 关键变化
- **I-21 向酒店取消**:源团期招募中时也允许办理(此前返回 808323 拒绝)。
- **I-20 转房**:源团期招募中时被拒,改返新码 **808327**(文案统一为「退团房所在团期当前阶段(招募中)不允许处理」)。
- **I-17 列表**:每行新增 `transferAllowed`(Boolean)与 `transferBlockedReason`(String),指示该行是否允许转出与置灰理由。
---
## 一、背景
团期成团前处于招募中,此时转房打破户数基线而重新成团,与计划配置窗口冲突。旧流程让两个动作都被拒;本次改为**招募中只放开「取消」、保持拦「转出」**:
- **向酒店取消**(全量退团)不改库存配置,只通知酒店降间,允许执行;
- **转出**(转给别户/别团)改动源目标两处计划基线,禁止执行。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 退团转房列表 | GET | `/v3/admin/order/house-console/room-transfers` | 修改接口 | 行新增 `transferAllowed` 与 `transferBlockedReason` 字段(additive) |
| 2 | 转出 | POST | `/v3/admin/order/house-console/room-transfers/{id}/transfer` | 修改接口 | 源团期招募中返回 808327;错误码文案含源团期阶段中文 |
| 3 | 向酒店取消 | POST | `/v3/admin/order/house-console/room-transfers/{id}/cancel-hotel` | 修改接口 | 源团期招募中放开;不再返回 808323;阶段不符返回 808327 |
---
## 三、接口详情
### 1. 退团转房列表 `GET /v3/admin/order/house-console/room-transfers`
**VO**: `HouseRoomTransferPageRespVO → HouseRoomTransferRespVO`
#### 使用场景
房务控制台「退团转房」页面加载列表与汇总。房务查看待处理转房行、其状态与无法转出时的原因。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| pageNo | Query | Integer | ✅ | ≥1 | 页码 |
| pageSize | Query | Integer | ✅ | 1~100 | 每页条数 |
| status | Query | String | ❌ | PENDING / TRANSFERRED / CANCELLED | 状态筛选;缺省 PENDING |
| groupBatchId | Query | Long(string) | ❌ | - | 团期 ID 过滤(来源团期) |
| cityName | Query | String | ❌ | - | 城市名称过滤 |
| risk | Query | String | ❌ | NORMAL / NEAR / OVERDUE / NO_DEADLINE | 风险过滤 |
#### 出参
| 字段 | 类型 | 说明 |
|------|------|------|
| records | List[HouseRoomTransferRespVO] | 分页行 |
| total | Long | 总行数 |
| page | Integer | 当前页码 |
| pageSize | Integer | 每页条数 |
| summary | Object | 待处理汇总(以间为单位) |
| summary.pendingRooms | Integer | 待处理总间数 |
| summary.overdueRooms | Integer | 已过免费取消期限的间数 |
| summary.nearRooms | Integer | 临近期限的间数 |
| summary.noDeadlineRooms | Integer | 未设期限的间数 |
**行数据关键字段**(HouseRoomTransferRespVO):
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long(string) | 转房行 ID |
| sourceType | String | 来源类型:ORDER(常规单)/ GROUP_BATCH(团期) |
| sourceOrderId | Long(string) | 源订单 ID(团期来源时为离团子单) |
| sourceGroupBatchId | Long(string) | 源团期 ID(仅团期来源时有值) |
| teamNo | String | 源订单团号(常规单来源时取 order_main.team_no;团期来源时为 null) |
| sourceBatchNo | String | 源团期批次号(仅团期来源时有值) |
| stayDate | Date | 入住日期(yyyy-MM-dd) |
| cityName | String | 城市名 |
| hotelName | String | 酒店名 |
| roomTypeName | String | 房型名 |
| roomCount | Integer | 原始间数 |
| remainingCount | Integer | 剩余待处理间数 |
| status | String | 状态:PENDING(待处理)/ TRANSFERRED(已转出)/ CANCELLED(已取消) |
| statusLabel | String | 状态中文 |
| cancelDays | Integer | 免费取消提前天数(未设为 null) |
| deadlineAt | LocalDateTime | 免费取消截止时刻(未设为 null) |
| risk | String | 风险码:NORMAL / NEAR / OVERDUE / NO_DEADLINE(仅 PENDING 有意义) |
| riskLabel | String | 风险中文 |
| readOnly | Boolean | 是否对当前操作人只读:源单由他人处理,或源团期两个动作都不允许 |
| readOnlyReason | String | 只读理由 |
| **transferAllowed** | **Boolean** | **✨ 新增:是否可转出(I-20)。PENDING 行仅源团期资源准备中为 true,招募中为 false(仍可向酒店取消),其余阶段为 false。已处理行同样计算但不作展示依据** |
| **transferBlockedReason** | **String** | **✨ 新增:转出置灰提示。可转出时为 null;招募中为「所在团期招募中,仅可向酒店取消」;其余不可处理阶段与 readOnlyReason 相同** |
#### 请求示例
```http
GET /v3/admin/order/house-console/room-transfers?pageNo=1&pageSize=20&status=PENDING&groupBatchId=2105934717486563329
Authorization: Bearer <token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"id": "2105935060039565313",
"sourceType": "GROUP_BATCH",
"sourceOrderId": "2105934624691712001",
"sourceGroupBatchId": "2105934717486563329",
"teamNo": null,
"sourceBatchNo": "T5-260928-01",
"stayDate": "2027-05-12",
"cityName": "海拉尔",
"hotelName": "阿尔善国际维景度假温泉酒店",
"roomTypeName": "高级套房",
"roomCount": 2,
"remainingCount": 2,
"status": "PENDING",
"statusLabel": "待处理",
"cancelDays": 3,
"deadlineAt": "2027-05-11 18:00:00",
"risk": "NORMAL",
"riskLabel": "正常",
"readOnly": false,
"readOnlyReason": null,
"transferAllowed": false,
"transferBlockedReason": "所在团期招募中,仅可向酒店取消"
},
{
"id": "2105935060047953921",
"sourceType": "GROUP_BATCH",
"sourceOrderId": "2105934624699100225",
"sourceGroupBatchId": "2105934717486563329",
"teamNo": null,
"sourceBatchNo": "T5-260928-01",
"stayDate": "2027-05-13",
"cityName": "海拉尔",
"hotelName": "阿尔善国际维景度假温泉酒店",
"roomTypeName": "标准间",
"roomCount": 3,
"remainingCount": 2,
"status": "PENDING",
"statusLabel": "待处理",
"cancelDays": null,
"deadlineAt": null,
"risk": "NO_DEADLINE",
"riskLabel": "未设期限",
"readOnly": false,
"readOnlyReason": null,
"transferAllowed": false,
"transferBlockedReason": "所在团期招募中,仅可向酒店取消"
}
],
"total": 3,
"page": 1,
"pageSize": 20,
"summary": {
"pendingRooms": 7,
"overdueRooms": 0,
"nearRooms": 2,
"noDeadlineRooms": 5
}
},
"success": true
}
```
#### 空数据 / 降级响应
当无符合条件的行时,`records` 为空数组,`total=0`;`summary` 按 `status=PENDING` 的全库统计(不受筛选影响)。接口 GET 查询无降级;异常时返回 HTTP 500 + 500001。
#### 错误响应
```json
{
"code": 400,
"message": "分页参数不合法",
"data": null,
"success": false
}
```
#### 业务边界
- `transferAllowed=false` 时前端应禁用「转出」按钮,显示 `transferBlockedReason` 作置灰提示,但保留「向酒店取消」按钮可用。
- `transferAllowed=true` 时 `transferBlockedReason` 恒为 null;前端可隐藏转出提示。
- `readOnly=true` 时两个按钮都禁用,提示来自 `readOnlyReason`(源单由他人处理);此时 `transferAllowed` 同样为 false 但提示词不同(后者指阶段,前者指权限)。
- 已处理行(status=TRANSFERRED / CANCELLED)虽然 `transferAllowed` 同样计算,但不作展示依据;前端按 `status` 判断是否展示操作区域。
---
### 2. 转出 `POST /v3/admin/order/house-console/room-transfers/{id}/transfer`
**VO**: `HouseRoomTransferSaveReqVO → HouseRoomTransferRespVO`
#### 使用场景
房务把待处理的退团房间转给另一张常规订单或另一个团期。只有源团期处于资源准备中时可转,招募中及其他阶段被拒。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| id | Path | Long(string) | ✅ | 存在且 status=PENDING | 转房行 ID |
| targetType | Body | String | ✅ | ORDER / GROUP_BATCH | 目标类型(常规单/团期) |
| targetOrderId | Body | Long(string) | 条件 | 当 targetType=ORDER 时必填 | 目标订单 ID |
| targetRequirementId | Body | Long(string) | 条件 | 当 targetType=ORDER 时必填 | 目标需求 ID |
| targetGroupBatchId | Body | Long(string) | 条件 | 当 targetType=GROUP_BATCH 时必填 | 目标团期 ID |
| roomCount | Body | Integer | ✅ | ≥1,≤剩余待处理间数 | 转出间数 |
| hotelConfirmNo | Body | String | ✅ | - | 酒店确认号 |
| proofFileIds | Body | List[String] | ✅ | 非空列表 | 凭证文件 ID 列表 |
| remark | Body | String | ❌ | - | 备注 |
#### 出参
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long(string) | 转房行 ID(与入参相同) |
| status | String | 更新后状态:PENDING(部分转出)或 TRANSFERRED(全部转出) |
| remainingCount | Integer | 更新后剩余待处理间数 |
| targetType | String | 目标类型 |
| targetOrderId | Long(string) | 目标订单 ID(可为 null) |
| targetTeamNo | String | 目标订单团号(可为 null) |
| targetGroupBatchId | Long(string) | 目标团期 ID(可为 null) |
| targetBatchNo | String | 目标团期批次号(可为 null) |
| handledAt | LocalDateTime | 处理时间 |
| handlerName | String | 处理人姓名 |
(其余字段同 I-17 出参)
#### 请求示例
```json
{
"targetType": "GROUP_BATCH",
"targetGroupBatchId": "2105928497547640834",
"roomCount": 2,
"hotelConfirmNo": "HX20270513002",
"proofFileIds": ["1007", "1008"],
"remark": "退团户要求转给该团期同城"
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"id": "2105935060047953921",
"status": "PENDING",
"remainingCount": 1,
"targetType": "GROUP_BATCH",
"targetOrderId": null,
"targetGroupBatchId": "2105928497547640834",
"targetBatchNo": "T3-260928-01",
"handledAt": "2026-10-02 14:30:20",
"handlerName": "房务A"
},
"success": true
}
```
#### 空数据 / 降级响应
无空数据场景(成功返回处理后的行)。无降级;异常时返回 HTTP 500 + 500001。
#### 错误响应
```json
{
"code": 808327,
"message": "退团房所在团期当前阶段(招募中)不允许处理",
"data": null,
"success": false
}
```
**可能的错误码**:
| 错误码 | 含义 | 触发条件 |
|--------|------|---------|
| 808320 | 转房记录不存在 | 行 ID 不存在或已软删 |
| 808321 | 该房间已处理 | 行 status ≠ PENDING,或并发下被先抢 |
| 808322 | 目标不可转入 | 不同城市、不同入住日期、无团号、或无转入空房 |
| 808323 | 目标团期当前阶段({0})不能转入,仅资源准备中可转入 | 仅 GROUP 目标阶段不符时出现;{0} 为**目标**团期状态中文 |
| 808324 | 转出间数超过剩余 {0} 间 | 转出间数 > 源计划剩余间数,或 > 目标缺口 |
| 808325 | 请填写酒店确认号并上传凭证 | hotelConfirmNo 或 proofFileIds 缺失 |
| 808326 | 只有原单处理人可以处理退团房间 | 操作人不是源单房务处理人 |
| 808327 | 退团房所在团期当前阶段({0})不允许处理 | 源团期阶段不符(招募中、物料准备中、已出发等);{0} 为**源**团期状态中文 |
#### 业务边界
- 源团期只在「资源准备中」允许转出;「招募中」返回 808327;「物料准备中」或更后期阶段同样返回 808327,文案显示实际阶段中文。
- 目标团期(仅 GROUP_BATCH)只在「资源准备中」允许转入;其他阶段返回 808323,文案显示**目标**阶段中文。
- 目标团期不存在返回 808322(不走 808323)。
- 转出后,如果 remainingCount=0 则行 status 变为 TRANSFERRED;否则保持 PENDING。
- 幂等键为「操作人 + 行 ID + 转出摘要」,3 秒内重复提交相同目标和间数只记一次。
---
### 3. 向酒店取消 `POST /v3/admin/order/house-console/room-transfers/{id}/cancel-hotel`
**VO**: `HouseRoomTransferCancelReqVO → HouseRoomTransferRespVO`
#### 使用场景
房务不再转房,直接向酒店通知取消,退款给客户。源团期处于资源准备中或招募中时均允许执行;其他阶段(物料准备中、已出发等)被拒。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| id | Path | Long(string) | ✅ | 存在且 status=PENDING | 转房行 ID |
| cancelFee | Body | BigDecimal | ✅ | ≥0,精确到 2 位小数 | 取消费用(元;0 表示免费取消) |
| proofFileIds | Body | List[String] | ✅ | 非空列表 | 凭证文件 ID 列表(酒店回执) |
| remark | Body | String | ❌ | - | 备注 |
#### 出参
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long(string) | 转房行 ID(与入参相同) |
| status | String | 更新后状态:CANCELLED |
| cancelReason | String | 取消原因:HOTEL_CANCELLED(人工向酒店取消) |
| cancelFee | BigDecimal(string) | 取消费用 |
| handledAt | LocalDateTime | 处理时间 |
| handlerName | String | 处理人姓名 |
(其余字段同 I-17 出参)
#### 请求示例
```json
{
"cancelFee": "0.00",
"proofFileIds": ["1007"],
"remark": "酒店同意免费取消,按规则办理退订"
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"id": "2105935060039565313",
"status": "CANCELLED",
"cancelReason": "HOTEL_CANCELLED",
"cancelFee": "0.00",
"handledAt": "2026-10-02 13:45:30",
"handlerName": "房务A"
},
"success": true
}
```
#### 空数据 / 降级响应
无空数据场景(成功返回处理后的行)。无降级;异常时返回 HTTP 500 + 500001。
#### 错误响应
```json
{
"code": 808327,
"message": "退团房所在团期当前阶段(物料准备中)不允许处理",
"data": null,
"success": false
}
```
**可能的错误码**:
| 错误码 | 含义 | 触发条件 |
|--------|------|---------|
| 808320 | 转房记录不存在 | 行 ID 不存在或已软删 |
| 808321 | 该房间已处理 | 行 status ≠ PENDING,或并发下被先抢 |
| 808325 | 请填写酒店确认号并上传凭证 | proofFileIds 缺失或为空 |
| 808326 | 只有原单处理人可以处理退团房间 | 操作人不是源单房务处理人 |
| 808327 | 退团房所在团期当前阶段({0})不允许处理 | 源团期阶段不符(物料准备中、已出发等);{0} 为**源**团期状态中文 |
本接口不返回 808323(无目标概念)。
#### 业务边界
- 源团期在「资源准备中」或「招募中」均允许取消;「物料准备中」、「已出发」等阶段返回 808327。
- 取消成功后,行 status 变为 CANCELLED,cancelReason=HOTEL_CANCELLED。
- 源计划对应间数被软删(plan_deleted_at 标记);如有剩余间数,生成新计划记录「新间数=原间数-取消间数」。
- 幂等键为「操作人 + 行 ID」,3 秒内重复提交只记一次。
- 取消费用为 0 时表示酒店同意免费取消;>0 时表示需客户或预留从团费扣除。
---
## 四、契约约束与正确调用方式
### 常规订单来源的行
常规单退团产生的转房行,sourceType=ORDER,sourceOrderId 是离团子单,teamNo 从 order_main 反查。
### 团期来源的行
团期成团过程中由房务释放的转房行,sourceType=GROUP_BATCH,sourceOrderId 为离团子单(可为 null),teamNo 恒为 null(团期无团号),sourceGroupBatchId + sourceBatchNo 标识来源团期。
### transferAllowed 与 transferBlockedReason 联用
前端根据 transferAllowed 控制转出按钮:
- `true`:按钮可点,transferBlockedReason 为 null,不显示置灰提示;
- `false` + `transferBlockedReason="所在团期招募中,仅可向酒店取消"`:转出禁用,显示该提示,保留取消按钮可用;
- `false` + `transferBlockedReason` 为其他值(如"物料准备中"等):整行只读,两个按钮都禁用。
### 源/目标团期阶段判定
- **I-21 向酒店取消**:源团期 `RESOURCE_PREPARING` || `RECRUITING` 放行;其余返回 808327;
- **I-20 转出**:仅源团期 `RESOURCE_PREPARING` 放行;其余返回 808327;目标团期(仅 GROUP_BATCH)仅 `RESOURCE_PREPARING` 放行,其余返回 808323;
- **I-17 列表**:`transferAllowed` 与 `transferBlockedReason` 由业务侧编排返回(无存储),实时计算。
---
## 五、数据库行为
- 无新增或删除字段;
- 无表结构变更、无 Flyway。源团期阶段判定在服务层完成:源团期 `RECRUITING` 时 I-21 向酒店取消照常写 `house_room_transfer`(状态转 CANCELLED、原因 HOTEL_CANCELLED),I-20 转出在写库前即返回 808327、不落任何行;
- I-17 列表行返回的 `transferAllowed` 与 `transferBlockedReason` 由业务侧实时计算,无存储(源团期状态通过 order_group_batch.batch_status 关联查询);
- 操作日志(`house_operation_log`)记录处理人、操作类型、摘要,支持二期团期转房审计。
---
## 六、边界行为
### 6.1 业务边界
**I-17 列表**:
- `transferAllowed=false` 时前端应禁用「转出」按钮,显示 `transferBlockedReason` 作置灰提示,但保留「向酒店取消」按钮可用;
- `transferAllowed=true` 时 `transferBlockedReason` 恒为 null;前端可隐藏转出提示;
- `readOnly=true` 时两个按钮都禁用,提示来自 `readOnlyReason`(源单由他人处理);此时 `transferAllowed` 同样为 false 但提示词不同;
- 已处理行(status=TRANSFERRED / CANCELLED)虽然 `transferAllowed` 同样计算,但不作展示依据;前端按 `status` 判断是否展示操作区域。
**I-20 转出**:
- 源团期只在「资源准备中」允许转出;「招募中」返回 808327;「物料准备中」或更后期阶段同样返回 808327,文案显示实际阶段中文;
- 目标团期(仅 GROUP_BATCH)只在「资源准备中」允许转入;其他阶段返回 808323,文案显示**目标**阶段中文;
- 目标团期不存在返回 808322(不走 808323);
- 转出后,如果 remainingCount=0 则行 status 变为 TRANSFERRED;否则保持 PENDING;
- 幂等键为「操作人 + 行 ID + 转出摘要」,3 秒内重复提交相同目标和间数只记一次。
**I-21 向酒店取消**:
- 源团期在「资源准备中」或「招募中」均允许取消;「物料准备中」、「已出发」等阶段返回 808327;
- 取消成功后,行 status 变为 CANCELLED,cancelReason=HOTEL_CANCELLED;
- 源计划对应间数被软删(plan_deleted_at 标记);如有剩余间数,生成新计划记录「新间数=原间数-取消间数」;
- 幂等键为「操作人 + 行 ID」,3 秒内重复提交只记一次;
- 取消费用为 0 时表示酒店同意免费取消;>0 时需客户或从团费扣除;
- 本接口不返回 808323(无目标概念)。
## 六.6、修改前后对比
| 场景 | 修改前 | 修改后 |
|------|--------|--------|
| I-21 源团期招募中 | 返回 808323「目标团期已确认」(误导) | 放开执行;操作成功 |
| I-20 源团期招募中 | 返回 808323「目标团期已确认」 | 返回 808327「源团期阶段…招募中…不允许」(指向源侧) |
| I-20 源团期物料准备中 | 返回 808323「已确认」 | 返回 808327 + 源团期实际阶段中文 |
| I-17 行字段 | 无转出可行性标记 | 新增 transferAllowed + transferBlockedReason |
| 错误码 808323 使用 | 同时用于源、目标阶段不符 | 仅用于 GROUP 目标阶段不符,文案明确含「目标」 |
| 错误码 808327 | 无此码 | 新增,用于源团期阶段不符,文案明确含「源」与实际阶段 |
## 六.7、影响评估
**前端**:
- 需适配 I-17 行数据新增的两字段:transferAllowed 控制转出按钮状态,transferBlockedReason 作置灰提示文案;
- I-20、I-21 错误码文案调整,需更新各 toast/提示的映射(808323 专用于「目标团期阶段」,808327 专用于「源团期阶段」);
- 招募中场景下转出被拒(808327)与向酒店取消成功的对比体验,前端按新码分流处理。
**后端**:
- 代码层修改集中在 HouseRoomTransferManager 业务判定与 HouseRoomTransferRespVO 返回值构造,无 DB 迁移;
- 源团期状态 == 招募中时,I-21 放行、I-20 拦;由 GroupBatchStatus 枚举与 RECRUITING 常量驱动,存存逻辑一致;
- 错误文案由 HouseConsoleErrorCode 808323 与 808327 的 `{0}` 占位符承载,无需前端约定新码段。
**回滚**:
- PR revert 即可恢复旧逻辑(代码无迁移);新VO字段 transferAllowed / transferBlockedReason 前端如若忽视不显示,旧 UI 仍可用(字段补齐不减少现有消费)。
---
## 七、不影响范围
- I-18(修改期限)、I-19(候选目标)、I-22(异常检查)三个端点不受影响;
- 历史转房行数据不回填新字段(列表行是实时计算,非持久化);
- 常规单来源的转房行逻辑无变化(仅团期来源的招募中场景放开);
- 其他团期阶段(资源准备中、物料准备中、已出发等)的行为不变。
---
## 八、测试环境已验证
**部署与验证**:
- commit 704ecdd887;部署状态 `STATE=ok`;
- 测试端点:network 路由验证 / HTTP 状态码验证 / 业务返回码验证。
**AC-4 通过**:I-21 源团期 RECRUITING 下取消成功
- 前置:T5 团期先 RESOURCE_PREPARING 后成团变 RECRUITING;3 条 PENDING 行;
- 请求:`POST /v3/admin/order/house-console/room-transfers/2105935060039565313/cancel-hotel` body `{cancelFee:"0.00", proofFileIds:[1007], ...}`;
- 响应:HTTP 200,行 status=CANCELLED。
**AC-5 通过**:I-20 源团期 RECRUITING 下转出返回 808327
- 前置:同上 T5 RECRUITING;
- 请求:`POST /v3/admin/order/house-console/room-transfers/2105935060047953921/transfer` 转给常规单;
- 响应:HTTP 200 code=808327 msg="退团房所在团期当前阶段(招募中)不允许处理"。
**AC-9 通过**:I-20 目标侧 808323 文案含目标团期阶段
- 前置:源 T5 RESOURCE_PREPARING,目标 T3 RECRUITING;
- 请求:`POST .../transfer` targetType=GROUP_BATCH;
- 响应:HTTP 200 code=808323 msg="目标团期当前阶段(招募中)不能转入,仅资源准备中可转入"(文案含**目标**);
- I-21 同源行同时刻调用返回 200(无目标概念,不返回 808323)。
**AC-10 通过**:列表行字段
- RECRUITING 期间三行:transferAllowed=false,transferBlockedReason="所在团期招募中,仅可向酒店取消";
- 重新成团回 RESOURCE_PREPARING 后:剩余两行 transferAllowed=true,transferBlockedReason=null。
---
## 十、相关文档
- 后端对接文档:`API-SPEC-HOUSE` v1.1.21 §12.10(I-20 转房)与 §12.11(I-21 取消);
- 错误码说明:`HouseConsoleErrorCode` 808320-808327;
- VO 定义:`HouseRoomTransferRespVO` / `HouseRoomTransferSaveReqVO` / `HouseRoomTransferCancelReqVO`;
- 业务实现:`HouseRoomTransferManager` 与 `HouseRoomTransferQueryManager`。
---
## 关联 / 联系人
- **后端负责人**: @wx
- **Issue**: https://git.1814.love/wx/HL/issues/8711
- **PR**: https://git.1814.love/wx/HL/pulls/8730
- **部署日期**: 2026-10-02
- **测试报告**: `D:/work2/_scratch/0928-house-proto/t20/bug-transfer-orphan/api-test/report.md`