diff --git a/changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md b/changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md index 92aaded1..e43c4edd 100644 --- a/changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md +++ b/changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md @@ -743,8 +743,8 @@ GET /v3/admin/order/house-console/room-transfers?status=PENDING&cityCode=海拉 |------|------|------|------|------|------| | id | Path | Long | ✅ | - | 转房行 ID | | cancelDays | Body | Integer | ❌ | 0~60;null 表示清除期限 | 入住前几天免费取消 | -| cancelCutoff | Body | String | ❌ | HH:mm;cancelDays 非空而本字段空时取 18:00;cancelDays 为 null 时本字段被忽略、一并清空 | 截止当天的时刻 | -| remindDays | Body | Integer | ❌ | 0~30;cancelDays 非空而本字段空时取 1;cancelDays 为 null 时本字段被忽略、一并清空 | 提前几天提醒 | +| cancelCutoff | Body | String | ❌ | HH:mm;cancelDays 非空而本字段空时取 18:00;cancelDays 为 null 时本字段被忽略,**该行原值保留、不置空**(#8597 订正) | 截止当天的时刻 | +| remindDays | Body | Integer | ❌ | 0~30;cancelDays 非空而本字段空时取 1;cancelDays 为 null 时本字段被忽略,**该行原值保留、不置空**(#8597 订正) | 提前几天提醒 | #### 出参 `Result` @@ -784,7 +784,7 @@ GET /v3/admin/order/house-console/room-transfers?status=PENDING&cityCode=海拉 #### 空数据 / 降级响应 -`cancelDays` 传 null 表示清除期限:`cancelDays` / `cancelCutoff` / `remindDays` 三列一并置空,返回行的 `deadlineAt=null`、`risk=NO_DEADLINE`、`riskLabel="未设免费取消期"`。 +`cancelDays` 传 null 表示清除期限:**只有 `cancelDays` 变成 null**,`cancelCutoff` / `remindDays` 保留该行原值(从未设过期限时是默认的 `"18:00"` / `1`);返回行的 `deadlineAt=null`、`risk=NO_DEADLINE`、`riskLabel="未设免费取消期"`。判「有没有设期限」只看 `cancelDays === null` 或 `risk === "NO_DEADLINE"`,不要看 `cancelCutoff` / `remindDays` 是否为 null(#8597 订正)。 #### 错误响应 @@ -1620,7 +1620,7 @@ DELETE /v3/admin/order/house-console/stay-templates/1839000000000000001 | 模板保存 / 删除 | `house_stay_template` | 新建 / 整份覆盖;删除为软删 | | 团期转交 | 团期认领字段、团期时间线 | CAS 换持有人,写时间线;计划与分房不动 | -**显式 SET NULL 说明**: 设置期限时 `cancelDays` 传 null 会把 cancel_days / cancel_cutoff / remind_days 三列一并清空,`deadlineAt` 随之为 null;其余写接口未传的可选字段保持原值,不会被写成 null。 +**显式 SET NULL 说明**: 设置期限时 `cancelDays` 传 null **只把免费退改天数清空**,截止时刻与提醒天数保留该行原值(两列非空),`deadlineAt` 随之为 null(#8597 订正);其余写接口未传的可选字段保持原值,不会被写成 null。 --- diff --git a/changelogs-v2/2026-09/30_8597_清空退团房退改期限保留截止时刻与提醒天数并补回已取消源单的退团房待办-修改接口-管理后台.md b/changelogs-v2/2026-09/30_8597_清空退团房退改期限保留截止时刻与提醒天数并补回已取消源单的退团房待办-修改接口-管理后台.md new file mode 100644 index 00000000..f9c462ea --- /dev/null +++ b/changelogs-v2/2026-09/30_8597_清空退团房退改期限保留截止时刻与提醒天数并补回已取消源单的退团房待办-修改接口-管理后台.md @@ -0,0 +1,451 @@ +--- +schema: "hl-changelog/v2" +ticket: "8597" +title: "退团房清空免费退改期限:截止时刻与提醒天数保留原值(订正 #8491 交接件三处表述),异常检查补回源单已取消的退团房待办" +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: "PR #8599(提交 ff68637542)已合入 dev-v3;测试服 hl-order-service-v3 运行提交 d57498d38、BEHIND 0/N、STATE ok、2026-09-30 05:32:57,ff68637542 是 d57498d381 的祖先。两个端点的路径与方法均未变,网关 /v3/admin/** 路由块已通配,零网关改动。" +updated_at: "2026-09-30" +base: "dev-v3" +--- + +# 房务控制台: 退团房期限清空口径订正 + 异常检查退团房待办补全 + +> **存放目录**: 二期 → `changelogs-v2/2026-09/` +> +> **服务**: hl-order-service-v3 +> **Issue**: #8597 +> **PR**: #8599 +> **日期**: 2026-09-30 +> **影响范围**: 管理后台「房务控制台」的「退团房」页签(设期限弹窗)与「异常检查」页签(待办列表) + +--- + +## ⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写) + +- 🔴 **订正 #8491 交接件的三处表述**:`PUT .../room-transfers/{id}/deadline` 传 `cancelDays: null` 清除期限时,**只有 `cancelDays` 变成 `null`,`cancelCutoff` 与 `remindDays` 保留该行原值,不会被置空**。`changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md` 的第 746、747 行(入参表)、第 787 行(空数据 / 降级响应)、第 1623 行(显式 SET NULL 说明)写的「三列一并清空 / 本字段被忽略、一并清空」与实际行为不符,以本条为准;该文件第 1847 行的测试环境读数(`cancel_cutoff / remind_days 保留原值`)才是正确的那一条。 +- 🔴 **判「这一行有没有设免费退改期限」只能看 `cancelDays === null` 或 `risk === "NO_DEADLINE"`**,不能看 `cancelCutoff` / `remindDays` 是否为 `null`——清空后这两个字段仍有值(该行从未设过期限时是建表默认的 `"18:00"` 与 `1`)。 +- **清除期限从「必定失败」改为成功**:本次改动前,`cancelDays: null` 的请求 100% 返回 **808932**「房务状态已被并发修改,请刷新后重试」(并非真的并发冲突),现在返回 `code=200` 并给出更新后的整行。 +- **异常检查 `tasks[]` 的 `TRANSFER_PENDING` 条目不再漏行**:待处理退团房行的窗口归属改为只看源团期 / 源订单的**出发日**,**不看源单状态**;源单查不到、或源单出发日为空时同样列出。取消订单恰恰是产生退团房最常见的原因,订正前这些待办在控制台唯一的待办载体上看不到。**同一窗口下本端点返回的 `tasks` 行数会比订正前多。** + +--- + +## 一、背景(选填) + +退团房行有两处独立缺陷,都发生在「房务控制台」已交付的端点上: + +1. 清期限走的乐观锁写口,其入参守卫要求截止时刻与提醒天数非空(两列在库中是 NOT NULL);旧代码在 `cancelDays` 为 `null` 时把这两项也一起传 `null`,守卫直接返回「影响行数 0」,上层把 0 当成版本冲突抛 808932。表现是「清除期限」按钮永远失败,而错误文案指向刷新重试,看不出是入参问题。 +2. 异常检查的退团房待办原先用「窗口内的团期集合 / 散单集合」判归属,这两个集合按占用口径排除了已取消的单,于是「订单取消 → 释放房间 → 待转出」这条最常见的链路产出的待办从不出现。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 设置 / 清除退团房免费退改期限 | PUT | `/v3/admin/order/house-console/room-transfers/{id}/deadline` | 修改 | 清除期限由必定失败改为成功;`cancelCutoff` / `remindDays` 保留原值(订正交接件表述) | +| 2 | 房务异常检查 | GET | `/v3/admin/order/house-console/audit` | 修改 | `tasks[]` 的 `TRANSFER_PENDING` 按源单出发日归属、不看源单状态,补回源单已取消的行 | + +--- + +## 三、接口详情 + +### 1. 设置 / 清除退团房免费退改期限 `PUT /v3/admin/order/house-console/room-transfers/{id}/deadline` + +**VO**: `HouseRoomTransferDeadlineSaveReqVO → HouseRoomTransferRespVO` + +#### 使用场景 + +「退团房」页签某一行点「设期限」,录入酒店给的免费取消规则(入住前几天、当天几点前)与提前几天提醒;也用于把已设的期限清掉。清掉后该行的风险分档变为 `NO_DEADLINE`,前端应按 `cancelDays` 是否为 `null` 渲染「未设免费取消期」,而不是按 `cancelCutoff` / `remindDays` 是否有值判断。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | ✅ | 必须是退团房**父行**(子行是转出明细,不可改期限);不存在或已删返 808320 | 退团房行 ID | +| cancelDays | Body | Integer | ❌ | 0~60,越界返 808328;传 `null` 表示清除期限 | 入住前几天可免费取消 | +| cancelCutoff | Body | String | ❌ | `HH:mm` 24 小时制(`00:00`~`23:59`),格式不符返 808328;`cancelDays` 非空而本字段为空 / 空白时取 `18:00`;**`cancelDays` 为 `null` 时本字段被忽略,该行原值保留** | 截止当天的时刻 | +| remindDays | Body | Integer | ❌ | 0~30,越界返 808328;`cancelDays` 非空而本字段为空时取 `1`;**`cancelDays` 为 `null` 时本字段被忽略,该行原值保留** | 提前几天提醒 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| (整行) | HouseRoomTransferRespVO | 更新后的该退团房父行,字段与退团房分页 `records[]` 同构 | +| cancelDays | Integer | 入住前几天免费取消;**清除期限后为 `null`** | +| cancelCutoff | String | 截止当天的时刻 `HH:mm`;**清除期限后仍是该行原值(从未设过则是 `"18:00"`),不会变成 `null`** | +| remindDays | Integer | 提前几天提醒;**清除期限后仍是该行原值(从未设过则是 `1`),不会变成 `null`** | +| deadlineAt | LocalDateTime | 免费取消截止时刻 = 入住晚 − `cancelDays` 天的 `cancelCutoff`;`cancelDays` 为 `null` 或缺入住晚时为 `null` | +| risk | String | 风险码 `OVERDUE` / `NEAR` / `NO_DEADLINE` / `NORMAL`;仅 `PENDING` 行有值,其余为 `null` | +| riskLabel | String | 风险中文;`NO_DEADLINE` 对应「未设免费取消期」 | +| status / statusLabel | String | 行状态 `PENDING` / `TRANSFERRED` / `CANCELLED` 与中文;本端点只对 `PENDING` 行成功 | +| id / sourceOrderId / sourceGroupBatchId / hotelId / roomTypeId | Long(String) | 雪花 ID,JSON 中为字符串 | +| teamNo | String | 源订单团号(批量读订单主表团号);取不到为 `null` | +| stayDate | LocalDate | 入住晚 | +| roomCount / remainingCount | Integer | 原始间数 / 剩余待处理间数 | +| readOnly / readOnlyReason | Boolean / String | 对当前操作人是否只读(源单由他人处理)与理由 | + +#### 请求示例 + +```json +PUT /v3/admin/order/house-console/room-transfers/1950000000000000001/deadline +{ + "cancelDays": null +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "id": "1950000000000000001", + "sourceType": "ORDER", + "sourceOrderId": "1930000000000000001", + "teamNo": "HL20261001A", + "sourceGroupBatchId": null, + "sourceBatchNo": null, + "stayDate": "2026-10-02", + "cityName": "海拉尔", + "hotelId": "100001", + "hotelName": "海拉尔草原酒店", + "roomTypeId": "300001", + "roomTypeName": "豪华双床房", + "roomCount": 2, + "remainingCount": 2, + "status": "PENDING", + "statusLabel": "待处理", + "cancelDays": null, + "cancelCutoff": "18:00", + "remindDays": 1, + "deadlineAt": null, + "risk": "NO_DEADLINE", + "riskLabel": "未设免费取消期", + "readOnly": false, + "readOnlyReason": null + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +- 本端点恒返回整行,没有空响应形态。 +- 清除期限(`cancelDays: null`)是正常成功路径:`data.cancelDays = null`、`data.deadlineAt = null`、`data.risk = "NO_DEADLINE"`、`data.riskLabel = "未设免费取消期"`,而 `data.cancelCutoff` 与 `data.remindDays` 仍是该行原值。 +- 源单(订单需求 / 团期)读不到时,只影响「谁是源单处理人」的判定,不影响本端点的写入结果;非源单处理人且非超管一律返 808326。 + +#### 错误响应 + +```json +{ + "code": 808328, + "message": "退改期限参数不合法", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808320 | 转房记录不存在 | `id` 不存在、已删、或不是父行 | +| 808321 | 该房间已处理 | 行已不是 `PENDING`(已转出 / 已取消) | +| 808326 | 只有原单处理人可以处理退团房间 | 非源单处理人且非超管 | +| 808328 | 退改期限参数不合法 | `cancelDays` 超 0~60、`remindDays` 超 0~30、`cancelCutoff` 不是 `HH:mm` | +| 808932 | 房务状态已被并发修改,请刷新后重试 | 真并发写冲突(行版本在锁定读与写之间被改)。**订正前 `cancelDays: null` 会恒定命中这一条,订正后不再出现这种假冲突** | +| 100502 | 修改处理中,请勿重复提交 | 3 秒幂等窗口内重复提交同一请求 | + +#### 业务边界 + +- 入参不走 Bean Validation,范围与格式错误统一以 **808328** 返回,HTTP 状态仍是 200,不是 400。 +- 清除期限只清 `cancelDays` 这一项语义;`cancelCutoff` / `remindDays` 是「下次设期限时的默认值」,保留它们不影响「有没有期限」的判定,因为 `deadlineAt` 与 `risk` 都只在 `cancelDays` 非空时才成立。 +- 该行处于 `PENDING` 才可改期限;`TRANSFERRED` / `CANCELLED` 返 808321。 +- 只有源单处理人或超管可改;源订单来源看该需求的持有人,团期来源看该团期的房务认领人。 +- 同一行的改期限、转房、向酒店取消共用一把行级锁,前端不必自己串行化。 + +### 2. 房务异常检查 `GET /v3/admin/order/house-console/audit` + +**VO**: `HouseConsoleAuditReqVO → HouseConsoleAuditRespVO` + +#### 使用场景 + +「异常检查」页签:按出发日期区间一次列出数据对不上的问题(`issues`)和还没办完的事(`tasks`)。本次订正只影响 `tasks` 里 `TRANSFER_PENDING`(退团房未结清)这一类条目的**取行范围**,字段结构未变;前端按 `refId` 跳转退团房处理页的逻辑不变。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| scope | Query | String | ❌ | `mine` / `all`,默认 `mine`;其他取值返 400 | 只作用于 `tasks`:`mine` 只留当前登录人是处理人的条目;`issues` 不受它影响 | +| departDateFrom | Query | LocalDate | ❌ | `yyyy-MM-dd`,默认今天 | 出发日期区间起(含) | +| departDateTo | Query | LocalDate | ❌ | `yyyy-MM-dd`,默认起始日 +30 天;早于起始日或跨度超 92 天返 808313 | 出发日期区间止(含) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| issues | Array | 数据不一致问题,不归属处理人,不受 `scope` 影响;无则空数组 | +| tasks | Array | 待处理事项,受 `scope` 过滤;无则空数组 | +| issues[].code / tasks[].code | String | 问题码 / 待办码,取值见「六.5、枚举」 | +| tasks[].taskCode | String | 待办码(仅 `tasks` 有值),与 `code` 同值 | +| issues[].codeLabel / tasks[].codeLabel | String | 中文标签,后端给出直接展示;`TRANSFER_PENDING` 为「退团房未结清」 | +| tasks[].orderId | Long(String) | 源订单 ID;退团房条目取该行的源订单 | +| tasks[].teamNo | String | 源订单团号;取不到为 `null` | +| tasks[].groupBatchId / tasks[].batchNo | Long(String) / String | 源团期 ID 与批次号;散单来源为 `null` | +| tasks[].stayDate | LocalDate | 入住晚 | +| tasks[].hotelId / hotelName / roomTypeId / roomTypeName | Long(String) / String | 酒店与房型 | +| tasks[].refId | Long(String) | 关联单据 ID,`TRANSFER_PENDING` 为退团房父行 ID,前端据此跳转 | +| tasks[].detail | String | 说明文案,`TRANSFER_PENDING` 为「剩余 N 间待转出或向酒店取消」 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/audit?scope=mine&departDateFrom=2026-10-01&departDateTo=2026-10-31 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "issues": [], + "tasks": [ + { + "code": "TRANSFER_PENDING", + "codeLabel": "退团房未结清", + "taskCode": "TRANSFER_PENDING", + "orderId": "1930000000000000001", + "teamNo": "HL20261001A", + "groupBatchId": null, + "batchNo": null, + "stayDate": "2026-10-02", + "hotelId": "100001", + "hotelName": "海拉尔草原酒店", + "roomTypeId": "300001", + "roomTypeName": "豪华双床房", + "refId": "1950000000000000001", + "detail": "剩余 2 间待转出或向酒店取消" + } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +```json +{ "code": 200, "message": "成功", "data": { "issues": [], "tasks": [] }, "success": true } +``` + +- `issues` 与 `tasks` 恒为数组,不会是 `null`。 +- 退团房条目的源单读不到(源订单或源团期查不到、或出发日为空)时,该条目**仍然列出**,`teamNo` / `batchNo` 可能为 `null`;口径是「宁可多报一条,也不让待办从唯一载体上消失」。 +- `STOCK_LEDGER_MISMATCH`(库存账不平)只在资源侧全局库存追踪开关打开时检查,开关关闭时不产出该问题码。 + +#### 错误响应 + +```json +{ + "code": 808313, + "message": "日期跨度不能超过 92 天", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | scope 取值非法 | `scope` 不是 `mine` / `all` | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808313 | 日期跨度不能超过 {0} 天 | 出发日期区间跨度超 92 天,或止日早于起日 | + +#### 业务边界 + +- `TRANSFER_PENDING` 的归属判据是**源团期 / 源订单的出发日落在窗口内**,与源单当前状态(含已取消)无关;这是本次订正的点。 +- 只列 `PENDING` 的退团房父行;已转出、已取消的行不是待办。 +- `scope=mine` 的「我的」按源单处理人判:散单来源看该需求持有人,团期来源看该团期房务认领人;源单读不到时该条目在 `mine` 下不会出现(无法判定处理人)。 +- 窗口跨度上限 92 天,与控房表查询的 62 天不是同一个上限,别复用。 +- 纯读接口,不写数据。 + +--- + +## 四、契约约束与正确调用方式(接口类必写) + +### ✅ 正确 / ❌ 错误 payload 对照 + +| 场景 | payload / 判断 | +|------|----------------| +| ✅ 设期限 | `{ "cancelDays": 3, "cancelCutoff": "18:00", "remindDays": 1 }` | +| ✅ 设期限只给天数 | `{ "cancelDays": 3 }` → 截止时刻取 `18:00`、提醒天数取 `1` | +| ✅ 清除期限 | `{ "cancelDays": null }`(或整个 body 只有 `{}`)→ 200,`cancelCutoff` / `remindDays` 保留原值 | +| ✅ 判「未设期限」 | `data.cancelDays === null`,或 `data.risk === "NO_DEADLINE"` | +| ❌ 判「未设期限」 | `data.cancelCutoff === null && data.remindDays === null`——清除期限后这两项仍有值,该判断恒为 false | +| ❌ 清除期限时显式传空 | `{ "cancelDays": null, "cancelCutoff": "", "remindDays": null }` 能成功,但 `cancelCutoff` / `remindDays` 一样被忽略,不要指望用它们清值 | +| ❌ 期限天数越界 | `{ "cancelDays": 61 }` → 808328(不是 400) | + +### 切换状态时的必要动作 + +- 清除期限成功后,前端应以返回的整行直接替换列表行,不要只把 `cancelDays` 置空——`risk` / `riskLabel` / `deadlineAt` 都由后端重算,本地推算会与「退团房分页」的汇总读数对不上。 +- 「异常检查」页签的待办条数在订正后可能增加;若页面上有与之对照的徽标计数,改为直接用本端点返回的 `tasks.length`,不要沿用按订单状态自行过滤后的口径。 + +--- + +## 五、数据库行为(涉及写操作时必写) + +| 写操作 | 外部可观察行为 | +|--------|----------------| +| 设期限(`cancelDays` 非空) | 该行的免费退改天数、截止时刻、提醒天数三项按入参(含默认值)整体更新;行版本 +1;写一条订单级操作日志「退团房 {入住晚} {酒店名} 免费退改期限改为入住前 N 天 HH:mm」 | +| 清除期限(`cancelDays` 为 `null`) | **只有免费退改天数被清空**;截止时刻与提醒天数保持该行原值;行版本 +1;写一条订单级操作日志「…免费退改期限改为未设」 | +| 并发保护 | 行级分布式锁 + 行版本比对;版本在锁定读与写之间被改则整笔回滚并返 808932 | + +- 两个写路径都不产生跨服务调用,不发消息。 +- 「异常检查」端点是纯读,不写任何数据。 + +--- + +## 六、边界行为 + +- 清除期限后再次设期限,若只传 `cancelDays`,截止时刻与提醒天数会被**重新按默认值 `18:00` / `1` 覆盖**(不是沿用清除前保留下来的那两个值);要沿用旧值必须显式回传。 +- 从未设过期限的新行,其截止时刻与提醒天数是建表默认的 `"18:00"` 与 `1`,所以「一行从没设过期限」与「设过又被清掉」在这两个字段上不可区分;唯一区分点是操作日志。 +- 风险分档 `NEAR` 按「日」比较(今天 ≥ 截止日 − 提醒天数),同一天上午和下午不会给出不同分档。 +- 退团房待办的取行只设窗口下限(入住晚不早于窗口起日),不设上限:待处理池量级小,多读回的行由源单出发日过滤掉。 +- `scope=all` 时任何房务都能看到全部待办条目,但看得见不等于能写;改期限仍按源单处理人校验(808326)。 + +--- + +## 六.5、枚举 / 数据字典(接口出现枚举时必写) + +### risk(退团房风险分档) + +| 值 | 中文(`riskLabel`) | 判据 | +|----|--------------------|------| +| `OVERDUE` | 已过免费取消期 | 现在已过截止时刻 | +| `NEAR` | 临近免费取消期 | 今天 ≥ 截止日 − 提醒天数 | +| `NO_DEADLINE` | 未设免费取消期 | `cancelDays` 为空(或该行缺入住晚) | +| `NORMAL` | 正常 | 其余 | + +> 仅 `status = PENDING` 的行有值,其余行 `risk` / `riskLabel` 均为 `null`。 + +### status(退团房行状态) + +| 值 | 中文(`statusLabel`) | 说明 | +|----|----------------------|------| +| `PENDING` | 待处理 | 还有剩余间数待转出或向酒店取消;只有这一状态能改期限 | +| `TRANSFERRED` | 已转出 | 全部间数已转给别的订单 / 团期 | +| `CANCELLED` | 已取消 | 已向酒店取消 | + +### taskCode / code(异常检查待办码,`tasks[]`) + +| 值 | 中文(`codeLabel`) | 说明 | +|----|--------------------|------| +| `TRANSFER_PENDING` | 退团房未结清 | 本次订正影响的就是这一类的取行范围 | +| `HOTEL_CANCEL_PENDING` | 原酒店待取消 | 改配留下的原订尚未确认取消 | +| `INQUIRY_PENDING` | 新订 / 变更待确认 | — | +| `STAY_UNARRANGED` | 住宿待落实 | — | + +> `issues[]` 的 `code` 取值域是另一组(`STOCK_OVERBOOKED` / `STOCK_LEDGER_MISMATCH` / `STOCK_ROW_MISSING` / `TRANSFER_TARGET_GONE` / `PLAN_COUNT_MISMATCH`),本次未变。 + +--- + +## 六.6、修改前后对比(修改/删除类接口必写,新增跳过) + +### 字段级对比 + +| 字段 | 之前交接件写的 | 现在的实际行为 | +|------|----------------|----------------| +| `data.cancelCutoff`(清除期限后) | 被一并置空为 `null` | **保留该行原值**(从未设过则为 `"18:00"`) | +| `data.remindDays`(清除期限后) | 被一并置空为 `null` | **保留该行原值**(从未设过则为 `1`) | +| `data.cancelDays`(清除期限后) | `null` | `null`(不变) | +| `data.deadlineAt`(清除期限后) | `null` | `null`(不变) | +| `data.risk` / `riskLabel`(清除期限后) | `NO_DEADLINE` / 「未设免费取消期」 | 同(不变) | +| 异常检查 `tasks[]` 结构 | — | 字段与类型均未变 | + +### 行为级对比 + +| 行为 | 之前 | 现在 | +|------|------|------| +| `PUT .../deadline` 传 `cancelDays: null` | 恒定返回 808932「房务状态已被并发修改,请刷新后重试」,期限清不掉 | 返回 200 并给出更新后的整行 | +| `PUT .../deadline` 传 `cancelDays` 非空 | 成功 | 成功(口径不变) | +| `GET .../audit` 的 `TRANSFER_PENDING` | 源订单 / 源团期已取消的退团房行不出现在 `tasks` 里 | 按源单出发日归属、不看源单状态,这些行会出现 | +| `GET .../audit` 的 `TRANSFER_PENDING`(源单查不到 / 出发日为空) | 不出现 | 出现(宁可多报一条) | +| `GET .../audit` 的 `issues[]` | — | 口径不变 | + +--- + +## 六.7、影响评估(修改/删除类必写) + +| 项 | 评估 | +|----|------| +| 需要前端改代码 | **是(1 处必改)**:凡按 `cancelCutoff` / `remindDays` 是否为 `null` 判「有没有设期限」的地方,改为按 `cancelDays === null` 或 `risk === "NO_DEADLINE"` 判。 | +| 需要前端改代码 | **可能(1 处)**:「异常检查」页签若自行按订单状态过滤过待办条目,去掉该过滤,直接用后端返回的 `tasks`。 | +| 兼容性 | 无字段增删、无类型变化、无路径与方法变化;只有取值与取行范围变化。 | +| 「清除期限」功能 | 从不可用变为可用,前端原有的 808932 报错提示分支在该场景不再触发(真并发冲突仍会返回它,不要删该分支)。 | +| 读数变化 | 同一出发日窗口下「异常检查」的待办行数只会增加或不变,不会减少。 | +| 其他消费方 | 退团房分页、转房、向酒店取消三个端点的契约未变;本次不涉及小程序端。 | + +--- + +## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面) + +- 退团房分页 `GET /v3/admin/order/house-console/room-transfers`、转入候选 `GET .../room-transfers/{id}/candidates`、转房 `POST .../room-transfers/{id}/transfer`、向酒店取消 `POST .../room-transfers/{id}/cancel-hotel`:字段与口径均未变。 +- 控房表(查询 / 调房量 / 调价 / 导出)、住宿模板、批量认领、改配取消确认、团期转交:未触及。 +- `issues[]` 的五类问题码及其判据未变。 +- 房务只读标识 `readOnly` / `readOnlyReason` 的口径未变(其口径见 #8491 的两份交接件)。 +- 通知、消息模板、跳转链接未变。 +- 无数据库结构变更,无新增 Flyway 脚本,无网关路由改动。 + +--- + +## 八、测试环境已验证 + +| 项 | 读数 | 依据 | +|----|------|------| +| hl-order-service-v3 运行的提交 | `d57498d38`,BEHIND `0/N`,STATE `ok`,时间 2026-09-30 05:32:57 | 测试服部署登记脚本 `/opt/hulalv/scripts/deploy-status.sh`,2026-09-30 本次读取 | +| 本次改动在运行的字节里 | 是 | `git merge-base --is-ancestor ff68637542 origin/dev-v3` 返回 0;`origin/dev-v3` 头为 `d57498d381` | +| 清除期限 | `cancelDays=null` → `code=200`;库里免费退改天数为 NULL,截止时刻 / 提醒天数保留原值;`risk=NO_DEADLINE` | 取证提交 `ff6863754`,读数原文记在 `changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md` 第 1847 行 | +| 设期限(回归) | `cancelDays=3` → `code=200`、该行 `risk=OVERDUE`;`cancelDays=0` + `remindDays=1` → `code=200`、`risk=NEAR` | 同上文件第 1845、1846 行,复测提交 `ff6863754` | +| 退团房待办含源单已取消的行 | 源订单已取消 + 退团房行 `PENDING` + 窗口覆盖出发日 → `code=200`,`tasks` 含 `TRANSFER_PENDING`「退团房未结清」 | 同上文件第 1861 行,取证提交 `ff6863754` | +| 用例覆盖 | `HouseRoomTransferManagerTest#updateDeadline_nullCancelDays_keepsRowCutoffAndRemind`;`HouseRoomTransferMapperMysqlTest#casUpdateDeadline_nullCancelDaysWithRowValues_writesNullAndKeepsCutoffRemind`;`HouseConsoleAuditManagerTest#audit_pendingTransferSourceOrderCancelledInWindow_listed` / `#audit_pendingTransferSourceBatchCancelledInWindow_listed` / `#audit_pendingTransferSourceOrderMissing_listed` / `#audit_pendingTransferSourceOrderDepartOutsideWindow_notListed` | 随 `ff68637542` 新增 | + +--- + +## 九、相关历史 PR(纠错 / 功能演进时必写) + +| PR / 提交 | 内容 | 与本条的关系 | +|-----------|------|--------------| +| PR #8564(`7c21cf0e40`) | 房务控制台整套(#8491),首次引入本文两个端点 | 被订正的表述出自它的交接件 | +| PR #8599(`ff68637542`) | 本条的两处修复(#8597) | 本条正文描述的就是它合入后的行为 | + +--- + +## 十、相关文档 + +- 本条 Issue:#8597(PR #8599) +- 被订正的交接件:`changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md`(接口 8 与接口 12) +- 只读标识口径:`changelogs-v2/2026-09/30_8491_房务配房接口字段与口径调整-修改接口-管理后台.md` + +--- + +## 关联 / 联系人 + +### 链接 + +- Issue: #8597 +- PR: #8599 +- 分支基线: `dev-v3` + +### 联系人 + +- 后端: wx +- 前端: mmg(管理后台)