文件
hl-api-changelog/changelogs-v2/2026-09/26_8390_房务读侧权限门补齐16个端点与ADMIN菜单撤授-修改接口-管理后台.md
T
Mimingguang bcfe323b60
changelog-filename-gate / validate (push) Failing after 1s
docs(changelog): 回写 #8385~#8390 前端 not_required(6 条 grep 实证零改动)
88caa74 房务接口审计批次:#8385 订房计划守卫/#8386 住宿驳回加门+删车务口/#8387 旧房间分配三口下线/#8388 回执认领校验+读门/#8389 家庭维度写口下线/#8390 16 读端点补门+菜单撤授。前端按钮后端字段驱动+错误码拦截器透 message+被删端点零调用/入口仅房务角色页,6 条均 not_required;#8387/#8389 后端已标,余 4 条翻 not_required,owner/ref 留空,status_note 引号内追充实证。
2026-09-27 09:55:25 +08:00

50 KiB
原始文件 Blame 文件历史

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