changelog-filename-gate / validate (push) Failing after 1s
- #8659:候选页 inventoryStatus 按日历状态取值;控房表新增 calendarStatus / calendarStatusName(前端加一列展示);扣减拒绝分 808906 / 808907 / 808901。
- #8662:删除 PUT /v3/admin/order/{id}/hotel-requirement;询房预览补房务读守卫,非房务角色返回 808090。
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
239 行
9.6 KiB
Markdown
239 行
9.6 KiB
Markdown
---
|
||
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<InquiryPreviewRespVO>`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| 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
|