docs(changelog): #6979 供应商状态流转契约
changelog-filename-gate / validate (push) Successful in 2s

这个提交包含在:
lc
2026-09-03 08:10:43 +08:00
父节点 c579da759e
当前提交 f68b6d80d4
@@ -0,0 +1,511 @@
---
schema: "hl-changelog/v2"
ticket: "6979"
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-03"
status_note: "PR #7007 已合并 dev-v3 并部署 TEST;当前状态统一使用 SUSPENDED,新增恢复合作和解除黑名单接口,前端需按状态矩阵调整菜单。"
updated_at: "2026-09-03"
base: "dev-v3"
---
# 供应商模块:统一暂停状态与拉黑归档流转
> **服务**: `hl-resource-service`(8082)
> **Issue**: #6979
> **PR**: #7007
> **影响范围**: 管理后台供应商列表、详情与状态操作菜单
## ⚠️ 关键变化
- 当前供应商状态不再返回 `FROZEN`,暂停合作统一为 `SUSPENDED`。
- 新增“恢复合作”和“解除黑名单”接口;拉黑只允许从 `SUSPENDED` 发起。
- 归档只允许从 `SUSPENDED` 或 `BLACKLIST` 进入门禁;清账能力未交付时仍返回 `395032`,不会归档。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 分页查询供应商 | GET | `/admin/supplier/items/page` | 响应枚举修改 | 当前状态不再返回 `FROZEN` |
| 2 | 有界查询供应商 | GET | `/admin/supplier/items/list` | 响应枚举修改 | 当前状态不再返回 `FROZEN` |
| 3 | 查询供应商详情 | GET | `/admin/supplier/items/{supplierId}/basic-info/view` | 响应枚举修改 | 暂停状态统一返回 `SUSPENDED` |
| 4 | 暂停合作 | POST | `/admin/supplier/items/{supplierId}/suspend` | 行为修改 | `ACTIVE → SUSPENDED` |
| 5 | 恢复合作 | POST | `/admin/supplier/items/{supplierId}/resume` | 新增接口 | `SUSPENDED → ACTIVE` |
| 6 | 拉入黑名单 | POST | `/admin/supplier/items/{supplierId}/blacklist` | 行为修改 | 仅允许 `SUSPENDED → BLACKLIST` |
| 7 | 解除黑名单 | POST | `/admin/supplier/items/{supplierId}/unblacklist` | 新增接口 | `BLACKLIST → SUSPENDED` |
| 8 | 清账归档 | POST | `/admin/supplier/items/{supplierId}/archive` | 行为修改 | 仅 `SUSPENDED/BLACKLIST` 可进入清账门禁 |
## 三、接口详情
### 1. 分页查询供应商 `GET /admin/supplier/items/page`
**VO**: `SupplierPageReqVO / PageResult<SupplierListItemRespVO>`
#### 使用场景
供应商列表页分页查询,并依据每行 `status` 显示状态菜单。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| page / pageSize | Query | Integer | 否 | 页码 ≥ 1;每页 ≤ 100 | 分页参数 |
| status | Query | String | 否 | 当前六状态之一 | 状态筛选 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| records[].status | String | 当前状态;不再返回 `FROZEN` |
| records[].updateTime | LocalDateTime | 状态动作的乐观锁版本 |
#### 请求示例
```http
GET /admin/supplier/items/page?page=1&pageSize=20&status=SUSPENDED
```
#### 响应示例
```json
{"code":200,"data":{"records":[{"supplierId":"2094672459314745346","status":"SUSPENDED","updateTime":"2026-09-03 08:03:00"}],"total":1,"page":1,"pageSize":20},"success":true}
```
#### 空数据 / 降级响应
无匹配数据返回 `records: []`;本接口无业务降级分支。
#### 错误响应
```json
{"code":401,"message":"缺少有效的 Authorization 头","data":null,"success":false}
```
#### 业务边界
- `status=FROZEN` 不再是合法筛选值;请改用 `SUSPENDED`。
### 2. 有界查询供应商 `GET /admin/supplier/items/list`
**VO**: `SupplierListReqVO / List<SupplierListItemRespVO>`
#### 使用场景
供应商选择器或短列表查询,并依据 `status` 显示状态菜单。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| status | Query | String | 否 | 当前六状态之一 | 状态筛选 |
| limit | Query | Integer | 否 | 1~200,默认 50 | 最大返回数 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| data[].status | String | 当前状态;暂停合作为 `SUSPENDED` |
| data[].updateTime | LocalDateTime | 状态动作的乐观锁版本 |
#### 请求示例
```http
GET /admin/supplier/items/list?status=BLACKLIST&limit=50
```
#### 响应示例
```json
{"code":200,"data":[{"supplierId":"2094672459314745346","status":"BLACKLIST","updateTime":"2026-09-03 08:04:00"}],"success":true}
```
#### 空数据 / 降级响应
无匹配数据返回 `data: []`;本接口无业务降级分支。
#### 错误响应
```json
{"code":400,"message":"供应商状态不合法","data":null,"success":false}
```
#### 业务边界
- 当前状态只接受 `DRAFT/VETTING/ACTIVE/SUSPENDED/BLACKLIST/ARCHIVED`。
### 3. 查询供应商详情 `GET /admin/supplier/items/{supplierId}/basic-info/view`
**VO**: `SupplierBasicInfoRespVO`
#### 使用场景
进入供应商详情或执行状态动作前,读取当前状态和最新并发版本。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| supplierId | Path | String | 是 | 正整数雪花 ID | 供应商 ID |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| status | String | 当前六状态之一 |
| updateTime | LocalDateTime | 后续状态动作原样回传 |
#### 请求示例
```http
GET /admin/supplier/items/2094672459314745346/basic-info/view
```
#### 响应示例
```json
{"code":200,"data":{"supplierId":"2094672459314745346","status":"ACTIVE","updateTime":"2026-09-03 08:05:00"},"success":true}
```
#### 空数据 / 降级响应
详情无空对象或降级结果;目标不存在时返回业务错误。
#### 错误响应
```json
{"code":395001,"message":"供应商不存在","data":null,"success":false}
```
#### 业务边界
- 暂停合作的当前主表状态统一返回 `SUSPENDED`,历史记录仍可能展示旧快照 `FROZEN`。
### 4. 暂停合作 `POST /admin/supplier/items/{supplierId}/suspend`
**VO**: `SupplierStatusChangeReqVO / SupplierStatusChangeRespVO`
#### 使用场景
在 `ACTIVE` 行点击“暂停合作”。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| supplierId | Path | String | 是 | 正整数雪花 ID | 供应商 ID |
| reason | Body | String | 是 | 1~500 字符 | 审计原因 |
| expectedUpdateTime | Body | LocalDateTime | 是 | `yyyy-MM-dd HH:mm:ss` | 最近读取的版本 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| status | String | 固定为 `SUSPENDED` |
| updateTime | LocalDateTime | 新并发版本 |
#### 请求示例
```json
{"reason":"暂停业务合作","expectedUpdateTime":"2026-09-03 08:02:00"}
```
#### 响应示例
```json
{"code":200,"data":{"supplierId":"2094672459314745346","status":"SUSPENDED","updateTime":"2026-09-03 08:03:00"},"success":true}
```
#### 空数据 / 降级响应
写接口无空成功结果,也不提供降级成功。
#### 错误响应
```json
{"code":395005,"message":"当前状态不允许执行该操作","data":null,"success":false}
```
#### 业务边界
- 仅 `ACTIVE` 可调用;成功后继续通知车务停用关联车队。
### 5. 恢复合作 `POST /admin/supplier/items/{supplierId}/resume`
**VO**: `SupplierStatusChangeReqVO / SupplierStatusChangeRespVO`
#### 使用场景
在 `SUSPENDED` 行点击“恢复合作”。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| supplierId | Path | String | 是 | 正整数雪花 ID | 供应商 ID |
| reason | Body | String | 是 | 1~500 字符 | 审计原因 |
| expectedUpdateTime | Body | LocalDateTime | 是 | `yyyy-MM-dd HH:mm:ss` | 最近读取的版本 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| status | String | 固定为 `ACTIVE` |
| updateTime | LocalDateTime | 新并发版本 |
#### 请求示例
```json
{"reason":"恢复业务合作","expectedUpdateTime":"2026-09-03 08:03:00"}
```
#### 响应示例
```json
{"code":200,"data":{"supplierId":"2094672459314745346","status":"ACTIVE","updateTime":"2026-09-03 08:04:00"},"success":true}
```
#### 空数据 / 降级响应
写接口无空成功结果,也不提供降级成功。
#### 错误响应
```json
{"code":395014,"message":"数据已被他人修改,请刷新后重试","data":null,"success":false}
```
#### 业务边界
- 仅 `SUSPENDED` 可调用;恢复供应商不会自动启用车队。
### 6. 拉入黑名单 `POST /admin/supplier/items/{supplierId}/blacklist`
**VO**: `SupplierStatusChangeReqVO / SupplierStatusChangeRespVO`
#### 使用场景
在 `SUSPENDED` 行点击“拉入黑名单”。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| supplierId | Path | String | 是 | 正整数雪花 ID | 供应商 ID |
| reason | Body | String | 是 | 1~500 字符 | 审计原因 |
| expectedUpdateTime | Body | LocalDateTime | 是 | `yyyy-MM-dd HH:mm:ss` | 最近读取的版本 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| status | String | 固定为 `BLACKLIST` |
| updateTime | LocalDateTime | 新并发版本 |
#### 请求示例
```json
{"reason":"列入合作黑名单","expectedUpdateTime":"2026-09-03 08:03:00"}
```
#### 响应示例
```json
{"code":200,"data":{"supplierId":"2094672459314745346","status":"BLACKLIST","updateTime":"2026-09-03 08:04:00"},"success":true}
```
#### 空数据 / 降级响应
写接口无空成功结果,也不提供降级成功。
#### 错误响应
```json
{"code":395005,"message":"当前状态不允许执行该操作","data":null,"success":false}
```
#### 业务边界
- 仅 `SUSPENDED` 可调用;`ACTIVE` 直接拉黑返回 `395005`,成功后通知车务停用关联车队。
### 7. 解除黑名单 `POST /admin/supplier/items/{supplierId}/unblacklist`
**VO**: `SupplierStatusChangeReqVO / SupplierStatusChangeRespVO`
#### 使用场景
在 `BLACKLIST` 行点击“解除黑名单”。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| supplierId | Path | String | 是 | 正整数雪花 ID | 供应商 ID |
| reason | Body | String | 是 | 1~500 字符 | 审计原因 |
| expectedUpdateTime | Body | LocalDateTime | 是 | `yyyy-MM-dd HH:mm:ss` | 最近读取的版本 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| status | String | 固定为 `SUSPENDED` |
| updateTime | LocalDateTime | 新并发版本 |
#### 请求示例
```json
{"reason":"解除合作黑名单","expectedUpdateTime":"2026-09-03 08:04:00"}
```
#### 响应示例
```json
{"code":200,"data":{"supplierId":"2094672459314745346","status":"SUSPENDED","updateTime":"2026-09-03 08:05:00"},"success":true}
```
#### 空数据 / 降级响应
写接口无空成功结果,也不提供降级成功。
#### 错误响应
```json
{"code":395005,"message":"当前状态不允许执行该操作","data":null,"success":false}
```
#### 业务边界
- 仅 `BLACKLIST` 可调用;成功后回到 `SUSPENDED`,不会直接恢复交易或自动启用车队。
### 8. 清账归档 `POST /admin/supplier/items/{supplierId}/archive`
**VO**: `SupplierArchiveRespVO`
#### 使用场景
在 `SUSPENDED` 或 `BLACKLIST` 行点击“清账归档”。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| supplierId | Path | String | 是 | 正整数雪花 ID | 供应商 ID;无请求体 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| data | SupplierArchiveRespVO | 当前清账能力未交付,不会返回成功数据 |
#### 请求示例
```http
POST /admin/supplier/items/2094672459314745346/archive
```
#### 响应示例
```json
{"code":395032,"message":"暂无法确认财务已清账,不能归档","data":null,"success":false}
```
#### 空数据 / 降级响应
当前固定失败关闭,不提供空数据或降级成功。
#### 错误响应
```json
{"code":395005,"message":"当前状态不允许执行该操作","data":null,"success":false}
```
#### 业务边界
- `ACTIVE` 先返回 `395005`;`SUSPENDED/BLACKLIST` 进入清账门禁后返回 `395032`,两者均零写入。
## 四、契约约束与正确调用方式
| 当前状态 | 仅显示以下操作 | 正确接口 |
|---|---|---|
| `ACTIVE` | 暂停合作 | `/suspend` |
| `SUSPENDED` | 恢复合作、拉入黑名单、清账归档 | `/resume`、`/blacklist`、`/archive` |
| `BLACKLIST` | 解除黑名单、清账归档 | `/unblacklist`、`/archive` |
- `expectedUpdateTime` 必须使用最近一次详情或列表返回值;成功后使用响应的新版本刷新页面。
- 统一响应可能由 HTTP 200 承载业务失败,必须同时判断 `code` 和 `success`。
## 五、数据库行为
- 当前主表中的旧 `FROZEN` 已转换为 `SUSPENDED`;历史审批与变更快照不改写。
- 四个状态接口成功时更新当前状态和版本,并各写一条既有变更审计;失败路径不写主体、审批或审计。
## 六、边界行为
- 未登录返回业务码 `401`。
- 生命周期写操作要求 `SUPER_ADMIN` 和 `supplier:status:manage`;不满足返回 `395004`。
- `395005` 表示来源状态错误;`395014` 表示版本过期;`395032` 表示清账能力不可用;`100502` 表示 5 秒内重复提交。
- 暂停、拉黑继续通知车务停用关联车队;恢复、解除黑名单不会自动启用车队。
## 六.5、枚举 / 数据字典
### `status`(供应商当前生命周期)
| 值 | 中文 | 菜单语义 |
|---|---|---|
| `ACTIVE` | 合作中 | 仅暂停合作 |
| `SUSPENDED` | 暂停合作 | 恢复、拉黑、清账归档 |
| `BLACKLIST` | 黑名单 | 解除黑名单、清账归档 |
`DRAFT/VETTING/ARCHIVED` 保持既有语义;当前接口不再产生或返回主表状态 `FROZEN`。
## 六.6、修改前后对比
| 场景 | 修改前 | 修改后 |
|---|---|---|
| 暂停合作 | 当前状态进入 `FROZEN` | 当前状态进入 `SUSPENDED` |
| 恢复合作 | 无独立接口 | 新增 `/resume` |
| `ACTIVE` 直接拉黑 | 可进入黑名单 | 返回 `395005`,零写入 |
| 解除黑名单 | 无独立接口 | 新增 `/unblacklist`,返回 `SUSPENDED` |
| 归档来源 | `ACTIVE/BLACKLIST` | `SUSPENDED/BLACKLIST` |
## 六.7、影响评估
- **是否破坏向后兼容**: 是。依赖当前态 `FROZEN` 或 `ACTIVE` 直接拉黑的调用方式必须调整。
- **前端是否必须同步上线**: 是。
- **前端 workaround 清理点**: 删除当前态 `FROZEN` 的筛选、文案与按钮分支。
## 七、不影响范围
- **仅影响**: 管理后台供应商当前状态展示与生命周期操作。
- **零影响**: 草稿建档、注册审批、收款账户、资源绑定、小程序与历史审批/变更快照展示。
## 八、测试环境已验证
- `ACTIVE → SUSPENDED → BLACKLIST → SUSPENDED → ACTIVE` 全链路通过,列表与详情状态一致。
- `ACTIVE` 直接拉黑/归档返回 `395005`;两种可归档来源均返回 `395032` 且零写入。
- 乐观锁、重复提交、未认证门禁通过;四次成功动作写四条变更审计,审批记录未增加。
- 部署提交:[`8d38f69d2d6ebd99fb25fb6b0f5dc50ce4cfa1c0`](https://git.1814.love:8443/wx/HL/commit/8d38f69d2d6ebd99fb25fb6b0f5dc50ce4cfa1c0)。
## 十、相关文档
- [Issue #6979](https://git.1814.love:8443/wx/HL/issues/6979)
- [PR #7007](https://git.1814.love:8443/wx/HL/pulls/7007)
## 前端动作与当前状态
- 移除当前态 `FROZEN`,统一映射 `SUSPENDED` 为“暂停合作”。
- 按状态矩阵接入两个新增接口并收紧按钮显隐;`395032` 不得更新页面为已归档。
- **当前状态:待前端接入。**
## 关联 / 联系人
- **Issue**: [#6979](https://git.1814.love:8443/wx/HL/issues/6979)
- **PR**: [#7007](https://git.1814.love:8443/wx/HL/pulls/7007)
- **后端负责人**: @lc