88caa74 房务接口审计批次:#8385 订房计划守卫/#8386 住宿驳回加门+删车务口/#8387 旧房间分配三口下线/#8388 回执认领校验+读门/#8389 家庭维度写口下线/#8390 16 读端点补门+菜单撤授。前端按钮后端字段驱动+错误码拦截器透 message+被删端点零调用/入口仅房务角色页,6 条均 not_required;#8387/#8389 后端已标,余 4 条翻 not_required,owner/ref 留空,status_note 引号内追充实证。
50 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 8390 | 房务数据权限:16 个读端点补房务角色门(808090),ADMIN 角色菜单撤授权 | admin | wx(GIT) | 修改接口 | deployed | verified | not_required | 房务日历/详情/对账/酒店视图/转单候选/待办/旧抢单池等 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。 | 2026-09-26 | 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<CalendarRespVO>
使用场景
房务侧日历首页,按月展示每天的团数与状态点分布(进行中/待配房/询房中/异常/已完成)。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| 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[] |
请求示例
GET /admin/house/calendar?month=2026-04&scope=mine HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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,不降级、不报错。
错误响应
{
"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<List<DayTourItemVO>>
使用场景
点击日历某天单元格,下钻查看该天按「团」折叠的明细列表(团期单按 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 才返) |
请求示例
GET /admin/house/calendar/day?date=2026-04-22&scope=mine HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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": "配房中" }
]
}
空数据 / 降级响应
当天无团时返回空数组 []。
错误响应
{
"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 | 底部按钮可用性 / 房务侧权限标志 |
请求示例
GET /admin/house/orders/5566778?requirementId=70123 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"viewedRequirementId": "70123",
"historicalRequirement": false,
"voided": false,
"orderDetailReady": true
}
}
空数据 / 降级响应
tripItinerary/travelers 取数异常时降级为空列表、transport 降级为 null,同时 orderDetailReady=false,前端据此提示「订单信息暂不可用,请稍后刷新」,接口本身不因此失败。
错误响应
{
"code": 808090,
"message": "未登录或非房务角色,无权操作",
"success": false,
"data": null
}
业务边界
- 非房务角色一律返 808090。
/admin/house/orders/{orderId}/requirement-history(历史需求版本列表)是定制师订单详情的有意跨角色端点,不在本单 16 个之列,勿混淆。
4. 房务酒店列表 GET /v3/admin/house/hotels
VO: HouseHotelListReqVO → Result<PageResult<HouseHotelListItemVO>>
使用场景
房务侧酒店列表: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(待办数) |
请求示例
GET /v3/admin/house/hotels?city=hailar&status=active&page=1&pageSize=20 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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 不可用时按 §四契约约束降级。
错误响应
{
"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 核房历史行) |
请求示例
GET /v3/admin/house/hotels/1001 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": { "basic": { }, "roomTypes": [ ], "priceCalendar30d": [ ], "recentCheckLog": [ ] }
}
空数据 / 降级响应
basic 强依赖 resource,酒店不存在抛 808500;roomTypes/priceCalendar30d/recentCheckLog 为软依赖,取数失败各自降级为空数组,不影响整体返回。
错误响应
{
"code": 808090,
"message": "未登录或非房务角色,无权操作",
"success": false,
"data": null
}
业务边界
- 非房务角色一律返 808090(先于 808500 判定)。
6. 酒店核房历史 GET /v3/admin/house/hotels/{hotelId}/check-log
VO: HouseInventoryCheckHistoryReqVO → Result<PageResult<HouseInventoryCheckLogItemVO>>
使用场景
酒店详情「完整核房历史」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 | 核房时间 |
请求示例
GET /v3/admin/house/hotels/1001/check-log?page=1&pageSize=20 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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。
错误响应
{
"code": 808090,
"message": "未登录或非房务角色,无权操作",
"success": false,
"data": null
}
业务边界
- 非房务角色一律返 808090。
7. 酒店维度操作日志 GET /v3/admin/house/hotels/{hotelId}/operation-log
VO: HouseOperationLogReqVO → Result<PageResult<HouseOperationLogItemVO>>
使用场景
按酒店维度查操作日志(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 不同结构) |
请求示例
GET /v3/admin/house/hotels/1001/operation-log?page=1&pageSize=20 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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。
错误响应
{
"code": 808090,
"message": "未登录或非房务角色,无权操作",
"success": false,
"data": null
}
业务边界
- 非房务角色一律返 808090。
8. 订单维度操作日志 GET /v3/admin/house/orders/{orderId}/operation-log
VO: HouseOperationLogReqVO → Result<HouseOrderOperationLogRespVO>
使用场景
订单详情弹窗「操作日志」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(按类型聚合计数) |
请求示例
GET /v3/admin/house/orders/5566778/operation-log HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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 为空对象。
错误响应
{
"code": 808090,
"message": "未登录或非房务角色,无权操作",
"success": false,
"data": null
}
业务边界
- 非房务角色一律返 808090。
9. 转单候选员工 GET /v3/admin/house/staff
VO: HouseTransferCandidateReqVO → Result<HouseStaffListRespVO>
使用场景
转单弹窗展示可选候选房务员工列表,按当前在跟订单数升序排列,最多 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 长度) |
请求示例
GET /v3/admin/house/staff?online=false&excludeMe=true HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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。
错误响应
{
"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<HouseTodoListRespVO>
使用场景
房务待办中心: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 类,含查询时派生类型) |
请求示例
GET /v3/admin/order/todos?scope=mine&status=OPEN HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": { "list": [ ], "total": 0, "stats": { } }
}
空数据 / 降级响应
无待办返回 list: []、total: 0,stats 各分类计数为 0。
错误响应
{
"code": 808090,
"message": "未登录或非房务角色,无权操作",
"success": false,
"data": null
}
普通房务传 scope=all 或 scope=others(本单读门通过后):
{
"code": 582204,
"message": "无权查看全部/他人房务待办(仅房务组长或超管可查看)",
"success": false,
"data": null
}
业务边界
- 非房务角色一律先返 808090(读门在
assertScopeAllowed之前调用)。 - 通过读门后,普通房务传
scope=all/others返 582204;只有房务组长/超管可用这两档。
11. 月度对账汇总 GET /v3/admin/house/reconciliation/monthly
VO: MonthlyReconciliationReqVO → Result<MonthlyReconciliationRespVO>
使用场景
月度对账首页:配房完毕订单按酒店汇总的概览 + 明细列表,含核单实际花销只读展示(无录入写口)。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| 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 出参) |
请求示例
GET /v3/admin/house/reconciliation/monthly?month=2026-05 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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)。
错误响应
{
"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<List<HotelReconciliationOrderVO>>
使用场景
点击某酒店账单行「查看订单」,展开该酒店当月逐订单明细。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| 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 | 是否已录入实际花销 |
请求示例
GET /v3/admin/house/reconciliation/monthly/hotel-orders?month=2026-07&hotelId=1234567890123 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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 } ]
}
空数据 / 降级响应
无匹配订单返回空数组 []。
错误响应
{
"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 |
请求示例
GET /v3/admin/house/reconciliation/monthly/export?month=2026-05&format=pdf HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
文件流接口,body 为二进制文件(非 JSON 包装),响应头示意:
{
"httpStatus": 200,
"headers": {
"Content-Type": "application/pdf",
"Content-Disposition": "attachment; filename=house-reconciliation-2026-05.pdf"
},
"body": "<binary file stream>"
}
空数据 / 降级响应
不适用(文件流接口,无数据也导出表头/空表格)。
错误响应
format 非法(不属于空/xlsx/pdf)时,在角色门之前由 Controller 直接抛出:
{
"code": 808171,
"message": "导出格式非法(仅支持 Excel/PDF)",
"success": false,
"data": null
}
非房务角色(format 合法时):
{
"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<PageResult<HouseGrabPageItemRespVO>>
⚠️ 源码已标 @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 | 是否返工单 / 上个房务姓名 |
请求示例
GET /v3/admin/order/grab-pool/hotel-requirements?page=1&pageSize=20 HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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/空。
错误响应
{
"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<HouseMyOrderPageRespVO>
⚠️ 源码已标 @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 类) |
请求示例
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 <room_manager token>
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": { "list": [ ], "total": 0, "stats": { } }
}
空数据 / 降级响应
无接单记录返回 list: []、total: 0,stats 各分类计数为 0。
错误响应
{
"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 |
请求示例
GET /v3/admin/order/orders/5566778/rooms HTTP/1.1
Host: api.test.1814.love:9443
Authorization: Bearer <room_manager token>
响应示例
{
"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 表本身保留、级联软删仍对存量数据有效。
错误响应
{
"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:wx/HL#8390
- PR-1(order-v3,读门):wx/HL#8393
- PR-2(hl-user-service,菜单撤授权):#8392
- 相关单号:#8386 / #8388(认领人校验同规则)、#8389(家庭维度写口下线,本单为其 GET 端点补读门)
- API-SPEC:各端点错误码已补 808090
关联 / 联系人
联系人
- 后端负责人: @wx