文件
hl-api-changelog/changelogs-v2/2026-10/02_8711_房务转房退给酒店招募中放开-修改接口-管理后台.md

25 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 8711 房务转房与退给酒店:招募中放开,源团期阶段栅栏按动作分码(#8711) admin wx(GIT) 修改接口 deployed verified pending 团期招募中时,「向酒店取消」放开、「转出」保持拒绝。源团期阶段不符时错误码统一用 808327 并随文案标出阶段。列表行新增两字段标示当前行能否转出,前端据此控制转出按钮与提示。 2026-10-02 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 相同

请求示例

GET /v3/admin/order/house-console/room-transfers?pageNo=1&pageSize=20&status=PENDING&groupBatchId=2105934717486563329
Authorization: Bearer <token>

响应示例

{
  "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。

错误响应

{
  "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 出参)

请求示例

{
  "targetType": "GROUP_BATCH",
  "targetGroupBatchId": "2105928497547640834",
  "roomCount": 2,
  "hotelConfirmNo": "HX20270513002",
  "proofFileIds": ["1007", "1008"],
  "remark": "退团户要求转给该团期同城"
}

响应示例

{
  "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。

错误响应

{
  "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 出参)

请求示例

{
  "cancelFee": "0.00",
  "proofFileIds": ["1007"],
  "remark": "酒店同意免费取消,按规则办理退订"
}

响应示例

{
  "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。

错误响应

{
  "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: wx/HL#8711
  • PR: wx/HL#8730
  • 部署日期: 2026-10-02
  • 测试报告: D:/work2/_scratch/0928-house-proto/t20/bug-transfer-orphan/api-test/report.md