From d1ab03e7c0fc5e2e0270d4c022bb8e886f044b09 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Wed, 30 Sep 2026 11:05:20 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20#8615=20=E8=BD=AC=E6=88=BF?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E4=BD=9C=E5=BA=9F=E5=8E=9F=E5=9B=A0=E4=B8=8E?= =?UTF-8?q?=E9=80=80=E6=88=BF=E7=BB=99=E9=85=92=E5=BA=97=E5=8F=A3=E5=BE=84?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 转房列表 cancelReason 新增三个系统作废取值(本团已再分配 / 本团已撤销该晚计划 / 团期已解散) - 退房给酒店(I-21)对团期来源行补批次门禁与超量码 808324 - 附测试环境实测结论 Co-Authored-By: Claude Opus 5.5 (1M context) --- ...废原因与退房给酒店口径调整-修改接口-管理后台.md | 314 ++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 changelogs-v2/2026-09/30_8615_转房记录作废原因与退房给酒店口径调整-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/30_8615_转房记录作废原因与退房给酒店口径调整-修改接口-管理后台.md b/changelogs-v2/2026-09/30_8615_转房记录作废原因与退房给酒店口径调整-修改接口-管理后台.md new file mode 100644 index 00000000..2a95ebfc --- /dev/null +++ b/changelogs-v2/2026-09/30_8615_转房记录作废原因与退房给酒店口径调整-修改接口-管理后台.md @@ -0,0 +1,314 @@ +--- +schema: hl-changelog/v2 +ticket: "8615" +title: "退团转房:作废原因新增三个系统取值,向酒店取消追加团期栅栏与联动缩减" +consumer: admin +author: wx(GIT) +change_type: 修改接口 +backend_status: deployed +gateway_status: not_required +frontend_status: pending +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "" +updated_at: "2026-09-30" +base: dev-v3 +--- + +# 退团转房:作废原因新增三个系统取值,向酒店取消追加团期栅栏与联动缩减 + +## ⚠️ 关键变化 + +- 退团转房池列表(#8491 I-17)的 `records[].cancelReason` 由原来只有一个取值 `HOTEL_CANCELLED`,扩展为**四个**取值,新增 `GROUP_REALLOCATED` / `GROUP_PLAN_REMOVED` / `GROUP_DISBANDED` 三个由团期写口在同一事务内系统作废时写入的取值;这三种情况下 `handlerName` 恒为 `null`(无人工处理人),`handledAt` 仍会写入系统作废发生的时刻。前端按 `cancelReason` 分支展示原因文案的地方需要扩展这三个分支,否则会落到未知分支的兜底展示。 +- 退团房向酒店取消(#8491 I-21,`POST .../room-transfers/{id}/cancel-hotel`)新增两个错误码:**团期来源行**在源团期已不在「资源准备中」阶段时返回 `808323`(此前 I-21 对团期来源行不做团期状态校验,任何状态都能直接取消,改动后与转房接口 I-20 同口径);团期来源行的源计划空余不足以覆盖本行待转间数时返回 `808324`。这两个码此前只出现在 I-20 的错误表里,I-21 的错误表新增了它们。 +- 退团房向酒店取消**成功后**,若该行是团期来源行,会在同一事务内按取消的间数**同步缩减**源团期计划行的 `roomCount`(联动 `shrinkForTransfer`);这一步不改变本接口的响应结构,只影响该房间此后是否还会被团期自动分配算进可用余量。 +- 转房接口(#8491 I-20)不在本次变更接口清单内——其错误表已发布过 `808324`,本次只是把此前一个应报 `808324`(源计划空余不足)却会先报到 `808932`(并发冲突)的边界情况改为直接报 `808324`,错误码本身与文案都没有新增,属于既有契约内的分支修正,前端已有的 `808324`/`808932` 处理分支不需要改动。 + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 退团转房池分页 | GET | `/v3/admin/order/house-console/room-transfers` | 响应字段取值扩展 | `cancelReason` 新增三个系统作废取值,系统作废行 `handlerName` 为空 | +| 2 | 退团房向酒店取消 | POST | `/v3/admin/order/house-console/room-transfers/{id}/cancel-hotel` | 新增错误码 + 联动行为 | 团期来源行新增 808323/808324 校验;取消成功后同步缩减源团期计划间数 | + +## 三、接口详情 + +### 1. 退团转房池分页 `GET /v3/admin/order/house-console/room-transfers` + +**VO**: `HouseRoomTransferPageReqVO → HouseRoomTransferPageRespVO` + +#### 使用场景 + +房务控制台「退团房」页签展示当前待处理 / 已转出 / 已取消的转房行列表。本次改动只涉及列表行里 `cancelReason` 取值集合的扩展,请求参数与响应结构均未变化。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| page | Query | Integer | ❌ | 默认 1 | 页码 | +| pageSize | Query | Integer | ❌ | 1~100,默认 20 | 每页条数 | +| status | Query | String | ❌ | PENDING / TRANSFERRED / CANCELLED,默认 PENDING | 状态筛选 | +| risk | Query | String | ❌ | OVERDUE / NEAR / NO_DEADLINE / NORMAL | 风险筛选,仅对 PENDING 行有意义 | +| cityCode | Query | String | ❌ | ≤64 | 城市中文名 | +| keyword | Query | String | ❌ | ≤64 | 团号或酒店名关键词 | +| stayDateFrom | Query | LocalDate | ❌ | yyyy-MM-dd | 入住晚起(含) | +| stayDateTo | Query | LocalDate | ❌ | yyyy-MM-dd | 入住晚止(含) | + +#### 出参 + +| 字段 | 类型 | 说明 | +|------|------|------| +| records | List | 当前页转房行 | +| records[].id | Long(String) | 转房行 ID | +| records[].sourceType | String | 来源类型 ORDER / GROUP_BATCH | +| records[].sourceGroupBatchId / sourceBatchNo | Long(String) / String | 团期来源行的源团期 ID 与批次号 | +| records[].stayDate | LocalDate | 入住晚 | +| records[].hotelName / roomTypeName | String | 酒店名 / 房型名 | +| records[].roomCount | Integer | 原始间数 | +| records[].remainingCount | Integer | 剩余待处理间数;部分被系统收敛时会减少,行仍保持 PENDING | +| records[].status / statusLabel | String | PENDING / TRANSFERRED / CANCELLED 及中文 | +| records[].cancelReason | 【改动】String | 仅 CANCELLED 行有值。`HOTEL_CANCELLED`=房务人工向酒店取消(不变);新增 `GROUP_REALLOCATED`=本团已再分配、`GROUP_PLAN_REMOVED`=本团已撤销该晚计划、`GROUP_DISBANDED`=团期已解散,三者都是团期写口系统作废,见「六.5」 | +| records[].handlerName | 【改动】String | 处理人姓名;`cancelReason` 为三个新增取值之一时恒为 `null`(无人工处理人) | +| records[].handledAt | LocalDateTime | 处理时间;系统作废时同样写入作废发生的时刻,不为 null | +| records[].readOnly / readOnlyReason | Boolean / String | 对当前登录人是否只读及理由 | +| total / page / pageSize | long / int / int | 分页信息 | +| summary.pendingRooms 等 4 项 | int | 全部 PENDING 行的风险汇总,恒不受本次筛选条件影响 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/room-transfers?status=CANCELLED&cityCode=海拉尔&page=1&pageSize=20 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "records": [ + { + "id": "9100234", "sourceType": "GROUP_BATCH", "sourceOrderId": null, "teamNo": null, + "sourceTeamNo": "HLT-20261012-003", "sourceGroupBatchId": "500321", "sourceBatchNo": "HLT-20261012-003", + "stayDate": "2026-10-12", "cityName": "海拉尔", "hotelId": "60088", "hotelName": "海拉尔国际大酒店", + "roomTypeId": "70012", "roomTypeName": "高级大床房", "roomCount": 2, "remainingCount": 2, + "status": "CANCELLED", "statusLabel": "已取消", "cancelDays": 3, "cancelCutoff": "18:00", "remindDays": 1, + "deadlineAt": "2026-10-09T18:00:00", "risk": "NORMAL", "riskLabel": "正常", + "targetType": null, "targetOrderId": null, "targetTeamNo": null, "targetRequirementId": null, + "targetGroupBatchId": null, "targetBatchNo": null, "hotelConfirmNo": null, "proofFileIds": [], + "cancelFee": null, "cancelReason": "GROUP_REALLOCATED", "handlerName": null, + "handledAt": "2026-09-30T10:02:11", "remark": null, "readOnly": false, "readOnlyReason": null + } + ], + "total": 1, + "page": 1, + "pageSize": 20, + "summary": { "pendingRooms": 6, "overdueRooms": 0, "nearRooms": 1, "noDeadlineRooms": 0 } + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +```json +{ "code": 200, "message": "成功", "data": { "records": [], "total": 0, "page": 1, "pageSize": 20, "summary": { "pendingRooms": 0, "overdueRooms": 0, "nearRooms": 0, "noDeadlineRooms": 0 } }, "success": true } +``` + +#### 错误响应 + +```json +{ + "code": 400, + "message": "status 取值非法", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | status 取值非法 / risk 取值非法 | 入参校验,未变 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色,未变 | + +#### 业务边界 + +- 团期写口(分房重算、加入团期自动分房、改计划间数、删计划、团期解散)触发系统收敛时:某计划行下全部 PENDING 转房行都被覆盖,则行整体变为 CANCELLED 并写入对应 `cancelReason`;只覆盖了一部分,则行仍是 PENDING,仅 `remainingCount` 减少,`cancelReason`/`handlerName`/`handledAt` 均不变。 +- 系统作废(`cancelReason` 为 `GROUP_REALLOCATED`/`GROUP_PLAN_REMOVED`/`GROUP_DISBANDED`)的行没有人工处理人:`handlerName` 为 `null`;触发它的管理员记在操作日志里,不在本接口暴露。 +- `summary` 恒统计全部 PENDING 行,不受本次筛选条件影响。 + +### 2. 退团房向酒店取消 `POST /v3/admin/order/house-console/room-transfers/{id}/cancel-hotel` + +**VO**: `HouseRoomTransferCancelReqVO → HouseRoomTransferRespVO` + +#### 使用场景 + +退团房没有合适的转入对象时,房务直接向酒店办理取消,上传凭证并可录入取消费用。本次改动只影响**团期来源行**(`sourceType=GROUP_BATCH`):新增源团期状态与源计划余量两道校验,取消成功后联动缩减源团期计划间数;常规单来源行(`sourceType=ORDER`)的路径与校验顺序不变。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | ✅ | - | 转房行 ID | +| proofFileIds | Body | List | ✅ | 1~9 个,元素非空 | 取消凭证文件 ID | +| cancelFee | Body | BigDecimal | ❌ | ≥0,最多 2 位小数 | 取消费用(元) | +| remark | Body | String | ❌ | ≤200 字 | 备注 | + +#### 出参 + +| 字段 | 类型 | 说明 | +|------|------|------| +| (整行) | HouseRoomTransferRespVO | 取消后的该行,字段同接口 1 的 `records[]` | +| status / statusLabel | String | CANCELLED / 已取消 | +| cancelReason | String | 恒为 `HOTEL_CANCELLED`;本接口触发的取消不会写入三个系统作废取值,那三个只由团期写口的系统收敛写入 | +| hotelConfirmNo | String | 本接口不接收确认号,恒为 `null`(未变) | +| cancelFee / proofFileIds | BigDecimal(String) / List | 录入的取消费与凭证 | + +#### 请求示例 + +```json +{ + "proofFileIds": [88031, 88032], + "cancelFee": 0, + "remark": "酒店已免费取消" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "id": "9100235", "sourceType": "GROUP_BATCH", "sourceOrderId": null, "teamNo": null, + "sourceTeamNo": "HLT-20261012-004", "sourceGroupBatchId": "500322", "sourceBatchNo": "HLT-20261012-004", + "stayDate": "2026-10-15", "cityName": "满洲里", "hotelId": "60090", "hotelName": "满洲里丽景大酒店", + "roomTypeId": "70020", "roomTypeName": "行政大床房", "roomCount": 3, "remainingCount": 3, + "status": "CANCELLED", "statusLabel": "已取消", "risk": null, "riskLabel": null, + "hotelConfirmNo": null, "proofFileIds": ["88031", "88032"], "cancelFee": "0.00", + "cancelReason": "HOTEL_CANCELLED", "handlerName": "王芳", "handledAt": "2026-09-30T10:15:32", + "remark": "酒店已免费取消", "readOnly": false, "readOnlyReason": null + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本接口无列表数据;任何失败都返回非 200 的 `code`,该行状态不变。 + +#### 错误响应 + +```json +{ + "code": 808324, + "message": "转出间数超过剩余 1 间", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | 凭证最多 9 个 / 凭证文件 ID 不能为空 / 取消费用不能为负 / 取消费用最多 2 位小数 / 备注不能超过 200 字 | 入参校验,未变 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色,未变 | +| 808320 | 转房记录不存在 | id 不存在(未变;锁前锁后各判一次,同一个码) | +| 808326 | 只有原单处理人可以处理退团房间 | 非源单持有人且非超管,未变 | +| 808325 | 请填写酒店确认号并上传凭证 | 未上传凭证(本接口不要求确认号,文案沿用同一错误码),未变 | +| 808323 | 目标团期已确认,不能转入 | 【新增,仅团期来源行】源团期已不在「资源准备中」阶段。文案沿用 I-20 已发布的措辞,这里没有「目标」,实际含义是「源团期已不可再改动,不能取消这一行」 | +| 808321 | 该房间已处理 | 行已不是 PENDING,未变 | +| 808932 | 房务状态已被并发修改,请刷新后重试 | 【团期来源行新增触发场景】源团期或源计划行在读取时已不存在(并发被删/改);以及原有的 CAS 并发冲突 | +| 808324 | 转出间数超过剩余 {0} 间 | 【新增,仅团期来源行】源计划行已分配间数 > `roomCount − 本行剩余间数`,即空余不足以覆盖本行待取消间数;`{0}` = `max(roomCount − 已分配间数, 0)` | +| 100502 | 取消处理中,请勿重复提交 | 3 秒内重复提交,未变 | + +#### 业务边界 + +- 只有源单持有人或超管可操作;凭证为空返回 808325(业务码,不是 400)。 +- 校验顺序(团期来源行):808320(不锁定读)→ 808326 → 808325 → 808323(源团期栅栏)→ 808320(锁定读复验,同一个码)→ 808321 → 808932(源团期/源计划缺失)→ 808324(源计划空余不足)→ 808932(CAS 并发冲突)。常规单来源行没有 808323/808324 两步,其余顺序不变。 +- 团期来源行取消成功后,源团期计划行的 `roomCount` 会在同一事务内按取消间数同步缩减;这一步对本接口响应体不可见,影响的是该房间此后是否还计入团期自动分配的可用余量。 +- 成功后该行整行变为 CANCELLED,不再出现在待处理汇总里;已部分转出的行取消的是剩余部分。 + +## 四、契约约束与正确调用方式 + +- 接口 2 的团期来源行取消前,前端应先确认该行所属团期仍处于允许改动的阶段;若已收到 808323,不应重试,应提示房务该团期已不可再取消这一行。 +- 接口 2 收到 808324 时,`message` 里的数字是此刻可取消的上限间数(可能为 0),不代表本行 `remainingCount`;不要直接拿 `remainingCount` 去重试提交。 +- 接口 1 展示已取消行的原因文案时必须按 `cancelReason` 四个取值分支处理,不要假设该字段只有一个可能值;系统作废三种取值下 `handlerName` 为 `null` 是正常状态,不是数据缺失。 + +## 五、数据库行为 + +| 场景 | 团期计划行 `room_count` | 转房行 `status` / `remaining_count` | +|------|------|------| +| 常规单来源行(ORDER)取消 | 不涉及团期计划 | CANCELLED,其余字段照旧写入 | +| 团期来源行取消,源计划空余充足 | 按本次取消间数同步减少(`shrinkForTransfer`) | CANCELLED | +| 团期来源行取消,源计划空余不足(命中 808324) | 不变 | 不变,整体事务回滚 | +| 团期写口触发系统收敛(分房重算 / 自动分房 / 改计划间数 / 删计划 / 团期解散) | 视触发写口而定 | 全部覆盖:CANCELLED + 对应 `cancel_reason`;部分覆盖:仍 PENDING,`remaining_count` 减少 | + +## 六、边界行为 + +- 转房行不存在或已被删除 → 808320(接口 2,未变)。 +- 团期整团解散时,其下全部 PENDING 转房行在同一事务内作废(`cancelReason=GROUP_DISBANDED`),此后对这些行调用接口 2 会先命中 808321(已非 PENDING)。 +- 源团期推进出「资源准备中」阶段后,其团期来源的 PENDING 行调用接口 2 会命中 808323;同一场景下调用 I-20(转房)此前已经是 808323,两个接口现在口径一致。 +- 本单上线前已存在、且所属计划行已被删除但转房行仍是 PENDING 的存量脏数据,不会被本次新增的系统收敛机制回溯处理,仍会出现在接口 1 的列表里;对这类行调用接口 2,源团期已不在「资源准备中」时返回 808323,否则因源计划不存在返回 808932。 + +## 六.5、枚举 + +**cancelReason**(`house_room_transfer.cancel_reason`,仅 `status=CANCELLED` 时有值) + +| 取值 | 中文 | 说明 | 是否本次新增 | +|------|------|------|------| +| HOTEL_CANCELLED | 房务人工向酒店取消 | 房务通过接口 2 主动办理 | 否 | +| GROUP_REALLOCATED | 本团已再分配 | 计划行还在,但空余已不足以覆盖待转间数——本团别的户分到了这批房,或计划间数被调少 | 是 | +| GROUP_PLAN_REMOVED | 本团已撤销该晚计划 | 待转房挂的计划行已不在活跃计划里:被删除、被替换成不同房型/酒店的新行,或缩减到 0 被整行删除 | 是 | +| GROUP_DISBANDED | 团期已解散 | 流团/解散时该团全部待转房一并作废 | 是 | + +## 六.6、修改前后对比 + +**字段取值** + +| 字段 | 改前 | 改后 | +|------|------|------| +| 接口 1 `records[].cancelReason` | 仅 `HOTEL_CANCELLED` 一个取值 | 新增 `GROUP_REALLOCATED` / `GROUP_PLAN_REMOVED` / `GROUP_DISBANDED` 三个取值 | +| 接口 2 错误码集合 | 808320 / 808321 / 808325 / 808326 / 808932(另有 400 / 808090 / 100502) | 团期来源行新增 808323、808324 | + +**行为** + +| 行为 | 改前 | 改后 | +|------|------|------| +| 本团某户分到已释放的团期房 | 原 PENDING 转房行照常留在池里,房务仍可在接口 2 / I-20 继续处理,存在一房两卖风险 | 团期写口在同一事务内收敛该计划行下的 PENDING 转房行,全部覆盖则整行作废并写入对应 `cancelReason`,部分覆盖则 `remaining_count` 减少 | +| 接口 2 对团期来源行的源团期状态 | 不做校验,源团期任意状态都能直接取消 | 源团期不在「资源准备中」时返回 808323 | +| 接口 2 取消团期来源行后源计划间数 | 不变 | 按取消间数同步缩减 | +| 接口 2 团期来源行空余不足 | 无此校验 | 返回 808324,行与源计划均不写入 | + +## 六.7、影响评估 + +- 是否破坏向后兼容:否。`cancelReason` 是新增取值,不是重命名或语义变更;两个错误码是接口 2 的新增分支,不是复用/覆盖已有码的语义。 +- 前端是否必须同步上线:是。不同步的话,系统作废行在列表上会落入未处理的 `cancelReason` 分支;团期来源行取消撞上 808323/808324 时若没有对应分支,会呈现为未识别错误。 +- 前端 workaround 清理点:若此前把「`cancelReason` 非 `HOTEL_CANCELLED`」当异常兜底处理,需要改为按四个取值分别展示;否则无需清理,只需新增分支。 + +## 七、不影响范围 + +- 常规单来源行(`sourceType=ORDER`)在接口 2 的校验顺序、错误码与响应结构均未变化。 +- 接口 1 的入参、分页结构、`summary` 汇总口径未变化。 +- 转房接口 I-20 的请求/响应结构、错误码集合未新增,本单不改变其契约,仅修正一处原本应报 808324 却先报到 808932 的边界分支,不在本次变更接口清单内。 +- 房务分房重建(H11)等团期写口自身的请求/响应契约未变化,只是这些写口在检测到需要收敛的 PENDING 转房行时会按本单的规则写入新的 `cancelReason`。 + +## 八、测试环境已验证 + +测试环境已验证(部署提交 405fc8db0,验证时刻 2026-09-30 10:22~10:41): +1. 转出间数超过计划剩余:`POST /v3/admin/order/house-console/room-transfers/{id}/cancel-hotel`,body code=808324("转出间数超过剩余 0 间"),转房行与计划行均无变化。 +2. 团内户取消离团后另一户经重算分到房:`POST /v3/admin/order/{id}/cancel/pre-trip` + `POST /v3/admin/house/group-batches/{id}/allocations/rebuild`,转房行由 PENDING 变为 CANCELLED,`cancelReason=GROUP_REALLOCATED`,处理人为空,有对应操作日志。 +3. 计划仍有空余时人工办理转出:`POST .../cancel-hotel`,body code=200,转房行变 CANCELLED/HOTEL_CANCELLED,计划 roomCount 由 2 减为 0 并软删(预期)。 + +## 十、相关文档 + +- 工单 #8615 +- 参考发布契约:`changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md`(I-17 / I-20 / I-21 的既有字段与错误码定义) + +## 关联 / 联系人 + +- 关联工单:#8615 +- 关联历史 changelog:#8491