文件
hl-api-changelog/changelogs-v2/2026-08/27_6474_供应商详情分离变更记录与审批流水-新增接口-管理后台.md
T
lc 605859b12f
changelog-filename-gate / validate (push) Successful in 2s
docs: 交付供应商历史分离契约(#6474)
2026-08-27 16:44:13 +08:00

23 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 6474 供应商详情分离变更记录与审批流水 admin lc(GIT) 新增接口 deployed verified pending PR #6519 已合并 dev-v3;补充缺陷 PR #6534 已恢复旧兼容查询。hl-resource-service 已由 Deploy Panel 任务 9e69fd2c 精确发布提交 aec1de2d 至 TEST,并以真实 SUPER_ADMIN、ADMIN 和受限 CUSTOMIZER 身份完成 Gateway 只读验收。 2026-08-27 dev-v3

供应商管理:详情分离变更记录与审批流水

供应商详情新增两个相互独立的只读分页接口:“变更记录”只返回供应商主体发生过的业务变化,“审批记录”只返回供应商主体审批事实。两个列表独立计数、独立筛选、独立排序,收款账户记录不会混入。

旧 /admin/supplier/items/{supplierId}/approval-records/page 继续保留,用于仍需主体与账户变更混合列表的兼容场景;本次不删除、不改名,也不要求现有调用方同步切换。

一、背景

旧审批记录接口承载的是主体与收款账户变更的兼容混合列表,不能同时满足详情页“业务变更”和“审批过程”两种独立展示语义。若管理端在本地拆分或二次计数,会出现分页总数不准确、账户记录混入主体历史、审批技术字段误展示等问题。

本次由后端直接提供两个稳定的业务白名单视图:

  • “变更记录”展示操作类型、变更前后业务摘要、原因、生命周期状态、操作人、历史角色和发生时间。
  • “审批记录”展示审批业务、申请人、状态、动作、审批人安全展示值、意见、提交时间和完成时间。
  • 两个接口都在查询前校验可信管理身份、角色和 supplier:approval:read 权限。

二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 查询供应商主体变更记录 GET /admin/supplier/items/{supplierId}/change-records/page 新增只读接口 独立分页返回主体变更业务摘要
2 查询供应商主体审批流水 GET /admin/supplier/items/{supplierId}/approval-history/page 新增只读接口 独立分页返回主体审批过程与结果

三、接口详情

1. 查询供应商主体变更记录 GET /admin/supplier/items/{supplierId}/change-records/page

VO: SupplierChangeRecordPageReqVO / SupplierChangeRecordRespVO

使用场景

管理端进入供应商详情的“变更记录”页签时调用。服务端完成主体记录筛选、分页和业务摘要投影;前端不要从旧混合列表中再次筛选主体记录,也不要自行拼接前后值摘要。

入参

字段 位置 类型 必填 约束 说明
supplierId Path String 是 正整数 ID 字符串 目标供应商;不得转为 JavaScript Number
page Query Integer 否 默认 1,最小 1 当前页;兼容别名 pageNo
pageSize Query Integer 否 默认 20,范围 1..100 每页条数
operationType Query String 否 CREATE、UPDATE、ENABLE、DISABLE、DELETE 操作类型精确筛选
fieldName Query String 否 非空白,最长 64 字符 发生变化的业务字段精确筛选
status Query String 否 生命周期编码 按变更后的供应商状态筛选
from Query String 条件必填 yyyy-MM-dd HH:mm:ss 与 to 成对传入,按发生时间筛选,含边界
to Query String 条件必填 yyyy-MM-dd HH:mm:ss,不得早于 from 与 from 成对传入,含边界
sortBy Query String 否 occurredAt 或 changeLogId,默认 occurredAt 服务端白名单排序字段
sortDirection Query String 否 ASC 或 DESC,不区分大小写,默认 DESC 排序方向

出参 Result<PageResult<SupplierChangeRecordRespVO>>

分页对象固定包含 records、total、page、pageSize。

字段 类型 说明
records Array 当前页主体变更记录;无数据时为 []
total Integer 符合筛选条件的主体变更总数,不包含账户记录
page Integer 当前页码
pageSize Integer 当前页容量
records[].operationType String 操作类型编码
records[].oldValue String 变更前中文业务摘要;空值使用明确占位
records[].newValue String 变更后中文业务摘要;空值使用明确占位
records[].valueAvailability String FULL 或 LEGACY_MASKED_UNRECOVERABLE
records[].changeReason String 业务变更原因或稳定占位
records[].status String/null 变更后的供应商生命周期中文名
records[].operatorName String 操作人展示名;系统操作为“系统”,依赖降级为“未知管理员”
records[].operatorRole String 操作发生时的角色快照中文名
records[].occurredAt String 变更发生时间,格式 yyyy-MM-dd HH:mm:ss

响应不会返回变更日志 ID、管理员内部 ID、原始审计 JSON、TraceId、账户 ID、证明附件或其他主体敏感快照字段。

请求示例

GET /admin/supplier/items/2092800000000000001/change-records/page?page=1&pageSize=20&operationType=UPDATE&sortBy=occurredAt&sortDirection=DESC
Authorization: Bearer <admin-token>

GET 请求无请求体。

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "records": [
      {
        "operationType": "UPDATE",
        "oldValue": "供应商全称:示例旅行服务公司",
        "newValue": "供应商全称:示例旅行服务有限公司",
        "valueAvailability": "FULL",
        "changeReason": "修正供应商主体名称",
        "status": "合作中",
        "operatorName": "示例管理员",
        "operatorRole": "超级管理员",
        "occurredAt": "2026-08-27 15:20:00"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}

空数据 / 降级响应

筛选结果为空仍返回成功分页,不回退到旧混合列表:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "records": [],
    "total": 0,
    "page": 1,
    "pageSize": 20
  }
}

历史记录无法恢复完整原值时,仍返回已有安全摘要,并明确标记:

{
  "operationType": "UPDATE",
  "oldValue": "138****8000",
  "newValue": "139****9000",
  "valueAvailability": "LEGACY_MASKED_UNRECOVERABLE",
  "changeReason": "历史记录",
  "status": "合作中",
  "operatorName": "未知管理员",
  "operatorRole": "管理员",
  "occurredAt": "2026-07-01 10:00:00"
}

错误响应

非法筛选、分页、排序或时间范围返回参数错误,例如只传 from:

{
  "code": 400,
  "message": "from和to必须同时传入",
  "success": false,
  "data": null
}

供应商不存在或已删除:

{
  "code": 395001,
  "message": "供应商不存在",
  "success": false,
  "data": null
}

业务边界

  • 要求 Gateway 登录态、可信管理员身份、允许的读角色以及 supplier:approval:read 平台权限;任一门禁失败时不查询历史数据。
  • 代码角色矩阵沿用 ADMIN、FINANCE、SUPER_ADMIN;角色允许不等于拥有平台权限,两项必须同时满足。
  • 只读取供应商主体变更,不包含收款账户变更或审批流水。
  • oldValue、newValue 是后端形成的中文业务摘要,不是可回填编辑表单的结构化快照。
  • User 姓名依赖异常只影响 operatorName,分页仍成功且不会以管理员内部 ID 降级。
  • 接口只读,成功、空数据和失败场景均不修改供应商、审批、缓存或消息状态。

2. 查询供应商主体审批流水 GET /admin/supplier/items/{supplierId}/approval-history/page

VO: SupplierApprovalHistoryPageReqVO / SupplierApprovalHistoryRespVO

使用场景

管理端进入供应商详情的“审批记录”页签时调用。该列表只表达主体审批申请、过程和结果,不包含主体字段变更摘要,也不包含收款账户审批。

入参

字段 位置 类型 必填 约束 说明
supplierId Path String 是 正整数 ID 字符串 目标供应商;不得转为 JavaScript Number
page Query Integer 否 默认 1,最小 1 当前页;兼容别名 pageNo
pageSize Query Integer 否 默认 20,范围 1..100 每页条数
bizType Query String 否 大写字母开头,仅大写字母、数字、下划线,最长 32 字符 审批业务类型精确筛选
approvalStatus Query String 否 大写字母开头,仅大写字母、数字、下划线,最长 20 字符 审批状态精确筛选
action Query String 否 大写字母开头,仅大写字母、数字、下划线,最长 32 字符 审批动作或结果精确筛选
from Query String 条件必填 yyyy-MM-dd HH:mm:ss 与 to 成对传入,按提交时间筛选,含边界
to Query String 条件必填 yyyy-MM-dd HH:mm:ss,不得早于 from 与 from 成对传入,含边界
sortBy Query String 否 submittedAt 或 finishedAt,默认 submittedAt 服务端白名单排序字段
sortDirection Query String 否 ASC 或 DESC,不区分大小写,默认 DESC 排序方向

出参 Result<PageResult<SupplierApprovalHistoryRespVO>>

分页对象固定包含 records、total、page、pageSize。

字段 类型 说明
records Array 当前页主体审批记录;无数据时为 []
total Integer 符合筛选条件的主体审批总数,不包含账户审批
page Integer 当前页码
pageSize Integer 当前页容量
records[].bizType String 审批业务类型编码
records[].bizTypeName String 审批业务类型中文名;未知编码显示“其他供应商审批”
records[].applicantName String 申请人展示名;系统申请为“系统”,依赖降级为“未知申请人”
records[].approvalStatus String 审批状态编码
records[].approvalStatusName String 审批状态中文名;未知编码显示“未知状态”
records[].action String/null 审批动作或结果编码
records[].actionName String 审批动作中文名;未知编码显示“其他动作”
records[].approverName String 安全展示值:“系统”“待审批”或“外部审批人”
records[].opinion String 审批意见;无意见时为 —
records[].submittedAt String 提交时间;历史缺失时使用该审批事实的创建时间,格式 yyyy-MM-dd HH:mm:ss
records[].finishedAt String/null 完成时间;审批中为 null,格式 yyyy-MM-dd HH:mm:ss

响应不会返回审批日志 ID、申请人内部 ID、requestNo、spNo、审批模板 ID、企微用户 ID、候选或详情摘要、候选或详情 JSON/密文、apply/sync 技术状态。

请求示例

GET /admin/supplier/items/2092800000000000001/approval-history/page?page=1&pageSize=20&approvalStatus=APPROVED&sortBy=submittedAt&sortDirection=DESC
Authorization: Bearer <admin-token>

GET 请求无请求体。

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "records": [
      {
        "bizType": "PROFILE_CREATE",
        "bizTypeName": "供应商建档审批",
        "applicantName": "示例管理员",
        "approvalStatus": "APPROVED",
        "approvalStatusName": "已通过",
        "action": "SYSTEM_AUTO_APPROVE",
        "actionName": "系统自动通过",
        "approverName": "系统",
        "opinion": "—",
        "submittedAt": "2026-08-27 14:00:00",
        "finishedAt": "2026-08-27 14:00:01"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}

空数据 / 降级响应

不存在符合条件的审批事实时返回成功空分页:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "records": [],
    "total": 0,
    "page": 1,
    "pageSize": 20
  }
}

申请人姓名依赖不可用时,该记录仍正常返回,applicantName 为 未知申请人;不会回退为内部管理员 ID。

错误响应

非法枚举、分页、排序或反向时间范围返回参数错误,例如:

{
  "code": 400,
  "message": "from不能晚于to",
  "success": false,
  "data": null
}

无业务读取权限时返回拒绝结果,且不查询审批历史:

{
  "code": 403,
  "message": "无权访问供应商数据",
  "success": false,
  "data": null
}

业务边界

  • 权限条件与变更记录接口相同:可信管理身份、ADMIN/FINANCE/SUPER_ADMIN 读角色和 supplier:approval:read 平台权限缺一不可。
  • 只读取供应商主体审批,不包含收款账户审批或主体字段变更记录。
  • 时间筛选基于提交时间;历史提交时间为空时使用该审批事实的创建时间。
  • 申请人名称每页最多批量补全一次;User 服务异常、空响应或缺失用户时安全降级,整页不返回 500。
  • 审批人只返回业务安全展示值,不暴露企微或外部审批系统标识。
  • 接口只读,不推进审批状态,不触发审批回调,也不产生缓存、消息或配置副作用。

四、契约约束与正确调用方式

详情页接入映射

页面区域 正确接口 数据范围
变更记录 GET /admin/supplier/items/{supplierId}/change-records/page 仅主体变更
审批记录 GET /admin/supplier/items/{supplierId}/approval-history/page 仅主体审批
旧兼容混合列表 GET /admin/supplier/items/{supplierId}/approval-records/page 主体与账户变更,契约不变

✅ 正确 / ❌ 错误调用对照

场景 调用 / 结果
✅ 分别加载两个页签 两条新接口分别维护自己的 page、pageSize、筛选和 total
✅ 查询完整时间区间 同时传 from=2026-08-01 00:00:00 与 to=2026-08-31 23:59:59
✅ 兼容旧页面 继续调用旧 approval-records/page,无需因本次新增接口修改
❌ 在前端拆旧混合列表 分页后再过滤会得到错误总数,也不能生成审批流水
❌ 只传一侧时间 返回 400,不执行历史查询
❌ 把 supplierId 转为 Number 可能丢失精度;ID 必须始终按 String 传输和比较

调用方必须同时检查 code、message、success 和 data���不能只用 HTTP 状态判断业务成功。

六、边界行为

  • 未登录或登录态失效:统一结果业务码 401,不进入供应商查询。
  • 已登录但角色或平台权限不满足:业务码 403,不读取历史。
  • supplierId<=0、page<1、pageSize 不在 1..100、非法筛选或排序:业务码 400。
  • 供应商不存在或已软删除:业务码 395001。
  • from、to 必须同时传入,且 from<=to;时间边界包含起止时刻。
  • 请求超过最后一页:返回原请求页码、空 records 和真实 total,不自动改页。
  • 两个新接口的 total 分别统计自己的数据集,互不借用;账户变更和账户审批都不进入。
  • 旧 approval-records/page 的路径、请求、响应及主体/账户混合语义保持兼容。

六.5、枚举 / 数据字典

operationType(变更记录操作类型)

所属字段: SupplierChangeRecordPageReqVO.operationType / SupplierChangeRecordRespVO.operationType | 类型: String

值 中文 说明
CREATE 创建 创建供应商主体
UPDATE 更新 更新主体业务字段
ENABLE 启用 恢复可用状态
DISABLE 停用 暂停或禁用合作
DELETE 删除 软删除业务事实

status(变更后的供应商生命周期)

所属字段: SupplierChangeRecordPageReqVO.status | 类型: String

值 中文展示
DRAFT 草稿
VETTING 注册审核中
ACTIVE 合作中
SUSPENDED 暂停合作
FROZEN 已冻结
BLACKLIST 黑名单
ARCHIVED 已归档

valueAvailability(变更摘要可用性)

所属字段: SupplierChangeRecordRespVO.valueAvailability | 类型: String

值 中文 调用方处理
FULL 完整业务摘要可用 正常展示 oldValue / newValue
LEGACY_MASKED_UNRECOVERABLE 历史原值不可恢复 展示现有摘要,并标注其为历史脱敏值;不要提示用户重试

bizType(主体审批业务类型)

所属字段: SupplierApprovalHistoryPageReqVO.bizType / SupplierApprovalHistoryRespVO.bizType | 类型: String

值 中文展示 说明
PROFILE_CREATE 供应商建档审批 注册或建档审批
STATUS_CHANGE 供应商状态变更审批 生命周期状态变更审批
其他合法大写编码 其他供应商审批 为历史及后续业务保留兼容

approvalStatus(主体审批状态)

所属字段: SupplierApprovalHistoryPageReqVO.approvalStatus / SupplierApprovalHistoryRespVO.approvalStatus | 类型: String

值 中文展示
PENDING 审批中
APPROVED 已通过
REJECTED 已驳回
CANCELED / CANCELLED 已撤销
FAILED 失败
其他合法大写编码 未知状态

action(主体审批动作或结果)

所属字段: SupplierApprovalHistoryPageReqVO.action / SupplierApprovalHistoryRespVO.action | 类型: String

值 中文展示
SUBMIT 已提交
SYSTEM_AUTO_APPROVE 系统自动通过
APPLY_FAIL 结果应用失败
APPROVE 通过
REJECT 驳回
REVOKE 撤销
CANCEL 取消
其他合法大写编码 其他动作

七、不影响范围

  • 仅新增:供应商详情的两个管理端只读分页契约。
  • 保持兼容:旧 approval-records/page 继续返回主体与账户变更混合列表。
  • 零影响:供应商详情、创建、更新、提交、归档、暂停合作、拉黑、删除及收款账户管理接口。
  • 零影响:供应商写权限、数据范围、状态机、审批提交/结果应用、事务、锁、幂等、审计与软删除语义。
  • 零影响:数据库结构与历史数据;本次无 migration、数据回填或破坏性数据操作。
  • 零影响:Gateway 顶级路由、Nacos 配置、Redis、MQ 和跨服务写链路。
  • 后端仓库未修改任何管理端前端源码;管理端接入状态保持 pending。

八、测试环境已验证

  • 本地自动化:主功能定向测试 79 项零失败;补充空上下文兼容测试套件 51 项零失败;最终 hl-resource-service 全量 2158 项零失败、零错误(38 项条件跳过);GatewayRouteAuditTest 4 项零失败。
  • 合并:主 PR #6519 合并提交 3dc80ad69630127d91fa973a682051b2ef5c41d8;验收发现旧兼容接口空上下文缺陷后,补充工单 #6532 / PR #6534 以最小修复合入,最新合并提交为 aec1de2db2b4ed07d78877c1ccd4e532919bdaf3。
  • TEST 精确部署:Deploy Panel 任务 9e69fd2c 成功发布 aec1de2db2b4ed07d78877c1ccd4e532919bdaf3;构建退出码 0,任务期 10 次有效采样均至少 2 个运行进程、2 个健康启用 Nacos 实例,零不可用采样,部署窗日志无失败标记。
  • 真实 Gateway:使用现有真实 SUPER_ADMIN 与同账号可切换的 ADMIN 身份,两条新接口的成功分页、字段白名单、独立总数、筛选、排序、边界参数和旧接口兼容均通过;CUSTOMIZER 返回 403,未认证返回 401。
  • TEST 数据覆盖:目标供应商存在 18 条主体变更事实和 1 条主体审批事实;对应账户变更、账户审批均为 0,验证两个新接口没有跨域混页。审批样本为 APPROVED / SYSTEM_AUTO_APPROVE;环境没有外部审批人样本,未伪造业务数据。
  • 身份说明:代码角色矩阵仍包含 ADMIN、FINANCE、SUPER_ADMIN。TEST 角色目录存在 FINANCE,但当前获批真实账号不能切换到该角色;按工单确认使用现有 SUPER_ADMIN(并覆盖 ADMIN)替代 FINANCE 实测,不创建、不修改、不伪造财务账号。
  • 清理:验收全程只读,供应商数据指纹前后一致,业务测试数据创建数为 0;真实会话均已失效处理,无数据库、Redis、MQ 或临时配置需要清理。

九、相关历史 PR

PR Issue 说明 是否仍有效
#6486 #6436 冻结供应商授权完整值与历史摘要语义 ✅ 有效,本次沿用
#6519 #6474 新增主体变更记录与主体审批流水独立分页 ✅ 本次主功能
#6534 #6532 修复旧兼容接口投影空上下文时的空指针 ✅ 有效,保障历史兼容

十、相关文档

  • 关联 Issue:#6474
  • 关联 PR:#6519
  • 补充缺陷 Issue:#6532
  • 补充缺陷 PR:#6534
  • 管理端接入:将“变更记录”“审批记录”分别切换到两条新接口;前端引用待回填。

撤回

  1. 管理端先停止请求两个新路径,并恢复使用旧 approval-records/page,避免代码回退窗口产生请求失败。
  2. 从最新 dev-v3 创建独立回退分支,对主功能合并执行 git revert -m 1 --no-edit 3dc80ad69630127d91fa973a682051b2ef5c41d8,验证后经独立 PR 合入。
  3. #6532 的空上下文修复可独立保留,它修复的是旧兼容接口且不依赖两个新路径。若明确要求连同该修复一起撤回,再按从新到旧顺序执行 git revert -m 1 --no-edit aec1de2db2b4ed07d78877c1ccd4e532919bdaf3;这样会重新引入旧兼容接口在特定记录上的 500 风险,不作为推荐方案。
  4. 使用 Deploy Panel 两阶段客户端,仅滚动部署 hl-resource-service;本次无数据库、配置、Redis 或 MQ 恢复步骤,也无不可逆数据影响。
  5. 撤回后经 Gateway 验证两个新路径不可用、旧兼容接口按选定回退范围正常,复测未认证和越权,并确认 Resource 双实例、Nacos、日志及零写入。
  6. 同步发布本 Changelog 的撤回说明;不得仅改文件名表达状态。

关联 / 联系人

链接

联系人

  • 后端负责人: @lc