--- schema: "hl-changelog/v2" ticket: "8662" title: "询房预览接口补房务读守卫" consumer: "admin" author: "wx(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "not_required" frontend_status: "not_required" frontend_owner: "" frontend_ref: "" target_release: "" verified_at: "" status_note: "" updated_at: "2026-10-02" base: "dev-v3" --- # 询房预览接口补房务读守卫 > **存放目录**: 二期 → `changelogs-v2/2026-10/` > > **服务**: hl-order-service-v3 > **Issue**: #8662 > **日期**: 2026-10-02 > **影响范围**: 管理后台房务询房话术预览功能 --- ## ⚠️ 关键变化 - 接口 `POST /v3/admin/order/inquiry/preview` 新增房务读守卫。 - **非房务角色**(定制师、管理员、其他后台角色)调用返回 **808090**「未登录或非房务角色,无权操作」。 - **房务角色**(房务管理员、超管)放行,功能无改动。 --- ## 一、背景 二期房务功能收口中,#8390 统一给 16 个旧只读端点(日历 / 房务详情 / 酒店视图 / 转单候选 / 待办 / 月度对账 / 旧抢单池 / 订单房间)挂上房务读守卫,唯独询房话术预览这一个接口漏过,导致定制师、运营等非房务角色能调通,拿到酒店联系人与微信(源码:`HouseReadGuard.java` 类 javadoc)。本次补上后,受此守卫覆盖的端点共 17 个。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 询房话术预览 | POST | `/v3/admin/order/inquiry/preview` | 修改 | 新增房务读守卫,非房务角色返回 808090 | --- ## 三、接口详情 ### 1. 询房话术预览 `POST /v3/admin/order/inquiry/preview` **VO**: `InquiryPreviewReqVO → InquiryPreviewRespVO` #### 使用场景 房务在配房弹窗点击「询房」时预览即将发往酒店的话术(所见即所发),确认无误后复制到企业微信。本次修改:非房务角色被拒。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | hotelId | Body | Long | ✅ | - | 酒店 ID;按此取 resource 联系人渲染文案 | | orderId | Body | Long | ❌ | - | 订单 ID(取团号);`assignmentId` 有效时以其所属订单为准 | | assignmentId | Body | Long | ❌ | - | 配房 ID;存在且与 `hotelId` 匹配时,日期/房型/间数/支付方式优先取该配房快照 | | stayDate | Body | LocalDate | ❌ | - | 入住日期;`assignmentId` 命中时被快照值覆盖 | | roomCount | Body | Integer | ❌ | ≥1 | 房间数;`assignmentId` 命中时被快照值覆盖;都缺省时按 1 间渲染 | | roomCategory | Body | String | ❌ | 字典 room_category | 房型类别;`assignmentId` 未命中时用于查房型中文名,查不到则原样回退为传入的 code | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | messageBody | String | 渲染后的固定格式订房确认话术(模板见下) | | contactName | String | 联系人姓名,resource 按 hotelId 带出,前端只读回显 | | contactWechat | String | 联系人微信号,resource 按 hotelId 带出,前端只读回显 | 话术固定模板(`InquiryMessageTemplate.SEND_BASE`,占位符按 `Map` 渲染,缺失值替换为空串): ``` 呼籁旅行 - 订房确认书: 团号:${teamNo} 日期:${stayDate} 房型:${roomTypeName}${roomCount}间 备注:${tags} 1.${paymentText},价格保密。 2.${breakfastText}${invoiceText} 3.核房电话:${phone} 辛苦确认后回复 @${replyContacts} ``` #### 请求示例 ```json { "hotelId": 1900000001, "orderId": 1900000000, "stayDate": "2026-04-28", "roomCount": 1 } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "messageBody": "呼籁旅行 - 订房确认书:\n团号:HL20261001A\n日期:4.28\n房型:待补充1间\n备注:协议酒店\n1.领队前台现付,价格保密。\n2.含早含发票\n3.核房电话:0470-8888888\n辛苦确认后回复 @王前台", "contactName": "王前台", "contactWechat": "hailar_holiday" }, "success": true } ``` 上例未传 `roomCategory`、也未传 `assignmentId`,`roomTypeName` 按规则取不到任何来源,渲染为空缺省文案(源码常量 `EMPTY_VALUE_TEXT`)——`${roomTypeName}${roomCount}间` 模板不插空格,故渲染结果是该空缺省文案与 `1间` 的无分隔拼接(见上方 JSON 示例的 `messageBody`),前端如需展示分隔需自行处理,后端不改模板。日期按 `M.d` 格式渲染(无补零),`2026-04-28` → `4.28`。ID、团号、酒店联系人等取值均为说明用的构造值。 #### 空数据 / 降级响应 - 酒店联系信息查询抛异常(`loadHotelExtended` 捕获全部 `RuntimeException`):静默降级,`contactName`/`contactWechat` 返回**空字符串 `""`(不是 NULL)**,`messageBody` 仍正常渲染,缺省字段分别落空值常量(`EMPTY_VALUE_TEXT`)/空标签常量(`EMPTY_TAG_TEXT`)/现付默认文案。 - `assignmentId` 传了但查不到记录、或与 `hotelId` 不匹配:静默降级为按请求参数 + 资源数据重新生成(不报错,仅记一条 `log.warn`),不是 assignment 快照。 - `assignmentId` 命中但与请求里的 `orderId` 不一致:忽略请求 `orderId`,改用该配房记录的真实 `orderId`(同样静默降级,仅记日志)。 - 不落库,房务可反复调用,无状态。 #### 错误响应 ```json { "code": 808090, "message": "未登录或非房务角色,无权操作", "data": null, "success": false } ``` 其他错误: | code | message | 触发 | |------|---------|------| | 400 | hotelId 不能为空 | hotelId 未传 | | 400 | 房间数最小为 1 | roomCount 传了但 < 1 | #### 业务边界 - 只校验角色(房务管理员/超管放行),**不校验订单归属**:房务可预览任意订单的询房话术,与 #8390 覆盖的其余 16 个只读端点行为一致。 - 该守卫只拦「有角色但非房务」;零角色账号(网关未透传 `X-Admin-Role`)按既有口径仍放行,不受本次改动影响(`HouseReadGuard.java` 类 javadoc,#7609 G-2 定案)。 - 预览不落库,无副作用,可反复调用。 - `contactWechat` 仅供复制,前端不做交互(不拨电话、不主动跳转)。 --- ## 四、契约约束与正确调用方式 ### 权限对照 | 角色 | 改前 | 改后 | 说明 | |------|------|------|------| | 房务管理员 | 200 放行 | 200 放行 | 无改动 | | 超管 | 200 放行 | 200 放行 | 无改动 | | 定制师(CUSTOMIZER) | 200 放行(缺陷) | 808090 拒绝 | **新增限制** | | 其他后台角色(如 ADMIN) | 200 放行(缺陷) | 808090 拒绝 | **新增限制** | | 零角色账号(网关未透传 `X-Admin-Role`) | 放行 | 放行 | 无改动(#7609 G-2 口径,本次刻意不收) | --- ## 五、数据库行为 不落库,本接口无数据写入。 --- ## 六、边界行为 - 错误码 808090 与所有同域房务读端点保持一致,可统一处理。 - 权限守卫受 Nacos 开关 `group-batch.acl.enforce.house-read-role` 控制。测试服 2026-09-30~10-02 期间该开关为开启状态(实测 ADMIN/CUSTOMIZER 均返回 808090,见「八、测试环境已验证」);生产环境以当时的配置为准,前端按本文档的错误码契约接即可,无需关心开关本身的开关状态。 --- ## 六.5 枚举 不适用(接口无新增枚举)。 --- ## 六.6、修改前后对比 | 项 | 改前 | 改后 | |----|------|------| | 权限校验 | ❌ 无房务守卫,任何角色可调 | ✅ 新增房务读守卫,非房务返回 808090 | | 功能逻辑 | 预览话术、返回联系人 | 不变(仅权限改动) | --- ## 六.7、影响评估 - **前端无需改动**:经 hl-ui v2.1 核实,该接口的封装函数(`src/api/housekeeper/inquiry.js` 的 `previewInquiry`)仅有一处调用——`src/views/housekeeper/components/useHousekeeperInquiryCopy.js`,位于房务专属视图目录下,本就只在房务角色登录后的界面里被触达。非房务角色(定制师等)侧没有调用这个接口的代码,本次收紧权限不会让任何现有页面报错。 --- ## 七、不影响范围 - 房务配房流程(使用此接口的场景)功能不变。 - 小程序端、H5 端接口无改动。 - #8390 已覆盖的其余 16 个旧只读端点本身无行为变化,本次只是把本端点补入同一套守卫。 --- ## 八、测试环境已验证 测试服环境,2026-09-30~10-02,经网关实测,与修前基线逐字段比对(基线快照:`p50_ac3_room_manager.json`/`p50_ac3_super_admin.json`;本轮:`p6_ac3_roommanager.json`/`p6_ac3_superadmin.json`/`p6_ac3_admin.json`/`p6_ac3_consultant.json`)。 ``` POST /v3/admin/order/inquiry/preview 房务管理员(ROOM_MANAGER):200,messageBody/contactName/contactWechat 与修前基线逐字节相同 ✓ 超管(SUPER_ADMIN):200,messageBody/contactName/contactWechat 与修前基线逐字节相同 ✓ 管理员(ADMIN):808090 未登录或非房务角色,无权操作 ✓ 定制师(CUSTOMIZER):808090 未登录或非房务角色,无权操作 ✓ ``` --- ## 十、相关文档 - **Issue**: [#8662](https://git.1814.love:8443/wx/HL/issues/8662) - **PR**: [#8705](https://git.1814.love:8443/wx/HL/pulls/8705) - **背景工单**: [#8390](https://git.1814.love:8443/wx/HL/issues/8390)(原覆盖 16 个读端点统一守卫,本次 #8662 补上第 17 个——即本端点) ## 关联 / 联系人 **关联工单**: #8662 **同批删除**: 旧住宿需求提交接口 **后端负责人**: @wx