From e66936fc5a07fb91d9368614d1a37aeca7838c45 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Fri, 2 Oct 2026 17:13:25 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20#8711=20=E6=88=BF=E5=8A=A1?= =?UTF-8?q?=E8=BD=AC=E6=88=BF=E6=8B=9B=E5=8B=9F=E4=B8=AD=E6=94=BE=E5=BC=80?= =?UTF-8?q?=E9=80=80=E7=BB=99=E9=85=92=E5=BA=97=EF=BC=8C=E6=BA=90/?= =?UTF-8?q?=E7=9B=AE=E6=A0=87=E5=9B=A2=E6=9C=9F=E9=98=B6=E6=AE=B5=E6=A0=85?= =?UTF-8?q?=E6=A0=8F=E5=88=86=E7=A0=81=20808327/808323=EF=BC=8C=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=96=B0=E5=A2=9E=20transferAllowed/transferBlockedRe?= =?UTF-8?q?ason?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- ...¡转房退给酒店招募中放开-修改接口-管理后台.md | 563 ++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 changelogs-v2/2026-10/02_8711_房务转房退给酒店招募中放开-修改接口-管理后台.md diff --git a/changelogs-v2/2026-10/02_8711_房务转房退给酒店招募中放开-修改接口-管理后台.md b/changelogs-v2/2026-10/02_8711_房务转房退给酒店招募中放开-修改接口-管理后台.md new file mode 100644 index 00000000..d7363389 --- /dev/null +++ b/changelogs-v2/2026-10/02_8711_房务转房退给酒店招募中放开-修改接口-管理后台.md @@ -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 +``` + +#### 响应示例 + +```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`