docs(changelog): #8711 房务转房招募中放开退给酒店,源/目标团期阶段栅栏分码 808327/808323,列表新增 transferAllowed/transferBlockedReason
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -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`
|
||||
在新工单中引用
屏蔽一个用户