--- schema: "hl-changelog/v2" ticket: "7036" title: "供应商主体状态变更接入企微审批" consumer: "admin" author: "lc(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "68ecb96b" target_release: "" verified_at: "2026-09-03" status_note: "后端已部署并完成 TEST 验收;四类状态动作改为企微异步审批,前端需适配审批中与终态刷新。" updated_at: "2026-09-03" base: "dev-v3" --- # 供应商模块:主体状态变更接入企微审批 > **服务**: `hl-resource-service`、`hl-user-service` > **Issue**: #7036 > **PR**: #7043、#7046 > **影响范围**: 管理后台供应商状态操作及审批进度展示 ## ⚠️ 关键变化 列入黑名单、解除黑名单和两种清账归档不再同步改变状态:接口先返回企微审批单,审批通过后才异步迁移;解除黑名单需两个不同审批人依次通过,其余三项均为一级审批。 ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|---|---|---|---|---| | 1 | 列入黑名单 | POST | `/admin/supplier/items/{supplierId}/blacklist` | 行为及响应修改 | 先发起一级企微审批 | | 2 | 解除黑名单 | POST | `/admin/supplier/items/{supplierId}/unblacklist` | 行为及响应修改 | 两个不同审批人串行通过后生效 | | 3 | 清账归档 | POST | `/admin/supplier/items/{supplierId}/archive` | 行为及响应修改 | 按来源状态发起一级企微审批 | ## 三、接口详情 ### 1. 列入黑名单 `POST /admin/supplier/items/{supplierId}/blacklist` **VO**: `SupplierStatusChangeReqVO` → `SupplierStatusChangeRespVO` #### 使用场景 对 `SUSPENDED` 供应商发起“列入黑名单”一级审批。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | supplierId | Path | String | 是 | 正整数 | 供应商 ID | | reason | Body | String | 是 | 1~500 字符 | 变更原因 | | expectedUpdateTime | Body | String | 是 | `yyyy-MM-dd HH:mm:ss` | 页面读取到的并发版本 | #### 出参 `Result` | 字段 | 类型 | 说明 | |---|---|---| | supplierId | String | 供应商 ID | | status | String | 审批中仍为 `SUSPENDED`,通过后为 `BLACKLIST` | | updateTime | String | 当前并发版本 | | approval.approvalLogId | String | 审批记录 ID | | approval.provider | String | 固定为 `WECOM` | | approval.approvalStatus | String | `PENDING` / `APPROVED` / `REJECTED` / `CANCELED` | | approval.spNo | String | 企微审批单号 | | approval.syncStatus | String | 本地同步状态 | | approval.submittedAt | String | 提交时间 | | approval.finishedAt | String / null | 审批完成时间 | #### 请求示例 ```json { "reason": "严重违约", "expectedUpdateTime": "2026-09-03 10:18:27" } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "supplierId": "2094672459314745346", "status": "SUSPENDED", "updateTime": "2026-09-03 10:18:27", "approval": { "approvalLogId": "2095450331436515330", "provider": "WECOM", "approvalStatus": "PENDING", "spNo": "202609030012", "syncStatus": "REQUESTING", "submittedAt": "2026-09-03 18:05:57", "finishedAt": null } }, "success": true } ``` #### 空数据 / 降级响应 写接口不返回空成功数据;企微提交失败返回业务失败,供应商保持 `SUSPENDED`。 #### 错误响应 ```json { "code": 395005, "message": "当前状态不允许执行该操作", "data": null, "success": false } ``` #### 业务边界 - 需要供应商状态管理权限;`expectedUpdateTime` 不一致返回 `395014`。 - 顶层成功仅表示企微建单成功,前端不得立即展示为黑名单。 - 驳回或撤销不改变供应商状态。 ### 2. 解除黑名单 `POST /admin/supplier/items/{supplierId}/unblacklist` **VO**: `SupplierStatusChangeReqVO` → `SupplierStatusChangeRespVO` #### 使用场景 对 `BLACKLIST` 供应商发起“解除黑名单”两级串行审批。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | supplierId | Path | String | 是 | 正整数 | 供应商 ID | | reason | Body | String | 是 | 1~500 字符 | 解除原因 | | expectedUpdateTime | Body | String | 是 | `yyyy-MM-dd HH:mm:ss` | 页面读取到的并发版本 | #### 出参 `Result` | 字段 | 类型 | 说明 | |---|---|---| | supplierId | String | 供应商 ID | | status | String | 两级审批完成前仍为 `BLACKLIST`,通过后为 `SUSPENDED` | | updateTime | String | 当前并发版本 | | approval.approvalLogId | String | 审批记录 ID | | approval.provider | String | 固定为 `WECOM` | | approval.approvalStatus | String | 审批状态 | | approval.spNo | String | 企微审批单号 | | approval.syncStatus | String | 本地同步状态 | | approval.submittedAt | String | 提交时间 | | approval.finishedAt | String / null | 审批完成时间 | #### 请求示例 ```json { "reason": "整改完成,申请解除", "expectedUpdateTime": "2026-09-02 15:49:06" } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "supplierId": "2091381911266967553", "status": "BLACKLIST", "updateTime": "2026-09-02 15:49:06", "approval": { "approvalLogId": "2095450475109527553", "provider": "WECOM", "approvalStatus": "PENDING", "spNo": "202609030013", "syncStatus": "REQUESTING", "submittedAt": "2026-09-03 18:06:31", "finishedAt": null } }, "success": true } ``` #### 空数据 / 降级响应 写接口不返回空成功数据;企微提交失败返回业务失败,供应商保持 `BLACKLIST`。 #### 错误响应 ```json { "code": 395014, "message": "数据已被他人修改,请刷新后重试", "data": null, "success": false } ``` #### 业务边界 - 必须由企微模板配置的两个不同审批人按顺序通过,只完成一级时状态不变。 - 通过后的目标为 `SUSPENDED`,不会直接恢复到 `ACTIVE`。 - 驳回、撤销或审批链不完整均不改变供应商状态。 ### 3. 清账归档 `POST /admin/supplier/items/{supplierId}/archive` **VO**: `Void` → `SupplierArchiveRespVO` #### 使用场景 `SUSPENDED` 选择“终止且账清”,或 `BLACKLIST` 选择“拉黑且账清”,发起一级审批并在通过后归档。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | supplierId | Path | String | 是 | 正整数 | 供应商 ID | #### 出参 `Result` | 字段 | 类型 | 说明 | |---|---|---| | supplierId | String | 供应商 ID | | status | String | 审批中保持来源状态,通过后为 `ARCHIVED` | | clearanceRequestId | null | 本期不提供权威清账凭证 | | checkedAt | null | 本期不提供权威清账核验时间 | | ledgerRevision | null | 本期不提供账本版本 | | updateTime | String | 当前并发版本 | | approval.approvalLogId | String | 审批记录 ID | | approval.provider | String | 固定为 `WECOM` | | approval.approvalStatus | String | 审批状态 | | approval.spNo | String | 企微审批单号 | | approval.syncStatus | String | 本地同步状态 | #### 请求示例 ```http POST /admin/supplier/items/2095047059194138625/archive Authorization: Bearer <管理端登录凭证> 无请求体 ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "supplierId": "2095047059194138625", "status": "SUSPENDED", "clearanceRequestId": null, "checkedAt": null, "ledgerRevision": null, "updateTime": "2026-09-03 09:48:55", "approval": { "approvalLogId": "2095450500279549953", "provider": "WECOM", "approvalStatus": "PENDING", "spNo": "202609030014", "syncStatus": "REQUESTING" } }, "success": true } ``` #### 空数据 / 降级响应 写接口不返回空成功数据;企微提交失败返回业务失败,供应商保持来源状态。 #### 错误响应 ```json { "code": 395021, "message": "企业微信申请提交失败,请稍后重试", "data": null, "success": false } ``` #### 业务边界 - `SUSPENDED` 和 `BLACKLIST` 分别映射为两个固定变更事项,但均只需一级审批。 - 接口不接收清账证据;三个预留清账字段继续返回 `null`。 - 驳回或撤销不归档,`ARCHIVED` 仍为只读终态。 ## 四、契约约束与正确调用方式 1. 提交动作后以 `approval.approvalStatus` 展示审批中,不以 HTTP 200 推断状态已经变化。 2. 轮询 `GET /admin/supplier/items/{supplierId}/approval-history/page` 展示审批层级及终态,同时刷新 `GET /admin/supplier/items/{supplierId}/basic-info/view` 获取最终主体状态。 3. `GET /admin/supplier/items/{supplierId}/change-records/page` 只展示审批通过后真正发生的状态变化;审批中、驳回和撤销没有对应变更记录。 4. 企微表单的供应商、变更事项、当前状态、目标状态、申请原因、当前余额、最近拉黑记录、详情链接和申请人均由后端填写,前端不传审批人或模板控件值。 ## 五、数据库行为 提交成功后可立即查询到独立审批记录,但主体状态和变更记录不变;企微有效审批链全部通过后,状态迁移与新增关联变更记录同时完成。驳回或撤销只结束审批记录,不产生状态变更记录。 ## 六、边界行为 - 未登录或无供应商状态管理权限时拒绝写入。 - 同一供应商已有在途状态审批时复用该审批,不重复建单。 - 企微提交失败返回 `395021`;结果不确定返回 `395022`,前端不得自动重复提交。 - 暂停合作和恢复合作仍沿用原直接状态迁移,不进入本审批模板。 ## 六.5、枚举 / 数据字典 | 动作 | 来源状态 | 企微变更事项 | 审批层级 | 通过后状态 | |---|---|---|---:|---| | 列入黑名单 | `SUSPENDED` | 列入黑名单 | 1 | `BLACKLIST` | | 解除黑名单 | `BLACKLIST` | 解除黑名单 | 2 | `SUSPENDED` | | 清账归档 | `SUSPENDED` | 终止且账清 | 1 | `ARCHIVED` | | 清账归档 | `BLACKLIST` | 拉黑且账清 | 1 | `ARCHIVED` | ## 六.6、修改前后对比 | 行为 | 修改前 | 修改后 | |---|---|---| | 列入/解除黑名单 | 请求内直接迁移 | 企微审批通过后迁移 | | 清账归档 | 旧审批提供方语义 | 指定企微模板一级审批 | | 解除黑名单 | 无两级企微门禁 | 两个不同审批人串行通过 | | 接口响应 | 仅返回状态 | 追加 `approval` 审批受理信息 | ## 六.7、影响评估 - **是否破坏向后兼容**: 是;状态动作从同步完成改为异步审批。 - **前端是否必须同步上线**: 是。 - **前端 workaround 清理点**: 移除请求成功即展示目标状态的逻辑,改为展示审批中并刷新审批历史与主体状态。 ## 七、不影响范围 - **仅影响**: 管理后台供应商主体的列入黑名单、解除黑名单和清账归档。 - **零影响**: 暂停合作、恢复合作、供应商注册审批、后续新增账户审批、合同及资源绑定。 ## 八、测试环境已验证 - 四类动作均通过指定企微模板建单;审批完成前保持来源状态,审批记录与状态变更记录分表保存。 - “列入黑名单”“终止且账清”“拉黑且账清”一级通过后分别迁移为 `BLACKLIST`、`ARCHIVED`、`ARCHIVED`。 - “解除黑名单”一级通过后仍为 `BLACKLIST`,由第二位不同审批人通过后迁移为 `SUSPENDED`。 - 空申请原因返回业务 `400` 且未新增审批记录。 ## 十、相关文档 - [Issue #7036](https://git.1814.love:8443/wx/HL/issues/7036) - [PR #7043](https://git.1814.love:8443/wx/HL/pulls/7043) - [补充 PR #7046](https://git.1814.love:8443/wx/HL/pulls/7046) ## 前端动作与当前状态 - 三个写接口成功后展示“审批中”,并使用返回的 `approval` 信息追踪企微状态。 - 解除黑名单展示两级进度;一级通过时仍保持黑名单态。 - 仅以主体详情最终状态和变更记录确认动作生效。 - **当前状态:待前端处理。** ## 关联 / 联系人 - **Issue**: [#7036](https://git.1814.love:8443/wx/HL/issues/7036) - **PR**: [#7043](https://git.1814.love:8443/wx/HL/pulls/7043) - **Merge commit**: [7c99e96a8f7c0977492a620483d86a7340c400ee](https://git.1814.love:8443/wx/HL/commit/7c99e96a8f7c0977492a620483d86a7340c400ee) - **后端负责人**: @lc