--- 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