From 88caa74d34eefb2d86331c5d1bfd0b1ed45d98a4 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sat, 26 Sep 2026 23:52:47 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20=E6=88=BF=E5=8A=A1=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=AE=A1=E8=AE=A1=E6=89=B9=E6=AC=A1=20#8385~#8390=20?= =?UTF-8?q?=E4=BA=A4=E6=8E=A5=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #8385 团期订房计划建守卫(未认领团 808612)与 808660 释放文案 - #8386 住宿 supplier-reject 加角色门与认领校验,车务 supplier-reject 下线 - #8387 下线订单侧房间分配三口 /v3/admin/order/{id}/room - #8388 最终确认回执上传加认领校验、列表加读门、808184 带具体原因 - #8389 下线 POST /v3/admin/order/assignments/{assignmentId}/rooms - #8390 房务 16 个只读端点加角色读门,ADMIN 房务菜单撤授 Co-Authored-By: Claude Opus 5.5 (1M context) --- ...¿计划建守卫与释放文案改进-修改接口-管理后台.md | 373 +++++ ...门与认领校验删车务驳回口-修改接口-删除接口-管理后台.md | 331 ++++ ..._下线订单房间分配三口-删除接口-管理后台.md | 346 +++++ ...›ž执上传加认领校验列表加读门-修改接口-管理后台.md | 375 +++++ ...ˆ¿务家庭维度房间分配写口-删除接口-管理后台.md | 192 +++ ...¨补齐16个端点与ADMIN菜单撤授-修改接口-管理后台.md | 1360 +++++++++++++++++ 6 files changed, 2977 insertions(+) create mode 100644 changelogs-v2/2026-09/26_8385_团期订房计划建守卫与释放文案改进-修改接口-管理后台.md create mode 100644 changelogs-v2/2026-09/26_8386_住宿需求供应方驳回加角色门与认领校验删车务驳回口-修改接口-删除接口-管理后台.md create mode 100644 changelogs-v2/2026-09/26_8387_下线订单房间分配三口-删除接口-管理后台.md create mode 100644 changelogs-v2/2026-09/26_8388_房务最终确认回执上传加认领校验列表加读门-修改接口-管理后台.md create mode 100644 changelogs-v2/2026-09/26_8389_下线房务家庭维度房间分配写口-删除接口-管理后台.md create mode 100644 changelogs-v2/2026-09/26_8390_房务读侧权限门补齐16个端点与ADMIN菜单撤授-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/26_8385_团期订房计划建守卫与释放文案改进-修改接口-管理后台.md b/changelogs-v2/2026-09/26_8385_团期订房计划建守卫与释放文案改进-修改接口-管理后台.md new file mode 100644 index 00000000..ec0d9e64 --- /dev/null +++ b/changelogs-v2/2026-09/26_8385_团期订房计划建守卫与释放文案改进-修改接口-管理后台.md @@ -0,0 +1,373 @@ +--- +schema: "hl-changelog/v2" +ticket: "8385" +title: "团期订房计划建守卫:超管未认领团上禁建(808612),释放文案改为可操作指引" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "超管在未被房务整团认领的团期上新建订房计划时返回 808612(复用已有码);释放端点的 808660 文案改为可操作的指引(删除计划、联系超管接管)。后端改动已合入 dev-v3 并部署测试服,808612 新守卫与 808660 新文案均已网关实测。" +updated_at: "2026-09-26" +base: "dev-v3" +--- + +# order-v3:团期订房计划守卫与释放指引改进(管理后台) + +**服务**: hl-order-service-v3 +**PR**: #8395 +**Issue**: #8385 + +--- + +## ⚠️ 关键变化 + +1. **新增入口守卫**:超管(SUPER_ADMIN)在**未被房务认领的团期**上调用 `POST /v3/admin/house/group-batches/{groupBatchId}/room-plans` 时,返回 **808612**「该团期尚未被房务整团认领」,不落库。 + - 背景:超管本可在无人认领的团上直接建计划,导致"团没人管、但计划和库存都在"的状态。释放时房务因为 808660 无法释放,只剩逐条删计划这一条路。 + - 约束:超管要在团上排房,必须先 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover` 指派给某个房务,再建计划。 + +2. **改进释放文案**:端点 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/release` 的错误码 808660 文案改为: + 「该团仍有 {0} 条未取消的订房计划,无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管」 + - 改前文案提到的「走接管」对房务不可用(接管只有超管能做)。改后提供两条可行出口:删除计划(房务自助)/ 联系超管接管(超管权限)。 + +--- + +## 一、背景 + +团期整团认领的唯一真实指针是 `order_group_batch.house_claimer_id`。超管出于"人离职、团转手"的清理需要被允许越过认领校验,但在一个**没人认领、也不需要清理**的团上直接建计划,不属于这类处置。 + +释放时的 808660 守卫堵住了"有计划+无认领人"的释放口,文案则指向一个房务无法执行的操作(接管),导致房务被无谓地卡住。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 提交订房计划 | POST | `/v3/admin/house/group-batches/{groupBatchId}/room-plans` | 新增入口守卫 | 超管未认领团上返回 808612 | +| 2 | 释放认领 | POST | `/v3/admin/order/grab-pool/group-batches/{groupBatchId}/release` | 错误码文案改进 | 808660 文案改为可操作指引 | + +网关无改动(既有 `/v3/admin/house/` 与 `/v3/admin/order/` 前缀均可达);返回码、触发条件、已认领团的行为全部不变。 + +--- + +## 三、接口详情 + +### 1. 提交订房计划 `POST /v3/admin/house/group-batches/{groupBatchId}/room-plans` + +**VO**: `GroupBatchRoomPlanSaveReqVO → Result>` + +#### 使用场景 + +房务或超管在团期上提交多条订房计划(房型、房数、价格、结算方式、库存扣减等)。超管在未认领的团上调用时新增拒绝,改后只能通过先 takeover 指派给房务的方式排房。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | 是 | — | 团期 ID | +| items | Body | List | 是 | ≤200 条 | 订房计划行 | +| items[].stayDate | Body | LocalDate | 是 | 格式 `yyyy-MM-dd` | 入住日期 | +| items[].hotelId | Body | Long | 是 | — | 酒店 ID | +| items[].roomTypeId | Body | Long | 是 | — | 房型 ID | +| items[].roomCount | Body | Integer | 是 | ≥1 | 房间数 | +| items[].roomCategory | Body | String | 否 | ≤32 | 房型大类 code;服务端以 resource 权威值覆盖,仅用于前端回显 | +| items[].protoPrice | Body | BigDecimal | 否 | ≥0 | 协议价快照;不传按「日历价 → 酒店协议价」兜底 | +| items[].settlementPrice | Body | BigDecimal | 否 | ≥0 | 结算价快照;不传按「日历价 → 协议价」兜底 | +| items[].settleType | Body | String | 否 | `cash` \| `sign` \| `company` | 结算方式;不传取酒店资源配置 | +| items[].deductInventory | Body | Boolean | 否 | 默认 true | 是否扣库存 | +| items[].remark | Body | String | 否 | ≤512 | 备注 | +| items[].version | Body | Integer | 否 | — | 乐观锁版本;本端点(批量新建)不校验,只有单行修改端点必填并校验 | +| items[].replaceReason | Body | String | 否 | ≤256 | 替换原因;本端点不使用,仅单行修改端点在语义为"删旧建新"时使用 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| planId | String | 计划行 ID(雪花 ID,序列化为字符串) | +| groupBatchId | String | 团期 ID(雪花 ID,序列化为字符串) | +| stayDate | LocalDate | 入住日期 | +| hotelId / hotelName | String / String | 酒店 ID(序列化为字符串)及名称 | +| roomTypeId / roomTypeName | String / String | 房型 ID(序列化为字符串)及名称 | +| roomCount | Integer | 房间数 | +| protoPrice | String | 协议价快照,可为 null(BigDecimal,序列化为字符串) | +| settlementPrice | String | 结算价快照,可为 null(BigDecimal,序列化为字符串) | +| settleType | String | 结算方式快照,可为 null | +| planStatus | String | 状态码(PENDING / CONFIRMED) | +| version | Integer | 乐观锁版本号 | + +#### 请求示例 + +```http +POST /v3/admin/house/group-batches/2099918391610314754/room-plans HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +Content-Type: application/json + +{ + "items": [ + { + "stayDate": "2026-10-01", + "hotelId": 1001, + "roomTypeId": 5001, + "roomCount": 2, + "settlementPrice": 450.00, + "deductInventory": true, + "remark": "标准间" + } + ] +} +``` + +#### 响应示例 + +**成功(已认领团)**: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ + { + "planId": "2103794613192208385", + "groupBatchId": "2099918391610314754", + "stayDate": "2026-10-01", + "hotelId": "1001", + "hotelName": "丽思卡尔顿", + "roomTypeId": "5001", + "roomTypeName": "豪华标间", + "roomCount": 2, + "settlementPrice": "450.00", + "planStatus": "PENDING", + "version": 1, + "createTime": "2026-09-26 14:30:00" + } + ] +} +``` + +**失败(未认领团,超管)**: + +```json +{ + "code": 808612, + "message": "该团期尚未被房务整团认领", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +- 参数校验失败(日期格式错、房间数 ≤0):HTTP 200,`code=400`,message 含具体字段文案;不落库。 + +#### 错误响应 + +```json +{ + "code": 808612, + "message": "该团期尚未被房务整团认领", + "success": false, + "data": null +} +``` + +**其他错误码**: +- **808090**:未登录或非房务角色,无权操作。 +- **808091**:房务组长为只读监督角色,无权执行该操作(HOUSE_KEEPER_LEAD)。 +- **808613**:该团期由其他房务认领,无权操作。 +- **808600**:团期当前阶段不允许修改订房计划(订房计划仅在配置阶段可改)。 +- **808614**:该入住日在酒店的该房型上已有订房计划,请改用修改单行。 +- **808611**:团期出发日或结束日缺失,无法录入订房计划。 +- **808602**:入住日不在团期出行区间内。 +- **808603**:订房间数必须大于 0。 +- **808691**:无法确定房型大类(取权威房型数据失败),请稍后重试。 +- **808112**:房型不属于该酒店。 +- **100502**:3 秒幂等窗口内重复提交,「订房计划提交处理中,请勿重复提交」。 + +#### 业务边界 + +- **整团认领**:认领人本人或超管可提交;非认领房务提交返回 808613;团未认领(`house_claimer_id IS NULL`)时所有人(含超管)返回 808612。 +- **超管排房出口**:超管要在未认领的团上排房,必须先 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover` 指派给房务,再来提交计划。 +- **库存扣减**:`deductInventory=true` 时,CONFIRMED 状态的计划会扣掉 `resource_hotel_room_inventory` 对应房型该日的可用房数;PENDING 阶段不扣。 +- **日期范围**:入住日期必须在团期的出发日期与结束日期之间。 + +--- + +### 2. 释放认领 `POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/release` + +**VO**: `HouseGroupReleaseReqVO → Result` + +#### 使用场景 + +房务释放所有团期认领,团回到抢单池状态(`house_claimer_id → NULL`)。房务有订房计划待处理时返回 808660,文案告知删除计划或联系超管接管两条出口。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | 是 | — | 团期 ID | +| reason | Body | String | 否 | ≤200 字 | 释放原因;超管必填且不少于 10 字 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| — | null | 成功返回 null | + +#### 请求示例 + +```http +POST /v3/admin/order/grab-pool/group-batches/2099918391610314754/release HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +Content-Type: application/json + +{ + "reason": "已完成配房" +} +``` + +#### 响应示例 + +**成功**: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": null +} +``` + +**失败(有计划待删)**: + +```json +{ + "code": 808660, + "message": "该团仍有 4 条未取消的订房计划,无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +不适用。 + +#### 错误响应 + +```json +{ + "code": 808660, + "message": "该团仍有 4 条未取消的订房计划,无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管", + "success": false, + "data": null +} +``` + +**其他错误码**: +- **808655**:该团期不属于当前房务,无法释放;非认领人释放时触发(超管不受此限),CAS 并发失败(读到时归本人、提交时已被超管释放/接管)也报此码。 +- **808656**:该团期尚未被认领,无需释放。 +- **808657**:超管操作原因长度不足 10 字(房务释放时 reason 可为空)。 +- **808090**:未登录或非房务角色,无权操作。 +- **808091**:房务组长为只读监督角色,无权执行该操作(HOUSE_KEEPER_LEAD)。 + +#### 业务边界 + +- **释放前置**:须先删光全部未取消的订房计划(PENDING + CONFIRMED)才能释放;所有角色(含超管)受 808660 约束,release 本身不碰计划行。 +- **幂等**:幂等键按团(`groupBatchId`),窗口 5 秒;窗口内重复提交返回 100502「整团释放处理中,请勿重复提交」。 + +--- + +## 四、契约约束与正确调用方式 + +| 场景 | 调用方法 | 说明 | +|------|---------|------| +| ✅ 超管先排房再指派 | 先 `POST /v3/admin/order/grab-pool/group-batches/{id}/takeover` 指派给房务 → 再 `POST .../room-plans` 提交 | takeover 让团被指定房务认领,之后超管仍可在上面操作 | +| ✅ 房务提交计划 | `POST /v3/admin/house/group-batches/{id}/room-plans` | 认领的房务可随时提交,RESOURCE_PREPARING 阶段有效 | +| ✅ 房务释放有计划 | 先 `DELETE /v3/admin/house/group-batches/{id}/room-plans/{planId}` 逐条删 → 再 `POST .../release` | 删除由 `HouseGroupBatchClaimGuard` 控制,认领人可删(含他人建的),释放成功返回 200 | +| ❌ 超管在未认领团直接建计划 | — | 返回 808612,需先 takeover 指派 | +| ❌ 有计划待删时释放 | — | 返回 808660,不释放;先删后释 | + +--- + +## 五、数据库行为 + +无表结构变更、无 Flyway 迁移。 + +--- + +## 六、边界行为 + +| 场景 | 行为 | +|---|---| +| 超管 POST room-plans on 未认领团 | 返回 808612,`group_batch_room_plan` 零新增 | +| 超管 takeover 后 POST room-plans | 返回 200,计划行正常落库 | +| 房务释放、仍有 PENDING 计划 | 返回 808660,计数含 PENDING | +| 房务删掉全部计划、再释放 | 返回 200,团回到池里 | +| 超管释放、仍有计划 | 同样返回 808660(不分角色) | + +## 六.6、修改前后对比 + +| 维度 | 改前 | 改后 | +|---|---|---| +| 超管在未认领团上 POST room-plans | 返回 200,计划落库,团仍无主 | 返回 808612,不落库 | +| 释放时有计划、808660 文案 | 「无法释放(请先处理订房计划或走接管)」 | 「无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管」 | + +## 六.7、影响评估 + +- **兼容性**:hl-ui 在 `origin/v2.1` 上房务页面无调用 `takeover`(该接口仍在,超管直接用也可以);超管排房流程可能需要调整为"先指派再排"。 +- **前端要动的**:房务页面若有"直接排房"的超管入口,需提示"未认领团先指派再排";释放时若遇到 808660,按改后文案提示房务删除计划。 +- **数据影响**:无;本单仅加守卫,不改历史数据。 +- **其它服务**:product-v2、fleet、user-service 无改动。 + +--- + +## 七、不影响范围 + +- 已认领团的超管操作(确认、分房、delete 等)全部不动。 +- 更新/删除/确认计划的守卫不动(仍走 `assertWritableByCurrentUser`,超管仍可清理)。 +- 其他团期相关接口(认领、接管、整团确认等)无改动。 + +--- + +## 八、测试环境已验证 + +部署:测试环境 order-v3 `1f65d7894`(含 e2313790b)。网关 `api.test.1814.love:9443`。 + +| # | 场景 | 实测结果 | +|---|---|---| +| 1 | 超管 POST 未认领团(RESOURCE_PREPARING) | code 808612,计划行零新增 | +| 2 | 该团由超管 takeover 给某房务后,超管再 POST | code 200 | +| 3 | 超管 DELETE 上一步新增的计划行 | code 200,`releasedLogId=null` | +| 4 | 该房务 release | code 200,团回到无主 | +| 5 | 另一认领房务逐条 DELETE 自己团的 4 条 PENDING 计划 | 全部 200(`releasedLogId=null`) | +| 6 | 上一步之后 release | code 200 | +| 7 | 超管对他人认领团的存量计划行 DELETE(62 行) | 全部 200 | +| 8 | 认领房务在团上挂 1 条 PENDING 计划时 release | code 808660,message「该团仍有 1 条未取消的订房计划,无法释放:请先逐条删除订房计划后再释放;如需更换认领房务请联系超管接管」,与源码逐字一致 | +| 9 | 删掉该计划后再 release | code 200,团回到无主 | + +--- + +## 十、相关文档 + +- Issue:https://git.1814.love/wx/HL/issues/8385 +- PR:https://git.1814.love/wx/HL/pulls/8395 +- takeover API:`POST /v3/admin/order/grab-pool/group-batches/{groupBatchId}/takeover` +- 同批单号:#8386、#8387、#8388、#8389、#8390(房务接口审计批次) + +--- + +## 关联 / 联系人 + +### 联系人 + +- **后端负责人**: @wx diff --git a/changelogs-v2/2026-09/26_8386_住宿需求供应方驳回加角色门与认领校验删车务驳回口-修改接口-删除接口-管理后台.md b/changelogs-v2/2026-09/26_8386_住宿需求供应方驳回加角色门与认领校验删车务驳回口-修改接口-删除接口-管理后台.md new file mode 100644 index 00000000..9060268b --- /dev/null +++ b/changelogs-v2/2026-09/26_8386_住宿需求供应方驳回加角色门与认领校验删车务驳回口-修改接口-删除接口-管理后台.md @@ -0,0 +1,331 @@ +--- +schema: "hl-changelog/v2" +ticket: "8386" +title: "住宿需求驳回:加房务角色门与认领人校验;车务驳回接口下线" +consumer: "admin" +author: "wx(GIT)" +change_type: "删除接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "住宿需求供应方驳回新增房务角色门(808090/808091)与认领人校验(808110/808116,超管豁免);车务驳回接口删除,调用返回 HTTP 200 + code 404。已在测试环境网关实测各角色分支与已删除端点响应。" +updated_at: "2026-09-26" +base: "dev-v3" +--- + +# order-v3:住宿需求驳回守卫加固与车务驳回下线(管理后台) + +**服务**: hl-order-service-v3 +**PR**: #8397 +**Issue**: #8386 + +--- + +## ⚠️ 关键变化 + +1. **住宿需求供应方驳回** `POST /v3/admin/order/{id}/hotel-requirement/supplier-reject`: + - 新增房务角色门:`@HouseWriteGuarded`,非房务角色返回 **808090**,房务组长返回 **808091**。 + - 新增认领人校验:需求必须被当前操作人认领,否则返回 **808110**(非本人)/ **808116**(未认领,仅超管可驳)。 + - 超管可绕过认领限制(跳过本人限制,但仍受角色门约束)。 + +2. **车务需求供应方驳回** `POST /v3/admin/order/{id}/vehicle-requirement/supplier-reject` **已下线**: + - 端点删除,调用返回 404。 + - hl-ui 全部远程 ref 零调用,车务侧无此功能。 + +--- + +## 一、背景 + +### 住宿驳回 + +API-SPEC §2.8 与 CHANGELOG v6.3.8 均明文「新增房务/超管可用的住宿需求驳回端点」,但实现漏了角色门和认领人校验: +- 任意 ADMIN token 的后台账号(包括车控、定制师)都能驳回任意订单的需求。 +- 测试服已实证车控账号(VEHICLE_MANAGER)成功打回住宿需求两次。 +- 同类操作(转单、释放、提交配房)都带认领人校验,驳回不带属于遗漏。 +- 打回副作用重:需求行终态失活、flow 回退、定制师开返工待办 + 站内信。 + +### 车务驳回 + +车务从无"抢单 → 认领"的流程,用车需求也从不写入抢单人:整个车务侧"我的接单"接口(#8373)因此下线。供应方驳回同样是"假接口"——没有实际用途、hl-ui 零调用。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 住宿需求供应方驳回 | POST | `/v3/admin/order/{id}/hotel-requirement/supplier-reject` | 修改接口 | 新增角色门(808090/808091)与认领人校验(808110/808116) | +| 2 | 车务需求供应方驳回 | POST | `/v3/admin/order/{id}/vehicle-requirement/supplier-reject` | 删除接口 | 端点已删除,调用返回 404 | + +--- + +## 三、接口详情 + +### 1. 住宿需求供应方驳回 `POST /v3/admin/order/{id}/hotel-requirement/supplier-reject` + +**VO**: `RejectReqVO → Result` + +#### 使用场景 + +房务(ROOM_MANAGER)或超管在配房面板「驳回需求」,把已认领的住宿需求打回给定制师,触发返工。仅认领人或超管可操作;非房务角色返回权限错误。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | 是 | — | 订单 ID | +| returnRemark | Body | String | 是 | `@NotBlank`,≤500 字 | 驳回原因,不能为空 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| — | null | 成功返回 null | + +#### 请求示例 + +```http +POST /v3/admin/order/770145/hotel-requirement/supplier-reject HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +Content-Type: application/json + +{ + "returnRemark": "房型不符,请调整" +} +``` + +#### 响应示例 + +**成功**: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": null +} +``` + +**失败(非认领人)**: + +```json +{ + "code": 808110, + "message": "需求不属于当前用户", + "success": false, + "data": null +} +``` + +**失败(无房务权限)**: + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +- `returnRemark` 缺失或为空白:`@Valid` 校验不通过,HTTP 200 + `code: 400` + 具体字段错误信息(不进入业务逻辑)。 + +#### 错误响应 + +```json +{ + "code": 400, + "message": "打回/驳回备注不能为空", + "success": false, + "data": null +} +``` + +**校验顺序**(前一道不过不会走到后一道):写门(808090/808091)→ 参数校验(400)→ 582031(无生效需求行)→ 582083(需求状态不允许此操作)→ 认领人校验(808116/808110,超管豁免)→ 582086(已有生效配房不可驳回)→ 582083(并发 CAS 失败)。 + +**其他错误码**: +- **808090**:未登录或非房务角色,无权操作。 +- **808091**:房务组长为只读监督角色,无权执行该操作。 +- **582031**:订单无有效需求行。 +- **582083**:需求状态不允许此操作,请检查当前状态(团期订单只放行 PENDING/PROCESSING;非团期订单放行 PENDING/PROCESSING/PENDING_REVIEW;并发 CAS 失败同样报此码)。 +- **808116**:订单未抢单, 请先抢单再配房(需求未被认领,非超管不可驳)。 +- **808110**:需求不属于当前用户(已被他人认领,非超管不可驳)。 +- **582086**:该需求已有配房记录, 不能驳回, 请走替换/修改配房。 + +#### 业务边界 + +- **认领限制**:需求 `claimer_id` 必须等于当前操作人,或当前角色为 SUPER_ADMIN。房务组长(house_keeper_lead)无法驳回,返回 808091。 +- **超管豁免**:SUPER_ADMIN 绕过认领人限制(不受 808110/808116 约束),但仍受角色门约束(必须先过 `@HouseWriteGuarded`)。 +- **副作用**:驳回目标状态按订单类型区分——核心/定制订单需求驳回后进 `REJECTED_TO_CONSULTANT`,团期订单进 `REJECTED_TO_ADMIN`;触发返工待办与站内信。 +- **转单后生效**:若需求在驳回前被转单给其他房务,原持有人的驳回请求返回 808110(当前 `claimer_id` 已是新认领人)。 +- **已有配房记录不可驳回**:需求已生成生效配房(`hasActiveHotelAssignmentsByOrder` 为真)时返回 582086,须走替换/修改配房而非驳回。 + +--- + +### 2. 车务需求供应方驳回 `POST /v3/admin/order/{id}/vehicle-requirement/supplier-reject` + +**VO**: `已删除` + +**状态:已删除** + +#### 使用场景 + +该接口已删除,不可用。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| — | — | — | — | — | 已删除 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| — | — | 已删除 | + +#### 请求示例 + +```http +POST /v3/admin/order/770145/vehicle-requirement/supplier-reject HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +Content-Type: application/json + +{} +``` + +#### 响应示例 + +HTTP 状态码 **200**(非 404),业务错误码 404: + +```json +{ + "code": 404, + "message": "接口不存在: POST /v3/admin/order/770145/vehicle-requirement/supplier-reject", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +不适用(路由已删)。前端应只判 `code`,不依赖 HTTP 状态码。 + +#### 错误响应 + +```json +{ + "code": 404, + "message": "接口不存在: POST /v3/admin/order/770145/vehicle-requirement/supplier-reject", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 接口已删除,路由不存在;调用返回 HTTP 200 + `code: 404`(不是 HTTP 404),前端只判 `code`。 +- hl-ui 全部远程 ref 零调用;测试网关 nginx 日志 2026-09-12~09-26 窗口内该路径 19 次调用全部来自脚本(Python-urllib / curl / 空 UA),零浏览器 UA。 + +--- + +## 四、契约约束与正确调用方式 + +| 场景 | 调用方法 | 说明 | +|------|---------|------| +| ✅ 认领人驳回 | `POST /v3/admin/order/{orderId}/hotel-requirement/supplier-reject` | 该订单的认领房务直接调,返回 200;定制师开返工待办 | +| ✅ 超管代驳 | 同上(SUPER_ADMIN 身份)| 超管跳过认领限制,但仍需房务角色权限 | +| ✅ 订单详情按钮 | 查看 `actions.canRejectRequirement` | enabled 取决于 write 权限 + 认领人判定;false 时显示 disabledReason | +| ❌ 非认领人驳回 | — | 返回 808110,需求 status 不变 | +| ❌ 车务驳回 | — | 端点已删除,返回 HTTP 200 + code 404 | +| ❌ 定制师驳回 | — | 返回 808090 | + +--- + +## 五、数据库行为 + +无表结构变更、无 Flyway。 + +--- + +## 六、边界行为 + +| 场景 | 行为 | +|---|---| +| 认领人驳回 | 返回 200,需求进 REJECTED_TO_CONSULTANT 或 REJECTED_TO_ADMIN | +| 非认领人驳回 | 返回 808110,需求 status 不变 | +| 未认领需求 + 房务驳回 | 返回 808116,非超管不可驳 | +| 超管驳回未认领 | 返回 200,驳回处理 | +| 转单中驳回(原认领人) | 返回 808110(新认领人接管后视为他人所有) | +| 车务驳回 | 返回 HTTP 200 + code 404,无新增副作用 | + +## 六.6、修改前后对比 + +| 维度 | 改前 | 改后 | +|---|---|---| +| 车控账号调住宿驳回 | 返回 200,需求打回 | 返回 808090,需求 status 不变 | +| 非认领人驳回 | 返回 200,需求打回 | 返回 808110,status 不变 | +| 未认领需求 | 任意房务可驳 | 仅超管可驳,返回 808116 | +| 车务驳回 | 路由存在,可调用 | 路由删除,返回 HTTP 200 + code 404 | + +## 六.7、影响评估 + +- **兼容性**:hl-ui 房务驳回按钮的 `enabled` 判定由 `canRejectRequirement` 决定(已包含写权限与认领人两个维度),驳回失败收 808090/808091/808110/808116 四个新码。 +- **前端要动的**: + - 驳回按钮的 disabled 文案区分「无房务权限」(808090/808091)与「非本人认领」(808110)。 + - 处理 808116「未认领,仅超管可驳」的提示(普通房务不应遇到,因为按钮本身 disabled)。 + - 车务不再有驳回端点,移除 vehicle-requirement/supplier-reject 调用。 +- **数据影响**:无;仅加守卫,不改历史。 +- **其它服务**:fleet 的 `rejectVehicleRequirementFromFleet` 独立存在,本单不动。 + +--- + +## 七、不影响范围 + +- 团期管理员的 dispatch/reject(走独立 `GroupBatchPermissionGuard`),无改动。 +- 转单、释放、提交配房等其他配房写操作,无改动。 +- 车务用车需求的其他接口(提交、放行、派单),无改动。 +- 整团认领释放等认领链路,无改动。 + +--- + +## 八、测试环境已验证 + +部署:测试环境 order-v3 `1f65d7894`(含 e2313790b)。网关 `api.test.1814.love:9443`。 + +| # | 场景 | 实测结果 | +|---|---|---| +| 1 | VEHICLE_MANAGER 调住宿 supplier-reject | code 808090,需求状态不变 | +| 2 | house_keeper_lead 调住宿 supplier-reject | code 808091,需求状态不变 | +| 3 | 非认领 ROOM_MANAGER(需求已被他人认领)驳回 | code 808110,需求状态不变 | +| 4 | ROOM_MANAGER 对未认领需求驳回 | code 808116,需求状态不变 | +| 5 | 超管打回 / 认领人打回自己认领的需求 | 成功,核心订单需求进 REJECTED_TO_CONSULTANT | +| 6 | 车务 `POST /v3/admin/order/{id}/vehicle-requirement/supplier-reject` | HTTP 200 + code 404「接口不存在: POST 」 | + +调用来源:测试网关 nginx 日志 2026-09-12~09-26 窗口内车务 supplier-reject 共 19 次,全部是脚本(Python-urllib / curl / 空 UA),零浏览器 UA;hl-ui 全部远程 ref 零调用。 + +--- + +## 十、相关文档 + +- Issue:https://git.1814.love/wx/HL/issues/8386 +- PR:https://git.1814.love/wx/HL/pulls/8397 +- 相关单号:#8373(车务我的接单下线)、#8388(回执认领人校验同口径) +- API-SPEC:`docs/order-v3/api/API-SPEC-HOUSE-V1.1.html` §2.8 + +--- + +## 关联 / 联系人 + +### 联系人 + +- **后端负责人**: @wx diff --git a/changelogs-v2/2026-09/26_8387_下线订单房间分配三口-删除接口-管理后台.md b/changelogs-v2/2026-09/26_8387_下线订单房间分配三口-删除接口-管理后台.md new file mode 100644 index 00000000..40537f72 --- /dev/null +++ b/changelogs-v2/2026-09/26_8387_下线订单房间分配三口-删除接口-管理后台.md @@ -0,0 +1,346 @@ +--- +schema: "hl-changelog/v2" +ticket: "8387" +title: "订单房间分配:下线旧版三接口 GET/POST/PUT /v3/admin/order/{id}/room" +consumer: "admin" +author: "wx(GIT)" +change_type: "删除接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "订单侧旧版房间分配三个端点(GET list、POST add、PUT edit)已下线。API-SPEC 已标旧版,房务配房走 house 域 §2.5 接口。hl-ui 零调用。" +updated_at: "2026-09-26" +base: "dev-v3" +--- + +# order-v3:下线订单房间分配旧版接口(管理后台) + +**服务**: hl-order-service-v3 +**PR**: #8398 +**Issue**: #8387 + +--- + +## ⚠️ 关键变化 + +三个接口已删除,路由不存在: +- `GET /v3/admin/order/{id}/room`:查询订单房间分配列表(旧版) +- `POST /v3/admin/order/{id}/room`:新增房间分配(旧版) +- `PUT /v3/admin/order/{id}/room/{roomAssignmentId}`:编辑房间分配(旧版) + +调用返回 HTTP 200 + `code: 404`(不是 HTTP 404)。房务配房改用 house 域接口:新增/清空配房走 `POST/DELETE /v3/admin/order/hotel-requirements/{requirementId}/assignments` 等 API-SPEC-HOUSE §2.2~§2.4c 写口;查询订单房间用 `GET /v3/admin/order/orders/{orderId}/rooms`(§2.5)。 + +--- + +## 一、背景 + +订单侧房间分配(`order_room_assignment` 表)是房务配房的补充记录,实现早于 house 域统一接口。API-SPEC §8 已标为"旧版",注释指向 house 域 §2.5 为正式接口。 + +hl-ui 全部远程 ref 无调用;房务页面已改用 house 域配房端点。后台读写入口全删,仅保留 internal bundle 读——house 域 `RoomAssignmentVO` 与 hl-mp-service 的 C 端聚合仍需 `order_room_assignment` 表数据,故表与内部读逻辑保留,仅删对外读写接口。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 查询房间分配列表(旧) | GET | `/v3/admin/order/{id}/room` | 删除 | 路由已删,任何调用返回 404 | +| 2 | 新增房间分配(旧) | POST | `/v3/admin/order/{id}/room` | 删除 | 路由已删,返回 404 | +| 3 | 编辑房间分配(旧) | PUT | `/v3/admin/order/{id}/room/{roomAssignmentId}` | 删除 | 路由已删,返回 404 | + +房务配房改用 house 域接口(API-SPEC-HOUSE):提交配房 `POST /v3/admin/order/hotel-requirements/{requirementId}/assignments`(§2.2)、清空配房 `DELETE .../assignments`(§2.4b/§2.4c)、按天确认 `POST .../assignments/days/{dayNumber}/confirm`(§2.3)、调整入住 `PUT .../assignments/{id}/placement`(§2.3b);查询订单房间用 `GET /v3/admin/order/orders/{orderId}/rooms`(§2.5)。 + +--- + +## 三、接口详情 + +### 1. 房间分配查询 `GET /v3/admin/order/{id}/room` + +**VO**: `已删除` + +**状态**:已删除。路由不存在,返回 404。 + +#### 使用场景 + +该接口已删除。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| — | — | — | — | — | 已删除 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| — | — | 已删除 | + +#### 请求示例 + +```http +GET /v3/admin/order/770145/room HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +HTTP 状态码 **200**(非 404),业务错误码 404: + +```json +{ + "code": 404, + "message": "接口不存在: GET /v3/admin/order/770145/room", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +不适用。前端应只判 `code`,不依赖 HTTP 状态码。 + +#### 错误响应 + +```json +{ + "code": 404, + "message": "接口不存在: GET /v3/admin/order/770145/room", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 接口已删除,路由不存在;调用返回 HTTP 200 + `code: 404`。 +- 阳性对照:同 Controller 下 `GET /v3/admin/order/{id}` 正常返回 200,证明是该路由本身被删,不是订单不存在。 +- `order_room_assignment` 表数据仍可由服务内部通过 `internal/order/orders/{orderId}/assignments` 读取(该 internal 端点经网关直接拒绝「接口不可访问」,只服务间可达,不对前端暴露)。 + +--- + +### 2. 房间分配新增 `POST /v3/admin/order/{id}/room` + +**VO**: `已删除` + +**状态**:已删除。路由不存在,返回 404。 + +#### 使用场景 + +该接口已删除。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| — | — | — | — | — | 已删除 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| — | — | 已删除 | + +#### 请求示例 + +```http +POST /v3/admin/order/770145/room HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +Content-Type: application/json + +{} +``` + +#### 响应示例 + +HTTP 状态码 **200**(非 404),业务错误码 404: + +```json +{ + "code": 404, + "message": "接口不存在: POST /v3/admin/order/770145/room", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +不适用。前端应只判 `code`,不依赖 HTTP 状态码。 + +#### 错误响应 + +```json +{ + "code": 404, + "message": "接口不存在: POST /v3/admin/order/770145/room", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 路由已删除;调用返回 HTTP 200 + `code: 404`。 +- 新增房间分配改走 house 域 `POST /v3/admin/order/hotel-requirements/{requirementId}/assignments`(API-SPEC-HOUSE §2.2)。 + +--- + +### 3. 房间分配编辑 `PUT /v3/admin/order/{id}/room/{roomAssignmentId}` + +**VO**: `已删除` + +**状态**:已删除。路由不存在,返回 404。 + +#### 使用场景 + +该接口已删除。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| — | — | — | — | — | 已删除 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| — | — | 已删除 | + +#### 请求示例 + +```http +PUT /v3/admin/order/770145/room/123456 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +Content-Type: application/json + +{} +``` + +#### 响应示例 + +HTTP 状态码 **200**(非 404),业务错误码 404: + +```json +{ + "code": 404, + "message": "接口不存在: PUT /v3/admin/order/770145/room/123456", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +不适用。前端应只判 `code`,不依赖 HTTP 状态码。 + +#### 错误响应 + +```json +{ + "code": 404, + "message": "接口不存在: PUT /v3/admin/order/770145/room/123456", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 路由已删除;调用返回 HTTP 200 + `code: 404`。 +- 编辑房间分配改走 house 域 `PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement`(API-SPEC-HOUSE §2.3b)。 + +--- + +## 四、契约约束与正确调用方式 + +| 场景 | 改前 | 改后 | +|------|------|------| +| 查看订单的房间 | `GET /v3/admin/order/{orderId}/room` | 调用返回 HTTP 200 + code 404;改用 `GET /v3/admin/order/orders/{orderId}/rooms`(API-SPEC-HOUSE §2.5) | +| 新增房间 | `POST /v3/admin/order/{orderId}/room` | 路由已删,不可用;改用 `POST /v3/admin/order/hotel-requirements/{requirementId}/assignments`(§2.2) | +| 修改房间 | `PUT /v3/admin/order/{orderId}/room/{id}` | 路由已删,不可用;改用 `PUT /v3/admin/order/hotel-requirements/{requirementId}/assignments/{id}/placement`(§2.3b) | + +C 端房型统计(`GET /mp/order/{orderId}/hotels`)的 `roomTypeStats` 仍从 `order_room_assignment` 表经 internal bundle(hl-mp-service `MpServiceDetailAggregationService`)汇聚,对外无变化。internal 端点仅服务间可达,网关对前端直接拒绝「接口不可访问」。 + +--- + +## 五、数据库行为 + +无变更、无 Flyway。表 `order_room_assignment` 保留;存量数据由 internal bundle 继续读取。 + +--- + +## 六、边界行为 + +| 场景 | 行为 | +|---|---| +| 调用 GET /order/{id}/room | 返回 HTTP 200 + code 404 | +| 调用 POST /order/{id}/room | 返回 HTTP 200 + code 404 | +| 调用 PUT /order/{id}/room/{roomAssignmentId} | 返回 HTTP 200 + code 404 | +| 前端经网关调用 `GET /v3/internal/order/orders/{orderId}/assignments` | 网关直接拒绝,返回「接口不可访问」(设计如此,非前端可用入口) | +| 调用 `GET /mp/order/{orderId}/hotels` | 返回 200,roomTypeStats 从 `order_room_assignment` 经 internal bundle 读 | + +## 六.6、修改前后对比 + +| 维度 | 改前 | 改后 | +|------|------|------| +| 房间分配 GET | 路由存在,可查询 | 路由删除,返回 HTTP 200 + code 404 | +| 房间分配 POST | 路由存在,可新增 | 路由删除,返回 HTTP 200 + code 404 | +| 房间分配 PUT | 路由存在,可编辑 | 路由删除,返回 HTTP 200 + code 404 | + +## 六.7、影响评估 + +- **兼容性**:hl-ui 零调用这三个端点,无迁移负担。C 端房型统计无变化(经 internal bundle)。 +- **数据影响**:零;表与 API 读逻辑保留。 +- **其它服务**:hl-mp-service 无直接调用;fleet 无影响。 + +--- + +## 七、不影响范围 + +- House 域房间分配接口(§2.5 GET/POST/DELETE):无改动。 +- Internal bundle (`GET /v3/internal/order/orders/{orderId}/assignments`):保留,继续供 hl-mp-service 读。 +- `order_room_assignment` 表:保留;级联软删仍在各写口生效。 +- `RoomAssignmentVO` 与 `toRoomVO`:保留(bundle 在用)。 +- 网关配置:无改动。 + +--- + +## 八、测试环境已验证 + +部署:测试环境 order-v3 `1f65d7894`(含 e2313790b)。网关 `api.test.1814.love:9443`。 + +| # | 场景 | 实测结果 | +|---|---|---| +| 1 | `GET /v3/admin/order/{id}/room` | HTTP 200 + code 404「接口不存在: …」 | +| 2 | `POST /v3/admin/order/{id}/room` | HTTP 200 + code 404「接口不存在: …」 | +| 3 | `PUT /v3/admin/order/{id}/room/{roomAssignmentId}` | HTTP 200 + code 404「接口不存在: …」 | +| 4 | 阳性对照:同 Controller `GET /v3/admin/order/{id}` | 200(证明是路由被删,不是订单不存在) | +| 5 | 服务内部端口直连读 `order_room_assignment`(internal bundle) | 读数与只读 SQL 一致(经网关 internal 不可达,这是设计) | + +调用来源:测试网关 nginx 日志窗口内旧 `room` 三路径共 71 次,全部是脚本(Python-urllib / curl / 空 UA),零浏览器 UA。 + +--- + +## 十、相关文档 + +- Issue:https://git.1814.love/wx/HL/issues/8387 +- PR:https://git.1814.love/wx/HL/pulls/8398 +- 替代接口:写房间分配走 API-SPEC-HOUSE §2.5 的配房接口(`POST /v3/admin/order/hotel-requirements/{requirementId}/assignments` 等);读订单房间用 `GET /v3/admin/order/orders/{orderId}/rooms` +- API-SPEC:§8「房间分配」与 §11 错误码表已标下线 + +--- + +## 关联 / 联系人 + +### 联系人 + +- **后端负责人**: @wx diff --git a/changelogs-v2/2026-09/26_8388_房务最终确认回执上传加认领校验列表加读门-修改接口-管理后台.md b/changelogs-v2/2026-09/26_8388_房务最终确认回执上传加认领校验列表加读门-修改接口-管理后台.md new file mode 100644 index 00000000..7e0b27d2 --- /dev/null +++ b/changelogs-v2/2026-09/26_8388_房务最终确认回执上传加认领校验列表加读门-修改接口-管理后台.md @@ -0,0 +1,375 @@ +--- +schema: "hl-changelog/v2" +ticket: "8388" +title: "回执接口守卫:上传加认领人校验(808110/808186,超管不豁免)、列表加房务读门(808090)、缺分片改 808184" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "房务回执上传新增认领人校验(超管不豁免)与可选分片处理;列表新增房务读权限门(不受灰度开关控制,常开);错误码 808184 消息补五种原因的具体文案。测试环境网关已实测各分支,含缺分片 808184 新文案。" +updated_at: "2026-09-26" +base: "dev-v3" +--- + +# order-v3:房务最终确认回执接口守卫加固(管理后台) + +**服务**: hl-order-service-v3 +**PR**: #8394(原始改动) + #8399(808184 补 {0} 占位追加) +**Issue**: #8388 + +--- + +## ⚠️ 关键变化 + +1. **上传回执** `POST /admin/house/assignments/requirements/{requirementId}/receipts`: + - 新增认领人校验:需求必须被当前操作人认领,否则返回 **808110**(非本人);**SUPER_ADMIN 在本端点不豁免**,与 #8386 驳回需求口径相反。 + - 需求处于 `PENDING_CLAIM`(未认领)时先被状态闸口拦下,返回 **808186**;错误码 808116 仅作理论防御(正常调用路径不可达)。 + - 缺 file 分片 / 0 字节 file 分片改返 **808184**「回执上传失败:上传文件为空或缺少 file 分片」(改前返 HTTP 500),共 5 种原因文案。 + - 顺序:角色门(808090/808091)→ 需求存在(808100)→ 状态闸口(808186)→ 认领人校验(808110)→ 文件校验(808184)。 + +2. **回执列表** `GET /admin/house/assignments/requirements/{requirementId}/receipts`: + - 新增房务读权限门:非房务角色(定制师、车控等)返回 **808090**。房务全角色(含组长)可见;超管可见。 + - 无认领限制(组长可看全部、他人接手前查历史回执)。 + +--- + +## 一、背景 + +回执两个端点(上传、列表)无任何权限校验,导致: +1. 非认领人房务可为他人上传回执,破坏追责链。 +2. 定制师、车控等非房务角色可读回执列表含 OSS 直链与酒店联系信息。 + +同一配房模块的 10 个写操作都带认领人校验;回执缺失属遗漏。缺 file 分片时返回 500 属参数处理缺陷(hl-common-log 共性坑,本单仅止血)。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 上传最终确认回执 | POST | `/admin/house/assignments/requirements/{requirementId}/receipts` | 修改接口 | 加认领人校验、缺分片改 808184 | +| 2 | 回执列表 | GET | `/admin/house/assignments/requirements/{requirementId}/receipts` | 修改接口 | 加房务读权限门(808090) | + +--- + +## 三、接口详情 + +### 1. 上传最终确认回执 `POST /admin/house/assignments/requirements/{requirementId}/receipts` + +**VO**: `multipart/form-data: file → Result` + +#### 使用场景 + +房务在配房面板「最终确认」区上传回执照片/PDF,证明已与酒店确认房间。仅**该需求的认领人本人**可上传;非房务角色、房务组长、非认领人(含 SUPER_ADMIN)均返回权限错误——超管在本端点**不豁免**认领归属校验,这一点与 #8386(驳回需求超管豁免)口径不同。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementId | Path | Long | 是 | — | 酒店需求 ID | +| file | Form | MultipartFile | 是(业务校验) | ≤50MB,类型自动识别 | 回执文件;缺失返 808184 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| receiptId | String | 回执 ID(雪花 ID,序列化为字符串) | +| requirementId | String | 需求 ID(雪花 ID,序列化为字符串) | +| orderId | String | 订单 ID(雪花 ID,序列化为字符串) | +| fileName | String | 原始文件名 | +| ossUrl | String | OSS 公网 URL,`domain + "/" + ossKey` 拼接,不签名 | +| ossKey | String | OSS Object Key | +| fileSize | Long | 文件大小(字节) | +| fileType | String | 附件类型:`PDF` / `IMAGE` / `OTHER`(按扩展名识别) | +| createTime | LocalDateTime | 上传时间,序列化格式 `yyyy-MM-dd HH:mm:ss` | + +#### 请求示例 + +```http +POST /admin/house/assignments/requirements/2103126783048245250/receipts HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +Content-Type: multipart/form-data; boundary=----Boundary + +------Boundary +Content-Disposition: form-data; name="file"; filename="receipt.jpg" +Content-Type: image/jpeg + +[binary jpeg data] +------Boundary-- +``` + +#### 响应示例 + +**成功**: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "receiptId": "2103801203844689921", + "requirementId": "2103126783048245250", + "orderId": "2100542287908892650", + "fileName": "receipt.jpg", + "ossUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/house/finalize-receipt/...", + "ossKey": "house/finalize-receipt/2026-09/2103126783048245250_receipt.jpg", + "fileSize": 512000, + "fileType": "IMAGE", + "createTime": "2026-09-26 14:30:00" + } +} +``` + +**失败(非认领人)**: + +```json +{ + "code": 808110, + "message": "需求不属于当前用户", + "success": false, + "data": null +} +``` + +**失败(缺分片,实测)**: + +```json +{ + "code": 808184, + "message": "回执上传失败:上传文件为空或缺少 file 分片", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +- 文件为空或缺 `file` 分片:`code=808184`,message「回执上传失败:上传文件为空或缺少 file 分片」(改前返 HTTP 500)。 + +#### 错误响应 + +```json +{ + "code": 808110, + "message": "需求不属于当前用户", + "success": false, + "data": null +} +``` + +**其他错误码**: +- **808090**:未登录或非房务角色,无权操作。 +- **808091**:房务组长为只读监督角色,无权执行该操作。 +- **808100**:需求不存在。 +- **808186**:当前状态不允许上传回执(仅 CLAIMING/PENDING_FINALIZE 可传;PENDING_CLAIM 等其他状态走这个码,不是 808116)。 +- **808116**:订单未抢单, 请先抢单再配房(状态闸口之后理论不可达,作纵深防护保留)。 +- **808184**:回执上传失败:{0},`{0}` 按触发原因取以下固定文案之一——「上传文件为空或缺少 file 分片」/「文件大小超过 50MB」/「OSS 未配置」/「读取上传文件失败」/「文件存储服务暂不可用,请稍后重试」;异常原文只进服务端日志,不回显给客户端。 + +#### 业务边界 + +- **认领校验**:需求 `claimer_id` 必须等于当前操作人;**超管不豁免**,对他人认领的需求上传同样返回 808110。与 #8386(驳回需求,超管豁免)口径不同,前端不要套同一套按钮逻辑。 +- **校验顺序**:写门(808090/808091)→ 需求存在(808100)→ 状态闸口(808186)→ 认领归属(808116/808110)→ 文件校验(808184)。 +- **文件分片**:`file` 分片改为可选(`required=false`),缺失走 Service 内已有的空文件分支报业务码 808184(改前是 HTML form 漏填触发 `MissingServletRequestPartException`,落到兜底 `Exception` 处理器返 HTTP 200+code 500「系统繁忙」并打 ERROR 日志)。 +- **状态限制**:仅 CLAIMING(配房中)与 PENDING_FINALIZE(待确认)两个阶段可上传;PENDING_CLAIM(未抢单)等其他状态返 808186,不是 808116。 +- **无事务**:`upload()` 不加 `@Transactional`;OSS `putObject` 成功后才 `insert`,`insert` 失败可能残留孤儿 OSS 对象(由清理 job 兜底);OSS 失败(`putObject` 抛异常)返 808184 且不落库。 + +--- + +### 2. 回执列表 `GET /admin/house/assignments/requirements/{requirementId}/receipts` + +**VO**: `→ Result>` + +#### 使用场景 + +房务或超管查看某需求的全部回执列表(谁上传的、何时、文件链接)。非房务角色返回 808090;房务全角色可见无限制。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementId | Path | Long | 是 | — | 酒店需求 ID | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| receiptId | String | 回执 ID(雪花 ID,序列化为字符串) | +| requirementId | String | 需求 ID(雪花 ID,序列化为字符串) | +| orderId | String | 订单 ID(雪花 ID,序列化为字符串) | +| fileName | String | 原始文件名 | +| ossUrl | String | OSS 公网 URL,不签名 | +| ossKey | String | OSS Object Key | +| fileSize | Long | 文件大小(字节) | +| fileType | String | 附件类型:`PDF` / `IMAGE` / `OTHER` | +| createTime | LocalDateTime | 上传时间,序列化格式 `yyyy-MM-dd HH:mm:ss` | + +#### 请求示例 + +```http +GET /admin/house/assignments/requirements/2103126783048245250/receipts HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +**成功(房务)**: + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ + { + "receiptId": "2103801203844689921", + "requirementId": "2103126783048245250", + "orderId": "2100542287908892650", + "fileName": "receipt_1.jpg", + "ossUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/house/finalize-receipt/...", + "ossKey": "house/finalize-receipt/2026-09/2103126783048245250_receipt_1.jpg", + "fileSize": 512000, + "fileType": "IMAGE", + "createTime": "2026-09-26 14:30:00" + } + ] +} +``` + +**失败(定制师)**: + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +- 无回执:`code=200`,`data=[]`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +**说明**: +- **808090**(新增):非房务角色返回;该读门调 `HouseReadGuard.assertHouseReadPermission()`,**不受** #8390 引入的灰度开关 `group-batch.acl.enforce.house-read-role` 控制,是常开的角色门。 + +#### 业务边界 + +- **角色放行**:ROOM_MANAGER / house_keeper_lead / SUPER_ADMIN 均可读;零角色(#7609 G-2 保留项)亦放行。 +- **无认领限制**:组长可看全部需求回执(监督权),他人接手后也可查历史。 +- **直链有效期**:测试桶直链公共可读(无签名);生产桶读策略未定,前端不得缓存直链。 + +--- + +## 四、契约约束与正确调用方式 + +| 场景 | 调用方法 | 说明 | +|------|---------|------| +| ✅ 认领人上传 | `POST /admin/house/assignments/requirements/{id}/receipts`,`multipart: file=@...` | 该需求的当前认领人上传,返回 200 | +| ✅ 房务查列表 | `GET /admin/house/assignments/requirements/{id}/receipts` | 返回 200 + 全部回执 | +| ✅ 组长查列表 | 同上(house_keeper_lead 身份)| 无限制,200 返回 | +| ❌ 非认领人上传(含 SUPER_ADMIN) | — | 返回 808110,不落库;超管在本端点**不豁免**认领归属校验 | +| ❌ 未认领需求(PENDING_CLAIM)上传 | — | 返回 808186(先过状态闸口) | +| ❌ 定制师查列表 | — | 返回 808090 | +| ❌ 缺 file 分片 | 空 multipart 或无 file 字段 | 返回 808184 | + +--- + +## 五、数据库行为 + +无变更、无 Flyway。`house_finalize_receipt` 表与逻辑保留;认领人校验在内存判断,无新增存储。 + +--- + +## 六、边界行为 + +| 场景 | 行为 | +|---|---| +| 认领人上传 | 返回 200,回执入库入 OSS | +| 非认领人上传(含 SUPER_ADMIN) | 返回 808110,无新行 | +| 转单后原人上传 | 返回 808110(新人成认领人,原人失去权限) | +| PENDING_CLAIM 需求上传 | 返回 808186(状态闸口先于认领校验) | +| 组长列表查询 | 返回 200,全列表 | +| 定制师列表查询 | 返回 808090,无数据 | +| 缺 file 分片 / 0 字节 file 分片 | 返回 808184(HTTP 200、code 业务码,5 种原因文案) | + +## 六.6、修改前后对比 + +| 维度 | 改前 | 改后 | +|------|------|------| +| 非认领人上传(含超管) | 返回 200,入库 | 返回 808110 | +| 缺 file 分片 | HTTP 500、ERROR 日志 | HTTP 200、code 808184,仅 WARN 日志 | +| 定制师查列表 | 返回 200 + 列表 | 返回 808090 | + +## 六.7、影响评估 + +- **兼容性**:hl-ui 回执上传与列表操作收新错误码(808090/808091/808110/808186/808184);需处理新文案提示。 +- **前端要动的**: + - 上传失败按错误码区分:「权限不足」(808090/808091)、「非本人(含超管)」(808110)、「需求未认领」(808186)、「文件」(808184,5 种原因文案,需在 `message` 里判断具体原因而非只判 code)。 + - 定制师等非房务角色不再能查回执列表,改改导航或隐藏入口。 +- **数据影响**:无;仅加校验,不改历史回执。 +- **其它服务**:hl-mp-service、fleet 无直接调用。 + +--- + +## 七、不影响范围 + +- 房务订单详情页回执展示逻辑(虽然定制师不再能查全列表,已登录进详情页的现有回执链接仍有效)。 +- 回执删除接口(无,但实体带 `@TableLogic` 支持软删)。 +- 其他配房写操作(转单、释放、分房等),无改动。 + +--- + +## 八、测试环境已验证 + +部署:测试环境 order-v3 `1f65d7894`(含 e2313790b)。网关 `api.test.1814.love:9443`。 + +| # | 场景 | 实测结果 | +|---|---|---| +| 1 | 认领人上传 | code 200,新行 uploader_id = 认领人;已软删清理 | +| 2 | 非认领人上传 | code 808110,无新行 | +| 3 | PENDING_CLAIM 需求上传 | code 808186 | +| 4 | SUPER_ADMIN 对他人认领的需求上传 | code 808110(不豁免) | +| 5 | 缺 file 分片(multipart 只带无关字段)×2 | HTTP 200 + `{"code":808184,"message":"回执上传失败:上传文件为空或缺少 file 分片"}` | +| 6 | 0 字节 file 分片 | 同 #5,同码同文案 | +| 7 | 房务(认领人本人)查列表 | code 200,全部回执 | +| 8 | 定制师 / VEHICLE_MANAGER 查列表 | code 808090,无数据 | +| 9 | house_keeper_lead / 非认领 ROOM_MANAGER / SUPER_ADMIN 查列表 | code 200,全部回执 | + +#5、#6 场景 order-v3 日志对应 3 条 WARN `Business error [order.808184]`,无 `Unexpected error` ERROR 行。 + +--- + +## 十、相关文档 + +- Issue:https://git.1814.love/wx/HL/issues/8388 +- PR:https://git.1814.love/wx/HL/pulls/8394(原始改动)、https://git.1814.love/wx/HL/pulls/8399(808184 补 `{0}` 占位追加) +- 相关单号:#8386(认领人校验,超管在该单**有**豁免,口径与本单相反)、#8390(读权限门同配方) + +--- + +## 关联 / 联系人 + +### 联系人 + +- **后端负责人**: @wx diff --git a/changelogs-v2/2026-09/26_8389_下线房务家庭维度房间分配写口-删除接口-管理后台.md b/changelogs-v2/2026-09/26_8389_下线房务家庭维度房间分配写口-删除接口-管理后台.md new file mode 100644 index 00000000..1b68e319 --- /dev/null +++ b/changelogs-v2/2026-09/26_8389_下线房务家庭维度房间分配写口-删除接口-管理后台.md @@ -0,0 +1,192 @@ +--- +schema: "hl-changelog/v2" +ticket: "8389" +title: "房务配房:下线 POST /v3/admin/order/assignments/{id}/rooms 家庭维度写接口" +consumer: "admin" +author: "wx(GIT)" +change_type: "删除接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "house 域家庭维度房间分配写口(POST)已下线,GET 端点由 #8390 补读门。hl-ui 零调用。" +updated_at: "2026-09-26" +base: "dev-v3" +--- + +# order-v3:下线家庭维度房间分配写接口(管理后台) + +**服务**: hl-order-service-v3 +**PR**: #8396 +**Issue**: #8389 + +--- + +## ⚠️ 关键变化 + +`POST /v3/admin/order/assignments/{assignmentId}/rooms`(家庭维度房间分配)已删除,连同 `RoomAssignReqVO`/`RoomAssignRespVO` 与错误码 **808132**(入住人数必须 > 0)一并删除,码号不复用。 + +调用返回 HTTP 200 + `code: 404`(不是 HTTP 404)。GET 端点 `/v3/admin/order/orders/{orderId}/rooms` 由 #8390 补读权限门(ROOM_MANAGER/house_keeper_lead/SUPER_ADMIN 可读;非房务返 808090)。 + +--- + +## 一、背景 + +该端口对 `rooms[].roomGroupNo` 与 `rooms[].travelerCount` 无出行人校验,会写入脏数据(`F99` 分组、`travelerCount=99` 等)。无调用方,计划下线避免继续钉住问题。 + +表 `house_room_assignment` 与 GET 端点保留(级联软删仍有效);仅删写接口,该表此后无写入方,GET 端点出参里对新数据 `roomAssignments` 恒为空数组。`house_room_assignment` 只被 `HouseAssignmentService` 读;C 端聚合读的是 #8387 相关的 `order_room_assignment` 表(不同表),两者不要混淆。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 房间分配写 | POST | `/v3/admin/order/assignments/{assignmentId}/rooms` | 删除 | 路由已删,返回 404 | + +GET 端点 `/v3/admin/order/orders/{orderId}/rooms` 由 #8390 补读门(不在本单)。 + +--- + +## 三、接口详情 + +### 1. 房间分配写 `POST /v3/admin/order/assignments/{assignmentId}/rooms` + +**VO**: `已删除` + +**状态**:已删除。路由不存在,返回 404。 + +#### 使用场景 + +该接口已删除。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| — | — | — | — | — | 已删除 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| — | — | 已删除 | + +#### 请求示例 + +```http +POST /v3/admin/order/assignments/2103126783048245250/rooms HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +Content-Type: application/json + +{} +``` + +#### 响应示例 + +HTTP 状态码 **200**(非 404),业务错误码 404: + +```json +{ + "code": 404, + "message": "接口不存在: POST /v3/admin/order/assignments/2103126783048245250/rooms", + "success": false, + "data": null +} +``` + +#### 空数据 / 降级响应 + +不适用。前端应只判 `code`,不依赖 HTTP 状态码。 + +#### 错误响应 + +```json +{ + "code": 404, + "message": "接口不存在: POST /v3/admin/order/assignments/2103126783048245250/rooms", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 接口已删除,路由不存在;调用返回 HTTP 200 + `code: 404`。 +- 错误码 808132(入住人数必须 > 0)随本单一并删除,码号不复用。 +- hl-ui 零调用;测试网关 nginx 日志窗口内该路径 53 次调用全部是脚本(python-requests / Python-urllib),零浏览器 UA。 + +--- + +## 四、契约约束与正确调用方式 + +该接口已删除,不可调用。 + +--- + +## 五、数据库行为 + +无变更、无 Flyway。表 `house_room_assignment` 保留;级联软删仍有效。 + +--- + +## 六、边界行为 + +| 场景 | 行为 | +|---|---| +| 调用 POST /assignments/{id}/rooms | 返回 HTTP 200 + code 404 | +| 调用 GET /orders/{id}/rooms | 仍在(200);#8390 补读门后,非房务返 808090 | +| `house_room_assignment` 表 | 保留,此后无写入方,级联软删仍对存量数据有效 | + +## 六.6、修改前后对比 + +| 维度 | 改前 | 改后 | +|------|------|------| +| POST 家庭房间 | 路由存在,可写 | 路由删除,返回 404 | + +## 六.7、影响评估 + +- **兼容性**:hl-ui 零调用,无前端迁移。 +- **数据影响**:零;表与 GET 保留。 +- **其它服务**:无。 + +--- + +## 七、不影响范围 + +- 房间分配 GET:保留(#8390 补读门)。 +- 其他配房写(delete/updatePlacement 等):无改动。 +- 级联软删:保留。 + +--- + +## 八、测试环境已验证 + +部署:测试环境 order-v3 `1f65d7894`(含 e2313790b)。网关 `api.test.1814.love:9443`。 + +| # | 场景 | 实测结果 | +|---|---|---| +| 1 | `POST /v3/admin/order/assignments/{assignmentId}/rooms` | HTTP 200 + code 404「接口不存在: …」 | +| 2 | `GET /v3/admin/order/orders/{orderId}/rooms` | 200(仍在) | + +调用来源:测试网关 nginx 日志窗口内该路径 53 次,全部是脚本(python-requests / Python-urllib),零浏览器 UA。 + +--- + +## 十、相关文档 + +- Issue:https://git.1814.love/wx/HL/issues/8389 +- PR:https://git.1814.love/wx/HL/pulls/8396 +- 关联:#8390(补读权限门) + +--- + +## 关联 / 联系人 + +### 联系人 + +- **后端负责人**: @wx diff --git a/changelogs-v2/2026-09/26_8390_房务读侧权限门补齐16个端点与ADMIN菜单撤授-修改接口-管理后台.md b/changelogs-v2/2026-09/26_8390_房务读侧权限门补齐16个端点与ADMIN菜单撤授-修改接口-管理后台.md new file mode 100644 index 00000000..358491fe --- /dev/null +++ b/changelogs-v2/2026-09/26_8390_房务读侧权限门补齐16个端点与ADMIN菜单撤授-修改接口-管理后台.md @@ -0,0 +1,1360 @@ +--- +schema: "hl-changelog/v2" +ticket: "8390" +title: "房务数据权限:16 个读端点补房务角色门(808090),ADMIN 角色菜单撤授权" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "房务日历/详情/对账/酒店视图/转单候选/待办/旧抢单池等 16 个读端点补房务角色门:非房务角色返回 808090;开关 group-batch.acl.enforce.house-read-role 默认 true 可灰度。ADMIN 角色房务菜单由 Flyway 撤授权(PR-2 user-service)。测试服实测验证。" +updated_at: "2026-09-26" +base: "dev-v3" +--- + +# order-v3 + hl-user-service:房务读侧权限门补齐(管理后台) + +**服务**: hl-order-service-v3(PR-1,读门)/ hl-user-service(PR-2,菜单撤授权) +**PR**: PR-1 #8393(order-v3)/ PR-2 #8392(hl-user-service) +**Issue**: #8390 + +--- + +## ⚠️ 关键变化 + +### 1. 16 个房务旧只读端点补房务角色门(order-v3 PR-1) + +日历、房务订单详情、酒店视图(列表/详情/核房历史/操作日志)、转单候选、待办、月度对账(汇总/明细/导出)、旧抢单池、订单房间共 16 个端点,新增房务读权限校验(Service 层调 `HouseReadGuard.assertHouseReadPermissionOnLegacyEndpoint()`,17 个调用点对应这 16 个端点;不存在名为 `@HouseReadGuarded` 的注解): + +- **放行角色**:ROOM_MANAGER / house_keeper_lead / SUPER_ADMIN / 零角色 token(#7609 G-2 保留的 fail-open 行为,本单不收紧)。 +- **拦截角色**:定制师(CUSTOMIZER)、车控(VEHICLE_MANAGER)、物料(MATERIAL_ADMIN)、运营(OPERATOR)、客服(CUSTOMER_SERVICE)、团期管理员(GROUP_BATCH_MANAGER)、**ADMIN**、FINANCE 等一切名单外角色 → 返回 **808090**「未登录或非房务角色,无权操作」。 +- **开关**:`group-batch.acl.enforce.house-read-role`(默认 true,`@RefreshScope` 热刷新)。开关关闭时守卫照样被调用,只是不再抛出、改打 WARN 日志 `HOUSE_READ_ROLE_MISSING`(`enforced=false`)旁路观察,对前端可见的返回结果与改前一致;开关开(默认)时拒绝先打同一条 WARN(`enforced=true`)再抛 808090。 + +### 2. ADMIN 角色房务菜单撤授权(user-service PR-2) + +用户侧 `sys_role_menu` 在 ADMIN(管理员)身上撤掉房务管家菜单 5 行(目录 1 + 页面 4),不改 `sys_menu`;超管(SUPER_ADMIN)菜单不动。 + +--- + +## 一、背景 + +房务读侧权限门此前并不完整:新端点(#8375 起)已挂读门,但日历 / 订单详情 / 酒店视图 / 转单候选 / 待办 / 月度对账 / 旧抢单池 / 订单房间等 16 个早期端点缺权限守卫,定制师、车控等非房务角色可读房务订单金额、客人信息、酒店协议价等敏感数据;测试服 ADMIN 账号还被授了房务菜单。 + +测试环境已实证 7 个非房务角色对这 16 个端点全部返回 200(改前)。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 房务日历 | GET | `/admin/house/calendar` | 修改接口 | 新增读门(808090)| +| 2 | 日历单天下钻 | GET | `/admin/house/calendar/day` | 修改接口 | 新增读门(808090)| +| 3 | 房务订单详情 | GET | `/admin/house/orders/{orderId}` | 修改接口 | 新增读门(808090)| +| 4 | 房务酒店列表 | GET | `/v3/admin/house/hotels` | 修改接口 | 新增读门(808090)| +| 5 | 房务酒店详情 | GET | `/v3/admin/house/hotels/{hotelId}` | 修改接口 | 新增读门(808090)| +| 6 | 酒店核房历史 | GET | `/v3/admin/house/hotels/{hotelId}/check-log` | 修改接口 | 新增读门(808090)| +| 7 | 酒店维度操作日志 | GET | `/v3/admin/house/hotels/{hotelId}/operation-log` | 修改接口 | 新增读门(808090)| +| 8 | 订单维度操作日志 | GET | `/v3/admin/house/orders/{orderId}/operation-log` | 修改接口 | 新增读门(808090)| +| 9 | 转单候选员工 | GET | `/v3/admin/house/staff` | 修改接口 | 新增读门(808090)| +| 10 | 房务待办列表 | GET | `/v3/admin/order/todos` | 修改接口 | 新增读门(808090),scope=all/others 非组长/超管另返 582204 | +| 11 | 月度对账汇总 | GET | `/v3/admin/house/reconciliation/monthly` | 修改接口 | 新增读门(808090)| +| 12 | 月度对账订单明细 | GET | `/v3/admin/house/reconciliation/monthly/hotel-orders` | 修改接口 | 新增读门(808090)| +| 13 | 月度对账导出 | GET | `/v3/admin/house/reconciliation/monthly/export` | 修改接口 | 新增读门(808090),format 非法先返 808171 | +| 14 | 抢单池-房型需求列表(@Deprecated) | GET | `/v3/admin/order/grab-pool/hotel-requirements` | 修改接口 | 新增读门(808090)| +| 15 | 抢单池-我的接单(@Deprecated) | GET | `/v3/admin/order/grab-pool/my-claims/hotel` | 修改接口 | 新增读门(808090)| +| 16 | 订单房间分配查询(按家庭分组) | GET | `/v3/admin/order/orders/{orderId}/rooms` | 修改接口 | 新增读门(808090)| + +不在本单之列:旧抢单池监督视图 `GET /v3/admin/order/grab-pool/all-claims/hotel`(只有既有的 808092 门,不受本单读门覆盖,见七、不影响范围)。 + +--- + +## 三、接口详情 + +本单只加读门,16 个端点各自的入参/出参字段**没有变化**(本节按 origin/dev-v3 源码逐一列出改前已有的字段;下方“错误响应”一律新增了 808090 一条)。除标注外,读门校验顺序统一是**先过角色门(808090),角色门通过后再走该端点原有的业务校验**。 + +### 1. 房务日历 `GET /admin/house/calendar` + +**VO**: `CalendarQueryReqVO → Result` + +#### 使用场景 + +房务侧日历首页,按月展示每天的团数与状态点分布(进行中/待配房/询房中/异常/已完成)。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| month | Query | String | 是 | 正则 `^\d{4}-(0[1-9]\|1[0-2])$` | 月份,如 `2026-04` | +| scope | Query | String | 否 | — | `mine`(默认,我的)/ `all`(团队全部) | +| status | Query | String | 否 | 逗号分隔多选 | `inProgress`/`inquiry`/`pending`/`exception`,不填=全部 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| month | String | 月份 | +| summary | Object | 月度摘要:totalNights/inProgress/pending/inquiry/exception/confirmed | +| days | Array | 完整周排版的天列表(28~42 项),每项含 date/isCurrentMonth/isToday/tourCount/statusDots[] | + +#### 请求示例 + +```http +GET /admin/house/calendar?month=2026-04&scope=mine HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "month": "2026-04", + "summary": { "totalNights": 8, "inProgress": 1, "pending": 0, "inquiry": 3, "exception": 1, "confirmed": 2 }, + "days": [ { "date": "2026-04-01", "isCurrentMonth": true, "isToday": false, "tourCount": 1, "statusDots": [ { "status": "exception", "label": "异常", "count": 1, "color": "red" } ] } ] + } +} +``` + +#### 空数据 / 降级响应 + +当月无团时 `days[].tourCount` 为 0、`statusDots` 为空数组;`summary` 各字段为 0,不降级、不报错。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色(含 ADMIN)一律返 808090;ROOM_MANAGER / house_keeper_lead / SUPER_ADMIN / 零角色 token 放行。 +- `month` 格式错时由 `@Valid` 校验拦截,HTTP 200 + code 400,在角色门**之后**触发(先过 808090)。 + +--- + +### 2. 日历单天下钻 `GET /admin/house/calendar/day` + +**VO**: `CalendarDayQueryReqVO → Result>` + +#### 使用场景 + +点击日历某天单元格,下钻查看该天按「团」折叠的明细列表(团期单按 productBatchId 折叠,散客单一单一项)。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| date | Query | LocalDate(`yyyy-MM-dd`) | 是 | ISO DATE | 下钻日期 | +| scope | Query | String | 否 | — | `mine`(默认)/ `all` | +| status | Query | String | 否 | 逗号分隔多选 | 同日历首页状态桶 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| teamKey | String | 团分组键(`B:团期ID` 或 `O:订单ID`) | +| isGroup | Boolean | 是否团期单 | +| orderId / orderNo | String | 代表订单 ID / 订单号 | +| productName / productType | String | 产品名 / 类型 | +| customerName | String | 主联系人 | +| adultCount / roomCount | Integer | 当日成人数 / 当晚房间数 | +| hotelName / roomCategory / roomCategoryLabel | String | 酒店名 / 房型 code / 房型中文(未配房为空) | +| stayDate | LocalDate | 入住日 | +| status / statusLabel | String | 状态桶 / 中文标签 | +| claimerId / claimerName | String | 房务 ID / 姓名(`scope=all` 才返) | + +#### 请求示例 + +```http +GET /admin/house/calendar/day?date=2026-04-22&scope=mine HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ + { "teamKey": "B:1928374", "isGroup": true, "orderId": "5566778", "orderNo": "HL202604220001", + "productName": "华东双飞5日游", "productType": "GROUP", "customerName": "张三", + "adultCount": 32, "roomCount": 16, "hotelName": "杭州西湖国宾馆", "roomCategory": "STANDARD", + "roomCategoryLabel": "标准间", "stayDate": "2026-04-22", "status": "inProgress", "statusLabel": "配房中" } + ] +} +``` + +#### 空数据 / 降级响应 + +当天无团时返回空数组 `[]`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090;`date` 缺失由 `@NotNull` 校验拦截(在角色门之后)。 +- `claimerId`/`claimerName` 仅 `scope=all` 时返回,`scope=mine` 恒为空。 + +--- + +### 3. 房务订单详情 `GET /admin/house/orders/{orderId}` + +**VO**: `HouseOrderDetailRespVO`(无独立入参 VO,Path + 单个 Query 参数) + +#### 使用场景 + +房务侧订单详情聚合页:订单基本信息、跟单信息、4 步状态进度条、定制师需求区、配房行程、操作日志计数等一次性拉齐。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| orderId | Path | Long | 是 | — | 订单 ID | +| requirementId | Query | Long | 否 | — | 指定查看某个历史需求版本,不传取当前生效版本 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| viewedRequirementId | String | 本次展示的房型需求 ID | +| historicalRequirement / voided | Boolean | 是否历史版本 / 是否已作废 | +| voidReason / voidedAt | String / LocalDateTime | 作废原因/时间(仅 voided=true) | +| order | Object | 订单基本信息(详见 API-SPEC-HOUSE §2.0) | +| claim | Object | 房务跟单信息 | +| progress | Object | 4 步状态进度条 | +| requirement | Object | 定制师房型需求区 | +| tabCounts | Object | 4 个 Tab 徽标计数 | +| itinerary | Array | 配房行程 Tab(住宿视角,每天一项) | +| tripItinerary / travelers / transport | Array/Array/Object | 订单详情 Tab 三块数据;取数异常时各自降级为空/null | +| orderDetailReady | Boolean | 订单详情 Tab 三块是否全部取数成功 | +| pendingRescheduleAssignments | Array | 改期后待人工删除的旧日期配房,非空时禁止最终确认 | +| actions / permissions | Object | 底部按钮可用性 / 房务侧权限标志 | + +#### 请求示例 + +```http +GET /admin/house/orders/5566778?requirementId=70123 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "viewedRequirementId": "70123", + "historicalRequirement": false, + "voided": false, + "orderDetailReady": true + } +} +``` + +#### 空数据 / 降级响应 + +`tripItinerary`/`travelers` 取数异常时降级为空列表、`transport` 降级为 null,同时 `orderDetailReady=false`,前端据此提示「订单信息暂不可用,请稍后刷新」,接口本身不因此失败。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 +- `/admin/house/orders/{orderId}/requirement-history`(历史需求版本列表)是定制师订单详情的有意跨角色端点,不在本单 16 个之列,勿混淆。 + +--- + +### 4. 房务酒店列表 `GET /v3/admin/house/hotels` + +**VO**: `HouseHotelListReqVO → Result>` + +#### 使用场景 + +房务侧酒店列表:resource 端酒店主数据代理 + HOUSE 核房叠加 + 近 30 天统计,支持城市/等级/合作状态/结算方式/住宿形态/关键词等过滤。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| city | Query | String | 否 | 逗号分隔多选 | 城市代码,不传=全部 | +| level | Query | String | 否 | 逗号分隔多选 | 酒店等级 | +| status | Query | String | 否 | 字典 hotel_status | 合作状态 active/pause/end | +| settleType | Query | String | 否 | 字典 resource_settle_type | 结算方式 cash/sign/company | +| form | Query | String | 否 | 字典 hotel_form | 住宿形态 hotel/bnb/yurt/logcabin | +| keyword | Query | String | 否 | ≤32 字 | 酒店名/联系人/微信号模糊搜 | +| lastCheckedBefore | Query | LocalDate | 否 | `yyyy-MM-dd` | 仅看最近核房早于此日期的 | +| sortBy | Query | String | 否 | — | 默认 `city,asc,level,asc`,可切 `lastCheckTime,asc` | +| page | Query | Long | 否 | ≥1,默认 1 | 页码 | +| pageSize | Query | Long | 否 | ≥1 且 ≤200,默认 20 | 每页条数 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| hotelId | String | 酒店 ID | +| name / city / cityName / level / form | String | 酒店名 / 城市代码 / 城市中文 / 等级 / 住宿形态 | +| status / settleType | String | 合作状态 / 结算类型 | +| protoPrice | String | 协议价(元/间·晚) | +| contactPerson / wechat | String | 联系人 / 微信 | +| roomTypes | Integer | 房型数 | +| todayAvailable | Integer | 今日可用房数(无核房快照为 0) | +| lastCheckTime | LocalDateTime | 最近核房时间,没核过为 null | +| availFreshness | String | 快照新鲜度 fresh/stale/never_checked | +| stat30d | Object | 近 30 天统计:assignmentCount(配房单数)/ todoCount(待办数) | + +#### 请求示例 + +```http +GET /v3/admin/house/hotels?city=hailar&status=active&page=1&pageSize=20 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "list": [ { "hotelId": "1001", "name": "伯爵大酒店", "city": "hailar", "cityName": "海拉尔", + "level": "高档型", "status": "active", "settleType": "sign", "protoPrice": "480.00", + "todayAvailable": 8, "availFreshness": "fresh", "stat30d": { "assignmentCount": 45, "todoCount": 1 } } ], + "total": 1 + } +} +``` + +#### 空数据 / 降级响应 + +无匹配酒店返回 `list: []`、`total: 0`;resource 端 Feign 不可用时按 §四契约约束降级。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 + +--- + +### 5. 房务酒店详情 `GET /v3/admin/house/hotels/{hotelId}` + +**VO**: `HouseHotelDetailVO`(无独立入参 VO,只有 Path 参数) + +#### 使用场景 + +酒店详情弹窗 4 个 Tab 一次性聚合:基础信息、房型列表、30 天价格日历预览、最近 10 条核房记录。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| hotelId | Path | Long | 是 | — | 酒店 ID | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| basic | Object | Tab1 基础信息(resource 全字段,详见 `HouseHotelBasicFeignVO`) | +| roomTypes | Array | Tab2 房型列表(含设施,详见 `HouseRoomTypeFeignVO`) | +| priceCalendar30d | Array | Tab3 未来 30 天价格日历预览(详见 `HousePriceCalendarItemFeignVO`) | +| recentCheckLog | Array | Tab4 最近 10 条核房记录(字段同 §6 核房历史行) | + +#### 请求示例 + +```http +GET /v3/admin/house/hotels/1001 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "basic": { }, "roomTypes": [ ], "priceCalendar30d": [ ], "recentCheckLog": [ ] } +} +``` + +#### 空数据 / 降级响应 + +basic 强依赖 resource,酒店不存在抛 808500;roomTypes/priceCalendar30d/recentCheckLog 为软依赖,取数失败各自降级为空数组,不影响整体返回。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090(先于 808500 判定)。 + +--- + +### 6. 酒店核房历史 `GET /v3/admin/house/hotels/{hotelId}/check-log` + +**VO**: `HouseInventoryCheckHistoryReqVO → Result>` + +#### 使用场景 + +酒店详情「完整核房历史」Tab:分页查看某酒店全部核房记录,支持方式/操作人/价格变动/时间区间过滤。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| hotelId | Path | Long | 是 | — | 酒店 ID | +| startDate / endDate | Query | LocalDate | 否 | `yyyy-MM-dd` | 核房日期区间 | +| method | Query | String | 否 | — | 核房方式 phone/wechat/visit | +| operatorId | Query | Long | 否 | — | 核房操作人 ID | +| priceChangeOnly | Query | Boolean | 否 | — | 仅看价格变动了的核房 | +| page / pageSize | Query | Long | 否 | ≥1;pageSize ≤200,默认 20 | 分页 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| checkLogId / hotelId | String | 核房记录 ID / 酒店 ID | +| checkDate | LocalDate | 核房日期 | +| method / contactName | String | 核房方式 / 对接联系人 | +| totalAvailable | Integer | 总可用房数 | +| byRoomType | Object | 按房型分布 `{roomTypeId: {label, count}}` | +| note | String | 备注 | +| priceChange / priceChangeNote | Boolean / String | 价格是否变动 / 说明 | +| operatorId | String | 核房人 ID | +| createTime | LocalDateTime | 核房时间 | + +#### 请求示例 + +```http +GET /v3/admin/house/hotels/1001/check-log?page=1&pageSize=20 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "list": [ { "checkLogId": "70300", "hotelId": "1001", "checkDate": "2026-05-15", + "method": "phone", "contactName": "张经理", "totalAvailable": 8, "priceChange": false } ], "total": 1 } +} +``` + +#### 空数据 / 降级响应 + +无核房记录返回 `list: []`、`total: 0`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 + +--- + +### 7. 酒店维度操作日志 `GET /v3/admin/house/hotels/{hotelId}/operation-log` + +**VO**: `HouseOperationLogReqVO → Result>` + +#### 使用场景 + +按酒店维度查操作日志(P2 候用端点,原型 v1.1 暂无 UI 触发点,接口设计已完整保留)。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| hotelId | Path | Long | 是 | — | 酒店 ID | +| opType | Query | String | 否 | 逗号分隔多选 | 操作类型(见 HouseOpType 常量类) | +| source | Query | String | 否 | — | 数据来源 HOUSE/RESOURCE_ADMIN | +| operatorId | Query | Long | 否 | — | 操作人房务 ID | +| keyword | Query | String | 否 | ≤32 字 | summary/operatorName 模糊 | +| startDate / endDate | Query | LocalDateTime | 否 | ISO 8601 | 时间区间 | +| sortBy | Query | String | 否 | — | 默认 `time,desc` | +| page / pageSize | Query | Long | 否 | ≥1;pageSize ≤200,默认 20 | 分页 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | String | 日志 ID | +| time | LocalDateTime | 操作时间 | +| opType / opTypeLabel | String | 操作类型(英文/中文) | +| summary | String | 操作摘要(后端拼好) | +| operator | Object | 操作人 { userId, name } | +| source | String | 数据来源 | +| detail | Object | 关联实体 ID 集(orderId/requirementId/assignmentId/hotelId/inquiryId/reason/toUserName,按 opType 不同结构) | + +#### 请求示例 + +```http +GET /v3/admin/house/hotels/1001/operation-log?page=1&pageSize=20 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "list": [ { "id": "9001", "time": "2026-05-15T09:30:00", "opType": "CLAIM", + "opTypeLabel": "抢单", "summary": "李房务 抢单", "operator": { "userId": "1001", "name": "李房务" } } ], "total": 1 } +} +``` + +#### 空数据 / 降级响应 + +无日志返回 `list: []`、`total: 0`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 + +--- + +### 8. 订单维度操作日志 `GET /v3/admin/house/orders/{orderId}/operation-log` + +**VO**: `HouseOperationLogReqVO → Result` + +#### 使用场景 + +订单详情弹窗「操作日志」Tab:按订单查全部操作日志,包含跨服务动作(resource 改价、主 API 最终确认通过 MQ 写入本表),默认每页 50 条。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| orderId | Path | Long | 是 | — | 订单 ID | +| opType/source/operatorId/keyword/startDate/endDate/sortBy/page/pageSize | Query | — | 否 | 同上(§7) | pageSize 默认值为 50(订单维度) | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| records | Array | 日志列表(字段同 §7 出参行,按时间倒序) | +| total / page / pageSize | Integer | 总条数 / 当前页 / 每页条数 | +| summary | Object | 操作汇总:totalCount(总操作数)/ byOpType(按类型聚合计数) | + +#### 请求示例 + +```http +GET /v3/admin/house/orders/5566778/operation-log HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "records": [ ], "total": 2, "page": 1, "pageSize": 50, + "summary": { "totalCount": 2, "byOpType": { "CLAIM": 1, "ASSIGNMENT_CREATE": 1 } } } +} +``` + +#### 空数据 / 降级响应 + +无日志返回 `records: []`、`total: 0`,`summary.byOpType` 为空对象。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 + +--- + +### 9. 转单候选员工 `GET /v3/admin/house/staff` + +**VO**: `HouseTransferCandidateReqVO → Result` + +#### 使用场景 + +转单弹窗展示可选候选房务员工列表,按当前在跟订单数升序排列,最多 50 条。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| online | Query | Boolean | 否 | 默认 false | true=只看在线 | +| excludeMe | Query | Boolean | 否 | 默认 true | 是否过滤掉自己 | +| maxActive | Query | Integer | 否 | ≥0 | 过滤在跟订单数 ≤ N 的,不填不过滤 | +| keyword | Query | String | 否 | ≤32 字 | 按姓名/工号模糊 | +| fromUserId | Query | Long | 否 | 仅超管/集成可显式指定 | 默认取当前登录人 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| list | Array | 候选员工列表:userId/name/avatar/avatarColor/activeCount/online/isUpperLimitReached/selectable | +| total | Long | 总条数(无分页,等于 list 长度) | + +#### 请求示例 + +```http +GET /v3/admin/house/staff?online=false&excludeMe=true HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "list": [ { "userId": "1002", "name": "王房务", "avatar": "王", "avatarColor": "#5B8FF9", + "activeCount": 3, "online": true, "isUpperLimitReached": false, "selectable": true } ], "total": 1 } +} +``` + +#### 空数据 / 降级响应 + +无候选员工返回 `list: []`、`total: 0`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 +- `activeCount ≥ 30` 时 `isUpperLimitReached=true`,普通房务对应行 `selectable=false`;超管恒 `selectable=true`。 + +--- + +### 10. 房务待办列表 `GET /v3/admin/order/todos` + +**VO**: `HouseTodoPageReqVO → Result` + +#### 使用场景 + +房务待办中心:13 个筛选项 + 排序 + 9 类 stats 统计,`scope` 决定看「我的」「同事的」还是「全部」。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| scope | Query | String | 否 | — | `mine`(我的+未归属,默认)/ `others`(同事在跟)/ `all`(全部,仅房务组长/超管) | +| todoType | Query | String | 否 | 逗号分隔多选 | 待办类型,空=全部 | +| status | Query | String | 否 | — | `OPEN`/`RESOLVED`,空=全部 | +| urgency | Query | String | 否 | — | `danger`/`warn`/`normal`(运行时推导值) | +| keyword | Query | String | 否 | ≤32 字 | 匹配 title/reason/团号 | +| orderId / ownerUserId / hotelId | Query | Long | 否 | — | 订单 / 归属房务 / 酒店过滤 | +| createTimeFrom / createTimeTo | Query | LocalDateTime | 否 | ISO 8601 | 创建时间区间 | +| overdueMinutes | Query | Integer | 否 | — | 仅看超时 N 分钟以上 | +| sortBy | Query | String | 否 | — | 默认 `urgency,desc,createTime,desc` | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| list | Array | 待办列表(详见 `HouseTodoItemVO`),已按默认排序 | +| total | Long | 总条数(过滤后,分页前) | +| stats | Object | 按 todoType 分类统计(9 类,含查询时派生类型) | + +#### 请求示例 + +```http +GET /v3/admin/order/todos?scope=mine&status=OPEN HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "list": [ ], "total": 0, "stats": { } } +} +``` + +#### 空数据 / 降级响应 + +无待办返回 `list: []`、`total: 0`,`stats` 各分类计数为 0。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +普通房务传 `scope=all` 或 `scope=others`(本单读门通过后): + +```json +{ + "code": 582204, + "message": "无权查看全部/他人房务待办(仅房务组长或超管可查看)", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律先返 808090(读门在 `assertScopeAllowed` 之前调用)。 +- 通过读门后,普通房务传 `scope=all`/`others` 返 582204;只有房务组长/超管可用这两档。 + +--- + +### 11. 月度对账汇总 `GET /v3/admin/house/reconciliation/monthly` + +**VO**: `MonthlyReconciliationReqVO → Result` + +#### 使用场景 + +月度对账首页:配房完毕订单按酒店汇总的概览 + 明细列表,含核单实际花销只读展示(无录入写口)。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| month | Query | String | 是 | 正则 `^\d{4}-(0[1-9]\|1[0-2])$` | 对账月份,如 `2026-05` | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| month | String | 对账月份 | +| overview | Object | 概览:hotelCount/orderCount/roomNights/totalAmount/paidAmount(=totalAmount)/unpaidAmount(恒0)/totalActualExpense(全部未录入为 null)/monthOverMonth | +| hotels | Array | 单酒店账单行(字段见 §12 出参) | + +#### 请求示例 + +```http +GET /v3/admin/house/reconciliation/monthly?month=2026-05 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "month": "2026-05", "overview": { "hotelCount": 5, "orderCount": 30, "roomNights": 96, + "totalAmount": "86400.00", "paidAmount": "86400.00", "unpaidAmount": "0.00", "totalActualExpense": "81200.00" }, + "hotels": [ ] } +} +``` + +#### 空数据 / 降级响应 + +空月返回 `hotels: []` + 概览全 0(`totalActualExpense` 为 null,不是 0)。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 +- `month` 缺失或格式错时由 `@Valid` 拦截,HTTP 200 + code 400,先于读门以外的其它业务校验,但仍在角色门**之后**。 + +--- + +### 12. 月度对账订单明细 `GET /v3/admin/house/reconciliation/monthly/hotel-orders` + +**VO**: `HotelReconciliationOrderReqVO → Result>` + +#### 使用场景 + +点击某酒店账单行「查看订单」,展开该酒店当月逐订单明细。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| month | Query | String | 是 | 正则同上 | 对账月份 | +| hotelId | Query | Long | 否 | 有值优先按 ID 查 | 酒店 ID | +| hotelName | Query | String | hotelId 为空时必传 | — | 酒店名快照,也用于合并同名无 ID 历史配房行 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| orderId / orderNo | String | 订单 ID / 订单号 | +| teamNo | String | 规范团号,无值为 null | +| productName / productType | String | 产品名 / 类型 | +| departDate | LocalDate | 订单出发日期 | +| hotelId / hotelName | String | 酒店 ID(无 hotelId 时为 null)/ 酒店名快照 | +| roomNights | Integer | 该订单在该酒店本月间夜数 | +| totalAmount | String | 兼容旧字段,等于 actualExpenseAmount | +| actualExpenseAmount | String | 实际花销金额,未录入时为 null | +| expenseEntered | Boolean | 是否已录入实际花销 | + +#### 请求示例 + +```http +GET /v3/admin/house/reconciliation/monthly/hotel-orders?month=2026-07&hotelId=1234567890123 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ { "orderId": "2074303545149980674", "orderNo": "HL20260707092437709", "teamNo": "TEAM-2026-001", + "productName": "测试核心产品-单档-固定订金", "productType": "GROUP", "departDate": "2026-07-23", + "hotelId": "1234567890123", "hotelName": "呼伦贝尔香格里拉大酒店", "roomNights": 2, + "totalAmount": "580.00", "actualExpenseAmount": "580.00", "expenseEntered": true } ] +} +``` + +#### 空数据 / 降级响应 + +无匹配订单返回空数组 `[]`。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 +- `hotelId` 与 `hotelName` 均为空时由 `@Valid`/业务校验拦截(角色门之后)。 + +--- + +### 13. 月度对账导出 `GET /v3/admin/house/reconciliation/monthly/export` + +**VO**: `MonthlyReconciliationReqVO`(+ Query 参数 `format`)→ 文件流(`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` 或 `application/pdf`) + +#### 使用场景 + +导出月度对账 Excel(默认)或 PDF,含核单实际花销只读列。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| month | Query | String | 是 | 正则同 §11 | 对账月份 | +| format | Query | String | 否 | 默认 `xlsx` | `xlsx`(默认,不传按 xlsx 处理,向后兼容)/ `pdf`;其余取值报 808171 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| — | 二进制流 | `Content-Disposition: attachment`,文件名 `house-reconciliation-{month}.xlsx/pdf` | + +#### 请求示例 + +```http +GET /v3/admin/house/reconciliation/monthly/export?month=2026-05&format=pdf HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +文件流接口,body 为二进制文件(非 JSON 包装),响应头示意: + +```json +{ + "httpStatus": 200, + "headers": { + "Content-Type": "application/pdf", + "Content-Disposition": "attachment; filename=house-reconciliation-2026-05.pdf" + }, + "body": "" +} +``` + +#### 空数据 / 降级响应 + +不适用(文件流接口,无数据也导出表头/空表格)。 + +#### 错误响应 + +`format` 非法(不属于空/`xlsx`/`pdf`)时,**在角色门之前**由 Controller 直接抛出: + +```json +{ + "code": 808171, + "message": "导出格式非法(仅支持 Excel/PDF)", + "success": false, + "data": null +} +``` + +非房务角色(`format` 合法时): + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- **校验顺序与其它 15 个端点相反**:`format` 合法性校验在 Controller 方法体内直接判断,早于 Service 层的角色门;`format` 非法时无论角色都先报 808171,角色门不会被触发。`format` 合法后才轮到 808090。 + +--- + +### 14. 抢单池-房型需求列表 `GET /v3/admin/order/grab-pool/hotel-requirements` + +**VO**: `HouseGrabPageReqVO → Result>` + +⚠️ 源码已标 `@Deprecated`,`@ApiOperation` 注明「已废弃,改用 `/v3/admin/order/house-allocation/households`」;本单只补读门,不下线该端点。 + +#### 使用场景 + +旧版抢单池列表:展示可抢的房型需求,支持关键词/产品类型/定制师/出行日期区间等过滤。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| keyword | Query | String | 否 | ≤32 字 | 订单号/团号/客人姓名/电话/产品名模糊搜 | +| productType | Query | String | 否 | — | `CORE`/`ROUTE`/`CUSTOM`/`GROUP`,不填=全部 | +| productName | Query | String | 否 | — | 产品名模糊搜 | +| consultantId | Query | Long | 否 | — | 定制师 ID 精确过滤 | +| guestName | Query | String | 否 | — | 客人姓名/联系人模糊搜 | +| departDateFrom / departDateTo | Query | LocalDate | 否 | `yyyy-MM-dd` | 出行日期区间 | +| page | Query | Integer | 否 | ≥1,默认 1 | 页码 | +| pageSize | Query | Integer | 否 | ≥1 且 ≤100,默认 20 | 每页条数 | +| sortBy | Query | String | 否 | 默认 `createTime,desc` | 可切 `departDate,asc` | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id / orderId | String | 房型需求 ID / 订单 ID | +| orderNo / teamNo | String | 订单号 / 团号(未生成为 null) | +| guestName / personsDesc | String | 客人姓名 / 人数描述 | +| productType / productName / productNo | String | 产品类型 / 名称 / 编号 | +| route | String | 档位·夜数(后端拼) | +| departDate / nights | LocalDate / Integer | 出行日期 / 夜数 | +| cities | Array | 行程城市列表(中文,按行程顺序去重) | +| totalAmount | String | 订单总额 | +| consultantName / consultantId | String | 定制师姓名 / adminId | +| consultantRemark / requirementNote / dispatchRemark | String | 定制师订单级备注 / 需求备注摘要 / 提交房务备注 | +| special | Array | 特殊诉求标签 | +| requirementVersion | Integer | 需求版本号(>1 时前端标红「已修订」) | +| urgencyLevel / urgencyLabel / daysToDepart | String / String / Integer | 紧急度码/中文标签/距出发天数 | +| manualUrgent | Boolean | 定制师手动加急 | +| createTime | LocalDateTime | 创建时间 | +| isRework / reworkPrevClaimerName | Boolean / String | 是否返工单 / 上个房务姓名 | + +#### 请求示例 + +```http +GET /v3/admin/order/grab-pool/hotel-requirements?page=1&pageSize=20 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "list": [ { "id": "70123", "orderId": "30456", "orderNo": "26-0501", "guestName": "张先生一家", + "productType": "CORE", "productName": "额吉的故乡", "departDate": "2026-05-01", "nights": 5, + "totalAmount": "6840.00", "urgencyLevel": "URGENT", "urgencyLabel": "紧急" } ], "total": 1 } +} +``` + +#### 空数据 / 降级响应 + +无可抢需求返回 `list: []`、`total: 0`;`productType`/`productNo`/`cities` 取数据依赖 product-v2 Feign,失败时各自降级为 null/空。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 +- 端点已 `@Deprecated`,前端新功能应改接 `/v3/admin/order/house-allocation/households`;本单不改变该端点的可用性,仅补读门。 + +--- + +### 15. 抢单池-我的接单 `GET /v3/admin/order/grab-pool/my-claims/hotel` + +**VO**: `HouseMyOrderPageReqVO → Result` + +⚠️ 源码已标 `@Deprecated`,同 §14 改用 `/v3/admin/order/house-allocation/households`;本单只补读门。 + +#### 使用场景 + +房务查看自己已认领、正在跟进的订单列表,含 4 类状态(进行中/待最终确认/已确认/异常)统计。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| keyword | Query | String | 否 | ≤32 字 | 订单号/团号/客人姓名/电话/产品名模糊搜 | +| status | Query | String | 否 | — | `unfinished`/`allUnfinished`/`todo`(全部未完成)/`inProgress`(兼容)/`claiming`/`pendingConfirm`/`confirmed`/`exception`/`voided`;旧值 `inInquiry` 兼容映射到 `claiming` | +| productType / productName / consultantId / guestName | Query | — | 否 | — | 同 §14 | +| departDateFrom / departDateTo | Query | LocalDate | 否 | — | 出行日期区间 | +| stayDate | Query | LocalDate | 否 | — | 入住晚下钻,只看该晚有效配房所属订单 | +| claimedAtFrom / claimedAtTo | Query | LocalDateTime | 否 | ISO 8601 | 抢单时间区间 | +| city / hasException / hasTodo / hasUnreadMessage | Query | — | 否 | — | 本期未实现,接口预留字段 | +| page / pageSize / sortBy | Query | — | 否 | 同 §14 | 默认 `claimedAt,desc` | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| list | Array | 我的接单行(详见 `HouseMyOrderItemRespVO`) | +| total | Long | 总条数 | +| stats | Object | 按房务跟单状态的分类统计(`HouseMyOrderStatsVO`,进行中/待最终确认/已确认/异常 4 类) | + +#### 请求示例 + +```http +GET /v3/admin/order/grab-pool/my-claims/hotel?status=unfinished&page=1&pageSize=20 HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "list": [ ], "total": 0, "stats": { } } +} +``` + +#### 空数据 / 降级响应 + +无接单记录返回 `list: []`、`total: 0`,`stats` 各分类计数为 0。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 +- `city`/`hasException`/`hasTodo`/`hasUnreadMessage` 为接口预留字段,本期传了也不生效。 +- 端点已 `@Deprecated`;旧版监督视图 `GET /v3/admin/order/grab-pool/all-claims/hotel`(`listAllClaims`)**不在本单 16 个之列**,只受既有 808092 门约束,不加本单读门(见七、不影响范围)。 + +--- + +### 16. 订单房间分配查询(按家庭分组) `GET /v3/admin/order/orders/{orderId}/rooms` + +**VO**: `OrderRoomsRespVO`(无独立入参 VO,Path + 单个 Query 参数) + +#### 使用场景 + +按家庭分组查看某订单的房间分配详情:每个家庭的出行人 + 逐日配房卡片。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| orderId | Path | Long | 是 | — | 订单 ID | +| dayNumber | Query | Integer | 否 | 默认全部 | 第几天过滤 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| orderId | String | 订单 ID | +| families | Array | 家庭列表(按 `roomGroupNo` 分组),每项含 roomGroupNo/travelers[]/assignments[] | +| families[].travelers | Array | 出行人简化项:name/idType/phone(脱敏) | +| families[].assignments | Array | 逐日配房:dayNumber/stayDate/hotelName/roomCount/roomAssignments[] | +| roomAssignments[] | Array | 房间细分:id/bedType/bedTypeLabel/travelerCount/remark | + +#### 请求示例 + +```http +GET /v3/admin/order/orders/5566778/rooms HTTP/1.1 +Host: api.test.1814.love:9443 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "orderId": "5566778", "families": [ { "roomGroupNo": "F1", + "travelers": [ { "name": "张先生", "idType": "id", "phone": "138****5612" } ], + "assignments": [ { "dayNumber": 1, "stayDate": "2026-05-01", "hotelName": "海拉尔假日酒店", + "roomCount": 2, "roomAssignments": [ { "id": "70300", "bedType": "double", "bedTypeLabel": "大床", + "travelerCount": 2, "remark": "加床" } ] } ] } ] } +} +``` + +#### 空数据 / 降级响应 + +订单未配房返回 `families: []`;本单删除的家庭维度写口(#8389)下线后新数据的 `roomAssignments` 恒为空数组,`house_room_assignment` 表本身保留、级联软删仍对存量数据有效。 + +#### 错误响应 + +```json +{ + "code": 808090, + "message": "未登录或非房务角色,无权操作", + "success": false, + "data": null +} +``` + +#### 业务边界 + +- 非房务角色一律返 808090。 +- 与 #8389 下线的家庭维度写口 `POST /v3/admin/order/assignments/{assignmentId}/rooms` 是同一 Controller 的读写一对;写口已删(404),本端点是唯一保留的读口。 + +--- + +## 四、契约约束与正确调用方式 + +| 场景 | 结果 | 说明 | +|------|------|------| +| ✅ 房务角色(ROOM_MANAGER / house_keeper_lead / SUPER_ADMIN)GET 本单 16 个端点 | 200 + 数据 | 行为与改前同 | +| ✅ 零角色 token(网关未透传 `X-Admin-Role`) | 200 + 数据 | `#7609 G-2` 保留项,本单不改 | +| ✅ 定制师在订单详情查看行程 | 仍 200(走 `requirement-history`/`hotel-candidates`,不在本单 16 个端点内) | 有意跨角色端点,源码注释明确标注勿挂本门 | +| ⚙️ 开关 `group-batch.acl.enforce.house-read-role` 关闭后非房务角色再查 | 按改前逻辑放行 | 源码:`observeWhenToggleOff()` 只记 WARN(`enforced=false`)不抛异常,17 处调用点逐字退回"不调守卫" | +| ❌ 非房务角色(ADMIN/CUSTOMIZER/FINANCE/VEHICLE_MANAGER/GROUP_BATCH_MANAGER 等)GET 本单 16 个端点(开关默认 true) | 808090 | 新增拦截 | +| ❌ 非组长/超管查 `GET /v3/admin/order/todos?scope=all`(或查他人 scope) | 582204 | 既有二级门,先过 808090 才轮到它 | +| ❌ 导出对账单 `format` 非法 | 808171 | 在 808090 门之前触发(见 §三-13,校验顺序与其余 15 个端点相反) | + +--- + +## 五、数据库行为(PR-2) + +user-service Flyway `V20260926_002` 撤掉 ADMIN 角色在房务管家子树(目录 + 4 个页面)的 `sys_role_menu` 授权(5 行),不改 `sys_menu`,幂等可重跑。user-service 启动时清一次菜单缓存,撤权后 ADMIN 重新登录/刷新菜单即生效;SUPER_ADMIN 的菜单授权不受影响。 + +--- + +## 六、边界行为 + +| 场景 | 行为 | +|---|---| +| 房务组长(house_keeper_lead)查日历/待办等 | 200,与 ROOM_MANAGER/SUPER_ADMIN 一致(只读监督角色,读门不额外收窄) | +| 非房务角色查本单 16 个端点 | 808090 | +| 开关关闭后非房务角色再查 | 按改前逻辑放行(不是"零角色口径放行",是逐字回到不调守卫) | +| ADMIN 登录 hl-ui | 左侧无房务管家菜单(Flyway 撤授权后,需重新登录/刷新菜单缓存) | +| SUPER_ADMIN 登录 | 房务菜单照常显示 | + +## 六.6、修改前后对比 + +| 维度 | 改前 | 改后 | +|------|------|------| +| CUSTOMIZER 查月度对账 | 返回 200 + 数据 | 返回 808090 | +| VEHICLE_MANAGER 查日历 | 返回 200 + 数据 | 返回 808090 | +| ADMIN 查本单 16 个端点 | 返回 200 + 数据 | 返回 808090 | +| 房务组长查酒店视图/待办 | 返回 200 | 返回 200(不动) | +| ADMIN 角色菜单 | 有房务管家目录 + 4 个页面 | Flyway 撤授权,无该菜单 | + +## 六.7、影响评估 + +- **兼容性**:非房务角色对本单 16 个读端点集体拦截(角色是 user-service 侧的字符串值,order-v3 常量类 `AdminRoleConstants` 里已知的非房务角色含 ADMIN/CUSTOMIZER/FINANCE/VEHICLE_MANAGER/GROUP_BATCH_MANAGER,实际角色集合以 user-service 角色表为准);ADMIN 打开 hl-ui 左侧菜单无房务管家项。 +- **前端要动的**: + - 非房务角色页面若调这 16 个端点,新收 808090,需要补错误提示分支。 + - `GET /v3/admin/order/todos` 传 `scope=all` 或查他人时,非组长/超管会先过 808090、再撞 582204(既有错误码,未变)。 + - 导出对账单 `format` 非法时先收 808171,早于角色门(见 §三-13)。 + - ADMIN 账号因菜单撤授权,房务管家入口本身从左侧菜单消失(不是点击后报错)。 + - 定制师订单详情仍可看「行程」等 Tab(`requirement-history`/`hotel-candidates` 未挡),无改动。 +- **数据影响**:零;仅加权限门与撤菜单授权,不改业务数据。 +- **其它服务**:product-v2、fleet、hl-ui 后端无改动。 + +--- + +## 七、不影响范围 + +- 订单列表新端点(households/group-batches):已有读门,不动。 +- 团期看板/allocations/room-plans/confirm-check:已有读门,不动。 +- 认领人校验(#8386 / #8388):独立,不重叠。 +- 旧抢单池的监督视图 `GET /v3/admin/order/grab-pool/all-claims/hotel`(`listAllClaims`):**不在本单 16 个端点之列**,只受既有 808092「无权查看全部房务订单」门约束,本单未给它补 808090。 +- 零角色 token(#7609 G-2 保留):照常放行,本单不改。 + +--- + +## 八、测试环境已验证 + +部署:测试环境 order-v3 与 user-service 均为 dev-v3 `5cb43db94`(含 PR-1 `08747e304` 与 PR-2 `5f29f7ac6`),user-service Flyway `20260926.002 revoke admin house menus` 已执行(2026-09-26 21:55);order-v3 升到 `1f65d7894` 后的改后回归中,非房务角色调这 16 个端点仍为 808090。网关 `api.test.1814.love:9443`。 + +- 16 个端点:VEHICLE_MANAGER / CUSTOMIZER / ADMIN 等非房务角色 → HTTP 200 + code 808090「未登录或非房务角色,无权操作」;ROOM_MANAGER / house_keeper_lead / SUPER_ADMIN → 200 正常数据。 +- 零角色 token → 200(保留行为,按设计放行)。 +- ADMIN 菜单:user-service Flyway 执行后,`hl_user_service` 角色菜单表里 ADMIN 已无房务管家子树(只读 SQL 核对)。 +- `GET /admin/menu/my`:SUPER_ADMIN 返回 `/housekeeper` 及其 calendar / ledger / orders / todos 四个子菜单;ADMIN 返回的菜单树里没有任何 `/housekeeper` 路径;ROOM_MANAGER、house_keeper_lead 仍是这 5 个菜单,与改前一致。 + +--- + +## 九、开关说明 + +**key**: `group-batch.acl.enforce.house-read-role` +**默认**: `true` +**类型**: 热刷新 `@RefreshScope`(`GroupBatchAclToggle`) +**范围**: 只管本单新挂的 17 处方法入口(16 个端点,对账导出内部拆 2 个调用点算 1 个端点),已有的读门/写门调用点不读这个开关。 +**回滚**: Nacos 置 `false` 后热生效,17 处调用点逐字回到"不调守卫"(不是按零角色口径放行,比改前更窄的口径不会出现);关闭期间对本应被拒绝的访问仍打 `HOUSE_READ_ROLE_MISSING`(`enforced=false`)WARN 日志用于观察。 + +--- + +## 十、相关文档 + +- Issue:https://git.1814.love/wx/HL/issues/8390 +- PR-1(order-v3,读门):https://git.1814.love/wx/HL/pulls/8393 +- PR-2(hl-user-service,菜单撤授权):#8392 +- 相关单号:#8386 / #8388(认领人校验同规则)、#8389(家庭维度写口下线,本单为其 GET 端点补读门) +- API-SPEC:各端点错误码已补 808090 + +--- + +## 关联 / 联系人 + +### 联系人 + +- **后端负责人**: @wx