--- 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: "not_required" 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)。测试服实测验证。前端实证(mmg 2026-09-27):16 端点调用者全在 housekeeper 视图(菜单门);定制师仅调契约排除的 requirement-history;hotels 系/订单房间零调用;菜单后端下发,808090 拦截器透 message,零改动 not_required。" 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