文件
hl-api-changelog/changelogs-v2/2026-09/12_7512_服务人员按景区口径接入供应商关系-修改接口-管理后台.md
T
Mimingguang 10502ccdd3
changelog-filename-gate / validate (push) Failing after 2s
chore(changelog): 补回写漏网 8 条(7510×2/7511/7512 verified+not_required;7513/7530/7531/7535 not_required)
7510/7511/7512 代码早已随供应商关系系列交付(a17a2c2a/4bedc8be),实证后回写 verified 挂 a17a2c2a;
11_7510 为正本 12_7510 的重复副本(canonical_path),标 not_required 消除 pending;
7513/7530/7531/7535 实证前端零改动 not_required。
2026-09-13 09:51:52 +08:00

429 行
20 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "7512"
title: "服务人员按景区口径接入供应商关系"
consumer: "admin"
author: "lc(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "verified"
frontend_owner: "mmg"
frontend_ref: "a17a2c2a"
target_release: ""
verified_at: "2026-09-12"
status_note: "后端已部署并经 Gateway 验证;待前端展示服务人员列表供应商全称并接入服务人员供应商关系操作。 前端 2026-09-13 闭环 verified:实证代码早已随供应商关系系列交付(hl-admin a17a2c2a/4bedc8be),personnel/index.vue 已读 supplierFullName、SupplierRelationModal 挂 STAFF,免原因弹窗已整体移除全 10 模块不弹原因框 body 不带 changeReason,候选传 resourceModule+resourceId,订单门禁走兜底透原文原关系不变。零新增改动,仅补回写凭证。"
updated_at: "2026-09-13"
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 识别订单,因此服务人员不接订单门禁。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 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`。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `keyword` | Query | String | 否 | - | 姓名模糊搜索 |
| `staffType` | Query | String | 否 | 字典 `staff_type` | 人员类型筛选 |
| `status` | Query | Integer | 否 | `0` 下架,`1` 上架 | 状态筛选 |
| `page` | Query | Integer | 否 | 最小 1,默认 1 | 页码 |
| `pageSize` | Query | Integer | 否 | 默认 20 | 每页条数 |
本次不新增、不修改任何入参。
#### 出参 `Result<PageResult<StaffAdminListVO>>`
| 字段 | 类型 | 说明 |
|---|---|---|
| `code` / `message` / `success` | Integer / String / Boolean | 业务结果 |
| `data.total` / `page` / `pageSize` | Long | 分页元数据,口径不变 |
| `data.records[].staffId` | String | 人员 ID(雪花号序列化为字符串) |
| `data.records[].name` | String | 姓名 |
| `data.records[].staffType` | String | 人员类型 |
| `data.records[].settleType` | String | 结算方式 |
| `data.records[].phone` | String | 手机号 |
| `data.records[].status` | Integer | `0` 下架,`1` 上架 |
| `data.records[].tags` | Array | 标签列表 |
| **`data.records[].supplierFullName`** | **String 或 null** | **本次新增**:当前有效关系对应的供应商全称;未关联或供应商已删除时为 `null` |
其余既有字段(`highlights`、`rating`、`viewCount`、`sortOrder`、`coverUrl`、`approvalNo`、`pendingStatus`、`createdAt`、`updatedAt`)口径不变。
#### 请求示例
```http
GET /admin/staff/list?page=1&pageSize=20 HTTP/1.1
Authorization: Bearer <token>
```
#### 响应示例
```json
{"code":200,"message":"成功","success":true,"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}]}}
```
#### 空数据 / 降级响应
无匹配人员时 `records` 为空数组、`total` 为 `0`,不是错误。未关联供应商的人员返回 `supplierFullName: null`,字段始终存在。
#### 错误响应
```json
{"code":401,"message":"缺少有效的 Authorization 头","success":false,"data":null}
```
#### 业务边界
- `supplierFullName` 始终序列化,未关联时是 `null` 而不是缺字段,前端可直接读取、无需存在性判断。
- 该字段按当前页 ID 批量查询实时关系得到,不缓存;供应商改名后下次翻页即生效。
- 内部接口 `/internal/staff/**` 与小程序链路不返回该字段,口径不变。
### 2. 查询服务人员当前供应商 `GET /admin/supplier/resource-relations/{resourceModule}/{resourceId}/view`
**VO**: `-` / `Result<SupplierResourceRelationRespVO>`
#### 使用场景
打开服务人员详情或供应商关系弹窗时,回读当前供应商与关系版本;改绑、解绑前必须先调用它取版本对。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `resourceModule` | Path | String | 是 | 服务人员固定为 `STAFF` | 资源模块 |
| `resourceId` | Path | String | 是 | 正整数 | 服务人员 ID(列表 `staffId`) |
#### 出参 `Result<SupplierResourceRelationRespVO>`
| 字段 | 类型 | 说明 |
|---|---|---|
| `code` / `message` / `success` | Integer / String / Boolean | 业务结果 |
| `data.supplierId` | String | 当前供应商 ID |
| `data.supplierFullName` | String | 当前供应商全称 |
| `data.requiredTypeCode` | String | 生效类型;服务人员固定 `STAFF` |
| `data.relationUpdateTime` | String | 关系版本,格式 `yyyy-MM-dd HH:mm:ss`,改绑/解绑时回传 |
均为既有字段,本次不新增、不修改字段结构。
#### 请求示例
```http
GET /admin/supplier/resource-relations/STAFF/1002/view HTTP/1.1
Authorization: Bearer <token>
```
#### 响应示例
```json
{"code":200,"message":"成功","success":true,"data":{"supplierId":"2097974318146146305","supplierFullName":"陈巴尔虎旗天下草原旅游服务有限","requiredTypeCode":"STAFF","relationUpdateTime":"2026-09-12 12:20:31"}}
```
#### 空数据 / 降级响应
当前无关系时返回 `395038`,前端据此展示「未关联」,不是异常。
#### 错误响应
```json
{"code":395038,"message":"供应商资源关联不存在","success":false,"data":null}
```
```json
{"code":395035,"message":"资源不存在","success":false,"data":null}
```
#### 业务边界
- 服务人员必须传 `resourceModule=STAFF`,大小写不敏感但建议全大写。
- 该接口只读,不产生审计记录。
- 登录与查看权限、数据范围规则不变。
### 3. 设置或改绑服务人员供应商 `PUT /admin/supplier/resource-relations/{resourceModule}/{resourceId}/update`
**VO**: `SupplierResourceReassignReqVO / Result<SupplierResourceRelationRespVO>`
#### 使用场景
为服务人员首次绑定供应商,或把已有关系改绑到另一家供应商。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `resourceModule` | Path | String | 是 | 服务人员固定为 `STAFF` | 资源模块 |
| `resourceId` | Path | String | 是 | 正整数 | 服务人员 ID |
| `supplierId` | Body | String | 是 | 正整数 | 目标供应商,须合作中且具备 `STAFF` 类型 |
| `changeReason` | Body | String | **否(本次放宽)** | 最长 500 字 | 可省略、`null`、空串或空白 |
| `requiredTypeCode` | Body | String | 否 | 省略即 `STAFF` | 传其他值返回 400 |
| `expectedCurrentSupplierId` | Body | String | 改绑必填 | 正整数 | 当前关系的 `supplierId` |
| `expectedRelationUpdateTime` | Body | String | 改绑必填 | `yyyy-MM-dd HH:mm:ss` | 当前关系的 `relationUpdateTime` |
#### 出参 `Result<SupplierResourceRelationRespVO>`
| 字段 | 类型 | 说明 |
|---|---|---|
| `code` / `message` / `success` | Integer / String / Boolean | 业务结果 |
| `data.supplierId` / `supplierFullName` | String | 变更后的当前供应商 |
| `data.requiredTypeCode` | String | 生效类型,固定 `STAFF` |
| `data.relationUpdateTime` | String | 新的关系版本,供下次改绑/解绑使用 |
#### 请求示例
首次绑定(省略原因与类型):
```json
{"supplierId":"2097974318146146305"}
```
改绑(省略原因):
```json
{"supplierId":"2097937816108269570","expectedCurrentSupplierId":"2097974318146146305","expectedRelationUpdateTime":"2026-09-12 12:20:31"}
```
#### 响应示例
```json
{"code":200,"message":"成功","success":true,"data":{"supplierId":"2097937816108269570","supplierFullName":"示例服务人员供应商有限公司","requiredTypeCode":"STAFF","relationUpdateTime":"2026-09-12 12:20:44"}}
```
#### 空数据 / 降级响应
无空成功结果;权限、字典或供应商资格等必要依赖无法明确核验时返回失败且不改变关系。
#### 错误响应
```json
{"code":400,"message":"requiredTypeCode与资源模块不匹配","success":false,"data":null}
```
```json
{"code":395010,"message":"请先完成供应商注册","success":false,"data":null}
```
#### 业务边界
- 当前无关系时是首次绑定:不传版本对。
- 当前有关系且目标供应商不同才是真正改绑:必须传完整、最新版本对;两个版本字段必须同时提供或同时省略,否则返回 `400 expectedCurrentSupplierId与expectedRelationUpdateTime必须同时提供或同时省略`。
- **服务人员不检查订单**:无论该人员是否出现在任何订单或派工中,改绑都不会被拒绝,也不会返回 395059~395063。
- 目标供应商必须合作中且已挂 `STAFF` 类型,否则分别返回 `395010` 与 `400 requiredTypeCode与资源模块不匹配`。
- 版本过期返回 `395014`;失败不改变关系或审计。
### 4. 解绑服务人员供应商 `POST /admin/supplier/resource-relations/{resourceModule}/{resourceId}/unbind`
**VO**: `SupplierResourceUnbindReqVO / Result<Void>`
#### 使用场景
用户确认清除服务人员当前供应商关系时调用。注意该接口是 **POST**,不是 DELETE。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| `resourceModule` | Path | String | 是 | 服务人员固定为 `STAFF` | 资源模块 |
| `resourceId` | Path | String | 是 | 正整数 | 服务人员 ID |
| `expectedCurrentSupplierId` | Body | String | 是 | 正整数 | 当前关系的 `supplierId` |
| `expectedRelationUpdateTime` | Body | String | 是 | `yyyy-MM-dd HH:mm:ss` | 当前关系的 `relationUpdateTime` |
| `changeReason` | Body | String | 否 | 最长 500 字 | 可省略、`null`、空串或空白 |
#### 出参 `Result<Void>`
| 字段 | 类型 | 说明 |
|---|---|---|
| `code` / `message` / `success` | Integer / String / Boolean | 业务结果 |
| `data` | null | 成功时固定为 `null` |
#### 请求示例
```json
{"expectedCurrentSupplierId":"2097937816108269570","expectedRelationUpdateTime":"2026-09-12 12:20:44"}
```
#### 响应示例
```json
{"code":200,"message":"成功","success":true,"data":null}
```
#### 空数据 / 降级响应
当前无关系时返回 `395038`,不执行解绑。
#### 错误响应
```json
{"code":395014,"message":"数据已被其他操作修改,请刷新后重试","success":false,"data":null}
```
#### 业务边界
- 解绑必须使用当前关系最新版本对;版本过期返回 `395014`,原关系不变。
- 服务人员解绑同样不检查订单。
- 登录、`supplier:resource:manage` 服务端权限、数据范围、幂等、锁和审计规则保持不变;失败零写入。
## 四、契约约束与正确调用方式
| 场景 | 正确 payload | 调用结果 |
|---|---|---|
| 查询当前关系 | 无请求体,`resourceModule=STAFF` | 返回关系;无关系为 `395038` |
| 首次绑定 | `{"supplierId":"22"}` | 不传版本对、类型和原因 |
| 改绑 | `{"supplierId":"33","expectedCurrentSupplierId":"22","expectedRelationUpdateTime":"2026-09-12 12:20:31"}` | 两个版本字段必须成对且取最新关系值 |
| 解绑 | `{"expectedCurrentSupplierId":"33","expectedRelationUpdateTime":"2026-09-12 12:20:44"}` | POST 方法,可省略 `changeReason` |
| 错误:传其他类型 | `{"supplierId":"33","requiredTypeCode":"SCENIC"}` | 返回 `400 requiredTypeCode与资源模块不匹配`,不写入 |
| 错误:版本字段只传一个 | `{"supplierId":"33","expectedCurrentSupplierId":"22"}` | 返回 `400`,不写入 |
每次改绑或解绑前先回读当前关系,成功后重新刷新关系和服务人员列表。业务失败可能仍为 HTTP 200,必须同时判断响应体 `code` 与 `success`。
## 五、数据库行为
| 前端操作 | 外部可观察结果 |
|---|---|
| 首次绑定 | 生成一个当前有效关系,`requiredTypeCode=STAFF`;未填原因时不虚构默认原因 |
| 改绑 | 旧关系转为历史,新供应商成为唯一当前关系,并保留正常变更审计 |
| 解绑 | 当前关系消失并保留正常解绑审计;服务人员列表返回 `supplierFullName: null` |
| 版本、权限、类型或资格校验失败 | 当前关系和审计均不变化 |
本次不迁移或回填任何数据,不修改服务人员自身业务数据,不修改订单业务、订单数据或派工数据。
## 六、边界行为
- 未登录返回 `401`;关系查询要求查看权限,设置、改绑、解绑要求维护权限及对应数据范围。
- 服务人员未绑定供应商时,列表返回 `supplierFullName: null`,关系查询返回 `395038`。
- `changeReason` 最长 500 字;省略、`null`、空串或纯空白均按未填写处理。
- `requiredTypeCode` 省略时按 `STAFF` 生效;传入任何其他值一律返回 `400 requiredTypeCode与资源模块不匹配`。
- 服务人员改绑、解绑**不做订单核验**,不会返回未结束订单类错误码。
## 六.5、枚举 / 数据字典
### `resourceModule` / `requiredTypeCode`(服务人员供应商关系)
**所属字段**: Path `resourceModule`、Body/Response `requiredTypeCode` | **类型**: `String`
| 值 | 中文 | 说明 |
|---|---|---|
| `STAFF` | 服务人员管理 | 服务人员关系固定值;`requiredTypeCode` 可省略并由后端确定 |
### 业务错误码
本次**未新增**任何业务错误码。涉及的既有错误码:
| 值 | 中文 | 说明 |
|---|---|---|
| `400` | requiredTypeCode与资源模块不匹配 | 显式传入非 `STAFF` 类型时 |
| `395038` | 供应商资源关联不存在 | 查询或解绑时当前无有效关系 |
| `395014` | 数据已被其他操作修改,请刷新后重试 | 关系版本过期 |
| `395010` | 请先完成供应商注册 | 目标供应商非合作中状态 |
## 六.6、修改前后对比
### 字段级对比
| 字段 | 改前 | 改后 |
|---|---|---|
| `GET /admin/staff/list` 的 `records[].supplierFullName` | 不返回 | 每条固定返回 `String` 或 `null` |
| 服务人员设置/改绑的 `changeReason` | 必填,否则 `400 changeReason不能为空` | 可省略;原有非空值继续兼容 |
| 服务人员解绑的 `changeReason` | 已可省略 | 保持可省略 |
| 服务人员设置/改绑的 `requiredTypeCode` | 可省略,按 `STAFF` 生效 | 不变 |
### 行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 服务人员列表展示供应商 | 列表无名称字段 | 直接读取 `supplierFullName` |
| 服务人员设置、改绑 | 必须填写变更原因 | 可以不填 |
| 服务人员改绑、解绑 | 不检查订单 | 仍不检查订单(本次未引入门禁) |
## 六.7、影响评估
- **是否破坏向后兼容**: 否;新增可空字段、放宽一个可选入参。原来显式传 `changeReason` 的调用继续有效。
- **前端是否必须同步上线**: 是。
- **前端 workaround 清理点**: 服务人员列表直接展示 `supplierFullName`;复用供应商关系弹窗并传 `STAFF`;删除服务人员设置/改绑的原因弹框与必填校验;解绑请求确认使用 POST。
## 七、不影响范围
- **仅影响**: 管理端服务人员列表、服务人员供应商关系操作。
- **零影响**: 订单创建、状态流转、结算、支付、退款、订单数据、派工与团期人员配置;服务人员自身的创建、修改、审批、价格日历与标签;内部接口 `/internal/staff/**` 及其 `StaffBriefRespVO`、`StaffSimpleRespVO`。
- 景区、餐厅、备品、组合配品、游玩项目、酒店、服务、额外成本、车队各模块的字段、类型、原因规则和订单门禁保持原契约;数据库结构、配置、Redis、MQ 和 Gateway 路由无变化。
## 八、测试环境已验证
TEST 部署提交 `ab7644ea4cb98920fed71062369b272c06a30211`(hl-resource-service,Deploy Panel API 规范客户端,双实例健康启用 2/2);2026-09-12 经 Gateway 以真实 TEST 身份实测 13/13 通过:
| 场景 | 结果 |
|---|---|
| `GET /admin/staff/list` 每条记录含 `supplierFullName` 字段 | 通过 |
| 已关联人员(`staffId=1005`)返回当前供应商全称 | 通过 |
| 未关联的 11 名人员均返回 `supplierFullName: null` | 通过 |
| 未绑定人员查询关系返回 `395038` | 通过 |
| 首次绑定省略 `changeReason` 成功,view 回读为目标供应商 | 通过 |
| 改绑到另一家供应商、省略 `changeReason` 成功,view 回读为新供应商 | 通过 |
| 解绑省略 `changeReason` 成功,之后查询回到 `395038` | 通过 |
| 改绑、解绑前后 `order_staff_assignment`、`order_batch_staff` 摘要一致(订单侧零变化) | 通过 |
**当前状态:后端已部署并验证;待前端处理。**
## 九、相关历史 PR
| 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)
## 关联 / 联系人
### 链接
- **Issue**: [#7512](https://git.1814.love:8443/wx/HL/issues/7512)
- **PR**: [#7575](https://git.1814.love:8443/wx/HL/pulls/7575)
### 联系人
- **后端负责人**: @lc