docs: add supplier context handoff (#7059)
changelog-filename-gate / validate (push) Successful in 2s

这个提交包含在:
lc
2026-09-04 11:11:05 +08:00
父节点 cf78c344a1
当前提交 0df2c917bf
@@ -0,0 +1,306 @@
---
schema: "hl-changelog/v2"
ticket: "7059"
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-04"
status_note: "后端已部署并验证;待前端删除本地映射,仅透传资源上下文。"
updated_at: "2026-09-04"
base: "dev-v3"
---
# 资源管理:设置供应商候选按资源上下文过滤
> **服务**: hl-resource-service (8082)
> **PR**: #7064
> **Issue**: #7059
> **日期**: 2026-09-04
> **影响范围**: 管理后台资源管理、车务管理的“设置供应商”候选与绑定
---
## ⚠️ 关键变化
前端不再维护“资源分类 → 供应商类型”映射;候选查询传 `resourceModule`、`resourceId`,后端解析类型并只返回 `ACTIVE` 且匹配的供应商。
## 一、背景
#7042 要求前端传 `typeCode` 的结论已撤销。本次由后端统一解析资源上下文,避免景区混入车队供应商,并修正组合备品误用 `SUPPLIES` 的问题。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 有界查询供应商 | GET | `/admin/supplier/items/list` | 新增可选查询参数 | 完整资源上下文下由后端过滤候选 |
| 2 | 设置或改绑资源供应商 | PUT | `/admin/supplier/resource-relations/{resourceModule}/{resourceId}/update` | 调整类型解析 | 固定映射模块可省略 `requiredTypeCode` |
## 三、接口详情
### 1. 有界查询供应商 `GET /admin/supplier/items/list`
**VO**: `SupplierListReqVO / SupplierListItemRespVO`
#### 使用场景
资源管理或车务管理打开“设置供应商”候选列表时调用;搜索时沿用 `keyword`。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `resourceModule` | Query | String | 场景必填 | 与 `resourceId` 同传 | 当前资源模块,不是供应商类型 |
| `resourceId` | Query | String | 场景必填 | 正整数,与 `resourceModule` 同传 | 当前资源 ID |
| `keyword` | Query | String | 否 | 最长 500 | 搜索关键字 |
| `limit` | Query | Integer | 否 | 1~200,默认 50 | 候选列表建议传 200 |
| `typeCode` | Query | String | 否 | 通用列表兼容参数 | 设置供应商场景不要传;资源上下文存在时后端忽略调用方值 |
| `status` | Query | String | 否 | 通用列表兼容参数 | 设置供应商场景不要传;资源上下文存在时后端固定 `ACTIVE` |
#### 出参 `Result<List<SupplierListItemRespVO>>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `data[].supplierId` | String | 供应商 ID |
| `data[].supplierNo` | String | 供应商编号 |
| `data[].fullName` | String | 供应商全称 |
| `data[].shortName` | String | 供应商简称 |
| `data[].types` | Array | 类型列表;元素含 `typeCode`、`typeName`、`isPrimary` |
| `data[].status` | String | 资源上下文场景恒为 `ACTIVE` |
| 其他既有字段 | - | 响应结构未变化 |
#### 请求示例
```http
GET /admin/supplier/items/list?resourceModule=SCENIC&resourceId=3001000000000000019&limit=200
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": [
{
"supplierId": "2091381643661983746",
"supplierNo": "SUP2091381643661983746",
"fullName": "示例景区供应商",
"types": [{ "typeCode": "SCENIC", "typeName": "景区", "isPrimary": true }],
"status": "ACTIVE"
}
],
"success": true
}
```
#### 空数据 / 降级响应
没有符合条件的供应商时返回 `data: []`;资源不存在、上下文不完整或映射不可用时失败关闭,不降级为全量列表。
```json
{ "code": 200, "message": "成功", "data": [], "success": true }
```
#### 错误响应
```json
{
"code": 400,
"message": "resourceModule与resourceId必须同时提供或同时省略",
"data": null,
"success": false
}
```
#### 业务边界
- 完整资源上下文下,后端校验供应商列表权限、资源关系查看权限、资源存在性和数据范围。
- 后端只返回状态为 `ACTIVE` 且包含映射类型的供应商;调用方传入的 `typeCode`、`status` 不会覆盖该规则。
- 不传资源上下文时,原有 `typeCode`、`status`、`keyword`、`limit` 通用查询行为保持不变。
### 2. 设置或改绑资源供应商 `PUT /admin/supplier/resource-relations/{resourceModule}/{resourceId}/update`
**VO**: `SupplierResourceReassignReqVO / SupplierResourceRelationRespVO`
#### 使用场景
用户确认候选供应商后首次设置或改绑当前资源的供应商。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `resourceModule` | Path | String | 是 | 见映射表 | 当前资源模块 |
| `resourceId` | Path | String | 是 | 正整数 | 当前资源 ID |
| `supplierId` | Body | String | 是 | 正整数 | 候选供应商 ID |
| `requiredTypeCode` | Body | String | 否 | 最长 64 | 七个固定映射模块应省略,由后端解析 |
| `remark` | Body | String | 否 | 最长 500 | 关系备注 |
| `changeReason` | Body | String | 是 | 非空,最长 500 | 设置或改绑原因 |
| `expectedCurrentSupplierId` | Body | String | 改绑时必填 | 与版本时间同传 | 当前关系供应商 ID |
| `expectedRelationUpdateTime` | Body | String | 改绑时必填 | 与当前供应商 ID 同传 | 当前关系并发版本 |
#### 出参 `Result<SupplierResourceRelationRespVO>`
| 字段 | 类型 | 说明 |
|------|------|------|
| `data.supplierId` | String | 生效供应商 ID |
| `data.resourceModule` | String | 资源模块 |
| `data.resourceId` | String | 资源 ID |
| `data.requiredTypeCode` | String | 后端解析并冻结的供应商类型 |
| `data.updateTime` | String | 后续改绑或解绑使用的并发版本 |
| 其他既有字段 | - | 响应结构未变化 |
#### 请求示例
```json
{
"supplierId": "2091381643661983746",
"changeReason": "设置资源供应商"
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"supplierId": "2091381643661983746",
"resourceModule": "SUPPLIES_COMBO",
"resourceId": "2046122595429806082",
"requiredTypeCode": "SUPPLIES_COMBO",
"updateTime": "2026-09-04 12:00:00"
},
"success": true
}
```
#### 空数据 / 降级响应
写接口成功时返回完整关系;校验失败时返回业务错误和 `data: null`,不会创建或改写关系。
#### 错误响应
```json
{
"code": 395037,
"message": "供应商类型不满足资源关联要求",
"data": null,
"success": false
}
```
#### 业务边界
- 七个固定映射模块由后端确定 `requiredTypeCode`;前端不要提交本地映射值。
- 供应商必须处于 `ACTIVE` 且包含所需类型;失败时关系保持不变。
- 已有关系改绑仍必须传成对的并发版本字段,既有审计、权限和数据范围规则不变。
## 四、契约约束与正确调用方式
| 场景 | 正确调用 | 错误调用 |
|------|----------|----------|
| 打开候选列表 | `resourceModule=SCENIC&resourceId={id}&limit=200` | 前端把 `SCENIC` 映射为 `typeCode` 后只按类型查询 |
| 搜索候选 | 上述参数继续加 `keyword` | 搜索时丢失资源上下文 |
| 设置/改绑 | Path 传资源上下文,Body 省略 `requiredTypeCode` | 前端根据字典或资源分类拼 `requiredTypeCode` |
前端应删除资源分类与供应商类型的本地映射。`supplier_type` 在“系统管理 → 字典管理”维护供应商类型的名称、状态和排序;它不是资源模块映射配置。资源模块映射由后端统一维护。
## 五、数据库行为
设置或改绑时,关系中冻结后端解析出的 `requiredTypeCode`;本次无表结构、存量数据迁移或跨库写入变化,原有关系审计行为保持不变。
## 六、边界行为
- `resourceModule` 与 `resourceId` 只传一个 → `400`,`resourceModule与resourceId必须同时提供或同时省略`。
- 未知模块 → `395034`,`不支持的资源模块`;资源不存在或已删除 → `395035`。
- `ACTIVITY`、`COST_ITEM`、`STAFF` 本工单没有默认映射,按资源上下文查询会以 `400 requiredTypeCode不能为空` 失败关闭。
- 映射类型在 `supplier_type` 字典缺失或停用 → `400`,`供应商类型不合法或已停用`。
- 未登录 → 应用响应 `401`;无权限或超出资源数据范围 → 拒绝访问。
## 六.5、枚举 / 数据字典
### resourceModule 与 supplier_type
**所属字段**: `SupplierListReqVO.resourceModule / SupplierResourceRelationRespVO.requiredTypeCode` | **类型**: `String`
| `resourceModule` | 后端要求的 `supplier_type` | 页面 |
|------------------|-------------------------------|------|
| `SCENIC` | `SCENIC` | 景区管理 |
| `RESTAURANT` | `RESTAURANT` | 餐厅管理 |
| `SUPPLIES` | `SUPPLIES` | 备品管理 |
| `SUPPLIES_COMBO` | `SUPPLIES_COMBO` | 组合配品 |
| `HOTEL` | `HOTEL` | 酒店管理 |
| `SERVICE` | `SERVICE` | 服务管理 |
| `VEHICLE` | `FLEET` | 车务管理-车队管理 |
## 六.6、修改前后对比
### 字段级对比
| 字段 | 改前 | 改后 |
|------|------|------|
| 候选查询资源上下文 | 无 | 新增可选 `resourceModule`、`resourceId`,必须成对传入 |
| `requiredTypeCode` | 组合备品可能由调用方误传 `SUPPLIES` | 固定映射模块可省略,组合备品由后端解析为 `SUPPLIES_COMBO` |
### 行为级对比
| 行为 | 改前 | 改后 |
|------|------|------|
| 设置供应商候选 | 依赖前端映射 `typeCode`,可能展示不对应类型 | 后端按真实资源上下文过滤 `ACTIVE` 且匹配的供应商 |
| 无资源上下文的通用列表 | 按调用方筛选 | 保持不变 |
## 六.7、影响评估
- **是否破坏向后兼容**: 否;无资源上下文的原调用保持兼容。
- **前端是否必须同步上线**: 是。
- **前端 workaround 清理点**: 删除资源分类与供应商类型本地映射;候选查询改传 `resourceModule`、`resourceId`,绑定请求不再拼 `requiredTypeCode`。
## 七、不影响范围
- **仅影响**: 管理后台资源管理、车务管理的供应商候选和设置/改绑类型解析。
- **零影响**: 供应商分页管理、供应商注册审批、既有响应字段、数据库结构、Redis、MQ 和其他前端源码。
## 八、测试环境已验证
```text
SCENIC 候选:忽略错误 typeCode/status,只返回 ACTIVE + SCENIC,FLEET-only 为 0 ✓
SUPPLIES_COMBO 候选及绑定:返回/冻结 SUPPLIES_COMBO,绑定后已解绑恢复原关系状态 ✓
VEHICLE 候选:忽略错误 typeCode/status,只返回 ACTIVE + FLEET ✓
通用列表 typeCode/status/keyword/limit:保持兼容 ✓
上下文不完整、无默认映射、失效类型、资源不存在:均失败关闭 ✓
```
## 九、相关历史 PR
| PR / 提交 | Issue | 说明 | 是否仍有效 |
|-----------|-------|------|------------|
| changelog `5046bce` | #7042 | 原要求前端维护映射 | ❌ 已更正 |
| changelog `a0daab2` | #7042 | 撤销前端映射要求,转后端工单 | ✅ 有效 |
| **PR #7064** | **#7059** | 后端按资源上下文解析、过滤并修正组合备品映射 | ✅ 最新 |
## 十、相关文档
- 关联 Issue: [wx/HL#7059](https://git.1814.love:8443/wx/HL/issues/7059)
- 关联 PR: [wx/HL#7064](https://git.1814.love:8443/wx/HL/pulls/7064)
- 被更正记录: [#7042 Changelog](./03_7042_设置供应商列表按资源类型过滤-前端缺陷-管理后台.md)
## 关联 / 联系人
### 链接
- **Issue**: [#7059](https://git.1814.love:8443/wx/HL/issues/7059)
- **PR**: [#7064](https://git.1814.love:8443/wx/HL/pulls/7064)
- **Merge commit**: [5999b532e9f9725ac3b36f859ee48f6f0b8aa43c](https://git.1814.love:8443/wx/HL/commit/5999b532e9f9725ac3b36f859ee48f6f0b8aa43c)
### 联系人
- **后端负责人**: @lc