--- schema: "hl-changelog/v2" ticket: "8386" title: "住宿需求驳回:加房务角色门与认领人校验;车务驳回接口下线" 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: "住宿需求供应方驳回新增房务角色门(808090/808091)与认领人校验(808110/808116,超管豁免);车务驳回接口删除,调用返回 HTTP 200 + code 404。已在测试环境网关实测各角色分支与已删除端点响应。前端实证(mmg 2026-09-27):车务驳回口零调用;住宿驳回按钮 enabled/disabledReason 由后端 canRejectRequirement 驱动,新码拦截器透 message,零改动 not_required。" 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