docs(7512): 服务人员按景区口径接入供应商关系 Changelog(supplierFullName / 原因可省略 / 无订单门禁)Refs #7512
changelog-filename-gate / validate (push) Failing after 2s

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t6wjBsvqsFKFSkCzsYsuS
这个提交包含在:
lc
2026-09-12 12:15:55 +08:00
共同撰写人 Claude Opus 5
父节点 57be23bcd1
当前提交 3a15e3de25
@@ -0,0 +1,189 @@
---
schema: "hl-changelog/v2"
ticket: "7512"
title: "服务人员按景区口径接入供应商关系"
consumer: "admin"
author: "lc(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: "2026-09-12"
status_note: "后端已部署并经 Gateway 验证;待前端展示服务人员列表供应商全称并接入服务人员供应商关系操作。"
updated_at: "2026-09-12"
base: "dev-v3"
---
# 服务人员:按景区口径接入供应商关系
> **服务**: hl-resource-service(8082)
> **PR**: #7575
> **Issue**: #7512
> **日期**: 2026-09-12
> **影响范围**: 管理端服务人员列表、服务人员供应商查询/设置/改绑/解绑
## ⚠️ 关键变化
- 服务人员列表每条记录固定返回 `supplierFullName`;无当前供应商时为 `null`。
- 服务人员首次绑定、改绑都可以省略 `changeReason`;此前缺原因返回 `400 changeReason不能为空`。解绑此前已可省略,保持不变。
- **服务人员不检查订单**:改绑、解绑不做未结束订单门禁,不新增任何错误码。这与景区(395059)、餐厅(395060)、游玩项目(395061)、酒店(395062)、服务(395063)不同。
- `requiredTypeCode` 规则**没有变化**:服务人员本来就固定 `STAFF`,可省略;传其他类型仍返回 `400 requiredTypeCode与资源模块不匹配`。
## 一、背景
服务人员此前不展示当前供应商全称,且设置、改绑必须填写变更原因。本次按景区、餐厅、游玩项目、酒店、服务现行口径冻结服务人员所需的展示字段与原因规则。与前述模块不同的是,订单侧不按服务人员资源 ID 识别订单(V3 派工表 `order_staff_assignment`、`order_batch_staff` 的 `staff_id` 是用户服务员工账号 ID,与资源侧 `staff` 表无对应字段),因此服务人员不接订单门禁。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 服务人员列表 | GET | `/admin/staff/list` | 响应字段新增 | 每条记录固定返回 `supplierFullName` |
| 2 | 查询服务人员当前供应商 | GET | `/admin/supplier/resource-relations/{resourceModule}/{resourceId}/view` | 调用契约补充 | 服务人员传 `resourceModule=STAFF` |
| 3 | 设置或改绑服务人员供应商 | PUT | `/admin/supplier/resource-relations/{resourceModule}/{resourceId}/update` | 请求与行为修改 | 可省略 `changeReason` |
| 4 | 解绑服务人员供应商 | POST | `/admin/supplier/resource-relations/{resourceModule}/{resourceId}/unbind` | 无变化 | 原因此前已可省略,保持 |
## 三、接口详情
### 1. 服务人员列表 `GET /admin/staff/list`
**VO**: `StaffQueryRequest / PageResult<StaffAdminListVO>`
#### 使用场景
服务人员管理列表初始化、翻页、筛选或刷新时调用;列表「供应商」列直接读取 `supplierFullName`。
#### 出参新增字段
| 字段 | 类型 | 必返回 | 说明 |
|---|---|---|---|
| `supplierFullName` | String | 是 | 当前有效关系对应的供应商全称;未关联或供应商已删除时为 `null` |
其余字段(`staffId`、`name`、`staffType`、`settleType`、`phone`、`highlights`、`rating`、`viewCount`、`sortOrder`、`coverUrl`、`status`、`approvalNo`、`pendingStatus`、`tags`、`createdAt`、`updatedAt`)与分页元数据(`total`、`page`、`pageSize`)口径不变。
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"total": 12,
"page": 1,
"pageSize": 20,
"records": [
{ "staffId": "1005", "name": "刘大山", "staffType": "LEADER", "status": 1,
"supplierFullName": "陈巴尔虎旗天下草原旅游服务有限" },
{ "staffId": "1002", "name": "李雪梅", "staffType": "GUIDE", "status": 1,
"supplierFullName": null }
]
}
}
```
#### 业务边界
- `supplierFullName` 始终序列化,未关联时是 `null` 而不是缺字段,前端可直接读取、无需存在性判断。
- 该字段按当前页 ID 批量查询实时关系得到,不缓存;供应商改名后下次翻页即生效。
- 内部接口 `/internal/staff/**` 与小程序链路不返回该字段,口径不变。
### 2. 查询服务人员当前供应商 `GET /admin/supplier/resource-relations/STAFF/{staffId}/view`
#### 入参
| 字段 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| `resourceModule` | Path | String | 是 | 服务人员固定传 `STAFF` |
| `resourceId` | Path | Long | 是 | 服务人员 ID(列表 `staffId`) |
#### 业务边界
- 未关联供应商时返回 `395038`,前端据此展示「未关联」。
- 资源不存在返回 `395035`。
### 3. 设置或改绑服务人员供应商 `PUT /admin/supplier/resource-relations/STAFF/{staffId}/update`
#### 入参
| 字段 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| `supplierId` | Body | Long | 是 | 目标供应商 ID,须为合作中且具备 `STAFF` 类型 |
| `changeReason` | Body | String | **否(本次放宽)** | 最长 500 字;省略或全空白按 `null` 存储 |
| `requiredTypeCode` | Body | String | 否 | 省略即 `STAFF`;传其他值返回 400 |
| `expectedCurrentSupplierId` | Body | Long | 改绑必填 | 乐观并发围栏,取自 view |
| `expectedRelationUpdateTime` | Body | String | 改绑必填 | 同上,与前者必须同时提供或同时省略 |
#### 请求示例(首次绑定,省略原因与类型)
```json
{ "supplierId": 2097974318146146305 }
```
#### 请求示例(改绑,省略原因)
```json
{
"supplierId": 2097937816108269570,
"expectedCurrentSupplierId": 2097974318146146305,
"expectedRelationUpdateTime": "2026-09-12 12:20:31"
}
```
#### 错误响应
| code | 场景 |
|---|---|
| `400 requiredTypeCode与资源模块不匹配` | 显式传了非 `STAFF` 的类型 |
| `400 expectedCurrentSupplierId与expectedRelationUpdateTime必须同时提供或同时省略` | 两个并发围栏字段只传其一 |
| `395010` | 目标供应商非合作中 |
| `395014` | 并发围栏不匹配(他人已改) |
| `395035` | 服务人员不存在 |
#### 业务边界
- **不做订单门禁**:无论该服务人员是否出现在任何订单中,改绑都不被拦截,也不会返回 395059~395063 中的任何一个。
- 原因放宽只作用于服务人员;其他未豁免模块(如备品 `SUPPLIES`)省略原因仍返回 `400 changeReason不能为空`。
- 关系版本围栏与审计链路照常执行,省略原因时审计记录的原因列为 `null`。
### 4. 解绑服务人员供应商 `POST /admin/supplier/resource-relations/STAFF/{staffId}/unbind`
本次无变化。请求体需带 `expectedCurrentSupplierId` 与 `expectedRelationUpdateTime`,`changeReason` 可省略(此前已对全部资源模块豁免)。注意该接口是 **POST**,不是 DELETE。
## 四、前端动作
1. 服务人员列表新增「供应商」列,读 `supplierFullName`,为 `null` 时展示「未关联」。
2. 服务人员供应商的设置、改绑表单去掉「变更原因」必填校验(字段可保留为选填)。
3. 改绑、解绑仍需先调 view 取 `supplierId` 与关系版本,作为并发围栏回传。
4. 不需要为服务人员处理「存在未结束订单」类拦截提示,该模块不会返回此类错误码。
## 五、当前状态
- 后端:已合并 `dev-v3`(PR #7575,merge `58659736a`),已部署 TEST(部署提交 `ab7644ea4`)。
- Gateway 验证:已通过,覆盖列表字段两种取值、首次绑定/改绑/解绑省略原因、订单侧数据零变化。
- 前端:待接入。
## 六、同系列历史
| PR | Issue | 范围 | 已生效 |
|---|---|---|---|
| #7358 | #7357 | 景区改绑供应商免原因 | 是 |
| #7401 | #7400 | 全部资源供应商解绑免原因 | 是 |
| #7493 | #7387 | 餐厅列表、原因规则与订单门禁 | 是 |
| #7507 | #7500 | 游玩项目列表、原因规则与订单门禁 | 是 |
| #7552 | #7509 | 酒店列表、原因规则与订单门禁 | 是 |
| #7554 | #7510 | 服务列表、原因规则与订单门禁 | 是 |
| #7573 | #7511 | 额外成本列表、固定类型与原因规则(不含订单门禁) | 是 |
| **#7575** | **#7512** | 服务人员列表与原因规则(无订单门禁) | **是,服务人员最新契约** |
## 关联 / 联系人
### 链接
- **Issue**: [#7512](https://git.1814.love:8443/wx/HL/issues/7512)
- **PR**: [#7575](https://git.1814.love:8443/wx/HL/pulls/7575)
### 联系人
- **后端负责人**: @lc