From a60a791132894e4925e3a43c58f37350c7e474a8 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Wed, 30 Sep 2026 06:42:04 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20#8491=20=E6=88=BF=E5=8A=A1?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=8F=B0=E6=8E=A5=E5=8F=A3=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E3=80=81=E6=97=A7=E5=88=97=E8=A1=A8=E4=B8=8B=E7=BA=BF=E3=80=81?= =?UTF-8?q?=E9=85=8D=E6=88=BF=E6=8E=A5=E5=8F=A3=E5=8F=A3=E5=BE=84=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 三份交接件:新增房务控制台接口,下线旧的房务列表接口,调整配房接口的字段与口径。每份的「测试环境已验证」一节填测试服实测读数。 Co-Authored-By: Claude Opus 5.5 (1M context) --- ...0_8491_房务控制台接口-新增接口-管理后台.md | 1889 +++++++++++++++++ ...91_房务旧列表接口下线-删除接口-管理后台.md | 614 ++++++ ...¡配房接口字段与口径调整-修改接口-管理后台.md | 1726 +++++++++++++++ 3 files changed, 4229 insertions(+) create mode 100644 changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md create mode 100644 changelogs-v2/2026-09/30_8491_房务旧列表接口下线-删除接口-管理后台.md create mode 100644 changelogs-v2/2026-09/30_8491_房务配房接口字段与口径调整-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md b/changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md new file mode 100644 index 00000000..92aaded1 --- /dev/null +++ b/changelogs-v2/2026-09/30_8491_房务控制台接口-新增接口-管理后台.md @@ -0,0 +1,1889 @@ +--- +schema: "hl-changelog/v2" +ticket: "8491" +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" +--- + +# 房务控制台: 新增 17 个管理端接口(批量认领 / 改配取消确认 / 控房表 / 退团房转房 / 异常检查 / 住宿模板 / 团期转交) + +> **存放目录**: 二期 → `changelogs-v2/2026-09/` +> +> **服务**: hl-order-service-v3 +> **Issue**: #8491 +> **日期**: 2026-09-29 +> **影响范围**: 管理后台新菜单「房务控制台」(`/housekeeper/console`)及团期抢单池的「团期转交」 + +--- + +## ⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写) + +- 新增菜单「房务控制台」,路由 `/housekeeper/console`;原菜单「待处理」(`/housekeeper/todos`)已删除。 +- 房务通知里的跳转链接改指控制台:退团房为 `/housekeeper/console?tab=transfer&id={transferId}`,团期为 `/housekeeper/console?groupBatchId={groupBatchId}`。 +- 本文 17 个接口全部为新增,路径前缀 `/v3/admin/order/house-console/**`(16 个)与 `/v3/admin/order/grab-pool/**`(团期转交 1 个)。 +- 房务人员名单取不到时,「团期转交」一律拒绝并返回 **808343**「房务人员名单暂不可用,请稍后重试」,不会像超管「整团接管」那样降级放行。 +- 住宿模板的改 / 删只允许创建人本人或超管,否则返回 **808352**「只能修改或删除自己创建的模板」。 +- 控房表是否可调房量由响应字段 `stockTrackingEnabled` 决定(对应 resource 全局开关 `hotel.stock.enabled`),前端按该字段控制「调房量」入口。 + +--- + +## 一、背景(选填) + +#8491 把房务的日常操作收拢到一个控制台:批量认领待认领的常规单需求与团期;改配(换酒店 / 减间数)后对原酒店做取消确认;按酒店 × 房型 × 入住夜查看、调整控房总数与价格并导出;处理退团释放出来的房间(设免费取消期限、转给其他订单或团期、或向酒店取消);一屏查看数据不一致与未办完的事项;按「逐晚城市 / 酒店 / 房型」保存住宿模板复用;团期持有人把整团转交给其他房务。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 改配原订取消确认 | PUT | `/v3/admin/order/house-console/changes/{changeId}/cancel-confirm` | 新增 | 上传原酒店取消凭证,HELD 转 CANCEL_CONFIRMED | +| 2 | 批量认领 | POST | `/v3/admin/order/house-console/claims/batch` | 新增 | 常规单需求与团期一次最多 50 条,逐条返回结果 | +| 3 | 控房表查询 | GET | `/v3/admin/order/house-console/room-control` | 新增 | 酒店 × 房型 × 入住夜的总数、已用、剩余、价格与团组用房 | +| 4 | 控房表调房量 | PUT | `/v3/admin/order/house-console/room-control/stock` | 新增 | 改单格控房总数,带已用间数 CAS | +| 5 | 控房表调价 | PUT | `/v3/admin/order/house-console/room-control/price` | 新增 | 按日期区间改控房价 / 结算价 | +| 6 | 控房表导出 | GET | `/v3/admin/order/house-console/room-control/export` | 新增 | xlsx,两个工作表,不含价格 | +| 7 | 退团房分页 | GET | `/v3/admin/order/house-console/room-transfers` | 新增 | 退团释放房间列表与待处理汇总 | +| 8 | 设置退团房免费取消期限 | PUT | `/v3/admin/order/house-console/room-transfers/{id}/deadline` | 新增 | 设 / 清免费取消期限与提醒天数 | +| 9 | 退团房转入候选 | GET | `/v3/admin/order/house-console/room-transfers/{id}/candidates` | 新增 | 同城同晚的订单 / 团期候选及不可转入原因 | +| 10 | 退团房转房 | POST | `/v3/admin/order/house-console/room-transfers/{id}/transfer` | 新增 | 把房间转给订单需求或团期 | +| 11 | 退团房向酒店取消 | POST | `/v3/admin/order/house-console/room-transfers/{id}/cancel-hotel` | 新增 | 上传取消凭证,行置为 CANCELLED | +| 12 | 房务异常检查 | GET | `/v3/admin/order/house-console/audit` | 新增 | issues 数据不一致 + tasks 待办 | +| 13 | 住宿模板列表 | GET | `/v3/admin/order/house-console/stay-templates` | 新增 | 全部创建人的模板,按创建时间倒序 | +| 14 | 住宿模板详情 | GET | `/v3/admin/order/house-console/stay-templates/{id}` | 新增 | 逐晚城市 / 酒店 / 房型明细 | +| 15 | 住宿模板保存 | POST | `/v3/admin/order/house-console/stay-templates` | 新增 | id 为空新建,否则修改 | +| 16 | 住宿模板删除 | DELETE | `/v3/admin/order/house-console/stay-templates/{id}` | 新增 | 软删,仅创建人或超管 | +| 17 | 团期转交 | POST | `/v3/admin/order/grab-pool/group-batches/{groupBatchId}/transfer` | 新增 | 团期持有人或超管把整团交给其他房务 | + +--- + +## 三、接口详情 + +本节示例 JSON 的字段名、类型、错误码与文案逐一取自源码;ID、日期、金额、姓名等取值为说明用的构造值。所有接口统一返回 `Result` 信封(`code` / `message` / `data` / `traceId` / `success`),示例省略 `traceId`;业务失败与入参校验失败均为 HTTP 200,靠 `code` 区分。ID 与金额字段序列化为字符串。 + +### 1. 改配原订取消确认 `PUT /v3/admin/order/house-console/changes/{changeId}/cancel-confirm` + +**VO**: `HouseAssignmentChangeConfirmReqVO → Result` + +#### 使用场景 + +订单房务详情(`GET /admin/house/orders/{orderId}`)的 `changes[]` 里 `oldStatus=HELD` 的改配记录,房务向原酒店取消后,在该记录上点「确认取消」并上传取消凭证。`changeId` 取自 `changes[].changeId`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| changeId | Path | Long | ✅ | - | 改配记录 ID | +| cancelProofFileIds | Body | List | ✅ | 1~9 个,元素非空 | 取消凭证文件 ID | +| cancelFee | Body | BigDecimal | ❌ | ≥0,最多 2 位小数 | 取消费用(元) | +| remark | Body | String | ❌ | ≤200 字 | 备注 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 成功无返回体,前端成功后重新拉取订单房务详情 | + +#### 请求示例 + +```json +{ + "cancelProofFileIds": [1930000000000000501], + "cancelFee": "120.00", + "remark": "酒店已电话确认取消" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本接口无列表数据,成功时 `data` 恒为 `null`。无降级分支:任何失败都返回非 200 的 `code`,记录不变。 + +#### 错误响应 + +```json +{ + "code": 808333, + "message": "原订已确认取消", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 400 | 请上传取消凭证 / 取消凭证 1~9 个 / 凭证文件 ID 不能为空 / 取消费用不能为负 / 取消费用最多 2 位小数 / 备注不能超过 200 字 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808332 | 变更记录不存在 | changeId 不存在 | +| 808116 | 订单未抢单, 请先抢单再配房 | 该需求无人持有 | +| 808110 | 需求不属于当前用户 | 当前登录人不是需求持有人(超管同样拒绝) | +| 808333 | 原订已确认取消 | 记录已不是 HELD,或并发确认时落后的一方 | +| 100503 | 资源被占用,请稍后重试 | 同一 changeId 正在被另一请求处理 | + +#### 业务边界 + +- 只有该需求的当前持有人能确认;超管**不豁免**,超管要确认须先把需求「指派」或「转单」给自己。 +- 只能从 `HELD` 确认一次,成功后 `oldStatus=CANCEL_CONFIRMED`;重复确认返回 808333。 +- 成功时覆盖写该记录的取消费用、凭证与备注,操作人记为确认人;不改配房行,不发通知。 +- 同一 `changeId` 在服务端串行处理(锁 30 秒),并发请求其一返回 100503 或 808333。 + +### 2. 批量认领 `POST /v3/admin/order/house-console/claims/batch` + +**VO**: `HouseConsoleBatchClaimReqVO → HouseConsoleBatchClaimRespVO` + +#### 使用场景 + +控房台待认领列表勾选多条常规单需求和 / 或团期,一次提交认领。结果逐条返回,前端按 `items[]` 标出成功与失败原因。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementIds | Body | List | ❌ | 元素非空 | 常规单住宿需求 ID 列表 | +| groupBatchIds | Body | List | ❌ | 元素非空 | 团期 ID 列表 | +| (跨字段) | Body | - | ✅ | 两列表各自去重后合计 1~50 条 | 违反时返回 400「请选择 1~50 条待认领记录」 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| successCount | Integer | 成功条数 | +| failCount | Integer | 失败条数 | +| items | List | 逐项结果,顺序:先需求后团期,各自按提交顺序去重 | +| items[].targetType | String | REQUIREMENT 常规单住宿需求 / GROUP_BATCH 团期 | +| items[].targetId | Long(String) | 需求 ID 或团期 ID | +| items[].orderId | Long(String) | 订单 ID,仅需求项有;团期项为空 | +| items[].teamNo | String | 团号;无团号为空 | +| items[].batchNo | String | 团期批次号,仅团期项有 | +| items[].success | Boolean | 是否认领成功 | +| items[].errorCode | Integer | 失败错误码;成功为空,系统异常也为空 | +| items[].errorMessage | String | 失败原因;成功为空 | + +#### 请求示例 + +```json +{ + "requirementIds": [1840000000000000001, 1840000000000000011], + "groupBatchIds": [1840000000000000002] +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "successCount": 2, + "failCount": 1, + "items": [ + { "targetType": "REQUIREMENT", "targetId": "1840000000000000001", "orderId": "1840000000000000003", "teamNo": "HL20261002A", "batchNo": null, "success": true, "errorCode": null, "errorMessage": null }, + { "targetType": "REQUIREMENT", "targetId": "1840000000000000011", "orderId": "1840000000000000013", "teamNo": "HL20261003B", "batchNo": null, "success": false, "errorCode": 808001, "errorMessage": "该需求已被其他房务认领或状态已变化,请刷新后重试" }, + { "targetType": "GROUP_BATCH", "targetId": "1840000000000000002", "orderId": null, "teamNo": null, "batchNo": "GB20261001-01", "success": true, "errorCode": null, "errorMessage": null } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +- 订单号 / 团号 / 批次号等展示字段是一次批量查询补齐的;该查询失败时这些字段为空,认领结果不受影响。 +- 单条出现非业务异常时该条 `success=false`、`errorCode=null`、`errorMessage="系统繁忙,请稍后重试"`,其余条目照常处理。 + +#### 错误响应 + +```json +{ + "code": 400, + "message": "请选择 1~50 条待认领记录", + "data": null, + "success": false +} +``` + +整体失败: + +| code | message | 触发 | +|------|---------|------| +| 400 | 请选择 1~50 条待认领记录 / 需求 ID 不能为空 / 团期 ID 不能为空 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 100502 | 批量认领处理中,请勿重复提交 | 同一批 3 秒内重复提交 | + +逐项失败(出现在 `items[].errorCode`)常见值:808001「该需求已被其他房务认领或状态已变化,请刷新后重试」、808002「需求已不存在」、808003「该需求已由您抢到,请勿重复抢单」、808004「需求所在订单已取消」,团期项沿用整团认领的既有错误码。 + +#### 业务边界 + +- 每一条独立认领、独立成败,不整体回滚;HTTP 与 `code` 为 200 只代表批次处理完,逐项看 `items[].success`。 +- 需求项走常规单抢单、团期项走整团认领,校验与单条认领一致。 +- 同一 ID 重复提交只处理一次;`successCount + failCount` 等于去重后的条数。 + +### 3. 控房表查询 `GET /v3/admin/order/house-console/room-control` + +**VO**: `HouseRoomControlListReqVO → HouseRoomControlRespVO` + +#### 使用场景 + +控制台「控房表」页签加载:按城市或酒店、入住夜区间列出每个酒店 × 房型 × 入住夜的控房总数、已用、剩余、已分配、价格,以及每格的团组用房明细。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| cityCode | Query | String | ❌ | ≤32 | 城市**编码**(如 `hailar`);与 hotelId 都不传则查全部酒店 | +| hotelId | Query | Long | ❌ | - | 传了只查这一家,优先于 cityCode | +| dateFrom | Query | LocalDate | ✅ | yyyy-MM-dd | 入住夜起(含) | +| dateTo | Query | LocalDate | ✅ | yyyy-MM-dd;跨度 ≤62 天(含首尾) | 入住夜止(含) | +| onlyWithRemain | Query | Boolean | ❌ | - | true 时只返回剩余为不限或 >0 的行 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| stockTrackingEnabled | Boolean | resource 全局库存追踪开关(`hotel.stock.enabled`);false 时行照常返回,但调房量会被拒(808312) | +| rows | List | 控房行,按(酒店, 入住夜, 房型)升序 | +| rows[].hotelId | Long(String) | 酒店 ID | +| rows[].hotelName | String | 酒店名称 | +| rows[].cityName | String | 城市中文名 | +| rows[].roomTypeId | Long(String) | 房型 ID | +| rows[].roomTypeName | String | 房型名称 | +| rows[].stayDate | LocalDate | 入住夜 | +| rows[].totalRooms | Integer | 控房总数 = 剩余 + 已用;null 表示不限量 | +| rows[].usedRooms | Integer | 已用(resource 当前值) | +| rows[].remainRooms | Integer | 剩余;null 表示不限量 | +| rows[].assignedRooms | Integer | 已分配:扣库存的配房行 + 已确认扣库存的团期计划行的间数合计 | +| rows[].protocolPrice | BigDecimal(String) | 控房价 | +| rows[].settlementPrice | BigDecimal(String) | 结算价 | +| rows[].usages | List | 团组用房明细 | +| rows[].usages[].orderId | Long(String) | 订单 ID;团期计划行为 null | +| rows[].usages[].teamNo | String | 团号;团期计划行或无团号为 null | +| rows[].usages[].batchNo | String | 团期批次号;散客订单为 null | +| rows[].usages[].roomCount | Integer | 用房间数 | +| rows[].usages[].roomSource | String | 恒为 STOCK(本表只列扣库存行) | +| rows[].usages[].roomSourceLabel | String | 控房 | +| rows[].usages[].confirmStatusLabel | String | 确认状态标签 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/room-control?cityCode=hailar&dateFrom=2026-10-01&dateTo=2026-10-07&onlyWithRemain=false +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "stockTrackingEnabled": true, + "rows": [ + { + "hotelId": "100001", + "hotelName": "海拉尔草原酒店", + "cityName": "海拉尔", + "roomTypeId": "300001", + "roomTypeName": "豪华双床房", + "stayDate": "2026-10-01", + "totalRooms": 12, + "usedRooms": 5, + "remainRooms": 7, + "assignedRooms": 5, + "protocolPrice": "320.00", + "settlementPrice": "300.00", + "usages": [ + { "orderId": "1930000000000000001", "teamNo": "HL20261001A", "batchNo": null, "roomCount": 3, "roomSource": "STOCK", "roomSourceLabel": "控房", "confirmStatusLabel": "已确认" }, + { "orderId": null, "teamNo": null, "batchNo": "GB20261001", "roomCount": 2, "roomSource": "STOCK", "roomSourceLabel": "控房", "confirmStatusLabel": "已确认" } + ] + } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +```json +{ "code": 200, "message": "成功", "data": { "stockTrackingEnabled": true, "rows": [] }, "success": true } +``` + +条件内无控房行时 `rows` 为空数组。resource 服务不可用时不降级为空表,返回 808900。 + +#### 错误响应 + +```json +{ + "code": 808313, + "message": "日期跨度不能超过 62 天", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | dateFrom 不能为空 / dateTo 不能为空 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808313 | 日期跨度不能超过 62 天 | dateFrom 晚于 dateTo,或跨度超 62 天 | +| 808330 | 查询范围过大,请缩小酒店或日期范围 | 结果超过 2000 行 | +| 808900 | 资源服务暂不可用,请稍后重试 | resource 调用失败 | + +#### 业务边界 + +- 本接口的 `cityCode` 是城市**编码**;退团房列表(接口 7)的 `cityCode` 是城市**中文名**,两处不要混用。 +- `stockTrackingEnabled=false` 时行照常返回,已用 / 剩余取 resource 当前值,但此时配房不扣减库存,已用 / 剩余不随配房变化;前端应据此隐藏或禁用「调房量」。 +- 纯读接口,无幂等要求,不写任何数据。 + +### 4. 控房表调房量 `PUT /v3/admin/order/house-console/room-control/stock` + +**VO**: `HouseRoomControlStockSaveReqVO → HouseRoomControlRowRespVO` + +#### 使用场景 + +控房表某一格(酒店 × 房型 × 入住夜)点「调房量」,修改控房总数。提交时带上页面读到的已用间数做并发校验。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| hotelId | Body | Long | ✅ | - | 酒店 ID | +| roomTypeId | Body | Long | ✅ | - | 房型 ID | +| stayDate | Body | LocalDate | ✅ | yyyy-MM-dd | 入住夜 | +| totalRooms | Body | Integer | ✅ | 0~500 | 新的控房总数 | +| expectedUsedRooms | Body | Integer | ✅ | ≥0 | 页面读到的已用间数(CAS 期望值) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| hotelId / hotelName / cityName | Long(String) / String / String | 酒店与城市 | +| roomTypeId / roomTypeName | Long(String) / String | 房型 | +| stayDate | LocalDate | 入住夜 | +| totalRooms | Integer | 调整后的控房总数;null 表示不限量 | +| usedRooms | Integer | 已用 | +| remainRooms | Integer | 剩余;null 表示不限量 | +| assignedRooms | Integer | 已分配间数 | +| protocolPrice / settlementPrice | BigDecimal(String) | 控房价 / 结算价 | +| usages | List | 团组用房明细,字段同接口 3 | + +#### 请求示例 + +```json +{ + "hotelId": 100001, + "roomTypeId": 300001, + "stayDate": "2026-10-01", + "totalRooms": 15, + "expectedUsedRooms": 5 +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "hotelId": "100001", "hotelName": "海拉尔草原酒店", "cityName": "海拉尔", + "roomTypeId": "300001", "roomTypeName": "豪华双床房", "stayDate": "2026-10-01", + "totalRooms": 15, "usedRooms": 5, "remainRooms": 10, "assignedRooms": 5, + "protocolPrice": "320.00", "settlementPrice": "300.00", + "usages": [ + { "orderId": "1930000000000000001", "teamNo": "HL20261001A", "batchNo": null, "roomCount": 3, "roomSource": "STOCK", "roomSourceLabel": "控房", "confirmStatusLabel": "已确认" } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +写入成功后服务端会重读这一行;重读失败时用写入结果拼出返回行,此时 `assignedRooms=0`、`usages=[]`,前端可再调接口 3 刷新整表。 + +#### 错误响应 + +```json +{ + "code": 808311, + "message": "已占用数已变化,请刷新后重试", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | hotelId 不能为空 / roomTypeId 不能为空 / stayDate 不能为空 / totalRooms 不能为空 / totalRooms 不能小于 0 / totalRooms 不能大于 500 / expectedUsedRooms 不能为空 / expectedUsedRooms 不能小于 0 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808312 | 库存追踪未开启,暂不能调整房量 | resource 全局开关关闭 | +| 808310 | 总房量不能小于已占用 {0} 间 | totalRooms 小于当前已用,{0} 为已用间数 | +| 808311 | 已占用数已变化,请刷新后重试 | expectedUsedRooms 与服务端当前已用不一致 | +| 100502 | 调房量处理中,请勿重复提交 | 同一操作人 3 秒内重复提交同一格 | +| 808900 | 资源服务暂不可用,请稍后重试 | resource 调用失败 | + +#### 业务边界 + +- 写守卫只要求房务角色,不校验认领人:控房是酒店级资源,不属于任何订单。 +- 失败时控房总数不变。808311 后前端应重新查询该格再提交。 +- 幂等键带操作人,两个房务 3 秒内改同一格不会被互判为重复提交,由 CAS 决定先后。 + +### 5. 控房表调价 `PUT /v3/admin/order/house-console/room-control/price` + +**VO**: `HouseRoomControlPriceSaveReqVO → Result` + +#### 使用场景 + +控房表选定酒店 × 房型,对一段入住夜批量修改控房价和 / 或结算价。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| hotelId | Body | Long | ✅ | - | 酒店 ID | +| roomTypeId | Body | Long | ✅ | - | 房型 ID | +| dateFrom | Body | LocalDate | ✅ | yyyy-MM-dd | 入住夜起(含) | +| dateTo | Body | LocalDate | ✅ | yyyy-MM-dd;跨度 ≤62 天 | 入住夜止(含) | +| protocolPrice | Body | BigDecimal | ❌ | >0,最多 2 位小数 | 控房价;不传则不改 | +| settlementPrice | Body | BigDecimal | ❌ | >0,最多 2 位小数 | 结算价;不传则不改 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | Integer | 受影响的天数(更新已有价格日历行 + 补建缺失日历行) | + +#### 请求示例 + +```json +{ + "hotelId": 100001, + "roomTypeId": 300001, + "dateFrom": "2026-10-01", + "dateTo": "2026-10-07", + "protocolPrice": "320.00", + "settlementPrice": "300.00" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": 7, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本接口无列表型数据;区间内缺失的价格日历行会被补建并计入 `data`。 + +#### 错误响应 + +```json +{ + "code": 808314, + "message": "协议价与结算价至少填写一项", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | hotelId 不能为空 / roomTypeId 不能为空 / dateFrom 不能为空 / dateTo 不能为空 / protocolPrice 必须大于 0 / protocolPrice 最多 2 位小数 / settlementPrice 必须大于 0 / settlementPrice 最多 2 位小数 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808313 | 日期跨度不能超过 62 天 | dateFrom 晚于 dateTo 或跨度超 62 天 | +| 808314 | 协议价与结算价至少填写一项 | 两个价格都没传 | +| 100502 | 调价处理中,请勿重复提交 | 3 秒内重复提交 | +| 808900 | 资源服务暂不可用,请稍后重试 | resource 调用失败 | + +#### 业务边界 + +- 只影响之后新生成的配房;已有配房行保存的是配房当时的价格快照,不回写。 +- 不传的价格字段保持原值,不会被清空。 +- 一次调用按区间整体写入,不需要前端逐日调用。 + +### 6. 控房表导出 `GET /v3/admin/order/house-console/room-control/export` + +**VO**: `HouseRoomControlListReqVO → xlsx 文件流` + +#### 使用场景 + +控房表页签点「导出」,按与查询相同的条件下载 Excel。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| cityCode | Query | String | ❌ | ≤32 | 城市编码;与 hotelId 都不传则导出全部酒店 | +| hotelId | Query | Long | ❌ | - | 只导出这一家,优先于 cityCode | +| dateFrom | Query | LocalDate | ✅ | yyyy-MM-dd | 入住夜起(含) | +| dateTo | Query | LocalDate | ✅ | yyyy-MM-dd;跨度 ≤62 天 | 入住夜止(含) | +| onlyWithRemain | Query | Boolean | ❌ | - | 只导出有剩余的行 | + +#### 出参 `xlsx 文件流(失败时为 Result JSON)` + +| 字段 | 类型 | 说明 | +|------|------|------| +| Content-Type | Header | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | +| Content-Disposition | Header | `attachment; filename=""; filename*=UTF-8''` | +| 文件名 | String | `控房表-{dateFrom}~{dateTo}.xlsx` | +| 工作表「每日房量」 | Sheet | 每个酒店 × 房型 × 入住夜一行;列:酒店 / 入住夜日期 / 房型 / 控房总数 / 已用 / 剩余;不限量时总数与剩余显示「不限」 | +| 工作表「团组用房」 | Sheet | 一条占用一行;列:酒店 / 入住夜日期 / 房型 / 团号 / 房源 / 用房间数 / 确认状态;团期计划行没有团号,团号列填批次号 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/room-control/export?hotelId=100001&dateFrom=2026-10-01&dateTo=2026-10-07 +``` + +#### 响应示例 + +```json +{ + "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Content-Disposition": "attachment; filename=\"%E6%8E%A7%E6%88%BF%E8%A1%A8-2026-10-01~2026-10-07.xlsx\"; filename*=UTF-8''%E6%8E%A7%E6%88%BF%E8%A1%A8-2026-10-01~2026-10-07.xlsx" +} +``` + +以上为响应头,响应体是二进制 xlsx。 + +#### 空数据 / 降级响应 + +条件内无数据时仍返回 xlsx,两个工作表只有表头。 + +#### 错误响应 + +```json +{ + "code": 808330, + "message": "查询范围过大,请缩小酒店或日期范围", + "data": null, + "success": false +} +``` + +失败时响应体是 `Result` JSON 而不是文件,错误码同接口 3(400 / 808090 / 808313 / 808330 / 808900),另有 100502「导出处理中,请勿重复点击」。前端下载前需按 Content-Type 判断是文件还是 JSON。 + +#### 业务边界 + +- 导出内容不含价格列。 +- 与接口 3 同一查询条件与同一 2000 行上限。 +- 同一条件 3 秒内重复点击返回 100502。 + +### 7. 退团房分页 `GET /v3/admin/order/house-console/room-transfers` + +**VO**: `HouseRoomTransferPageReqVO → HouseRoomTransferPageRespVO` + +#### 使用场景 + +控制台「退团房」页签(通知链接 `/housekeeper/console?tab=transfer&id={transferId}`):列出退团释放出来、需要转给别人或向酒店取消的房间,并给出待处理的风险汇总。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| 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 | 风险筛选 | +| cityCode | Query | String | ❌ | ≤64 | 城市**中文名**(如 `海拉尔`) | +| keyword | Query | String | ❌ | ≤64 | 团号或酒店名 | +| stayDateFrom | Query | LocalDate | ❌ | yyyy-MM-dd | 入住晚起(含) | +| stayDateTo | Query | LocalDate | ❌ | yyyy-MM-dd | 入住晚止(含) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| records | List | 当前页转房行 | +| records[].id | Long(String) | 转房行 ID | +| records[].sourceType | String | 来源类型 ORDER / GROUP_BATCH | +| records[].sourceOrderId | Long(String) | 源订单 ID(团期来源时为离团子单) | +| records[].teamNo | String | 源订单团号;空为 null | +| records[].sourceTeamNo | String | 源团号 / 批次号快照 | +| records[].sourceGroupBatchId | Long(String) | 源团期 ID | +| records[].sourceBatchNo | String | 源团期批次号 | +| records[].stayDate | LocalDate | 入住晚 | +| records[].cityName | String | 城市名 | +| records[].hotelId / hotelName | Long(String) / String | 酒店 | +| records[].roomTypeId / roomTypeName | Long(String) / String | 房型 | +| records[].roomCount | Integer | 原始间数 | +| records[].remainingCount | Integer | 剩余待处理间数 | +| records[].status / statusLabel | String | 状态及中文 | +| records[].cancelDays | Integer | 入住前几天免费取消;未设为 null | +| records[].cancelCutoff | String | 截止当天时刻 HH:mm | +| records[].remindDays | Integer | 提前几天提醒 | +| records[].deadlineAt | LocalDateTime | 免费取消截止时刻;未设期限为 null | +| records[].risk / riskLabel | String | 风险及中文;仅 PENDING 行有值 | +| records[].targetType | String | 最近一次转出的目标类型 ORDER / GROUP_BATCH | +| records[].targetOrderId / targetTeamNo | Long(String) / String | 目标订单与团号 | +| records[].targetRequirementId | Long(String) | 目标需求 ID | +| records[].targetGroupBatchId / targetBatchNo | Long(String) / String | 目标团期与批次号 | +| records[].hotelConfirmNo | String | 酒店确认号(转出时填写) | +| records[].proofFileIds | List | 凭证文件 ID | +| records[].cancelFee | BigDecimal(String) | 取消费用(元) | +| records[].cancelReason | String | 取消原因 HOTEL_CANCELLED | +| records[].handlerName / handledAt | String / LocalDateTime | 处理人与处理时间 | +| records[].remark | String | 备注 | +| records[].readOnly | Boolean | 对当前登录人是否只读(源单由他人处理) | +| records[].readOnlyReason | String | 「由 X 处理」;可写为 null | +| total | long | 总条数 | +| page / pageSize | int | 当前页 / 每页条数 | +| summary.pendingRooms | int | 全部 PENDING 行的待处理间数合计 | +| summary.overdueRooms | int | 其中已过免费取消期的间数 | +| summary.nearRooms | int | 其中临近免费取消期的间数 | +| summary.noDeadlineRooms | int | 其中未设免费取消期的间数 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/room-transfers?status=PENDING&cityCode=海拉尔&page=1&pageSize=20 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "records": [ + { + "id": "1950000000000000001", "sourceType": "ORDER", "sourceOrderId": "1930000000000000001", + "teamNo": "HL20261001A", "sourceTeamNo": "HL20261001A", "sourceGroupBatchId": null, "sourceBatchNo": null, + "stayDate": "2026-10-02", "cityName": "海拉尔", "hotelId": "100001", "hotelName": "海拉尔草原酒店", + "roomTypeId": "300001", "roomTypeName": "豪华双床房", "roomCount": 2, "remainingCount": 2, + "status": "PENDING", "statusLabel": "待处理", "cancelDays": 3, "cancelCutoff": "18:00", "remindDays": 1, + "deadlineAt": "2026-09-29 18:00:00", "risk": "NEAR", "riskLabel": "临近免费取消期", + "targetType": null, "targetOrderId": null, "targetTeamNo": null, "targetRequirementId": null, + "targetGroupBatchId": null, "targetBatchNo": null, "hotelConfirmNo": null, "proofFileIds": [], + "cancelFee": null, "cancelReason": null, "handlerName": null, "handledAt": null, "remark": null, + "readOnly": false, "readOnlyReason": null + } + ], + "total": 1, + "page": 1, + "pageSize": 20, + "summary": { "pendingRooms": 2, "overdueRooms": 0, "nearRooms": 2, "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": "risk 取值非法", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | status 取值非法 / risk 取值非法 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | + +#### 业务边界 + +- `summary` 始终统计全部 PENDING 行,不受本次筛选条件影响;列表按筛选条件分页。 +- `risk` 只对 PENDING 行有意义,其他状态行 `risk` / `riskLabel` 为 null;带 `risk` 筛选时服务端在内存中分页,`total` 仍是筛选后的总数。 +- `readOnly` 判据与写接口 8 / 10 / 11 的守卫一致:源单持有人本人为 false,超管豁免为 false,其余为 true 并给「由 X 处理」(持有人姓名取不到时为「由其他房务处理」)。 + +### 8. 设置退团房免费取消期限 `PUT /v3/admin/order/house-console/room-transfers/{id}/deadline` + +**VO**: `HouseRoomTransferDeadlineSaveReqVO → HouseRoomTransferRespVO` + +#### 使用场景 + +退团房列表某一行点「设期限」,录入酒店给的免费取消规则(入住前几天、当天几点前)与提前几天提醒;也可清除期限。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| 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 时本字段被忽略、一并清空 | 提前几天提醒 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| (整行) | HouseRoomTransferRespVO | 更新后的该转房行,字段同接口 7 的 `records[]` | +| cancelDays / cancelCutoff / remindDays | Integer / String / Integer | 更新后的期限参数 | +| deadlineAt | LocalDateTime | 截止时刻 = 入住晚 − cancelDays 天的 cancelCutoff;清除期限后为 null | +| risk / riskLabel | String | 按新期限重算的风险 | + +#### 请求示例 + +```json +{ + "cancelDays": 3, + "cancelCutoff": "18:00", + "remindDays": 1 +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "id": "1950000000000000001", "sourceType": "ORDER", "stayDate": "2026-10-02", + "hotelName": "海拉尔草原酒店", "roomTypeName": "豪华双床房", "remainingCount": 2, + "status": "PENDING", "statusLabel": "待处理", "cancelDays": 3, "cancelCutoff": "18:00", "remindDays": 1, + "deadlineAt": "2026-09-29 18:00:00", "risk": "NEAR", "riskLabel": "临近免费取消期", + "readOnly": false, "readOnlyReason": null + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +`cancelDays` 传 null 表示清除期限:`cancelDays` / `cancelCutoff` / `remindDays` 三列一并置空,返回行的 `deadlineAt=null`、`risk=NO_DEADLINE`、`riskLabel="未设免费取消期"`。 + +#### 错误响应 + +```json +{ + "code": 808328, + "message": "退改期限参数不合法", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808320 | 转房记录不存在 | id 不存在 | +| 808326 | 只有原单处理人可以处理退团房间 | 非源单持有人且非超管 | +| 808328 | 退改期限参数不合法 | cancelDays 超 0~60、remindDays 超 0~30、cancelCutoff 不是 HH:mm | +| 808321 | 该房间已处理 | 行已不是 PENDING | +| 808932 | 房务状态已被并发修改,请刷新后重试 | 并发写冲突 | +| 100502 | 修改处理中,请勿重复提交 | 3 秒内重复提交 | + +#### 业务边界 + +- 本接口的入参不走注解校验,范围与格式错误统一返回 808328(不是 400)。 +- 只有 PENDING 行可改;源单持有人或超管可操作。 +- 校验顺序:808320 → 808326 → 808328 → 808321 → 808932。 + +### 9. 退团房转入候选 `GET /v3/admin/order/house-console/room-transfers/{id}/candidates` + +**VO**: `Long id → List` + +#### 使用场景 + +退团房「转房」弹窗打开时加载:列出与该房间同城、同入住晚、非控房的常规单需求与团期,可转入的排在前面,不合格的带原因列出。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | ✅ | - | 转房行 ID | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| targetType | String | ORDER / GROUP_BATCH | +| orderId | Long(String) | 目标订单 ID;团期目标为 null | +| teamNo | String | 目标订单团号 | +| requirementId | Long(String) | 目标需求 ID(targetType=ORDER 时转房入参用) | +| groupBatchId | Long(String) | 目标团期 ID(targetType=GROUP_BATCH 时转房入参用) | +| batchNo | String | 目标团期批次号 | +| guestName | String | 客人姓名 / 团期名称 | +| stayDate | LocalDate | 入住晚 | +| cityName | String | 城市名 | +| needRoomCount | Integer | 该晚还差几间没配 | +| eligible | Boolean | 是否可转入 | +| ineligibleReason | String | 不可转入原因 | +| holderName | String | 目标当前处理人姓名;待认领为 null | +| readOnly | Boolean | 目标是否由他人处理(仅展示,不影响转入) | +| readOnlyReason | String | 「由 X 处理」 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/room-transfers/1950000000000000001/candidates +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { "targetType": "ORDER", "orderId": "1930000000000000009", "teamNo": "HL20261002C", "requirementId": "1940000000000000009", "groupBatchId": null, "batchNo": null, "guestName": "李女士", "stayDate": "2026-10-02", "cityName": "海拉尔", "needRoomCount": 2, "eligible": true, "ineligibleReason": null, "holderName": "张三", "readOnly": true, "readOnlyReason": "由 张三 处理" }, + { "targetType": "GROUP_BATCH", "orderId": null, "teamNo": null, "requirementId": null, "groupBatchId": "1960000000000000001", "batchNo": "GB20261001", "guestName": "呼伦贝尔秋季团", "stayDate": "2026-10-02", "cityName": "海拉尔", "needRoomCount": 3, "eligible": false, "ineligibleReason": "团期已确认", "holderName": null, "readOnly": false, "readOnlyReason": null } + ], + "success": true +} +``` + +#### 空数据 / 降级响应 + +```json +{ "code": 200, "message": "成功", "data": [], "success": true } +``` + +该转房行已不是 PENDING(已转出 / 已取消)时同样返回空数组,不报错。 + +#### 错误响应 + +```json +{ + "code": 808320, + "message": "转房记录不存在", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808320 | 转房记录不存在 | id 不存在 | + +#### 业务边界 + +- `readOnly` 只用于展示目标由谁处理,不影响能否转入;能否转入只看 `eligible`。 +- `ineligibleReason` 取值见「六.5 枚举」;可转入的候选排在前面。 +- 纯读接口,不写数据。 + +### 10. 退团房转房 `POST /v3/admin/order/house-console/room-transfers/{id}/transfer` + +**VO**: `HouseRoomTransferSaveReqVO → HouseRoomTransferRespVO` + +#### 使用场景 + +退团房「转房」弹窗选中一个候选后提交:把若干间房转给订单需求或团期,同时录入酒店确认号与凭证。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | ✅ | - | 转房行 ID | +| targetType | Body | String | ✅ | ORDER / GROUP_BATCH | 转入目标类型 | +| targetRequirementId | Body | Long | 条件 | targetType=ORDER 必填 | 目标需求 ID(取候选的 requirementId) | +| targetGroupBatchId | Body | Long | 条件 | targetType=GROUP_BATCH 必填 | 目标团期 ID(取候选的 groupBatchId) | +| roomCount | Body | Integer | ✅ | ≥1,且不超过剩余间数与目标缺口 | 转出间数 | +| hotelConfirmNo | Body | String | ✅ | 1~64 字 | 酒店确认号 | +| proofFileIds | Body | List | ✅ | 1~9 个,元素非空 | 凭证文件 ID | +| remark | Body | String | ❌ | ≤200 字 | 备注 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| (整行) | HouseRoomTransferRespVO | 转房后的该行,字段同接口 7 的 `records[]` | +| remainingCount | Integer | 扣减后的剩余待处理间数 | +| status / statusLabel | String | 剩余为 0 时为 TRANSFERRED / 已转出,否则仍为 PENDING | +| targetType / targetOrderId / targetTeamNo / targetRequirementId / targetGroupBatchId / targetBatchNo | String / Long(String) / String / Long(String) / Long(String) / String | 最近一次转出的目标 | +| hotelConfirmNo / proofFileIds / handlerName / handledAt | String / List / String / LocalDateTime | 本次转出录入的信息 | + +#### 请求示例 + +```json +{ + "targetType": "ORDER", + "targetRequirementId": 1940000000000000009, + "roomCount": 2, + "hotelConfirmNo": "HX20261002001", + "proofFileIds": [1930000000000000601], + "remark": "酒店已同意换住客" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "id": "1950000000000000001", "sourceType": "ORDER", "stayDate": "2026-10-02", + "hotelName": "海拉尔草原酒店", "roomTypeName": "豪华双床房", "roomCount": 2, "remainingCount": 0, + "status": "TRANSFERRED", "statusLabel": "已转出", "risk": null, "riskLabel": null, + "targetType": "ORDER", "targetOrderId": "1930000000000000009", "targetTeamNo": "HL20261002C", + "targetRequirementId": "1940000000000000009", "targetGroupBatchId": null, "targetBatchNo": null, + "hotelConfirmNo": "HX20261002001", "proofFileIds": ["1930000000000000601"], + "handlerName": "王房务", "handledAt": "2026-09-29 10:00:00", "remark": "酒店已同意换住客", + "readOnly": false, "readOnlyReason": null + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本接口无列表数据;任何失败都返回非 200 的 `code`,源行、目标与配房均不变。 + +#### 错误响应 + +```json +{ + "code": 808322, + "message": "目标不可转入:不同城", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | 请选择转入目标类型 / targetType 取值非法 / 请填写转出间数 / 转出间数至少 1 间 / 酒店确认号不能超过 64 字 / 凭证最多 9 个 / 凭证文件 ID 不能为空 / 备注不能超过 200 字 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808320 | 转房记录不存在 | id 不存在 | +| 808326 | 只有原单处理人可以处理退团房间 | 非源单持有人且非超管 | +| 808325 | 请填写酒店确认号并上传凭证 | 确认号为空或凭证为空 | +| 808322 | 目标不可转入:{0} | {0} 为具体原因,见「六.5 枚举」 | +| 808323 | 目标团期已确认,不能转入 | 目标团期已推进到确认;来源为团期(sourceType=GROUP_BATCH)且源团期已推进到确认时也返回本码,文案仍写「目标团期」 | +| 808321 | 该房间已处理 | 行已不是 PENDING | +| 808324 | 转出间数超过剩余 {0} 间 | roomCount 超过源剩余或目标缺口,{0} 为此刻可转间数 | +| 808608 | 订房计划已被其他操作修改,请刷新后重试 | 团期计划版本冲突 | +| 599602 | 应付款台账行已锁定 | 来源为 ORDER、源配房行对应的应付款台账行已有在途付款申请 | +| 808932 | 房务状态已被并发修改,请刷新后重试 | 并发写冲突 | +| 100502 | 转房处理中,请勿重复提交 | 3 秒内重复提交 | +| 100503 | 资源被占用,请稍后重试 | 源或目标正被另一写操作锁定 | + +#### 业务边界 + +- 只有源单持有人或超管可转;超管豁免持有人校验。目标由谁持有不影响转入。 +- `hotelConfirmNo` 与 `proofFileIds` 在 Swagger 标为必填,但不是注解校验:缺失时返回业务码 808325,不是 400。 +- 校验顺序:808320 → 808326 → 808325 → 808322(未指定转入目标)→ 锁内复验(808323 / 808321 / 808324 / 808322 其余原因)→ 808932。 +- 部分转出时该行保持 PENDING,`remainingCount` 减少;剩余为 0 时变为 TRANSFERRED。 +- 来源为 ORDER 时,源配房行的应付款台账行若已有在途付款申请,返回 599602 且整笔不落库。 +- 通知在事务提交后才发出、回滚时一条不发:目标处理人收 `HOUSE_ROOM_TRANSFERRED`(跳转 `/housekeeper/console?tab=transfer&id={transferId}`;目标待认领、没有处理人时不发),源单与目标单的定制师各收一条(同一人只发一次;团期目标只通知源单定制师)。 + +### 11. 退团房向酒店取消 `POST /v3/admin/order/house-console/room-transfers/{id}/cancel-hotel` + +**VO**: `HouseRoomTransferCancelReqVO → HouseRoomTransferRespVO` + +#### 使用场景 + +退团房没有合适的转入对象时,房务向酒店取消该房间,上传取消凭证并录入取消费用。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | ✅ | - | 转房行 ID | +| proofFileIds | Body | List | ✅ | 1~9 个,元素非空 | 取消凭证文件 ID | +| cancelFee | Body | BigDecimal | ❌ | ≥0,最多 2 位小数 | 取消费用(元) | +| remark | Body | String | ❌ | ≤200 字 | 备注 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| (整行) | HouseRoomTransferRespVO | 取消后的该行,字段同接口 7 的 `records[]` | +| status / statusLabel | String | CANCELLED / 已取消 | +| cancelReason | String | HOTEL_CANCELLED | +| cancelFee | BigDecimal(String) | 录入的取消费用 | +| proofFileIds | List | 取消凭证 | + +#### 请求示例 + +```json +{ + "proofFileIds": [1930000000000000701], + "cancelFee": "0.00", + "remark": "酒店已免费取消" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "id": "1950000000000000001", "sourceType": "ORDER", "stayDate": "2026-10-02", + "hotelName": "海拉尔草原酒店", "roomTypeName": "豪华双床房", "roomCount": 2, "remainingCount": 2, + "status": "CANCELLED", "statusLabel": "已取消", "risk": null, "riskLabel": null, + "cancelFee": "0.00", "cancelReason": "HOTEL_CANCELLED", "proofFileIds": ["1930000000000000701"], + "handlerName": "王房务", "handledAt": "2026-09-29 10:00:00", "remark": "酒店已免费取消", + "readOnly": false, "readOnlyReason": null + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本接口无列表数据;任何失败都返回非 200 的 `code`,该行不变。 + +#### 错误响应 + +```json +{ + "code": 808325, + "message": "请填写酒店确认号并上传凭证", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | 凭证最多 9 个 / 凭证文件 ID 不能为空 / 取消费用不能为负 / 取消费用最多 2 位小数 / 备注不能超过 200 字 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808320 | 转房记录不存在 | id 不存在 | +| 808326 | 只有原单处理人可以处理退团房间 | 非源单持有人且非超管 | +| 808325 | 请填写酒店确认号并上传凭证 | 未上传凭证(本接口不要求确认号,文案沿用同一错误码) | +| 808321 | 该房间已处理 | 行已不是 PENDING | +| 808932 | 房务状态已被并发修改,请刷新后重试 | 并发写冲突 | +| 100502 | 取消处理中,请勿重复提交 | 3 秒内重复提交 | + +#### 业务边界 + +- 只有源单持有人或超管可操作。 +- 凭证为空时返回 808325(业务码,不是 400)。 +- 成功后该行整行变为 CANCELLED,不再出现在待处理汇总里;已部分转出的行取消的是剩余部分。 +- 校验顺序:808320 → 808326 → 808325 → 808321 → 808932。 + +### 12. 房务异常检查 `GET /v3/admin/order/house-console/audit` + +**VO**: `HouseConsoleAuditReqVO → HouseConsoleAuditRespVO` + +#### 使用场景 + +控制台「异常检查」页签:按出发日期区间一次列出数据对不上的问题(issues)和还没办完的事(tasks),每条可按 `orderId` / `groupBatchId` / `refId` 跳转处理。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| scope | Query | String | ❌ | mine / all,默认 mine | 待办范围 | +| departDateFrom | Query | LocalDate | ❌ | yyyy-MM-dd;默认今天 | 出发日期起(含) | +| departDateTo | Query | LocalDate | ❌ | yyyy-MM-dd;默认起始日 +30 天;跨度 ≤92 天 | 出发日期止(含) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| issues | List | 数据不一致问题 | +| tasks | List | 待处理事项 | +| (Item)code | String | 问题码或待办码,见「六.5 枚举」 | +| (Item)codeLabel | String | 问题中文,前端直接展示 | +| (Item)taskCode | String | 待办码,仅 tasks 有值(与 code 相同) | +| (Item)orderId | Long(String) | 订单 ID | +| (Item)teamNo | String | 订单团号 | +| (Item)groupBatchId | Long(String) | 团期 ID | +| (Item)batchNo | String | 团期批次号 | +| (Item)stayDate | LocalDate | 入住晚 | +| (Item)hotelId / hotelName | Long(String) / String | 酒店 | +| (Item)roomTypeId / roomTypeName | Long(String) / String | 房型 | +| (Item)refId | Long(String) | 关联单据 ID(转房行 / 改配记录),用于跳转 | +| (Item)detail | String | 说明文字 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/audit?scope=mine&departDateFrom=2026-10-01&departDateTo=2026-10-31 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "issues": [ + { "code": "STOCK_LEDGER_MISMATCH", "codeLabel": "库存账不平", "taskCode": null, "orderId": null, "teamNo": null, "groupBatchId": null, "batchNo": null, "stayDate": "2026-10-02", "hotelId": "100001", "hotelName": "海拉尔草原酒店", "roomTypeId": "300001", "roomTypeName": "豪华双床房", "refId": null, "detail": "本地已用 5 间,库存日历已用 3 间" } + ], + "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 } +``` + +#### 错误响应 + +```json +{ + "code": 808313, + "message": "日期跨度不能超过 92 天", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | scope 取值非法 | scope 不是 mine / all | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808313 | 日期跨度不能超过 92 天 | 出发日期跨度超 92 天 | + +#### 业务边界 + +- `scope` 只作用于 `tasks`:mine 只列当前登录人持有的订单 / 团期的待办;`issues` 不受 scope 影响,始终是区间内全部。 +- `STOCK_LEDGER_MISMATCH`(库存账不平)只在 resource 全局库存追踪开关打开时检查;开关关闭时不会产出该问题码。 +- 纯读接口,不写数据。 + +### 13. 住宿模板列表 `GET /v3/admin/order/house-console/stay-templates` + +**VO**: `String keyword → List` + +#### 使用场景 + +控制台「住宿模板」页签,以及配房时「从模板带入」的下拉列表。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| keyword | Query | String | ❌ | - | 模板名称模糊匹配 | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | Long(String) | 模板 ID | +| name | String | 模板名称 | +| creatorName | String | 创建人姓名;房务名单里查不到时为 null | +| nightCount | Integer | 晚数 | +| createTime | LocalDateTime | 创建时间 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/stay-templates?keyword=海拉尔 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { "id": "1839000000000000001", "name": "海拉尔 3 晚标准", "creatorName": "张三", "nightCount": 3, "createTime": "2026-09-28 10:00:00" } + ], + "success": true +} +``` + +#### 空数据 / 降级响应 + +```json +{ "code": 200, "message": "成功", "data": [], "success": true } +``` + +房务名单取不到时列表照常返回,`creatorName` 为 null。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- 返回全部创建人的模板(不是只看自己的),按创建时间倒序,不分页。 +- 已删除的模板不返回。 +- 纯读接口,不写数据。 + +### 14. 住宿模板详情 `GET /v3/admin/order/house-console/stay-templates/{id}` + +**VO**: `Long id → HouseStayTemplateRespVO` + +#### 使用场景 + +打开模板编辑,或配房时选中模板后取出逐晚明细带入。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | ✅ | - | 模板 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | Long(String) | 模板 ID | +| name | String | 模板名称 | +| creatorId | Long(String) | 创建人 ID(前端据此判断是否显示改 / 删) | +| creatorName | String | 创建人姓名 | +| createTime | LocalDateTime | 创建时间 | +| nights | List | 逐晚安排 | +| nights[].day | Integer | 第几晚,从 1 起 | +| nights[].cityName | String | 城市名 | +| nights[].hotelId / hotelName | Long(String) / String | 酒店 | +| nights[].settleType | String | 结算方式编码(字典 `resource_settle_type`),可空 | +| nights[].settleTypeLabel | String | 结算方式名称;为空时为「待确认」 | +| nights[].rooms | List | 房型明细 | +| nights[].rooms[].roomTypeId / roomTypeName | Long(String) / String | 房型 | +| nights[].rooms[].roomCount | Integer | 间数 | +| nights[].rooms[].price | String | 单价(元) | +| nights[].rooms[].breakfast | String | INCLUDED / EXCLUDED / PENDING | +| nights[].rooms[].breakfastLabel | String | 早餐显示名 | + +#### 请求示例 + +```http +GET /v3/admin/order/house-console/stay-templates/1839000000000000001 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "id": "1839000000000000001", + "name": "海拉尔 3 晚标准", + "creatorId": "30001", + "creatorName": "张三", + "createTime": "2026-09-28 10:00:00", + "nights": [ + { + "day": 1, "cityName": "海拉尔", "hotelId": "100001", "hotelName": "海拉尔草原酒店", + "settleType": null, "settleTypeLabel": "待确认", + "rooms": [ + { "roomTypeId": "300001", "roomTypeName": "豪华双床房", "roomCount": 5, "price": "320.00", "breakfast": "INCLUDED", "breakfastLabel": "含早餐" } + ] + } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +模板不存在或已删除时不返回空对象,返回 808351。 + +#### 错误响应 + +```json +{ + "code": 808351, + "message": "模板不存在", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808351 | 模板不存在 | id 不存在或已删除 | + +#### 业务边界 + +- 任何房务都可查看任何人的模板;改 / 删权限见接口 15 / 16。 +- `settleTypeLabel` 由服务端按字典翻译,字典查不到时回退为原编码。 + +### 15. 住宿模板保存 `POST /v3/admin/order/house-console/stay-templates` + +**VO**: `HouseStayTemplateSaveReqVO → Result` + +#### 使用场景 + +「住宿模板」页签新建或编辑模板后保存。`id` 为空表示新建,有值表示修改该模板。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Body | Long | ❌ | - | 模板 ID;为空表示新建 | +| name | Body | String | ✅ | 1~50 字,服务端去首尾空格 | 模板名称,未删除模板中唯一 | +| nights | Body | List | ✅ | 1~30 晚 | 逐晚安排 | +| nights[].day | Body | Integer | ✅ | 1~30 | 第几晚 | +| nights[].cityName | Body | String | ❌ | ≤32 字 | 城市名 | +| nights[].hotelId | Body | Long | ❌ | - | 酒店 ID | +| nights[].hotelName | Body | String | ❌ | ≤100 字 | 酒店名 | +| nights[].settleType | Body | String | ❌ | ≤32 字 | 结算方式编码(字典 `resource_settle_type`),空为待确认 | +| nights[].rooms | Body | List | ✅ | 每晚 1~10 行 | 房型明细 | +| nights[].rooms[].roomTypeId | Body | Long | ❌ | - | 房型 ID | +| nights[].rooms[].roomTypeName | Body | String | ❌ | ≤100 字 | 房型名 | +| nights[].rooms[].roomCount | Body | Integer | ✅ | 1~500 | 间数 | +| nights[].rooms[].price | Body | BigDecimal | ❌ | ≥0,最多 2 位小数 | 单价(元) | +| nights[].rooms[].breakfast | Body | String | ❌ | INCLUDED / EXCLUDED / PENDING | 早餐 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | Long | 模板 ID(新建时为新 ID,修改时为原 ID) | + +#### 请求示例 + +```json +{ + "id": null, + "name": "海拉尔 3 晚标准", + "nights": [ + { + "day": 1, + "cityName": "海拉尔", + "hotelId": 100001, + "hotelName": "海拉尔草原酒店", + "settleType": null, + "rooms": [ + { "roomTypeId": 300001, "roomTypeName": "豪华双床房", "roomCount": 5, "price": "320.00", "breakfast": "INCLUDED" } + ] + } + ] +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": "1839000000000000001", + "success": true +} +``` + +#### 空数据 / 降级响应 + +本接口无列表数据;失败时模板不变(新建则不产生记录)。 + +#### 错误响应 + +```json +{ + "code": 808352, + "message": "只能修改或删除自己创建的模板", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | name 不能为空 / name 不能超过 50 字 / nights 不能为空 / nights 不能超过 30 晚 / day 不能为空 / day 从 1 起 / day 不能超过 30 / cityName 不能超过 32 字 / hotelName 不能超过 100 字 / settleType 不能超过 32 字 / rooms 不能为空 / 每晚 rooms 不能超过 10 行 / roomTypeName 不能超过 100 字 / roomCount 不能为空 / roomCount 至少 1 / roomCount 不能超过 500 / price 不能小于 0 / price 最多 2 位小数 / breakfast 只能是 INCLUDED / EXCLUDED / PENDING | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808350 | 模板名称已存在 | 与未删除模板重名(去首尾空格后比较) | +| 808351 | 模板不存在 | 修改时 id 不存在或已删除 | +| 808352 | 只能修改或删除自己创建的模板 | 修改他人创建的模板且当前登录人不是超管 | +| 100502 | 模板保存处理中,请勿重复提交 | 3 秒内重复提交 | + +#### 业务边界 + +- 修改只允许创建人本人或超管。 +- 名称唯一只在未删除模板中判断,已删除模板的名称可以复用。 +- 修改是整份覆盖:`nights` 以本次提交为准。 + +### 16. 住宿模板删除 `DELETE /v3/admin/order/house-console/stay-templates/{id}` + +**VO**: `Long id → Result` + +#### 使用场景 + +「住宿模板」页签删除一个模板。前端可按详情里的 `creatorId` 与当前登录人比对决定是否展示删除按钮,服务端仍会校验。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | ✅ | - | 模板 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 成功无返回体 | + +#### 请求示例 + +```http +DELETE /v3/admin/order/house-console/stay-templates/1839000000000000001 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +成功时 `data` 恒为 `null`;删除的是软删,列表与详情不再返回该模板。 + +#### 错误响应 + +```json +{ + "code": 808352, + "message": "只能修改或删除自己创建的模板", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808351 | 模板不存在 | id 不存在或已删除 | +| 808352 | 只能修改或删除自己创建的模板 | 删除他人创建的模板且当前登录人不是超管 | + +#### 业务边界 + +- 只允许创建人本人或超管删除。 +- 软删;删除后该名称可被新模板复用。 +- 已删除的模板再次删除返回 808351。 + +### 17. 团期转交 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/transfer` + +**VO**: `HouseGroupTransferReqVO → Result` + +#### 使用场景 + +团期持有人(或超管)把已整团认领的团期交给另一名房务,例如持有人休假。控制台团期视图(通知链接 `/housekeeper/console?groupBatchId={groupBatchId}`)上的「转交」按钮调用。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | +| toUserId | Body | Long | ✅ | 须是在职房务 | 接收人 userId | +| reason | Body | String | ✅ | 1~200 字 | 转交原因,写入团期时间线 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 成功无返回体 | + +#### 请求示例 + +```json +{ + "toUserId": 30002, + "reason": "本人休假,团期交给同组房务跟进" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +房务人员名单取不到时**不降级放行**,直接返回 808343,团期持有人不变。 + +#### 错误响应 + +```json +{ + "code": 808343, + "message": "房务人员名单暂不可用,请稍后重试", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | toUserId 不能为空 / reason 不能为空 / reason 长度不能超过 200 字 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 589500 | 团期不存在 | groupBatchId 不存在 | +| 808340 | 只有团期当前处理人可以转交 | 团期未被认领;或当前登录人不是持有人且不是超管;或并发下持有人已变 | +| 808341 | 不能转交给自己 | toUserId 等于当前持有人或当前登录人 | +| 808343 | 房务人员名单暂不可用,请稍后重试 | 房务人员名单取不到(异常 / 空) | +| 808342 | 接收人不是房务 | 名单里没有 toUserId | +| 808659 | 该团有 {0} 户存在逐户配房(首个订单 {1}),请先删除配房后再操作 | 团内有户存在有效的逐户配房 | +| 100502 | 团期转交处理中,请勿重复提交 | 5 秒内重复提交 | + +#### 业务边界 + +- 持有人本人或超管可转交;超管可转交任何已被认领的团期。 +- `reason` 必填,与户级转单(超管 ≥10 字、普通房务可选)规则不同。 +- 名单取不到一律拒绝(808343);只有超管「整团接管」(`POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover`)在名单取不到时仍放行,接收人姓名显示占位 `user-{id}`。 +- 名单里有接收人、但其姓名为空时,时间线里显示 `user-{id}`。 +- 只换持有人:订房计划与分房不变。团期时间线记一条「团期转交:A → B(原因)」,事务提交后通知接收人。 + +--- + +## 四、契约约束与正确调用方式(接口类必写) + +> 本节只写**后端接受/拒绝 payload 的规则**,不写 UI 渲染建议。 + +### ✅ 正确 / ❌ 错误 payload 对照 + +| 场景 | payload | +|------|---------| +| ✅ 批量认领只认领需求 | `{ "requirementIds": [1840000000000000001] }` | +| ❌ 批量认领两个列表都空 | `{ "requirementIds": [], "groupBatchIds": [] }` → 400 请选择 1~50 条待认领记录 | +| ✅ 转入订单需求 | `{ "targetType": "ORDER", "targetRequirementId": 1940000000000000009, "roomCount": 1, "hotelConfirmNo": "HX1", "proofFileIds": [1] }` | +| ❌ 转入团期却只给需求 ID | `{ "targetType": "GROUP_BATCH", "targetRequirementId": 1940000000000000009, "roomCount": 1, "hotelConfirmNo": "HX1", "proofFileIds": [1] }` → 808322 目标不可转入:未指定转入目标 | +| ❌ 转房不带凭证 | `{ "targetType": "ORDER", "targetRequirementId": 1940000000000000009, "roomCount": 1, "hotelConfirmNo": "HX1" }` → 808325 | +| ✅ 清除退团房期限 | `{ "cancelDays": null }` | +| ❌ 期限天数越界 | `{ "cancelDays": 61 }` → 808328(不是 400) | +| ✅ 只改结算价 | `{ "hotelId": 100001, "roomTypeId": 300001, "dateFrom": "2026-10-01", "dateTo": "2026-10-07", "settlementPrice": "300.00" }` | +| ❌ 调价两个价格都不传 | `{ "hotelId": 100001, "roomTypeId": 300001, "dateFrom": "2026-10-01", "dateTo": "2026-10-07" }` → 808314 | +| ❌ 调房量不带已用期望值 | `{ "hotelId": 100001, "roomTypeId": 300001, "stayDate": "2026-10-01", "totalRooms": 15 }` → 400 expectedUsedRooms 不能为空 | +| ❌ 团期转交不写原因 | `{ "toUserId": 30002 }` → 400 reason 不能为空 | + +### 切换状态时的必要动作 + +- 转房 `targetType` 切换时,把另一个目标 ID 字段置 null;服务端只读与 `targetType` 对应的那个字段。 +- 调房量提交的 `expectedUsedRooms` 必须取自最近一次查询该格的 `usedRooms`;收到 808311 后先重新查询再提交。 +- 控房表查询用城市**编码**,退团房列表用城市**中文名**,两个筛选框的取值来源不同。 +- 下载导出文件前按响应 `Content-Type` 判断:xlsx 为文件,`application/json` 为错误 `Result`。 + +--- + +## 五、数据库行为(涉及写操作时必写) + +| 前端提交 | 写入位置 | 行为 | +|----------|----------|------| +| 改配原订取消确认 | `house_assignment_change` | 该记录 HELD → CANCEL_CONFIRMED,覆盖取消费用 / 凭证 / 备注 / 操作人 | +| 批量认领 | 需求与团期的认领字段 | 逐条独立写,失败的条目不写 | +| 调房量 | resource 价格日历的库存列 | 按已用间数 CAS 更新控房总数 | +| 调价 | resource 价格日历的价格列 | 一次区间写入:已有日历行更新、缺失的补建;未传的价格列不动 | +| 设置期限 | `house_room_transfer` | 更新 cancel_days / cancel_cutoff / remind_days | +| 转房 | `house_room_transfer`、目标配房或团期计划、源配房或源团期计划 | 同一事务:父行扣剩余、写目标、扣源、写子行与日志;任一步失败整笔回滚 | +| 向酒店取消 | `house_room_transfer` | 父行置 CANCELLED、cancel_reason=HOTEL_CANCELLED | +| 模板保存 / 删除 | `house_stay_template` | 新建 / 整份覆盖;删除为软删 | +| 团期转交 | 团期认领字段、团期时间线 | CAS 换持有人,写时间线;计划与分房不动 | + +**显式 SET NULL 说明**: 设置期限时 `cancelDays` 传 null 会把 cancel_days / cancel_cutoff / remind_days 三列一并清空,`deadlineAt` 随之为 null;其余写接口未传的可选字段保持原值,不会被写成 null。 + +--- + +## 六、边界行为 + +- 已登录但不是房务角色(ROOM_MANAGER / SUPER_ADMIN 以外)→ 808090「未登录或非房务角色,无权操作」;原「房务组长」角色已取消,该类账号同样返回 808090。 +- 写接口统一经房务写守卫;只读接口经房务读守卫,两者都只认房务角色。 +- 资源不存在 → 各接口的业务码(808320 / 808332 / 808351 / 589500),HTTP 200。 +- resource 服务不可用 → 控房表相关接口返回 808900「资源服务暂不可用,请稍后重试」,不返回空表冒充无数据。 +- 所有写接口带防重(3 秒,团期转交 5 秒);重复提交返回 100502 与各接口自己的提示文案。 +- 并发写冲突 → 808932「房务状态已被并发修改,请刷新后重试」或 808311 / 808608(见各接口)。 + +--- + +## 六.5、枚举 / 数据字典(接口出现枚举时必写) + +### oldStatus(HouseAssignmentChangeConstants) + +**所属字段**: `HouseAssignmentChangeRespVO.oldStatus` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `HELD` | 原酒店待取消 | 改配后原酒店还没取消,可调接口 1 确认 | +| `CANCEL_CONFIRMED` | 已确认取消 | 已上传取消凭证 | + +### changeKind(HouseAssignmentChangeConstants) + +**所属字段**: `HouseAssignmentChangeRespVO.changeKind` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `HOTEL` | 换酒店 | 配房行换了酒店 | +| `ROOM_COUNT` | 减间数 | 配房行间数减少 | + +### status(退团房行状态) + +**所属字段**: `HouseRoomTransferRespVO.status`、`HouseRoomTransferPageReqVO.status` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `PENDING` | 待处理 | 还有剩余间数没转出也没取消 | +| `TRANSFERRED` | 已转出 | 剩余间数已全部转出 | +| `CANCELLED` | 已取消 | 已向酒店取消 | + +### risk(退团房风险) + +**所属字段**: `HouseRoomTransferRespVO.risk`、`HouseRoomTransferPageReqVO.risk` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `OVERDUE` | 已过免费取消期 | 现在已过截止时刻(入住晚 − cancelDays 天的 cancelCutoff) | +| `NEAR` | 临近免费取消期 | 今天 ≥ 截止日 − remindDays 天(按日比较) | +| `NO_DEADLINE` | 未设免费取消期 | cancelDays 为空 | +| `NORMAL` | 正常 | 其余 | + +### cancelReason(HouseRoomTransferCancelReason) + +**所属字段**: `HouseRoomTransferRespVO.cancelReason` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `HOTEL_CANCELLED` | - | 接口 11 写入;该值在代码里没有对应的中文标签,展示时请用行上的 `statusLabel`(已取消) | + +### sourceType / targetType(退团房来源与目标类型) + +**所属字段**: `HouseRoomTransferRespVO.sourceType`、`targetType`,`HouseRoomTransferSaveReqVO.targetType`,`HouseRoomTransferCandidateRespVO.targetType` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `ORDER` | 订单 | 常规单或团期户的订单需求 | +| `GROUP_BATCH` | 团期 | 团期整团 | + +### ineligibleReason / 808322 参数(转入不可用原因) + +**所属字段**: `HouseRoomTransferCandidateRespVO.ineligibleReason`、808322 文案 `{0}` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `团期已确认` | 团期已确认 | 候选原因 | +| `需求已失效` | 需求已失效 | 候选原因 | +| `需求改版中` | 需求改版中 | 候选原因 | +| `该晚已配满` | 该晚已配满 | 候选原因 | +| `无团号` | 无团号 | 候选原因,也是 808322 参数 | +| `该晚已有同酒店同房型` | 该晚已有同酒店同房型 | 候选原因,也是 808322 参数 | +| `未指定转入目标` | 未指定转入目标 | 808322 参数 | +| `目标不存在` | 目标不存在 | 808322 参数 | +| `团期户请转入所在团期` | 团期户请转入所在团期 | 808322 参数 | +| `不同城` | 不同城 | 808322 参数 | +| `目标当晚无住宿` | 目标当晚无住宿 | 808322 参数 | +| `目标当晚已有控房` | 目标当晚已有控房 | 808322 参数 | +| `不能转回原团期/原需求` | 不能转回原团期/原需求 | 808322 参数 | + +### 批量认领 targetType + +**所属字段**: `HouseConsoleBatchClaimRespVO.items[].targetType` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `REQUIREMENT` | 常规单住宿需求 | 来自 requirementIds | +| `GROUP_BATCH` | 团期 | 来自 groupBatchIds | + +### audit code(异常检查问题码与待办码) + +**所属字段**: `HouseConsoleAuditRespVO.Item.code` / `taskCode` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `STOCK_OVERBOOKED` | 超分配 | issues;已分配超过库存 | +| `STOCK_LEDGER_MISMATCH` | 库存账不平 | issues;本地已用与库存日历不一致,仅库存追踪开关打开时检查 | +| `STOCK_ROW_MISSING` | 库存日历缺行 | issues | +| `TRANSFER_TARGET_GONE` | 转房目标失效 | issues | +| `PLAN_COUNT_MISMATCH` | 计划数量不符 | issues | +| `TRANSFER_PENDING` | 退团房未结清 | tasks | +| `HOTEL_CANCEL_PENDING` | 原酒店待取消 | tasks;对应 HELD 改配记录 | +| `INQUIRY_PENDING` | 新订/变更待确认 | tasks | +| `STAY_UNARRANGED` | 住宿待落实 | tasks | + +### breakfast(HouseBreakfast) + +**所属字段**: `HouseStayTemplateSaveReqVO.nights[].rooms[].breakfast`、`HouseStayTemplateRespVO.nights[].rooms[].breakfast` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `INCLUDED` | 含早餐 | - | +| `EXCLUDED` | 不含早餐 | - | +| `PENDING` | 早餐待确认 | 未填时按此输出 | + +### roomSource(HouseRoomSource) + +**所属字段**: `HouseRoomControlRowRespVO.usages[].roomSource` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `STOCK` | 控房 | 控房表只列扣库存的行,本表恒为此值 | + +### settleType(字典 resource_settle_type) + +**所属字段**: `HouseStayTemplateRespVO.nights[].settleType` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| 空 | 待确认 | settleTypeLabel 输出「待确认」 | +| 字典编码 | 字典名称 | 字典查不到时 settleTypeLabel 回退为原编码 | + +### 错误码段 808300~808399(HouseConsoleErrorCode) + +**所属字段**: `Result.code` / **类型**: `Integer` + +| 值 | 中文 | 说明 | +|----|------|------| +| `808310` | 总房量不能小于已占用 {0} 间 | 调房量;{0} 为已用间数 | +| `808311` | 已占用数已变化,请刷新后重试 | 调房量 CAS 失败 | +| `808312` | 库存追踪未开启,暂不能调整房量 | 调房量;resource 全局开关关闭 | +| `808313` | 日期跨度不能超过 {0} 天 | 控房表 62 天;异常检查 92 天 | +| `808314` | 协议价与结算价至少填写一项 | 调价 | +| `808320` | 转房记录不存在 | 退团房 | +| `808321` | 该房间已处理 | 退团房行已不是 PENDING | +| `808322` | 目标不可转入:{0} | 转房;{0} 见上表 | +| `808323` | 目标团期已确认,不能转入 | 转房;源团期已确认时也返回本码 | +| `808324` | 转出间数超过剩余 {0} 间 | 转房;{0} 为此刻可转间数 | +| `808325` | 请填写酒店确认号并上传凭证 | 转房 / 向酒店取消 | +| `808326` | 只有原单处理人可以处理退团房间 | 退团房写操作 | +| `808328` | 退改期限参数不合法 | 设置期限 | +| `808330` | 查询范围过大,请缩小酒店或日期范围 | 控房表查询 / 导出 | +| `808332` | 变更记录不存在 | 改配取消确认 | +| `808333` | 原订已确认取消 | 改配取消确认 | +| `808340` | 只有团期当前处理人可以转交 | 团期转交 | +| `808341` | 不能转交给自己 | 团期转交 | +| `808342` | 接收人不是房务 | 团期转交 | +| `808343` | 房务人员名单暂不可用,请稍后重试 | 团期转交、户级转单、超管指派 | +| `808350` | 模板名称已存在 | 模板保存 | +| `808351` | 模板不存在 | 模板详情 / 保存 / 删除 | +| `808352` | 只能修改或删除自己创建的模板 | 模板保存 / 删除 | + +808327、808331 未使用。 + +### resource 错误码映射(HouseResourceErrorTranslator) + +**所属字段**: `Result.code` / **类型**: `Integer` + +控房表接口调用 resource 时,resource 返回的 3104xx 在本服务翻译成 8083xx 后返回,前端只会看到右侧的码。 + +| 值 | 中文 | 说明 | +|----|------|------| +| `310421` | 映射为 808313 | 参数固定为 62 | +| `310422` | 映射为 808330 | - | +| `310423` | 映射为 808312 | - | +| `310424` | 映射为 808310 | 参数为已用间数,取不到时为「?」 | +| `310425` | 映射为 808311 | - | +| 其他 3104xx | 原样透传 | 例如 310426「房型 {0} 不属于酒店 {1}」 | +| 返回为空 | 映射为 808900 | 资源服务暂不可用,请稍后重试 | + +--- + +## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面) + +- **仅影响**: 管理后台「房务控制台」菜单各页签;团期抢单池新增「转交」动作。 +- **零影响**: + - 小程序与 H5 + - 已有配房行的价格(调价只作用于之后新生成的配房) + +--- + +## 八、测试环境已验证 + +所有接口都经测试服网关调用。HTTP 状态恒为 200,下面的 `code` 指响应 body 里的 `code`,带 ✓ 标记。行尾 `@` 后面是当时测试服 order-v3 的部署提交。`bdde64a3a`、`9c7ac9382`、`ff6863754`、`3ecf38797` 四个提交都包含本单合并提交 `7c21cf0e40`。控房表相关接口另依赖 resource 部署提交 `bdde64a3a`,测试服已开 `hotel.stock.enabled=true`。 + +``` +PUT /v3/admin/order/house-console/changes/{changeId}/cancel-confirm HELD 改配记录,带 1 个凭证 + cancelFee=200.00 → code=200,oldStatus HELD→CANCEL_CONFIRMED,cancelFee="200.00" ✓ @bdde64a3a +PUT /v3/admin/order/house-console/changes/{changeId}/cancel-confirm 同一条再确认一次 → code=808333「原订已确认取消」 ✓ @bdde64a3a +POST /v3/admin/order/house-console/claims/batch 2 条待认领 + 1 条已被他人认领 → code=200,successCount=2、failCount=1,失败项 errorCode=808001;成功两条的认领人为本人 ✓ @bdde64a3a +GET /v3/admin/order/house-console/room-control 某晚已有 2 间控房配房 → code=200,usedRooms=2、assignedRooms=2、totalRooms=15、remainRooms=13 ✓ @9c7ac9382 +PUT /v3/admin/order/house-console/room-control/stock totalRooms=1(小于已占用 2) → code=808310 ✓ @9c7ac9382 +PUT /v3/admin/order/house-console/room-control/stock expectedUsedRooms=1(实际已占用 2) → code=808311 ✓ @9c7ac9382 +PUT /v3/admin/order/house-console/room-control/stock totalRooms=5 + expectedUsedRooms=2 → code=200;控房表该行 totalRooms 15→5、remainRooms 13→3 ✓ @9c7ac9382 +PUT /v3/admin/order/house-console/room-control/price protocolPrice / settlementPrice 620.00 / 615.00 → 680.00 / 675.00 → code=200;之后新提交的同酒店同房型同晚配房行快照 680.00 / 675.00,调价前已有的行仍是 620.00 / 615.00 ✓ @9c7ac9382 +GET /v3/admin/order/house-console/room-control/export 导出 → 响应为 xlsx 文件(非 JSON 包装);sheet「每日房量」表头 酒店 / 入住夜日期 / 房型 / 控房总数 / 已用 / 剩余,sheet「团组用房」表头 酒店 / 入住夜日期 / 房型 / 团号 / 房源 / 用房间数 / 确认状态,全文无价格列 ✓ @9c7ac9382 +GET /v3/admin/order/house-console/room-transfers 取消一张已确认常规订单(1 行非控房 2 间 + 1 行控房) → code=200,新增 1 行 PENDING、roomCount=2,控房行不出现 ✓ @bdde64a3a,重建夹具复测同样读数 ✓ @ff6863754 +GET /v3/admin/order/house-console/room-transfers 同一取消命令重放(返回 581017 订单已取消) → 行数仍为 1 ✓ @bdde64a3a +GET /v3/admin/order/house-console/room-transfers RESOURCE_PREPARING 团期里一户出行前取消 → code=200,新增 1 行 PENDING、sourceType=GROUP_BATCH、roomCount=2 ✓ @3ecf38797 +GET /v3/admin/order/house-console/room-transfers 整行转出后 → 该行 status=TRANSFERRED、remainingCount=0、标签「已转出」 ✓ @3ecf38797 +PUT /v3/admin/order/house-console/room-transfers/{id}/deadline cancelDays=3 → code=200,分页该行 risk=OVERDUE ✓ @9c7ac9382,复测 ✓ @ff6863754 +PUT /v3/admin/order/house-console/room-transfers/{id}/deadline cancelDays=0、remindDays=1 → code=200,risk=NEAR ✓ @9c7ac9382,复测 ✓ @ff6863754 +PUT /v3/admin/order/house-console/room-transfers/{id}/deadline cancelDays=null(清空) → code=200,库里 cancel_days 为 NULL(cancel_cutoff / remind_days 保留原值),risk=NO_DEADLINE ✓ @ff6863754 +GET /v3/admin/order/house-console/room-transfers/{id}/candidates 同城同晚有 RESOURCE_PREPARING 团期 → code=200,该团 eligible=true、needRoomCount=4 ✓ @3ecf38797 +GET /v3/admin/order/house-console/room-transfers/{id}/candidates 同城同晚有房务 B 处理的订单 → 该单 eligible=true,holderName 为房务 B 的姓名 ✓ @ff6863754 +GET /v3/admin/order/house-console/room-transfers/{id}/candidates 团期该晚 needRoomCount=0 → eligible=false,ineligibleReason「该晚已配满」 ✓ @ff6863754 +GET /v3/admin/order/house-console/room-transfers/{id}/candidates 已确认团期 → eligible=false、ineligibleReason「团期已确认」:测试服未单独调用,由 HouseRoomTransferQueryManagerTest#candidates_confirmedGroup_listedLastAsIneligibleWithReason 覆盖 +POST /v3/admin/order/house-console/room-transfers/{id}/transfer 转入团期,不带 hotelConfirmNo → code=808325「请填写酒店确认号并上传凭证」 ✓ @3ecf38797 +POST /v3/admin/order/house-console/room-transfers/{id}/transfer 转入 RESOURCE_PREPARING 团期,带齐确认号与凭证、整行 2 间 → code=200,源行 TRANSFERRED、remainingCount 2→0;该团该晚新增 1 行 2 间计划行(不扣控房、已确认);源订单该晚配房行被软删 ✓ @3ecf38797 +POST /v3/admin/order/house-console/room-transfers/{id}/transfer 转入房务 B 处理的订单 → code=200,目标配房行 confirmStatus=CONFIRMED;房务 B 站内信新增 HOUSE_ROOM_TRANSFERRED「退团房间已转入:<目标团号>」 ✓ @ff6863754 +POST /v3/admin/order/house-console/room-transfers/{id}/transfer 非源单处理人(房务 C)调用 → code=808326「只有原单处理人可以处理退团房间」 ✓ @ff6863754 +POST /v3/admin/order/house-console/room-transfers/{id}/transfer 部分转入 roomCount=1 → code=200,源行 remainingCount 2→1,仍为 PENDING ✓ @ff6863754 +POST /v3/admin/order/house-console/room-transfers/{id}/transfer 转入已确认团期 → 808323:测试服未单独调用,由 HouseRoomTransferManagerTest#transfer_recheck7GroupNoLongerWritable_throws808323 覆盖 +POST /v3/admin/order/house-console/room-transfers/{id}/cancel-hotel 不带凭证 → code=808325 ✓ @9c7ac9382 +POST /v3/admin/order/house-console/room-transfers/{id}/cancel-hotel 带凭证(真实 OSS 上传) → code=200,该行 PENDING→CANCELLED,cancel_fee=0.00 ✓ @9c7ac9382 +GET /v3/admin/order/house-console/audit 有 HELD 改配记录、尚未确认取消 → code=200,tasks 含 HOTEL_CANCEL_PENDING「原酒店待取消」 ✓ @bdde64a3a +GET /v3/admin/order/house-console/audit 源订单已取消、退团房行 PENDING,窗口覆盖出发日 → code=200,tasks 含 TRANSFER_PENDING「退团房未结清」 ✓ @ff6863754 +GET /v3/admin/order/house-console/audit issues 各类(超分配 / 账不平)正反例:开关打开时正常流程造不出超分配,测试服未单独造,由 HouseConsoleAuditManagerTest#audit_assignedAboveStockPlusUsed_reportsOverbooked 等逐类覆盖 +GET /v3/admin/order/house-console/stay-templates 新建后按关键字查 → code=200,列表含新模板 ✓ @bdde64a3a,复测 ✓ @9c7ac9382 +GET /v3/admin/order/house-console/stay-templates/{id} 测试服未单独调用,由 HouseStayTemplateServiceTest#get_roundTripNights_withLabelsAndPriceString 覆盖 +POST /v3/admin/order/house-console/stay-templates 新建 → code=200 ✓;同名再建 → code=808350「模板名称已存在」 ✓ @bdde64a3a,复测 ✓ @9c7ac9382 +DELETE /v3/admin/order/house-console/stay-templates/{id} 非创建人(房务 A)删除 → code=808352「只能修改或删除自己创建的模板」 ✓;创建人(超管测试账号)删除 → code=200 ✓ @bdde64a3a,复测 ✓ @9c7ac9382 +POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/transfer 持有人转给另一房务(团里已有 1 行计划) → code=200;团期配房列表 houseClaimerName 变为接收人,计划行数 1→1 ✓ @9c7ac9382 +POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/transfer 非持有人(房务 C)调用 → code=808340「只有团期当前处理人可以转交」 ✓ @9c7ac9382 +``` + +验证身份:房务 A / B / C、测试定制师、超管测试账号,全部是测试专用账号。落库读数来自测试服只读 SQL。 + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#8491](https://git.1814.love:8443/wx/HL/issues/8491) +- 契约文档: `docs/order-v3/api/API-SPEC-HOUSE-V1.1.html` §12 接口、§11.12 错误码 +- 同批变更: 同目录 `30_8491_房务配房接口字段与口径调整-修改接口-管理后台.md`、`30_8491_房务旧列表接口下线-删除接口-管理后台.md` + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8491](https://git.1814.love:8443/wx/HL/issues/8491) + +### 联系人 + +- **后端负责人**: @wx diff --git a/changelogs-v2/2026-09/30_8491_房务旧列表接口下线-删除接口-管理后台.md b/changelogs-v2/2026-09/30_8491_房务旧列表接口下线-删除接口-管理后台.md new file mode 100644 index 00000000..85be7f23 --- /dev/null +++ b/changelogs-v2/2026-09/30_8491_房务旧列表接口下线-删除接口-管理后台.md @@ -0,0 +1,614 @@ +--- +schema: "hl-changelog/v2" +ticket: "8491" +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" +--- + +# 房务旧列表: 下线 4 个已废弃的管理端列表接口(抢单池列表 / 我的接单 / 组长全部已抢订单 / 我的团) + +> **存放目录**: 二期 → `changelogs-v2/2026-09/` +> +> **服务**: hl-order-service-v3 +> **Issue**: #8491 +> **日期**: 2026-09-29 +> **影响范围**: 管理后台房务旧列表页(抢单池、我的接单、组长监督视图、我的团)的数据来源 + +--- + +## ⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写) + +- 以下 4 个 GET 接口从服务端删除,服务端已无这些路由映射,删除后返回形态本文不作约定,调用点一律移除: + - `GET /v3/admin/order/grab-pool/hotel-requirements` + - `GET /v3/admin/order/grab-pool/my-claims/hotel` + - `GET /v3/admin/order/grab-pool/all-claims/hotel` + - `GET /v3/admin/order/grab-pool/my-claims/group-batches` +- 替代接口:常规单统一走 `GET /v3/admin/order/house-allocation/households`,团期统一走 `GET /v3/admin/order/house-allocation/group-batches`(两者本次的字段调整见同目录修改接口文件)。 +- 「房务组长」角色同步取消:原「全部已抢订单」监督视图与「我的团 scope=all」不再存在,全体房务改用替代接口的 `scope=all` 查看全部。 + +--- + +## 一、背景(选填) + +这 4 个接口在 #8491 之前已标注「已废弃,改用 house-allocation 列表」,房务控制台上线两条统一列表后删除。它们的请求 / 响应 VO(`HouseMyOrderPageReqVO`、`HouseMyOrderPageRespVO`、`HouseMyOrderItemRespVO`、`HouseMyOrderStatsVO`、`HouseMyGroupPageReqVO`、`HouseMyGroupPageRespVO`、`HouseMyGroupItemRespVO`、`HouseMyGroupStatsVO`)随之删除。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 抢单池列表(常规单) | GET | `/v3/admin/order/grab-pool/hotel-requirements` | 删除 | 改用 households 列表 status=pendingClaim | +| 2 | 我的接单(常规单) | GET | `/v3/admin/order/grab-pool/my-claims/hotel` | 删除 | 改用 households 列表 scope=mine | +| 3 | 组长全部已抢订单(常规单) | GET | `/v3/admin/order/grab-pool/all-claims/hotel` | 删除 | 改用 households 列表 scope=all | +| 4 | 我的团(团期) | GET | `/v3/admin/order/grab-pool/my-claims/group-batches` | 删除 | 改用 group-batches 列表 status=claimed | + +--- + +## 三、接口详情 + +本节 4 个接口均已删除。入参 / 出参表与示例记录的是**删除前**的契约,仅供前端定位与清理调用点;字段名、类型、校验文案逐一取自删除前源码,ID、日期、姓名等取值为说明用的构造值。服务端已无这些路由映射,删除后返回形态本文不作约定。 + +### 1. 抢单池列表(常规单) `GET /v3/admin/order/grab-pool/hotel-requirements` + +**VO**: `HouseGrabPageReqVO → PageResult`(已删除接口,VO 类仍在代码中但无接口引用) + +#### 使用场景 + +删除前:房务在抢单池页查看待认领的常规单需求。现在改为 `GET /v3/admin/order/house-allocation/households?status=pendingClaim`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| keyword | Query | String | ❌ | ≤32 字 | 删除前:关键词 | +| productType | Query | String | ❌ | - | 删除前:产品类型 | +| productName | Query | String | ❌ | - | 删除前:产品名 | +| consultantId | Query | Long | ❌ | - | 删除前:定制师 | +| guestName | Query | String | ❌ | - | 删除前:客人姓名 | +| departDateFrom / departDateTo | Query | LocalDate | ❌ | - | 删除前:出行日期区间 | +| page | Query | Integer | ❌ | ≥1,默认 1 | 删除前:页码 | +| pageSize | Query | Integer | ❌ | 1~100,默认 20 | 删除前:每页条数 | +| sortBy | Query | String | ❌ | 默认 createTime,desc | 删除前:排序 | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| records | List | 删除前:行列表 | +| total | int | 删除前:总数 | +| records[].id / orderId | Long(String) | 删除前:需求 ID / 订单 ID | +| records[].orderNo / teamNo / guestName / personsDesc | String | 删除前:订单号 / 团号 / 客人 / 人数描述 | +| records[].productType / productName / productNo / route | String | 删除前:产品信息 | +| records[].departDate / nights / cities | LocalDate / Integer / List | 删除前:出行日期 / 夜数 / 城市 | +| records[].totalAmount | BigDecimal(String) | 删除前:订单总额 | +| records[].consultantName / consultantId / consultantRemark | String / Long(String) / String | 删除前:定制师信息 | +| records[].requirementNote / dispatchRemark / special | String / String / List | 删除前:需求备注 / 派单备注 / 特殊要求 | +| records[].requirementVersion | Integer | 删除前:需求版本 | +| records[].urgencyLevel / urgencyLabel / daysToDepart / manualUrgent | String / String / Integer / Boolean | 删除前:紧急度 | +| records[].createTime | LocalDateTime | 删除前:入池时间 | +| records[].isRework / reworkPrevClaimerName | Boolean / String | 删除前:返工标识 / 上一任认领人 | + +#### 请求示例 + +```http +GET /v3/admin/order/grab-pool/hotel-requirements?page=1&pageSize=20 +``` + +#### 响应示例 + +删除前(仅供清理对照): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "records": [ + { + "id": "1940000000000000011", + "orderId": "1930000000000000021", + "orderNo": "26-0915", + "teamNo": "26-0920", + "guestName": "李女士一家", + "productName": "呼伦贝尔秋色 6 日", + "departDate": "2026-10-05", + "isRework": false + } + ], + "total": 1, + "page": 1, + "pageSize": 20 + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +接口已删除,无空数据或降级形态可约定;前端移除调用点,空列表展示改由替代接口 `records` 为空时处理。 + +#### 错误响应 + +删除前(仅供清理对照): + +```json +{ + "code": 400, + "message": "keyword 长度不能超过 32 字", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | keyword 长度不能超过 32 字 / page 必须 ≥ 1 / pageSize 最大 100 | 删除前的入参校验 | + +#### 业务边界 + +- 服务端已无该路由映射,删除后返回形态本文不作约定,调用点一律移除。 +- 替代:`GET /v3/admin/order/house-allocation/households?status=pendingClaim`,排序可传 `sortBy=createTime,desc` 保持原默认顺序;替代接口的 `list` 字段名与本接口的 `records` 不同。 + +### 2. 我的接单(常规单) `GET /v3/admin/order/grab-pool/my-claims/hotel` + +**VO**: `HouseMyOrderPageReqVO → HouseMyOrderPageRespVO`(均已删除) + +#### 使用场景 + +删除前:房务查看本人已认领的常规单及状态统计。现在改为 `GET /v3/admin/order/house-allocation/households?scope=mine`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| keyword | Query | String | ❌ | ≤32 字 | 删除前:关键词 | +| status | Query | String | ❌ | `unfinished` / `allUnfinished` / `todo` / `inProgress` / `claiming` / `pendingConfirm` / `confirmed` / `exception` / `voided` / `inInquiry` | 删除前:跟单状态(前三个都表示全部未完成) | +| productType | Query | String | ❌ | CORE / ROUTE / CUSTOM / GROUP | 删除前:产品类型 | +| productName / guestName | Query | String | ❌ | - | 删除前:模糊搜 | +| consultantId | Query | Long | ❌ | - | 删除前:定制师 | +| departDateFrom / departDateTo | Query | LocalDate | ❌ | - | 删除前:出行日期区间 | +| stayDate | Query | LocalDate | ❌ | - | 删除前:入住晚下钻 | +| claimedAtFrom / claimedAtTo | Query | LocalDateTime | ❌ | - | 删除前:认领时间区间 | +| city / hasException / hasTodo / hasUnreadMessage | Query | String / Boolean | ❌ | - | 删除前:预留字段,未实现 | +| page | Query | Integer | ❌ | ≥1,默认 1 | 删除前:页码 | +| pageSize | Query | Integer | ❌ | 1~100,默认 20 | 删除前:每页条数 | +| sortBy | Query | String | ❌ | 默认 claimedAt,desc | 删除前:排序 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| list | List | 删除前:行列表 | +| total | Long | 删除前:总数 | +| stats | HouseMyOrderStatsVO | 删除前:inProgress / claiming / inInquiry(恒 0)/ pendingConfirm / confirmed / exception / voided | +| list[].id / orderId / consultantId / claimerId | Long(String) | 删除前:需求 / 订单 / 定制师 / 持有人 ID | +| list[].orderNo / teamNo / guestName / personsDesc / productType / productName / route | String | 删除前:订单与产品信息 | +| list[].departDate / nights / cities | LocalDate / Integer / List | 删除前:出行信息 | +| list[].totalAmount | BigDecimal(String) | 删除前:订单总额 | +| list[].consultantName / claimerName / claimedAt | String / String / LocalDateTime | 删除前:定制师 / 持有人 / 认领时间 | +| list[].houseStatus | String | 删除前:**中文状态**(与 houseStatusLabel 同值) | +| list[].houseStatusLabel | String | 删除前:中文状态 | +| list[].progressDesc / hotelSummary / lastAction | String | 删除前:进度文字 / 已配酒店摘要 / 最近动作 | +| list[].exceptionCount / todoCount / unreadMessageCount | Integer | 删除前:异常数 / 待办数 / 未读留言数 | +| list[].primaryAction | HousePrimaryActionVO | 删除前:code / label / url | +| list[].voided / requirementVersion / voidReason / voidedAt | Boolean / Integer / String / LocalDateTime | 删除前:作废信息 | + +#### 请求示例 + +```http +GET /v3/admin/order/grab-pool/my-claims/hotel?status=unfinished&page=1&pageSize=20 +``` + +#### 响应示例 + +删除前(仅供清理对照): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "list": [ + { + "id": "1940000000000000011", + "orderId": "1930000000000000021", + "orderNo": "26-0915", + "guestName": "李女士一家", + "claimerName": "张敏", + "houseStatus": "配房中", + "houseStatusLabel": "配房中", + "progressDesc": "5晚已配3晚", + "unreadMessageCount": 2, + "voided": false + } + ], + "total": 1, + "stats": { + "inProgress": 1, + "claiming": 1, + "inInquiry": 0, + "pendingConfirm": 0, + "confirmed": 0, + "exception": 0, + "voided": 0 + } + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +接口已删除,无空数据或降级形态可约定;前端移除调用点。 + +#### 错误响应 + +删除前(仅供清理对照): + +```json +{ + "code": 400, + "message": "pageSize 最大 100", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 400 | keyword 长度不能超过 32 字 / page 必须 ≥ 1 / pageSize 最大 100 | 删除前的入参校验 | + +#### 业务边界 + +- 服务端已无该路由映射,删除后返回形态本文不作约定,调用点一律移除。 +- 替代接口的 `houseStatus` 是枚举码(PENDING_CLAIM / CLAIMING / PENDING_FINALIZE / CONFIRMED / EXCEPTION),中文取 `houseStatusLabel`;按本接口 `houseStatus` 中文做判断的代码要改。 +- 替代接口 `scope` 默认 `all`,查本人认领的单必须显式传 `scope=mine`。 +- 本接口的 `voided` / `voidReason` / `voidedAt` / `progressDesc` / `hotelSummary` / `lastAction` 在替代列表中没有对应字段。 + +### 3. 组长全部已抢订单(常规单) `GET /v3/admin/order/grab-pool/all-claims/hotel` + +**VO**: `HouseMyOrderPageReqVO → HouseMyOrderPageRespVO`(均已删除) + +#### 使用场景 + +删除前:房务组长 / 超管的只读监督视图,查看全部已认领常规单。「房务组长」角色已取消,全体房务改用 `GET /v3/admin/order/house-allocation/households?scope=all`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| keyword | Query | String | ❌ | ≤32 字 | 删除前:同接口 2 | +| status | Query | String | ❌ | 同接口 2 | 删除前:跟单状态 | +| page | Query | Integer | ❌ | ≥1,默认 1 | 删除前:页码 | +| pageSize | Query | Integer | ❌ | 1~100,默认 20 | 删除前:每页条数 | +| 其余筛选 | Query | - | ❌ | 同接口 2 | 删除前:与接口 2 共用同一请求 VO | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| list | List | 删除前:同接口 2,行内 claimerId / claimerName 标该单属哪个房务 | +| total | Long | 删除前:总数 | +| stats | HouseMyOrderStatsVO | 删除前:同接口 2 | + +#### 请求示例 + +```http +GET /v3/admin/order/grab-pool/all-claims/hotel?page=1&pageSize=20 +``` + +#### 响应示例 + +删除前(仅供清理对照): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "list": [ + { + "id": "1940000000000000012", + "orderId": "1930000000000000022", + "orderNo": "26-0916", + "claimerId": "30002", + "claimerName": "王芳", + "houseStatus": "待最终确认", + "houseStatusLabel": "待最终确认" + } + ], + "total": 1, + "stats": { + "inProgress": 0, + "claiming": 0, + "inInquiry": 0, + "pendingConfirm": 1, + "confirmed": 0, + "exception": 0, + "voided": 0 + } + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +接口已删除,无空数据或降级形态可约定;前端移除调用点。 + +#### 错误响应 + +删除前(仅供清理对照): + +```json +{ + "code": 808092, + "message": "无权查看全部房务订单(仅房务组长或超管可查看)", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 808092 | 无权查看全部房务订单(仅房务组长或超管可查看) | 删除前:非组长 / 超管访问;该码已删除,不复用 | +| 400 | keyword 长度不能超过 32 字 / page 必须 ≥ 1 / pageSize 最大 100 | 删除前的入参校验 | + +#### 业务边界 + +- 服务端已无该路由映射,删除后返回形态本文不作约定,调用点一律移除。 +- 替代接口 `scope=all` 对全体房务开放;行内 `claimerName` 与 `readOnly` / `readOnlyReason` 标出该单由谁处理。 + +### 4. 我的团(团期) `GET /v3/admin/order/grab-pool/my-claims/group-batches` + +**VO**: `HouseMyGroupPageReqVO → HouseMyGroupPageRespVO`(均已删除) + +#### 使用场景 + +删除前:房务查看本人整团认领的团期(scope=mine),组长 / 超管可看全部已认领团(scope=all)。现在改为 `GET /v3/admin/order/house-allocation/group-batches?status=claimed`,配合 `scope=mine` 或 `scope=all`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| scope | Query | String | ❌ | mine / all,默认 mine | 删除前:all 仅组长 / 超管 | +| keyword | Query | String | ❌ | ≤32 字 | 删除前:团期号 / 产品名 | +| batchStatus | Query | String | ❌ | 团期九态之一 | 删除前:团期状态 | +| needsReconfirm | Query | Boolean | ❌ | - | 删除前:只看需求待重新确认的团 | +| departDateFrom / departDateTo | Query | LocalDate | ❌ | - | 删除前:出发日区间 | +| claimedAtFrom / claimedAtTo | Query | LocalDateTime | ❌ | - | 删除前:认领时间区间 | +| page | Query | Integer | ❌ | ≥1,默认 1 | 删除前:页码 | +| pageSize | Query | Integer | ❌ | 1~100,默认 20 | 删除前:每页条数 | +| sortBy | Query | String | ❌ | 默认 claimedAt,desc,可切 departDate,asc | 删除前:排序 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| total | Long | 删除前:总数 | +| stats | HouseMyGroupStatsVO | 删除前:total / needsReconfirm / cancelled(后两项在 total 超过 500 时为 null) | +| list | List | 删除前:行列表 | +| list[].groupBatchId / productId | Long(String) | 删除前:团期 / 产品 ID | +| list[].batchNo / productName / batchName / batchLabel / batchStatus / batchStatusLabel | String | 删除前:团期信息 | +| list[].departDate / endDate / enrollDeadline | LocalDate | 删除前:日期 | +| list[].enrolledRooms / enrolledPeople / activeOrderCount / hotelOrderCount / daysToDepart | Integer | 删除前:计数 | +| list[].hotelReady | Boolean | 删除前:酒店是否就绪 | +| list[].urgencyLevel / urgencyLabel | String | 删除前:紧急度 | +| list[].createTime | LocalDateTime | 删除前:创建时间 | +| list[].houseClaimerId / houseClaimerName / houseClaimedAt | Long(String) / String / LocalDateTime | 删除前:整团认领人与时间 | +| list[].requirementConfirmed | Boolean | 删除前:需求整体确认标记 | + +#### 请求示例 + +```http +GET /v3/admin/order/grab-pool/my-claims/group-batches?scope=mine&page=1&pageSize=20 +``` + +#### 响应示例 + +删除前(仅供清理对照): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "total": 1, + "stats": { + "total": 1, + "needsReconfirm": 0, + "cancelled": 0 + }, + "list": [ + { + "groupBatchId": "1950000000000000031", + "batchNo": "GB261005", + "batchName": "呼伦贝尔秋色 6 日", + "batchStatus": "PENDING_DEPARTURE", + "departDate": "2026-10-05", + "houseClaimerId": "30001", + "houseClaimerName": "张敏", + "houseClaimedAt": "2026-09-20 10:00:00", + "requirementConfirmed": true + } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +接口已删除,无空数据或降级形态可约定;前端移除调用点。 + +#### 错误响应 + +删除前(仅供清理对照): + +```json +{ + "code": 808092, + "message": "无权查看全部房务订单(仅房务组长或超管可查看)", + "data": null, + "success": false +} +``` + +| code | message | 触发 | +|------|---------|------| +| 808092 | 无权查看全部房务订单(仅房务组长或超管可查看) | 删除前:普通房务传 scope=all;该码已删除,不复用 | +| 400 | keyword 长度不能超过 32 字 / page 必须大于等于 1 / pageSize 最大 100 | 删除前的入参校验 | + +#### 业务边界 + +- 服务端已无该路由映射,删除后返回形态本文不作约定,调用点一律移除。 +- 替代接口没有 `needsReconfirm` 筛选;行字段 `requirementConfirmed` 仍在,可在前端按它标记。 +- 替代接口的统计为 `pendingClaim` / `claimed` / `all`,没有 needsReconfirm / cancelled 计数。 +- 替代接口 `scope` 默认 `all`,查本人整团认领的团必须显式传 `scope=mine`。 + +--- + +## 四、契约约束与正确调用方式(接口类必写) + +> 本节只写**后端接受/拒绝 payload 的规则**,不写 UI 渲染建议。 + +### ✅ 正确 / ❌ 错误 payload 对照 + +| 场景 | payload | +|------|---------| +| ❌ 抢单池列表 | `GET /v3/admin/order/grab-pool/hotel-requirements` → 路由已删除 | +| ✅ 抢单池列表 | `GET /v3/admin/order/house-allocation/households?status=pendingClaim&sortBy=createTime,desc` | +| ❌ 我的接单 | `GET /v3/admin/order/grab-pool/my-claims/hotel` → 路由已删除 | +| ✅ 我的接单 | `GET /v3/admin/order/house-allocation/households?scope=mine&status=unfinished` | +| ❌ 全部已抢订单 | `GET /v3/admin/order/grab-pool/all-claims/hotel` → 路由已删除 | +| ✅ 全部已抢订单 | `GET /v3/admin/order/house-allocation/households?scope=all&status=unfinished` | +| ❌ 我的团 | `GET /v3/admin/order/grab-pool/my-claims/group-batches` → 路由已删除 | +| ✅ 我的团 | `GET /v3/admin/order/house-allocation/group-batches?scope=mine&status=claimed` | + +### 切换状态时的必要动作 + +- 替代接口的状态筛选取值与旧接口不同:常规单 `status` 为 pendingClaim / unfinished(默认)/ claiming / pendingConfirm / confirmed / exception,空串=全部;团期 `status` 为 pendingClaim / claimed,不传=两者都要。旧值(`allUnfinished` / `todo` / `inProgress` / `voided` / `inInquiry`)传给替代接口会被入参校验拒绝(400)。 +- 两个替代接口的 `scope` 默认都是 `all`;原「我的接单」「我的团」对应的调用必须显式带 `scope=mine`。 +- 常规单替代接口 `sortBy` 只接受 departDate,asc(默认)/ createTime,desc / claimedAt,desc;团期替代接口只接受 departDate,asc(默认)/ claimedAt,desc / createTime,desc。 + +--- + +## 五、数据库行为(涉及写操作时必写) + +| 前端提交 | 写入位置 | 行为 | +|----------|----------|------| +| 4 个已删除接口均为只读 GET | 无 | 不涉及写入 | + +**显式 SET NULL 说明**: 不涉及。 + +--- + +## 六、边界行为 + +- 4 条路由已从服务端删除,删除后返回形态本文不作约定,前端不得依赖任何返回来判断,调用点一律移除。 +- 替代接口的读权限:全体房务(ROOM_MANAGER / SUPER_ADMIN)可用 `scope=all`;非房务角色返回 808090「未登录或非房务角色,无权操作」。 +- 808091、808092 随组长角色一并删除,不复用。 + +--- + +## 六.5、枚举 / 数据字典(接口出现枚举时必写) + +### 替代接口 houseStatus(HouseStateEnum) + +**所属字段**: `HouseAllocationHouseholdRespVO.houseStatus`(替代旧接口 `HouseMyOrderItemRespVO.houseStatus` 的中文值) / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `PENDING_CLAIM` | 待配房 | 尚未认领 | +| `CLAIMING` | 配房中 | 已认领、配房进行中 | +| `PENDING_FINALIZE` | 待最终确认 | 配房待最终确认 | +| `CONFIRMED` | 已完成 | 配房完成 | +| `EXCEPTION` | 异常 | 异常 | + +--- + +## 六.6、修改前后对比(修改/删除接口必写) + +### 字段级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| 我的接单 `list[].houseStatus` | 中文状态 | 替代接口为枚举码,中文取 `houseStatusLabel` | +| 我的接单 `list[].unreadMessageCount` | 未读留言数 | 替代接口 `unreadCount`(房务会话未读数) | +| 我的接单 `stats` | inProgress / claiming / inInquiry / pendingConfirm / confirmed / exception / voided | 替代接口 pendingClaim / claiming / pendingConfirm / confirmed / exception / unfinished / all | +| 我的接单 `progressDesc` / `hotelSummary` / `lastAction` / `voided` / `voidReason` / `voidedAt` | 有 | 替代列表无对应字段 | +| 抢单池列表 `records` | 行列表字段名 | 替代接口为 `list` | +| 我的团 `stats` | total / needsReconfirm / cancelled | 替代接口 pendingClaim / claimed / all | +| 我的团入参 `needsReconfirm` | 有 | 替代接口无;行字段 `requirementConfirmed` 保留 | + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 调用 4 个旧列表接口 | 返回列表 | 路由已删除,返回形态本文不作约定 | +| 普通房务查看全部已认领常规单 | 808091 | 替代接口 scope=all 放行 | +| 普通房务查看全部已认领团期 | 808092 | 替代接口 scope=all 放行 | + +--- + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 是。4 条路由删除,仍调用的页面拿不到数据。 +- **前端是否必须同步上线**: 是。服务端已无这 4 条路由,仍在调用的页面需改为替代接口。 +- **前端 workaround 清理点**: 旧抢单池页、我的接单页、组长监督视图、我的团页对这 4 个路径的调用;按 `houseStatus` 中文值、`unreadMessageCount`、`inInquiry` / `voided` 统计、808091 / 808092 做的分支。 + +--- + +## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面) + +- **仅影响**: 管理后台调用上述 4 个路径的页面。 +- **零影响**: + - 团期抢单池列表 `GET /v3/admin/order/grab-pool/group-batches` 保留(仍为废弃标注) + - 同一控制器的转单 `POST /v3/admin/order/hotel-requirements/{requirementId}/transfer` 保留(本次行为变化见同目录修改接口文件) + - 小程序与 H5 + +--- + +## 八、测试环境已验证 + +四个旧端点都用房务 A 身份经测试服网关调用,共三轮(00:30、00:31、01:18)。HTTP 状态恒为 200,下面的 `code` 指响应 body 里的 `code`,带 ✓ 标记。行尾 `@` 后面是当时测试服 order-v3 的部署提交,两个提交都包含本单合并提交 `7c21cf0e40`。 + +``` +GET /v3/admin/order/grab-pool/hotel-requirements 抢单池列表(常规单) → code=404 ✓ @bdde64a3a,复测 ✓ @9c7ac9382 +GET /v3/admin/order/grab-pool/my-claims/hotel 我的接单(常规单) → code=404 ✓ @bdde64a3a,复测 ✓ @9c7ac9382 +GET /v3/admin/order/grab-pool/all-claims/hotel 组长全部已抢订单(常规单) → code=404 ✓ @bdde64a3a,复测 ✓ @9c7ac9382 +GET /v3/admin/order/grab-pool/my-claims/group-batches 我的团(团期) → code=404 ✓ @bdde64a3a,复测 ✓ @9c7ac9382 +``` + +验证身份:房务 A,测试专用账号。 + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#8491](https://git.1814.love:8443/wx/HL/issues/8491) +- 契约文档: `docs/order-v3/api/API-SPEC-HOUSE-V1.1.html` §12 房务控制台 +- 同批变更: 同目录 `30_8491_房务控制台接口-新增接口-管理后台.md`、`30_8491_房务配房接口字段与口径调整-修改接口-管理后台.md` + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8491](https://git.1814.love:8443/wx/HL/issues/8491) + +### 联系人 + +- **后端负责人**: @wx diff --git a/changelogs-v2/2026-09/30_8491_房务配房接口字段与口径调整-修改接口-管理后台.md b/changelogs-v2/2026-09/30_8491_房务配房接口字段与口径调整-修改接口-管理后台.md new file mode 100644 index 00000000..713c95b0 --- /dev/null +++ b/changelogs-v2/2026-09/30_8491_房务配房接口字段与口径调整-修改接口-管理后台.md @@ -0,0 +1,1726 @@ +--- +schema: "hl-changelog/v2" +ticket: "8491" +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" +--- + +# 房务配房: 15 个既有接口的字段与口径调整(任务类型 / 只读标识 / 早餐 / 改配记录 / 转单名单校验 / 读权限开放) + +> **存放目录**: 二期 → `changelogs-v2/2026-09/` +> +> **服务**: hl-order-service-v3 +> **Issue**: #8491 +> **日期**: 2026-09-29 +> **影响范围**: 管理后台「房务控制台」的常规单 / 团期列表、订单房务详情弹窗、逐晚配房与改配、转单 / 超管指派、房务待办、团期房务看板与订房计划 + +--- + +## ⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写) + +- **户级转单与超管指派(接口 7)在房务人员名单取不到时改为拒绝**,返回 **808343**「房务人员名单暂不可用,请稍后重试」,需求持有人不变。改前该情况放行。只有超管「整团接管」`POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover` 在名单取不到时仍放行,接收人姓名显示占位 `user-{id}`。 +- **「房务组长」角色取消**:错误码 808091、808092、582204 已删除且不复用。原组长账号访问房务接口返回 808090「未登录或非房务角色,无权操作」。 +- **读权限对全体房务开放**:待办 `scope=all/others`、团期看板 `scope=ALL`、团期看板详情 / 房间需求 / 分房总览 / 确认前检查,普通房务都能看他人持有与未认领的数据;这些读接口不再因「不是本人认领」返回 808612 / 808613。写接口仍校验归属,仍返回 808612 / 808613 / 808110。 +- 列表与详情新增 `readOnly` / `readOnlyReason`: + - **户级**(常规单行、非团期订单的详情)**超管不豁免**:超管看别人持有的单同样 `readOnly=true`,要改须先「指派」或「转单」给自己; + - **团期级**(团期行、团期子订单的详情、看板详情)**超管豁免**,超管恒为 `readOnly=false`。 +- 配房行与团期订房计划新增早餐 `breakfast`(INCLUDED / EXCLUDED / PENDING)与房源 `roomSource`(STOCK / HOTEL);订单房务详情逐晚新增 `nightRoomSource`,配房行新增 `subtotal`。 +- 改晚次 / 酒店 / 房型 / 间数(接口 6)在换酒店或减间数时写一条改配记录,出现在订单房务详情的 `changes[]`;原订为非控房且未上传取消凭证时记录为 `HELD`,须在房务控制台做「取消确认」。 + +--- + +## 一、背景(选填) + +#8491 把房务日常操作收拢到「房务控制台」。本文件只写**既有接口**因此发生的变化:列表按任务类型(新订 / 修改 / 退团)筛选并带未读数与只读标识;配房与团期订房计划记录早餐;改配留痕;转单在名单取不到时不再放行;取消「房务组长」只读监督角色,改为全体房务可读、写仍按归属。控制台新增的 17 个接口见同目录新增接口文件,下线的 4 个旧列表接口见同目录删除接口文件。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 常规单配房列表 | GET | `/v3/admin/order/house-allocation/households` | 修改 | 新增入参 taskKind;行新增 taskKind / unreadCount / readOnly | +| 2 | 团期配房列表 | GET | `/v3/admin/order/house-allocation/group-batches` | 修改 | 新增入参 taskKind;行新增 taskKind / unreadCount / readOnly | +| 3 | 订单房务详情 | GET | `/admin/house/orders/{orderId}` | 修改 | 新增 taskKind / readOnly / changes;逐晚 nightRoomSource;配房行 breakfast / roomSource / subtotal | +| 4 | 逐晚提交配房 | POST | `/v3/admin/order/hotel-requirements/{requirementId}/assignments` | 修改 | items[] 新增 breakfast | +| 5 | 修改配房 | PUT | `/v3/admin/order/assignments/{id}` | 修改 | 新增 breakfast | +| 6 | 改晚次 / 酒店 / 房型 / 间数 | PUT | `/v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement` | 修改 | 新增 breakfast 与取消凭证 / 取消费 / 改配备注;换酒店或减间数写改配记录 | +| 7 | 转单 / 超管指派 | POST | `/v3/admin/order/hotel-requirements/{requirementId}/transfer` | 修改 | 名单取不到返回 808343 | +| 8 | 房务待办列表 | GET | `/v3/admin/order/todos` | 修改 | scope=all/others 对全体房务开放,582204 删除 | +| 9 | 团期房务看板列表 | GET | `/v3/admin/house/group-batches` | 修改 | scope=ALL 对全体房务开放,808092 删除 | +| 10 | 团期房务看板详情 | GET | `/v3/admin/house/group-batches/{groupBatchId}` | 修改 | 全体房务可看;新增 readOnly;计划行带早餐与房源 | +| 11 | 团期房间需求 | GET | `/v3/admin/house/group-batches/{groupBatchId}/room-requirements` | 修改 | 全体房务可看 | +| 12 | 团期分房总览 | GET | `/v3/admin/house/group-batches/{groupBatchId}/allocations` | 修改 | 全体房务可看;计划与分房行带早餐与房源 | +| 13 | 团期计划确认前检查 | GET | `/v3/admin/house/group-batches/{groupBatchId}/room-plans/confirm-check` | 修改 | 全体房务可看 | +| 14 | 团期订房计划保存 | POST | `/v3/admin/house/group-batches/{groupBatchId}/room-plans` | 修改 | items[] 新增 breakfast;响应带早餐与房源 | +| 15 | 团期订房计划修改 | PUT | `/v3/admin/house/group-batches/{groupBatchId}/room-plans/{planId}` | 修改 | 新增 breakfast;响应带早餐与房源 | + +--- + +## 三、接口详情 + +本节示例 JSON 的字段名、类型、错误码与文案逐一取自源码;ID、日期、金额、姓名等取值为说明用的构造值。所有接口统一返回 `Result` 信封(`code` / `message` / `data` / `traceId` / `success`),示例省略 `traceId`;业务失败与入参校验失败均为 HTTP 200,靠 `code` 区分。ID 与金额字段序列化为字符串。入参 / 出参表只列本次新增或口径变化的字段及路径参数,未列出的字段名、类型与含义均不变。 + +### 1. 常规单配房列表 `GET /v3/admin/order/house-allocation/households` + +**VO**: `HouseAllocationHouseholdPageReqVO → HouseAllocationHouseholdPageRespVO` + +#### 使用场景 + +房务控制台「常规单」页签。新增按任务类型(新订 / 修改 / 退团)筛选;行内显示任务类型标签、房务会话未读数,并按 `readOnly` 置灰操作按钮、用 `readOnlyReason` 提示「由谁处理」。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| taskKind | Query | String | ❌ | NEW / CHANGE / WITHDRAWAL,或不传 | **新增**。任务类型筛选;不传不筛。筛选在分页之前生效,`total` 与 `stats` 七个计数都按筛选后的结果算 | +| scope | Query | String | ❌ | all / mine,默认 all | 不变 | +| status | Query | String | ❌ | pendingClaim / unfinished / claiming / pendingConfirm / confirmed / exception,默认 unfinished | 不变 | +| page | Query | Integer | ❌ | ≥1,默认 1 | 不变 | +| pageSize | Query | Integer | ❌ | 1~100,默认 20 | 不变 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| list | List | 行列表 | +| total | Long | 总数(受 taskKind 筛选) | +| stats | HouseAllocationHouseholdStatsVO | pendingClaim / claiming / pendingConfirm / confirmed / exception / unfinished / all,结构不变,受 taskKind 筛选 | +| list[].taskKind | String | **新增**。NEW / CHANGE / WITHDRAWAL,规则见六.5 | +| list[].taskKindLabel | String | **新增**。新订 / 修改 / 退团 | +| list[].unreadCount | Integer | **新增**。该订单房务会话未读数;取不到按 0 | +| list[].readOnly | Boolean | **新增**。待认领、本人持有为 false;他人持有为 true;**超管看他人持有的单同样为 true** | +| list[].readOnlyReason | String | **新增**。readOnly=true 时为「由 {姓名} 处理」,姓名为空时为「由其他房务处理」;可写时为 null | + +#### 请求示例 + +```http +GET /v3/admin/order/house-allocation/households?scope=all&status=unfinished&taskKind=CHANGE&page=1&pageSize=20 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "list": [ + { + "id": "1940000000000000011", + "orderId": "1930000000000000021", + "orderNo": "26-0915", + "teamNo": "26-0920", + "guestName": "李女士一家", + "houseStatus": "CLAIMING", + "houseStatusLabel": "配房中", + "claimerId": "30002", + "claimerName": "王芳", + "isMine": false, + "canStartAllocation": false, + "taskKind": "CHANGE", + "taskKindLabel": "修改", + "unreadCount": 2, + "readOnly": true, + "readOnlyReason": "由 王芳 处理" + } + ], + "total": 1, + "stats": { + "pendingClaim": 0, + "claiming": 1, + "pendingConfirm": 0, + "confirmed": 0, + "exception": 0, + "unfinished": 1, + "all": 1 + } + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +- 筛选无结果:`list=[]`、`total=0`,`stats` 各项为 0。 +- 任务类型取数失败时按空集处理,行的 `taskKind` 显示 `NEW`;按 `CHANGE` / `WITHDRAWAL` 筛选返回空页。 +- 未读数取不到时 `unreadCount=0`,不影响列表其余字段。 + +#### 错误响应 + +```json +{ + "code": 400, + "message": "taskKind 只能是 NEW、CHANGE 或 WITHDRAWAL", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色(含原房务组长账号) | + +#### 业务边界 + +- 一行同时满足退团与修改时显示 `WITHDRAWAL`(退团优先)。 +- `readOnly` 只决定按钮状态;真正的拒绝由写接口的错误码决定(例如配房写接口的 808110)。 +- 超管要改别人持有的常规单,先调接口 7 把需求指派给自己,指派成功后该行 `readOnly=false`。 + +### 2. 团期配房列表 `GET /v3/admin/order/house-allocation/group-batches` + +**VO**: `HouseAllocationGroupPageReqVO → HouseAllocationGroupPageRespVO` + +#### 使用场景 + +房务控制台「团期」页签。与接口 1 相同,新增任务类型筛选、未读数与只读标识;团期的只读判定超管豁免。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| taskKind | Query | String | ❌ | NEW / CHANGE / WITHDRAWAL,或不传 | **新增**。任务类型筛选;不传不筛;筛选在分页之前生效 | +| batchStatus | Query | String | ❌ | RECRUITING / RESOURCE_PREPARING / MATERIAL_PREPARING / PENDING_DEPARTURE / TRAVELLING / TRIP_FINISHED / REVIEWING / SETTLED / CANCELLED | 取值不变(校验常量移入本 VO) | +| scope | Query | String | ❌ | all / mine,默认 all | 不变 | +| status | Query | String | ❌ | pendingClaim / claimed | 不变 | +| page | Query | Integer | ❌ | ≥1,默认 1 | 不变 | +| pageSize | Query | Integer | ❌ | 1~100,默认 20 | 不变 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| list | List | 行列表 | +| total | Long | 总数(受 taskKind 筛选) | +| stats | HouseAllocationGroupStatsVO | pendingClaim / claimed / all,结构不变 | +| list[].taskKind | String | **新增**。NEW / CHANGE / WITHDRAWAL | +| list[].taskKindLabel | String | **新增**。新订 / 修改 / 退团 | +| list[].unreadCount | Integer | **新增**。团下活跃子订单房务会话未读数之和;取不到按 0 | +| list[].readOnly | Boolean | **新增**。待认领、本人认领、超管为 false;他人认领为 true | +| list[].readOnlyReason | String | **新增**。readOnly=true 时为「由 {姓名} 处理」或「由其他房务处理」;可写时为 null | + +#### 请求示例 + +```http +GET /v3/admin/order/house-allocation/group-batches?scope=all&status=claimed&taskKind=WITHDRAWAL&page=1&pageSize=20 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "list": [ + { + "groupBatchId": "1950000000000000031", + "batchNo": "GB261005", + "batchName": "呼伦贝尔秋色 6 日", + "batchStatus": "PENDING_DEPARTURE", + "batchStatusLabel": "待出发", + "departDate": "2026-10-05", + "houseClaimerId": "30001", + "houseClaimerName": "张敏", + "isMine": true, + "canStartAllocation": true, + "taskKind": "WITHDRAWAL", + "taskKindLabel": "退团", + "unreadCount": 0, + "readOnly": false, + "readOnlyReason": null + } + ], + "total": 1, + "stats": { + "pendingClaim": 0, + "claimed": 1, + "all": 1 + } + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +- 筛选无结果:`list=[]`、`total=0`。 +- 任务类型取数失败按空集处理,行显示 `NEW`;未读数取不到按 0。 + +#### 错误响应 + +```json +{ + "code": 400, + "message": "taskKind 只能是 NEW、CHANGE 或 WITHDRAWAL", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色(含原房务组长账号) | + +#### 业务边界 + +- 团下任一订单为退团 / 修改,该团期即为退团 / 修改;退团另含「以该团期为来源、仍待处理的退团房转房」。 +- 团期只读判定超管豁免:超管对任何团期 `readOnly=false`。 + +### 3. 订单房务详情 `GET /admin/house/orders/{orderId}` + +**VO**: `orderId + requirementId → HouseOrderDetailRespVO` + +#### 使用场景 + +房务打开订单房务详情弹窗。新增任务类型、只读标识、改配记录 `changes[]`(控制台「取消确认」的入口数据),逐晚新增整晚房源,配房行新增早餐、房源与小计。注意本接口路径**没有** `/v3` 前缀。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| orderId | Path | Long | ✅ | - | 订单 ID | +| requirementId | Query | Long | ❌ | 须属于该订单 | 不传返回当前生效需求;传入可查看历史作废版本(不变) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| taskKind | String | **新增**。NEW / CHANGE / WITHDRAWAL;取数异常时为 NEW | +| taskKindLabel | String | **新增**。新订 / 修改 / 退团 | +| readOnly | Boolean | **新增**。团期子订单按团期判定(团期认领人,超管豁免);其余订单按需求持有人判定(**超管不豁免**);团期判定取数失败时回退为按需求持有人判定 | +| readOnlyReason | String | **新增**。「由 {姓名} 处理」或「由其他房务处理」;可写时为 null | +| changes | List | **新增**。改配记录,按创建时间倒序;无记录或取数失败时为 `[]` | +| changes[].changeId | Long | 改配记录 ID,调控制台「取消确认」用 | +| changes[].assignmentId | Long | 被改的配房行 ID | +| changes[].changeKind | String | HOTEL / ROOM_COUNT | +| changes[].oldStatus | String | HELD / CANCEL_CONFIRMED | +| changes[].oldStatusLabel | String | 原酒店待取消 / 已确认取消 | +| changes[].cancelFee | BigDecimal | 取消费用(元),可为 null | +| changes[].proofFileIds | List | 取消凭证文件 ID | +| changes[].remark | String | 备注 | +| changes[].operatorName | String | CANCEL_CONFIRMED 时为确认人,否则为改配操作人 | +| changes[].createTime | LocalDateTime | 记录时间 | +| itinerary[].nightRoomSource | String | **新增**。SELF / STOCK / HOTEL / MIXED / UNSET,见六.5 | +| itinerary[].nightRoomSourceLabel | String | **新增**。客人自订 / 控房 / 非控房 / 混合 / 待选择 | +| itinerary[].assignments[].breakfast | String | **新增**。INCLUDED / EXCLUDED / PENDING;库里为空输出 PENDING | +| itinerary[].assignments[].breakfastLabel | String | **新增**。含早餐 / 不含早餐 / 早餐待确认 | +| itinerary[].assignments[].roomSource | String | **新增**。STOCK / HOTEL,由 deductInventory 推导,不单独存储 | +| itinerary[].assignments[].roomSourceLabel | String | **新增**。控房 / 非控房 | +| itinerary[].assignments[].subtotal | BigDecimal | **新增**。roomCount × settlementPrice,2 位小数;任一为 null 时为 null | + +#### 请求示例 + +```http +GET /admin/house/orders/1930000000000000021 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "taskKind": "CHANGE", + "taskKindLabel": "修改", + "readOnly": false, + "readOnlyReason": null, + "changes": [ + { + "changeId": "1960000000000000041", + "assignmentId": "1970000000000000051", + "changeKind": "HOTEL", + "oldStatus": "HELD", + "oldStatusLabel": "原酒店待取消", + "cancelFee": null, + "proofFileIds": [], + "remark": "客人要求换到河景房", + "operatorName": "张敏", + "createTime": "2026-09-28 15:20:00" + } + ], + "itinerary": [ + { + "dayNumber": 1, + "stayDate": "2026-10-05", + "cityName": "海拉尔", + "nightRoomSource": "MIXED", + "nightRoomSourceLabel": "混合", + "assignments": [ + { + "assignmentId": "1970000000000000052", + "hotelName": "海拉尔河畔酒店", + "roomTypeName": "高级双床房", + "confirmStatus": "INQUIRING", + "roomCount": 2, + "settlementPrice": "380.00", + "deductInventory": true, + "breakfast": "INCLUDED", + "breakfastLabel": "含早餐", + "roomSource": "STOCK", + "roomSourceLabel": "控房", + "subtotal": "760.00" + }, + { + "assignmentId": "1970000000000000053", + "hotelName": "海拉尔雅园宾馆", + "roomTypeName": "标准大床房", + "confirmStatus": "INQUIRING", + "roomCount": 1, + "settlementPrice": null, + "deductInventory": false, + "breakfast": "PENDING", + "breakfastLabel": "早餐待确认", + "roomSource": "HOTEL", + "roomSourceLabel": "非控房", + "subtotal": null + } + ] + } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +- 无改配记录或改配记录取数失败:`changes=[]`,详情其余部分照常返回。 +- 某晚没有配房行且非客人自订:`nightRoomSource="UNSET"`,`assignments=[]`。 +- 任务类型取数异常:`taskKind="NEW"`。 + +#### 错误响应 + +```json +{ + "code": 808141, + "message": "该订单非房务可见", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 808100 | 需求不存在 | 传入的 requirementId 不属于该订单 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色(含原房务组长账号) | + +#### 业务边界 + +- `readOnly` 的口径因订单类型而异:团期子订单跟团期认领人走、超管豁免;其他订单跟需求持有人走、**超管不豁免**。 +- `changes[]` 里 `oldStatus=HELD` 的记录才可调控制台「取消确认」;`CANCEL_CONFIRMED` 为已办结。 +- 客人自订的那一晚 `nightRoomSource="SELF"`,不看配房行。 +- `subtotal` 只用结算价计算,不含协议价。 + +### 4. 逐晚提交配房 `POST /v3/admin/order/hotel-requirements/{requirementId}/assignments` + +**VO**: `AssignmentSubmitReqVO → AssignmentSubmitRespVO` + +#### 使用场景 + +房务在订单房务详情里为各晚提交配房。本次 `items[]` 每行新增早餐 `breakfast`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementId | Path | Long | ✅ | - | 住宿需求 ID | +| items | Body | List | ✅ | 非空 | 逐晚配房行(不变) | +| items[].breakfast | Body | String | ❌ | INCLUDED / EXCLUDED / PENDING;传空串校验失败 | **新增**。早餐;不传存为空、读出为 PENDING | +| items[].dayNumber | Body | Integer | ✅ | ≥1 | 不变 | +| items[].hotelId | Body | Long | ✅ | - | 不变 | +| items[].roomTypeId | Body | Long | ✅ | - | 不变 | +| items[].roomCount | Body | Integer | ✅ | ≥1 | 不变 | +| items[].deductInventory | Body | Boolean | ❌ | - | 不变;true 即控房(roomSource=STOCK) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| successCount | Integer | 不变 | +| failCount | Integer | 不变 | +| items | List | dayNumber / assignmentId / arrange / deductInventory,不变 | + +#### 请求示例 + +```json +{ + "items": [ + { + "dayNumber": 1, + "hotelId": 100001, + "roomTypeId": 300001, + "roomCount": 2, + "settlementPrice": "380.00", + "deductInventory": true, + "breakfast": "INCLUDED" + } + ] +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "successCount": 1, + "failCount": 0, + "items": [ + { + "dayNumber": 1, + "assignmentId": "1970000000000000052", + "arrange": "pending", + "deductInventory": true + } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本接口为写接口,无降级分支;失败返回非 200 的 `code`,不落库。 + +#### 错误响应 + +```json +{ + "code": 400, + "message": "breakfast 只能是 INCLUDED / EXCLUDED / PENDING", + "data": null, + "success": false +} +``` + +其余错误(本次未变,列出便于对照): + +| code | message | 触发 | +|------|---------|------| +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色(含原房务组长账号) | +| 808116 | 订单未抢单, 请先抢单再配房 | 需求无人持有 | +| 808110 | 需求不属于当前用户 | 当前登录人不是持有人(超管同样拒绝) | + +#### 业务边界 + +- 同一需求 3 秒内重复提交被防重拦截。 +- 提交时保留下来的既有配房行:`breakfast` 传了才改,不传保持原值。 +- `breakfast` 传 `null` 等同不传;传空串 `""` 返回 400。 + +### 5. 修改配房 `PUT /v3/admin/order/assignments/{id}` + +**VO**: `AssignmentUpdateReqVO → Result` + +#### 使用场景 + +房务修改单个配房行的价格、结算方式、备注。本次新增 `breakfast`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | ✅ | - | 配房行 ID | +| breakfast | Body | String | ❌ | INCLUDED / EXCLUDED / PENDING | **新增**。不传保持原值 | +| protoPrice | Body | BigDecimal | ❌ | ≥0 | 不变 | +| settlementPrice | Body | BigDecimal | ❌ | ≥0 | 不变 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 成功无返回体 | + +#### 请求示例 + +```json +{ + "settlementPrice": "360.00", + "breakfast": "EXCLUDED" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +写接口,成功时 `data` 恒为 `null`;无降级分支。 + +#### 错误响应 + +```json +{ + "code": 599602, + "message": "应付款台账行已锁定", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 400 | breakfast 只能是 INCLUDED / EXCLUDED / PENDING | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808116 | 订单未抢单, 请先抢单再配房 | 需求无人持有 | +| 808110 | 需求不属于当前用户 | 当前登录人不是持有人(超管同样拒绝) | + +#### 业务边界 + +- 599602(本次未变):已确认(CONFIRMED)的配房行改价格时,若该行对应的应付款台账行已有在途付款申请,拒绝改价,配房行不变。 +- 只改 `breakfast` 不涉及价格,不触发 599602。 + +### 6. 改晚次 / 酒店 / 房型 / 间数 `PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement` + +**VO**: `AssignmentPlacementUpdateReqVO → Result` + +#### 使用场景 + +房务对已配的某行换晚次、换酒店 / 房型、改间数。本次新增早餐,以及换酒店或减间数时对原订的取消信息(凭证、费用、备注)。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementId | Path | Long | ✅ | - | 当前生效住宿需求 ID | +| id | Path | Long | ✅ | - | 配房行 ID | +| dayNumber | Body | Integer | ✅ | ≥1 | 目标晚次(不变) | +| hotelId | Body | Long | ✅ | - | 目标酒店(不变) | +| roomTypeId | Body | Long | ✅ | - | 目标房型(不变) | +| roomCount | Body | Integer | ✅ | ≥1 | 目标间数(不变) | +| deductInventory | Body | Boolean | ❌ | - | 不变 | +| breakfast | Body | String | ❌ | INCLUDED / EXCLUDED / PENDING | **新增**。不传保持原值 | +| cancelProofFileIds | Body | List | ❌ | 最多 9 个 | **新增**。原订取消凭证 | +| cancelFee | Body | BigDecimal | ❌ | ≥0,整数最多 10 位、小数最多 2 位 | **新增**。原订取消费用(元) | +| changeRemark | Body | String | ❌ | ≤200 字 | **新增**。改配备注 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 成功无返回体;改配记录在订单房务详情 `changes[]` 查看 | + +#### 请求示例 + +```json +{ + "dayNumber": 1, + "hotelId": 100002, + "roomTypeId": 300005, + "roomCount": 1, + "deductInventory": false, + "breakfast": "INCLUDED", + "cancelProofFileIds": [], + "changeRemark": "客人要求换到河景房" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +写接口,成功时 `data` 恒为 `null`;无降级分支。 + +#### 错误响应 + +```json +{ + "code": 400, + "message": "cancelProofFileIds 最多 9 个", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 400 | cancelFee 不能为负数 / cancelFee 最多 10 位整数、2 位小数 / changeRemark 最长 200 字 / breakfast 只能是 INCLUDED / EXCLUDED / PENDING | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808116 | 订单未抢单, 请先抢单再配房 | 需求无人持有 | +| 808110 | 需求不属于当前用户 | 当前登录人不是持有人(超管同样拒绝) | + +#### 业务边界 + +- 何时写改配记录:换了酒店记 `changeKind=HOTEL`(优先);同酒店但间数减少记 `ROOM_COUNT`;间数增加或未变不写记录,此时 `cancelProofFileIds` / `cancelFee` / `changeRemark` 不落库。 +- 记录状态:原配房行是控房(`deductInventory=true`),或上传了至少一个取消凭证 → 直接 `CANCEL_CONFIRMED`;原行为非控房且没传凭证 → `HELD`,须在房务控制台对该记录做「取消确认」。 +- `HELD` 记录会让该订单在列表中显示 `taskKind=CHANGE`,并出现在房务控制台异常检查的 `HOTEL_CANCEL_PENDING`(原酒店待取消)项里;不生成房务待办。 +- 目标行的确认状态重置为 `INQUIRING`,已确认(CONFIRMED)的行同样重置。已知缺口 #8508:对 CONFIRMED 行执行本接口时不校验应付款台账行是否锁定(不返回 599602),也不处理该行的应付款。 + +### 7. 转单 / 超管指派 `POST /v3/admin/order/hotel-requirements/{requirementId}/transfer` + +**VO**: `HouseTransferReqVO → Result` + +#### 使用场景 + +普通房务把自己持有的常规单需求转给同事;超管把任意常规单需求指派给某个房务(包括指派给自己,用于解除 `readOnly`)。同一路径按登录身份分流。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementId | Path | Long | ✅ | - | 住宿需求 ID | +| toUserId | Body | Long | ✅ | 须在房务人员名单内 | 接收人 adminId | +| reason | Body | String | ❌ | ≤200 字;超管须 ≥10 字 | 原因;普通房务可不填(记为「转单」) | +| skipUpperLimit | Body | Boolean | ❌ | - | 历史字段,不变 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 成功无返回体,前端刷新列表与详情 | + +#### 请求示例 + +```json +{ + "toUserId": 30002, + "reason": "客人改到下周出行,转给负责该线路的同事" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +**无降级**:房务人员名单取不到(调用异常、返回空)时直接返回 808343,需求持有人不变。改前该情况放行。 + +#### 错误响应 + +```json +{ + "code": 808343, + "message": "房务人员名单暂不可用,请稍后重试", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 400 | toUserId 不能为空 / reason 长度不超过 200 字 | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色(含原房务组长账号) | +| 808016 | 超管指派原因长度不足 10 字 | 超管 reason 不足 10 字 | +| 808002 | 需求已不存在 | requirementId 不存在 | +| 808650 | 团期订单须整团认领后配房,不支持逐户认领 / 转单 | 团期子订单的需求(普通房务一律;超管在团期已被认领时) | +| 808010 | 需求不属于当前用户,无法转单 | 普通房务转别人持有的需求 | +| 808014 | 接收人就是当前归属人,无需操作 | toUserId 等于当前持有人 | +| 808013 | 一单转单次数达上限(3 次) | 普通房务;超管指派不受限 | +| 808011 | 接收人不存在或已离职 | 名单可用但不含 toUserId | +| 808001 | 该需求已被其他房务认领或状态已变化,请刷新后重试 | 并发改持有人;需求未被认领 | +| 808930 | 配房状态机非法流转:原状态={0},事件={1} | 需求状态不允许转单 | + +#### 业务边界 + +- 808343 同时作用于普通房务转单与超管指派;前端收到后保持弹窗,提示用户重试。 +- 名单可用、接收人在名单里但姓名为空时,接收人姓名记为 `user-{id}`。 +- 团期层面的换人不走本接口:团期持有人用新增接口「团期转交」,超管用「整团接管」`POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover`;后者在名单取不到时仍放行并记 `user-{id}`。 +- 需求级有写锁,同一需求的并发转单串行执行。 + +### 8. 房务待办列表 `GET /v3/admin/order/todos` + +**VO**: `HouseTodoPageReqVO → HouseTodoListRespVO` + +#### 使用场景 + +房务查看待办。`scope=all`(全部)与 `scope=others`(同事在处理)改为全体房务可用,不再限组长 / 超管。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| scope | Query | String | ❌ | mine / others / all,不传为 mine | **口径变化**:others / all 对全体房务开放 | +| todoType | Query | String | ❌ | 多选逗号分隔 | 不变 | +| 分页参数 | Query | - | ❌ | 继承通用分页参数 | 不变 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| list | List | 不变 | +| total | long | 不变 | +| stats | HouseTodoStatsVO | 不变;JSON 键为大写待办类型(SWAP_HOTEL / REFUND / … / UNREAD_CHAT) | + +#### 请求示例 + +```http +GET /v3/admin/order/todos?scope=all +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "list": [ + { + "id": "1980000000000000061", + "todoType": "REQUIREMENT_ADJUSTED", + "todoTypeLabel": "需求调整", + "title": "客人调整入住人数", + "status": "OPEN", + "orderId": "1930000000000000021", + "orderNo": "26-0915", + "teamNo": "26-0920", + "guestName": "李女士一家" + } + ], + "total": 1, + "stats": { + "SWAP_HOTEL": 0, + "REFUND": 0, + "INVENTORY_CHECK_OVERDUE": 0, + "RETURN_TO_HK": 0, + "REQUIREMENT_ADJUSTED": 1, + "HOTEL_REPLY_TIMEOUT": 0, + "PENDING_ARRANGE": 0, + "PENDING_FINALIZE": 0, + "UNREAD_CHAT": 0 + } + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无待办时 `list=[]`、`total=0`,`stats` 各项为 0。 + +#### 错误响应 + +```json +{ + "code": 582201, + "message": "查询范围取值非法(仅支持我的/他人/全部)", + "data": null, + "success": false +} +``` + +其余错误:582204 已删除,普通房务传 `scope=all` / `others` 不再报错。 + +#### 业务边界 + +- `scope=mine` 含未归属的待办;`others` 为同事在处理的;`all` 为全部。 +- 看到同事的待办不代表能处理:处理动作仍按订单归属校验。 + +### 9. 团期房务看板列表 `GET /v3/admin/house/group-batches` + +**VO**: `HouseGroupBatchBoardPageReqVO → PageResult` + +#### 使用场景 + +团期房务看板列表。`scope=ALL` 改为全体房务可用。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| scope | Query | String | ❌ | MINE / ALL,≤8 字符,默认 MINE | **口径变化**:ALL 对全体房务开放 | +| claimerAdminId | Query | Long | ❌ | - | 按认领人筛选(不变) | +| keyword | Query | String | ❌ | ≤32 字 | 不变 | +| page | Query | Long | ❌ | ≥1,默认 1 | 不变 | +| pageSize | Query | Long | ❌ | 1~50,默认 20 | 不变 | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| records | List | 结构不变 | +| total | int | 不变 | +| page | int | 不变 | +| pageSize | int | 不变 | + +#### 请求示例 + +```http +GET /v3/admin/house/group-batches?scope=ALL&page=1&pageSize=20 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "records": [ + { + "groupBatchId": "1950000000000000031", + "batchNo": "GB261005", + "batchName": "呼伦贝尔秋色 6 日", + "batchStatus": "PENDING_DEPARTURE", + "departDate": "2026-10-05", + "claimerAdminId": "30002", + "claimerName": "王芳", + "demandDays": 5, + "plannedDays": 5, + "confirmedDays": 3, + "mismatchDays": 0 + } + ], + "total": 1, + "page": 1, + "pageSize": 20 + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无数据时 `records=[]`、`total=0`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 400 | scope 非法 / keyword 最长 32 字 / pageSize 最大 50 | 入参校验 | + +808092 已删除,普通房务传 `scope=ALL` 不再报错。 + +#### 业务边界 + +- 列表只读,看得见不等于能写;写团期计划仍须本人认领(808612 / 808613)。 + +### 10. 团期房务看板详情 `GET /v3/admin/house/group-batches/{groupBatchId}` + +**VO**: `groupBatchId → HouseGroupBatchBoardRespVO` + +#### 使用场景 + +打开某个团期的房务看板。全体房务可看他人认领与未认领的团期,按新字段 `readOnly` 决定是否可编辑。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| readOnly | Boolean | **新增**。未认领、本人认领、超管为 false;他人认领为 true | +| readOnlyReason | String | **新增**。「由 {姓名} 处理」或「由其他房务处理」;可写时为 null | +| days[].plans | List | 计划行,新增 breakfast / breakfastLabel / roomSource / roomSourceLabel(同接口 14 出参) | +| 其余字段 | - | 不变 | + +#### 请求示例 + +```http +GET /v3/admin/house/group-batches/1950000000000000031 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "groupBatchId": "1950000000000000031", + "batchNo": "GB261005", + "claimerAdminId": "30002", + "claimerName": "王芳", + "readOnly": true, + "readOnlyReason": "由 王芳 处理", + "days": [ + { + "stayDate": "2026-10-05", + "dayNumber": 1, + "plans": [ + { + "planId": "1990000000000000071", + "hotelName": "海拉尔河畔酒店", + "roomTypeName": "高级双床房", + "roomCount": 12, + "deductInventory": true, + "breakfast": "INCLUDED", + "breakfastLabel": "含早餐", + "roomSource": "STOCK", + "roomSourceLabel": "控房" + } + ] + } + ] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +团期尚无订房计划时 `days[].plans=[]`;未认领团期 `readOnly=false`、`claimerAdminId=null`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "data": null, + "success": false +} +``` + +其余错误:查看他人认领或未认领的团期不再返回 808612 / 808613。 + +#### 业务边界 + +- 本接口的 Swagger 说明仍提到 808612 / 808613,以本文为准:读取不校验认领归属。 +- `readOnly=false` 的未认领团期仍须先整团认领才能写计划,否则写接口返回 808612。 + +### 11. 团期房间需求 `GET /v3/admin/house/group-batches/{groupBatchId}/room-requirements` + +**VO**: `groupBatchId → HouseGroupBatchRoomRequirementRespVO` + +#### 使用场景 + +看板里查看团期逐晚、逐户的房间需求。本次只放开读权限,响应结构不变。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| groupBatchId | Long | 不变 | +| days | List | 不变 | +| householdsWithoutBasis | List | 不变 | +| outOfRangeHouseholds | List | 不变 | + +#### 请求示例 + +```http +GET /v3/admin/house/group-batches/1950000000000000031/room-requirements +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "groupBatchId": "1950000000000000031", + "batchNo": "GB261005", + "departDate": "2026-10-05", + "endDate": "2026-10-10", + "days": [], + "householdsWithoutBasis": [], + "outOfRangeHouseholds": [] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无需求时各列表为 `[]`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "data": null, + "success": false +} +``` + +其余错误:查看他人认领或未认领的团期不再返回 808612 / 808613。 + +#### 业务边界 + +- 只读接口,全体房务可看;不涉及写入。 + +### 12. 团期分房总览 `GET /v3/admin/house/group-batches/{groupBatchId}/allocations` + +**VO**: `groupBatchId → GroupBatchRoomAllocationOverviewRespVO` + +#### 使用场景 + +看板里查看团期逐晚计划与分户分房。本次放开读权限,计划行与分房行新增早餐与房源。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| days[].plans[].breakfast | String | **新增**。INCLUDED / EXCLUDED / PENDING | +| days[].plans[].breakfastLabel | String | **新增**。含早餐 / 不含早餐 / 早餐待确认 | +| days[].plans[].roomSource | String | **新增**。STOCK / HOTEL | +| days[].plans[].roomSourceLabel | String | **新增**。控房 / 非控房 | +| days[].plans[].allocations[].breakfast | String | **新增**。取自所属计划行 | +| days[].plans[].allocations[].breakfastLabel | String | **新增**。取自所属计划行 | +| days[].plans[].allocations[].roomSource | String | **新增**。取自所属计划行 | +| days[].plans[].allocations[].roomSourceLabel | String | **新增**。取自所属计划行 | +| 其余字段 | - | 不变 | + +#### 请求示例 + +```http +GET /v3/admin/house/group-batches/1950000000000000031/allocations +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "groupBatchId": "1950000000000000031", + "batchNo": "GB261005", + "balanced": true, + "days": [ + { + "stayDate": "2026-10-05", + "plannedRooms": 12, + "allocatedRooms": 12, + "plans": [ + { + "planId": "1990000000000000071", + "hotelName": "海拉尔河畔酒店", + "roomTypeName": "高级双床房", + "plannedRooms": 12, + "allocatedRooms": 12, + "leftoverRooms": 0, + "breakfast": "INCLUDED", + "breakfastLabel": "含早餐", + "roomSource": "STOCK", + "roomSourceLabel": "控房", + "allocations": [ + { + "allocId": "2000000000000000081", + "orderId": "1930000000000000022", + "teamNo": "26-0921", + "orderNo": "26-0916", + "roomCount": 2, + "breakfast": "INCLUDED", + "breakfastLabel": "含早餐", + "roomSource": "STOCK", + "roomSourceLabel": "控房" + } + ] + } + ], + "households": [] + } + ], + "blockedHouseholds": [] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无计划时 `days=[]`;计划行没有分房时 `allocations=[]`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "data": null, + "success": false +} +``` + +其余错误:查看他人认领或未认领的团期不再返回 808612 / 808613。 + +#### 业务边界 + +- 分房行的早餐与房源不单独存储,恒等于所属计划行。 +- 同一控制器的两个写接口(保存手工分房、重建)仍校验认领归属。 + +### 13. 团期计划确认前检查 `GET /v3/admin/house/group-batches/{groupBatchId}/room-plans/confirm-check` + +**VO**: `groupBatchId → GroupBatchRoomConfirmCheckRespVO` + +#### 使用场景 + +确认团期订房计划前的预检。本次只放开读权限,响应结构不变。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| ready | Boolean | 不变 | +| days | List | 不变 | +| 其余字段 | - | 不变 | + +#### 请求示例 + +```http +GET /v3/admin/house/group-batches/1950000000000000031/room-plans/confirm-check +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "groupBatchId": "1950000000000000031", + "batchStatus": "PENDING_DEPARTURE", + "stageAllowed": true, + "baselineExists": true, + "hotelReady": true, + "ready": true, + "blockedByOutOfRange": false, + "days": [], + "noBaselineOrders": [], + "outOfRangeOrders": [] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无计划时 `days=[]`,`ready` 按检查结果给出。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "data": null, + "success": false +} +``` + +其余错误:查看他人认领或未认领的团期不再返回 808612 / 808613。 + +#### 业务边界 + +- 预检可看,确认计划的写接口仍校验认领归属。 + +### 14. 团期订房计划保存 `POST /v3/admin/house/group-batches/{groupBatchId}/room-plans` + +**VO**: `GroupBatchRoomPlanSaveReqVO → List` + +#### 使用场景 + +团期认领人批量新增订房计划行。本次 `items[]` 新增早餐,响应计划行新增早餐与房源。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | +| items | Body | List | ✅ | 1~200 行 | 计划行(不变) | +| items[].breakfast | Body | String | ❌ | INCLUDED / EXCLUDED / PENDING | **新增**。不传存为空、读出为 PENDING | +| items[].stayDate | Body | LocalDate | ✅ | - | 不变 | +| items[].hotelId | Body | Long | ✅ | - | 不变 | +| items[].roomTypeId | Body | Long | ✅ | - | 不变 | +| items[].roomCount | Body | Integer | ✅ | ≥1 | 不变 | +| items[].settleType | Body | String | ❌ | cash / sign / company | 不变 | +| items[].deductInventory | Body | Boolean | ❌ | - | 不变;true 即控房 | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| [].breakfast | String | **新增**。INCLUDED / EXCLUDED / PENDING | +| [].breakfastLabel | String | **新增**。含早餐 / 不含早餐 / 早餐待确认 | +| [].roomSource | String | **新增**。STOCK / HOTEL,由 deductInventory 推导 | +| [].roomSourceLabel | String | **新增**。控房 / 非控房 | +| 其余字段 | - | 不变 | + +#### 请求示例 + +```json +{ + "items": [ + { + "stayDate": "2026-10-05", + "hotelId": 100001, + "roomTypeId": 300001, + "roomCount": 12, + "settlementPrice": "360.00", + "settleType": "sign", + "deductInventory": true, + "breakfast": "INCLUDED" + } + ] +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { + "planId": "1990000000000000071", + "groupBatchId": "1950000000000000031", + "stayDate": "2026-10-05", + "hotelName": "海拉尔河畔酒店", + "roomTypeName": "高级双床房", + "roomCount": 12, + "settlementPrice": "360.00", + "settleType": "sign", + "deductInventory": true, + "breakfast": "INCLUDED", + "breakfastLabel": "含早餐", + "roomSource": "STOCK", + "roomSourceLabel": "控房", + "version": 0 + } + ], + "success": true +} +``` + +#### 空数据 / 降级响应 + +写接口,无降级分支;失败返回非 200 的 `code`,不落库。 + +#### 错误响应 + +```json +{ + "code": 808613, + "message": "该团期由其他房务认领,无权操作", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 400 | items 不能为空 / items 一次最多 200 行 / breakfast 只能是 INCLUDED / EXCLUDED / PENDING | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808612 | 该团期尚未被房务整团认领 | 团期未认领 | + +#### 业务边界 + +- 写仍校验团期认领归属;读权限的放开不影响本接口。 +- 团期计划行不涉及 599602。 + +### 15. 团期订房计划修改 `PUT /v3/admin/house/group-batches/{groupBatchId}/room-plans/{planId}` + +**VO**: `GroupBatchRoomPlanItemReqVO → GroupBatchRoomPlanRespVO` + +#### 使用场景 + +团期认领人修改单个订房计划行。本次新增早餐。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | - | 团期 ID | +| planId | Path | Long | ✅ | - | 计划行 ID | +| version | Body | Integer | ✅ | - | 乐观锁版本(不变) | +| breakfast | Body | String | ❌ | INCLUDED / EXCLUDED / PENDING | **新增**。不传保持原值 | +| replaceReason | Body | String | ❌ | ≤256 字 | 不变 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| breakfast | String | **新增** | +| breakfastLabel | String | **新增** | +| roomSource | String | **新增** | +| roomSourceLabel | String | **新增** | +| 其余字段 | - | 不变 | + +#### 请求示例 + +```json +{ + "version": 0, + "breakfast": "EXCLUDED" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "planId": "1990000000000000071", + "groupBatchId": "1950000000000000031", + "roomCount": 12, + "deductInventory": true, + "breakfast": "EXCLUDED", + "breakfastLabel": "不含早餐", + "roomSource": "STOCK", + "roomSourceLabel": "控房", + "version": 1 + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +写接口,无降级分支。 + +#### 错误响应 + +```json +{ + "code": 808612, + "message": "该团期尚未被房务整团认领", + "data": null, + "success": false +} +``` + +其余错误: + +| code | message | 触发 | +|------|---------|------| +| 400 | version 不能为空 / breakfast 只能是 INCLUDED / EXCLUDED / PENDING | 入参校验 | +| 808090 | 未登录或非房务角色,无权操作 | 非房务角色 | +| 808613 | 该团期由其他房务认领,无权操作 | 他人认领 | + +#### 业务边界 + +- 不传 `breakfast` 时原值保留:原地修改与「删旧建新」两条路径都保留原早餐。 + +--- + +## 四、契约约束与正确调用方式(接口类必写) + +> 本节只写**后端接受/拒绝 payload 的规则**,不写 UI 渲染建议。 + +### ✅ 正确 / ❌ 错误 payload 对照 + +| 场景 | payload | +|------|---------| +| ✅ 早餐未知时不传 | `{ "items": [ { "dayNumber": 1, "hotelId": 100001, "roomTypeId": 300001, "roomCount": 1 } ] }` → 读出 breakfast=PENDING | +| ✅ 显式标待确认 | `{ "breakfast": "PENDING" }` | +| ❌ 早餐传空串 | `{ "breakfast": "" }` → 400 breakfast 只能是 INCLUDED / EXCLUDED / PENDING | +| ❌ 早餐传中文 | `{ "breakfast": "含早" }` → 400 | +| ✅ 换酒店并附原订取消凭证 | `{ "dayNumber": 1, "hotelId": 100002, "roomTypeId": 300005, "roomCount": 1, "cancelProofFileIds": [1930000000000000501] }` → 记录直接 CANCEL_CONFIRMED | +| ❌ 取消凭证超 9 个 | `{ ..., "cancelProofFileIds": [1,2,3,4,5,6,7,8,9,10] }` → 400 cancelProofFileIds 最多 9 个 | +| ❌ 取消费为负 | `{ ..., "cancelFee": "-1" }` → 400 cancelFee 不能为负数 | +| ❌ 超管指派原因过短 | `{ "toUserId": 30002, "reason": "改派" }` → 808016 | +| ❌ 列表 taskKind 小写 | `?taskKind=change` → 400 taskKind 只能是 NEW、CHANGE 或 WITHDRAWAL | + +### 切换状态时的必要动作 + +- 超管要编辑别人持有的常规单:先调接口 7 把需求指派给自己,再调配房写接口;直接写返回 808110。 +- 收到 808343:持有人没变,保持当前界面,由用户重试。 +- 改晚次 / 酒店 / 房型 / 间数成功后,重新拉取订单房务详情,读取新的 `changes[]` 与 `taskKind`。 +- `HELD` 改配记录办结走房务控制台「取消确认」接口,`changeId` 取自 `changes[].changeId`。 + +--- + +## 五、数据库行为(涉及写操作时必写) + +| 前端提交 | 写入位置 | 行为 | +|----------|----------|------| +| 逐晚提交配房 | 配房行 `breakfast` 列 | 新建行:传则写入,未传写空(读出 PENDING);保留的既有行:传了才改 | +| 修改配房 | 配房行 | 未传 `breakfast` 保持原值;价格变更规则不变 | +| 改晚次 / 酒店 / 房型 / 间数 | 配房行、`house_assignment_change` | 目标行确认状态置 INQUIRING;换酒店或减间数时新增一条改配记录(HELD 或 CANCEL_CONFIRMED) | +| 转单 / 超管指派 | 需求认领字段 | CAS 换持有人;808343 / 808011 时不写 | +| 团期订房计划保存 | 团期订房计划行 `breakfast` 列 | 传则写入,未传写空 | +| 团期订房计划修改 | 团期订房计划行 | 未传 `breakfast` 保持原值,删旧建新时也带过去 | + +**显式 SET NULL 说明**: 本次新增字段都不支持「传 null 清空」:`breakfast` 传 null 等同不传(新建写空、修改保持原值),要改回待确认须显式传 `PENDING`。改配记录的 `cancelFee` / `cancelProofFileIds` / `changeRemark` 只在写入新记录时落库,间数增加或未变时忽略。 + +--- + +## 六、边界行为 + +- 已登录但不是房务角色(ROOM_MANAGER / SUPER_ADMIN 以外)→ 808090「未登录或非房务角色,无权操作」;原「房务组长」账号同样返回 808090。 +- 读接口不校验认领归属,全体房务可看;写接口仍校验:常规单 808116 / 808110,团期 808612 / 808613。 +- `readOnly` 与写接口的拒绝口径一致:户级超管不豁免(对应 808110 超管同样拒绝),团期级超管豁免。 +- 常规单列表的 Swagger 字段说明写「超管为 false」,与实际行为不符,以本文为准:户级 `readOnly` 超管不豁免。 +- 581045「房务角色无权查看订单详情,房务仅可配房」只改说明文字(去掉组长),码值与文案不变。 +- 599602 出现位置(本次均未变):修改配房对已确认行改价格、删除配房行 `DELETE /v3/admin/order/assignments/{id}`、清空需求配房 `DELETE /v3/admin/order/hotel-requirements/{requirementId}/assignments`,以及房务控制台退团房转房(来源为订单时)。含义:该配房行对应的应付款台账行已有在途付款申请而被锁定。团期订房计划不涉及 599602。 + +--- + +## 六.5、枚举 / 数据字典(接口出现枚举时必写) + +### taskKind(HouseTaskKind) + +**所属字段**: `HouseAllocationHouseholdRespVO.taskKind`、`HouseAllocationGroupRespVO.taskKind`、`HouseOrderDetailRespVO.taskKind`,及列表入参 `taskKind` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `WITHDRAWAL` | 退团 | 订单有未关闭的退款待办,或有以该订单为来源、仍待处理的退团房转房;团期行:团下任一订单为退团,或有以该团期为来源、仍待处理的转房;优先级最高 | +| `CHANGE` | 修改 | 订单有未关闭的需求调整待办(与列表 `isRework` 同一口径),或有 HELD 改配记录;团期行:团下任一订单为修改 | +| `NEW` | 新订 | 以上都不满足;某项判据取数失败时该项按空集处理 | + +### breakfast(HouseBreakfast) + +**所属字段**: 配房行与团期订房计划行的 `breakfast`(入参与出参) / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `INCLUDED` | 含早餐 | - | +| `EXCLUDED` | 不含早餐 | - | +| `PENDING` | 早餐待确认 | 未填时按此输出 | + +### roomSource(HouseRoomSource) + +**所属字段**: 配房行与团期订房计划行、团期分房行的 `roomSource` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `STOCK` | 控房 | deductInventory=true | +| `HOTEL` | 非控房 | deductInventory 为 false 或空 | + +### nightRoomSource(订单房务详情逐晚) + +**所属字段**: `HouseOrderDetailRespVO.itinerary[].nightRoomSource` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `SELF` | 客人自订 | 该晚客人自订 | +| `STOCK` | 控房 | 该晚配房行全部为控房 | +| `HOTEL` | 非控房 | 该晚配房行全部为非控房 | +| `MIXED` | 混合 | 该晚同时有控房与非控房 | +| `UNSET` | 待选择 | 该晚没有配房行 | + +### changeKind(HouseAssignmentChangeConstants) + +**所属字段**: `HouseOrderDetailRespVO.changes[].changeKind` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `HOTEL` | 换酒店 | 同时减间数也记为此值 | +| `ROOM_COUNT` | 减间数 | 同酒店、间数减少 | + +### oldStatus(HouseAssignmentChangeConstants) + +**所属字段**: `HouseOrderDetailRespVO.changes[].oldStatus` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `HELD` | 原酒店待取消 | 可在房务控制台做取消确认 | +| `CANCEL_CONFIRMED` | 已确认取消 | 已办结 | + +### 待办 scope + +**所属字段**: `HouseTodoPageReqVO.scope` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `mine` | 我的 | 含未归属;不传时的默认值 | +| `others` | 他人 | 同事在处理的;全体房务可用 | +| `all` | 全部 | 全体房务可用 | + +### 看板 scope + +**所属字段**: `HouseGroupBatchBoardPageReqVO.scope` / **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `MINE` | 我的 | 默认 | +| `ALL` | 全部 | 全体房务可用 | + +### 已删除错误码 + +**所属字段**: `Result.code` / **类型**: `Integer` + +| 值 | 中文 | 说明 | +|----|------|------| +| `808091` | 房务组长为只读监督角色,无权执行该操作 | 已删除,不复用 | +| `808092` | 无权查看全部房务订单(仅房务组长或超管可查看) | 已删除,不复用 | +| `582204` | 无权查看全部/他人房务待办(仅房务组长或超管可查看) | 已删除,不复用 | + +--- + +## 六.6、修改前后对比(修改/删除接口必写) + +### 字段级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| 常规单 / 团期列表入参 `taskKind` | 无 | NEW / CHANGE / WITHDRAWAL,可选 | +| 常规单 / 团期列表行 `taskKind` / `taskKindLabel` | 无 | 有 | +| 常规单 / 团期列表行 `unreadCount` | 无 | 有,取不到为 0 | +| 列表行、订单房务详情、看板详情 `readOnly` / `readOnlyReason` | 无 | 有 | +| 订单房务详情 `changes[]` | 无 | 改配记录,倒序 | +| 订单房务详情 `itinerary[].nightRoomSource` / `nightRoomSourceLabel` | 无 | 有 | +| 配房行 `breakfast` / `breakfastLabel` / `roomSource` / `roomSourceLabel` / `subtotal` | 无 | 有 | +| 团期计划行、分房行 `breakfast` / `breakfastLabel` / `roomSource` / `roomSourceLabel` | 无 | 有 | +| 配房写入参 `breakfast` | 无 | 可选 | +| 改晚次等入参 `cancelProofFileIds` / `cancelFee` / `changeRemark` | 无 | 可选 | + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 户级转单 / 超管指派,房务人员名单取不到 | 放行 | 返回 808343,不换持有人 | +| 原房务组长账号访问房务接口 | 可读,写返回 808091 | 返回 808090 | +| 普通房务查待办 scope=all / others | 返回 582204 | 放行 | +| 普通房务查看板 scope=ALL | 返回 808092 | 放行 | +| 查看他人认领 / 未认领团期的看板详情、房间需求、分房总览、确认前检查 | 返回 808612 / 808613 | 放行,看板详情给出 readOnly | +| 换酒店或减间数 | 不留记录 | 写改配记录(HELD 或 CANCEL_CONFIRMED) | + +--- + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 是。户级转单 / 超管指派在名单取不到时由放行改为 808343;808091 / 808092 / 582204 三个码删除,按这三个码映射文案或分支的前端逻辑不再被触发。新增字段均为追加,旧字段含义不变。 +- **前端是否必须同步上线**: 否。新增入参均为可选,旧前端不传照常工作;但 808343 的提示需要前端能展示后端 `message`。 +- **前端 workaround 清理点**: 组长只读视图、按 808091 / 808092 / 582204 做的分支与文案;按「是否本人认领」自行推导按钮置灰的逻辑,改为直接读 `readOnly` / `readOnlyReason`。 + +--- + +## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面) + +- **仅影响**: 管理后台房务相关页面(房务控制台常规单 / 团期列表、订单房务详情弹窗、团期房务看板与订房计划、房务待办)。 +- **零影响**: + - 小程序与 H5(本文接口均为管理端路由) + - 写接口的归属校验口径:常规单 808116 / 808110、团期 808612 / 808613,码值与文案不变 + - 配房行与团期计划行既有字段(价格、结算方式、确认状态)的含义与取值 + +--- + +## 八、测试环境已验证 + +所有接口都经测试服网关调用。HTTP 状态恒为 200,下面的 `code` 指响应 body 里的 `code`,带 ✓ 标记。行尾 `@` 后面是当时测试服 order-v3 的部署提交。`bdde64a3a`、`9c7ac9382`、`ff6863754`、`3ecf38797` 四个提交都包含本单合并提交 `7c21cf0e40`。 + +``` +GET /v3/admin/order/house-allocation/households 房务 A scope=all 查别人持有的单 → code=200,该行 readOnly=true,readOnlyReason「由<持有人姓名>处理」 ✓ @9c7ac9382 +GET /v3/admin/order/house-allocation/households 刚认领、未配房的单 → taskKind=NEW ✓ @bdde64a3a +GET /v3/admin/order/house-allocation/households 测试定制师提交需求调整(新增 OPEN 的 REQUIREMENT_ADJUSTED)后 → taskKind=CHANGE、isRework=true、todoCount=1 ✓ @ff6863754 +GET /v3/admin/order/house-allocation/households 改酒店产生 HELD 改配记录后 → 该单 taskKind=CHANGE ✓ @bdde64a3a +GET /v3/admin/order/house-allocation/households scope=all&taskKind=WITHDRAWAL → code=200,已配房后取消的单 taskKind=WITHDRAWAL;结果 5 行全是退团单,上面那张 CHANGE 单不在其中 ✓ @9c7ac9382 +GET /v3/admin/order/house-allocation/households HOUSE 会话发 2 条未读 → 该行 unreadCount=2;没有会话的行 unreadCount=0 ✓ @bdde64a3a +GET /v3/admin/order/house-allocation/households scope=mine&status=unfinished,需求最终确认后 → code=200,5 行,不含该需求所在单 ✓ @9c7ac9382,带房需求复测 ✓ @ff6863754 +GET /v3/admin/order/house-allocation/group-batches 团期转交后 scope=all 查该团 → code=200,houseClaimerName 变为接收人 ✓ @9c7ac9382 +GET /admin/house/orders/{orderId} 一晚两行(一行 breakfast=INCLUDED + deductInventory=true,一行两者都不传) → breakfastLabel「含早餐」/「早餐待确认」,roomSource STOCK / HOTEL,该晚 nightRoomSource=MIXED;客人自订晚 SELF,未配房晚 UNSET ✓ @bdde64a3a +GET /admin/house/orders/{orderId} 非控房已确认行改酒店不带凭证后 → changes[] 多一条 oldStatus=HELD;确认取消后该条 CANCEL_CONFIRMED、cancelFee="200.00";控房行改酒店的新 change 直接是 CANCEL_CONFIRMED ✓ @bdde64a3a +GET /admin/house/orders/{orderId} 间数 3→2 后 changes[] 多一条 changeKind=ROOM_COUNT、oldStatus=HELD;2→3 后没有新增 ✓ @bdde64a3a +POST /v3/admin/order/hotel-requirements/{requirementId}/assignments 一晚一行扣控房 + 一行不扣控房 → code=200 ✓ @bdde64a3a +POST /v3/admin/order/hotel-requirements/{requirementId}/assignments 对别人持有的需求提交 → code=808110;持有人经转单转给本人后同一请求 → code=200 ✓ @9c7ac9382 +POST /v3/admin/order/hotel-requirements/{requirementId}/assignments 控房表调价后提交同酒店同房型同晚 → code=200,快照价 680.00 / 675.00;调价前已有行仍是 620.00 / 615.00 ✓ @9c7ac9382 +PUT /v3/admin/order/assignments/{id} 测试服未单独调用,由 HouseAssignmentServiceTest#update_breakfast_overwritesWhenGivenKeepsWhenOmitted 覆盖 +PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement 非控房已确认行改酒店、不带凭证 → code=200,产生 oldStatus=HELD 改配记录 ✓ @bdde64a3a +PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement 控房行改酒店、不带凭证 → code=200,改配记录直接是 CANCEL_CONFIRMED ✓ @bdde64a3a +PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement 间数 3→2 → code=200,多一条 ROOM_COUNT / HELD 改配记录;2→3 → code=200,不新增 ✓ @bdde64a3a +POST /v3/admin/order/hotel-requirements/{requirementId}/transfer 持有人把需求转给房务 A → code=200,之后房务 A 对该需求提交配房成功 ✓ @9c7ac9382 +GET /v3/admin/order/todos 房务 A scope=all → code=200 ✓;scope=others → code=200 ✓ @9c7ac9382 +GET /v3/admin/house/group-batches 测试服未单独调用,由 HouseGroupBatchBoardManagerTest#page_scopeAllRoomManager_allClaims 覆盖 +GET /v3/admin/house/group-batches/{groupBatchId} 房务 A 查看房务 B 持有的团 → code=200,readOnly=true ✓ @9c7ac9382 +GET /v3/admin/house/group-batches/{groupBatchId}/room-requirements 团期转交前后查计划 → code=200,计划行数 1→1 ✓ @9c7ac9382;RESOURCE_PREPARING 团期该晚 needRoomCount=4 ✓ @ff6863754 +GET /v3/admin/house/group-batches/{groupBatchId}/allocations 团里一户出行前取消后 → code=200,该户 allocatedRooms 2→0,本团 leftoverRooms 0→2,计划行保留 ✓ @3ecf38797 +GET /v3/admin/house/group-batches/{groupBatchId}/room-plans/confirm-check 测试服未单独调用,由 HouseGroupBatchClaimGuardTest#assertReadable_others_noThrow、GroupBatchRoomDayConfirmManagerTest#check_staleAllocation_dayNotReadyAndBatchNotReady 覆盖 +POST /v3/admin/house/group-batches/{groupBatchId}/room-plans 团期持有人新建一行 2 间计划 → code=200 ✓ @9c7ac9382 +PUT /v3/admin/house/group-batches/{groupBatchId}/room-plans/{planId} 测试服未单独调用,由 GroupBatchRoomPlanManagerTest#update_replace_breakfastGiven_overridesOld、#update_breakfastOnly_pendingRow_inPlacePatchCarriesBreakfast 覆盖 +``` + +验证身份:房务 A / B / C、测试定制师、超管测试账号,全部是测试专用账号。落库读数来自测试服只读 SQL。 + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#8491](https://git.1814.love:8443/wx/HL/issues/8491) +- 契约文档: `docs/order-v3/api/API-SPEC-HOUSE-V1.1.html` §1.3 转单 / 超管指派、§11.12 错误码、§12 房务控制台 +- 同批变更: 同目录 `30_8491_房务控制台接口-新增接口-管理后台.md`、`30_8491_房务旧列表接口下线-删除接口-管理后台.md` +- 已知缺口: #8508(改晚次 / 酒店 / 房型 / 间数对已确认行的应付款处理) + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8491](https://git.1814.love:8443/wx/HL/issues/8491) + +### 联系人 + +- **后端负责人**: @wx