文件
hl-api-changelog/changelogs-v2/2026-08/29_6654_供应商合同字段可空与编辑信息入口-修改接口-管理后台.md
2026-08-29 16:32:05 +08:00

16 KiB

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 6654 供应商合同字段可空与编辑信息入口 admin lc(GIT) 修改接口 deployed verified verified mmg 5fe4e6fa v2.1 2026-08-29 PR #6665 已合并 dev-v3;hl-resource-service 已通过 Deploy Panel 任务 3dde08b9 部署提交 65ac86e9312b22dd2fc7313049a693fdb0ccad59。真实 TEST Gateway 已验证合同业务字段全空登记、补全、清空、删除,changeReason 必填失败零写入,expectedUpdateTime 省略成功及过期值拒绝;测试草稿已清理。合同与结算 table 的页面顺序和消费映射待前端处理。 2026-08-29 dev-v3

供应商合同字段可空与编辑信息入口

供应商合同继续走独立登记接口,不并入供应商档案聚合。合同的 12 个业务字段现在全部可空,changeReason 继续必填;合同更新、删除的 expectedUpdateTime 改为可选,但一旦提供,过期版本仍会被拒绝。

管理端新建供应商时可完全不登记合同,取得 supplierId 后再补录;编辑页通过基本信息详情读取合同,通过既有账户接口读取和维护结算信息。页面按“资质证照 → 合同信息 → 结算信息”排列属于前端消费工作,当前状态为待前端处理。

二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 独立登记供应商合同 POST /admin/supplier/items/{supplierId}/contracts/add 修改请求 12 个合同业务字段全部可空,changeReason 仍必填
2 独立更新供应商合同 PUT /admin/supplier/items/{supplierId}/contracts/{contractId}/update 修改请求 合同业务字段与新增一致且可清空,expectedUpdateTime 可选
3 独立删除供应商合同 DELETE /admin/supplier/items/{supplierId}/contracts/{contractId}/del 修改请求 expectedUpdateTime 可选,changeReason 仍必填
4 查询供应商基本信息 GET /admin/supplier/items/{supplierId}/basic-info/view 修改响应语义 contracts[] 的全部业务字段允许返回 null

三、接口详情

1. 独立登记供应商合同 POST /admin/supplier/items/{supplierId}/contracts/add

VO: SupplierContractCreateReqVO / SupplierContractRespVO

使用场景

供应商已经创建但合同资料尚未齐全时,可先登记一条纯合同记录,之后再补充。若暂时不需要合同记录,供应商新建请求直接省略废弃的 contracts 字段即可。

入参

字段 位置 类型 必填 约束 说明
supplierId Path String 是 正整数 ID 字符串 目标供应商
contractName Body String 否 最长 500 字符 合同名称
contractNo Body String 否 最长 100 字符 合同编号
contractType Body String 否 FRAME / SINGLE_TRIP / PURCHASE 合同类型
signDate Body String 否 yyyy-MM-dd 签署日期
startDate Body String 否 yyyy-MM-dd 有效期开始;仅与同时提供的结束日期做区间校验
endDate Body String 否 yyyy-MM-dd 有效期结束;两端都提供时不得早于开始日期
amount Body String 否 非负,最多 10 位整数和 2 位小数 合同金额
pricingMode Body String 否 最长 100 字符 计价方式
settleCycle Body String 否 最长 32 字符 结算周期说明
status Body String 否 DRAFT / ACTIVE / EXPIRED 合同状态
scanFileUrl Body String 否 最长 1000 字符 扫描件永久地址
remark Body String 否 最长 500 字符 合同备注
changeReason Body String 是 去空白后非空,最长 500 字符 审计原因,不属于可空业务字段

出参 Result<SupplierContractRespVO>

字段 类型 说明
data.contractId String 新合同雪花 ID
data.contractName 等 12 个业务字段 对应类型/null 未填写的字段返回 null
data.updateTime String 服务端合同版本,格式 yyyy-MM-dd HH:mm:ss

请求示例

{
  "changeReason": "合同资料暂缺,先登记记录"
}

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "contractId": "2094000000000000001",
    "contractName": null,
    "contractNo": null,
    "contractType": null,
    "signDate": null,
    "startDate": null,
    "endDate": null,
    "amount": null,
    "pricingMode": null,
    "settleCycle": null,
    "status": null,
    "scanFileUrl": null,
    "remark": null,
    "updateTime": "2026-08-29 16:10:00"
  }
}

空数据 / 降级响应

请求体除 changeReason 外可以不包含任何字段;成功后返回合同对象,业务字段全部为 null。不要把 null 自动替换为默认合同类型、状态、日期或金额。

错误响应

缺少审计原因时失败且不新增合同:

{ "code": 400, "message": "变更原因不能为空", "success": false, "data": null }

业务边界

  • 要求可信管理员、FINANCE/SUPER_ADMIN 写角色和 supplier:update 平台权限。
  • 合同登记不进入供应商审批,不推进供应商主体 updateTime。
  • POST /admin/supplier/items/add 和供应商资料更新中的废弃 contracts 字段仍被忽略;前端必须先获得 supplierId,再按需调用本接口。

2. 独立更新供应商合同 PUT /admin/supplier/items/{supplierId}/contracts/{contractId}/update

VO: SupplierContractUpdateReqVO / SupplierContractRespVO

使用场景

补齐、修改或清空一条已登记合同。该接口执行完整替换:未传或传 null 的业务字段会保存为 null,不是“保持原值”。

入参

字段 位置 类型 必填 约束 说明
supplierId Path String 是 正整数 ID 字符串 目标供应商
contractId Path String 是 正整数 ID 字符串 目标合同
新增接口的 12 个业务字段 Body 对应类型 否 与新增一致 完整替换;省略即清空对应字段
changeReason Body String 是 去空白后非空,最长 500 字符 审计原因
expectedUpdateTime Body String 否 yyyy-MM-dd HH:mm:ss 提供时必须等于当前合同版本;省略时由服务端锁串行更新

出参 Result<SupplierContractRespVO>

字段 类型 说明
data.contractId String 合同雪花 ID
data.contractName 等 12 个业务字段 对应类型/null 完整替换后的值,允许为 null
data.updateTime String 更新后的合同版本

请求示例

{
  "contractName": "2026 年度框架合同",
  "contractType": "FRAME",
  "startDate": "2026-09-01",
  "endDate": "2027-08-31",
  "status": "ACTIVE",
  "changeReason": "补齐已签署合同"
}

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "contractId": "2094000000000000001",
    "contractName": "2026 年度框架合同",
    "contractType": "FRAME",
    "startDate": "2026-09-01",
    "endDate": "2027-08-31",
    "status": "ACTIVE",
    "updateTime": "2026-08-29 16:10:01"
  }
}

空数据 / 降级响应

仅发送 changeReason 可把 12 个业务字段全部清空。若替换后的业务载荷与当前记录完全相同,返回“未检测到合同实际变化”,不会伪造新版本。

错误响应

提供过期版本时继续失败且零写入:

{ "code": 395014, "message": "数据已被修改,请刷新后重试", "success": false, "data": null }

业务边界

  • expectedUpdateTime 省略不等于关闭并发保护;分布式合同锁和数据库行锁仍串行化同一合同写入。
  • 客户端若选择发送版本,必须使用最近一次详情或写响应中的 updateTime。
  • changeReason 始终必填,失败时合同和审计均不写入。

3. 独立删除供应商合同 DELETE /admin/supplier/items/{supplierId}/contracts/{contractId}/del

VO: SupplierContractDeleteReqVO / Void

使用场景

删除不再保留的合同登记。删除为软删除,并记录完整审计原因。

入参

字段 位置 类型 必填 约束 说明
supplierId Path String 是 正整数 ID 字符串 目标供应商
contractId Path String 是 正整数 ID 字符串 目标合同
expectedUpdateTime Body String 否 yyyy-MM-dd HH:mm:ss 提供时执行版本匹配
changeReason Body String 是 去空白后非空,最长 500 字符 删除审计原因

出参 Result<Void>

字段 类型 说明
code Integer 成功时为 200
success Boolean 成功时为 true
data null 删除成功不返回业务对象

请求示例

{
  "changeReason": "合同登记作废"
}

响应示例

{ "code": 200, "message": "成功", "success": true, "data": null }

空数据 / 降级响应

expectedUpdateTime 可省略,但请求体不能省略,且必须包含非空 changeReason。

错误响应

{ "code": 400, "message": "变更原因不能为空", "success": false, "data": null }

业务边界

  • 提供的过期 expectedUpdateTime 仍返回 395014,不删除、不写审计。
  • 删除成功后基本信息详情的 contracts[] 不再返回该记录。
  • 权限、锁、幂等、审计和软删除边界均保持原有实现。

4. 查询供应商基本信息 GET /admin/supplier/items/{supplierId}/basic-info/view

VO: SupplierBasicInfoRespVO

使用场景

进入供应商编辑页时读取主体、资质和合同。前端将 data.contracts 绑定到“合同信息”table。

入参

字段 位置 类型 必填 约束 说明
supplierId Path String 是 正整数 ID 字符串 目标供应商

无 Query 参数,无请求体。

出参 Result<SupplierBasicInfoRespVO>

字段 类型 说明
data.contracts Array 当前未软删除合同,按 contractId 升序
data.contracts[].contractId String 合同雪花 ID
data.contracts[] 的 12 个业务字段 对应类型/null 合同登记未填写时返回 null
data.contracts[].updateTime String 合同当前版本

请求示例

GET /admin/supplier/items/2094000000000000000/basic-info/view
Authorization: Bearer <admin-token>

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "supplierId": "2094000000000000000",
    "contracts": [
      {
        "contractId": "2094000000000000001",
        "contractName": null,
        "contractType": null,
        "startDate": null,
        "endDate": null,
        "status": null,
        "updateTime": "2026-08-29 16:10:00"
      }
    ]
  }
}

空数据 / 降级响应

没有合同登记时 data.contracts 返回空数组;单份合同没有填写的业务字段返回 null,二者含义不同。

错误响应

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

业务边界

  • 只读接口要求可信管理员、可读角色和 supplier:view 平台权限。
  • 查询不推进主体或合同版本,不触发审批和写副作用。
  • 合同 table 的新增、编辑、删除分别调用前三个独立写接口,不把 contracts 回传给供应商聚合更新接口。

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

  • 新建供应商可完全省略合同;若需登记,先调用 POST /admin/supplier/items/add 获取字符串 supplierId,再调用合同新增接口。
  • 编辑页合同读取使用 GET /admin/supplier/items/{supplierId}/basic-info/view 的 data.contracts。
  • 编辑页结算读取继续使用 GET /admin/supplier/items/{supplierId}/account-info/list;新增账户继续使用 POST /admin/supplier/items/{supplierId}/bank-accounts/add,其审批和主体状态门禁不变。
  • 新建的 initialAccounts[] 与独立账户新增的 accounts[] 继续复用同一 SupplierBankAccountReqVO,可填写项一致:accountType、bankName、bankBranch、accountNo、proofFileUrls、settleMode、accountPeriod、invoiceType、taxRate。新建供应商最多携带 1 项初始账户。
  • supplierId、contractId、accountId 和 amount 按字符串处理,禁止转为 JavaScript Number。

五、数据库行为

  • Flyway migration V20260829_002 将 supplier_contract.contract_name、contract_type、start_date、end_date、status 从 NOT NULL 调整为可空。
  • 合同创建、更新、删除继续在本服务 schema 内完成;审计与业务写同事务提交或回滚。
  • 没有跨 schema 写入,没有新增 Redis、MQ、配置或路由变更。

六、边界行为

  • 业务字段为空不等于审计字段可空:合同新增、更新、删除均必须有 changeReason。
  • expectedUpdateTime 仅在合同更新和删除中可省略;提供时仍执行秒级版本比较。
  • 合同日期只在 startDate 与 endDate 同时存在时校验先后顺序。
  • 合同枚举字段为空时不校验;非空时仍只接受既有枚举值。
  • 账户查询和新增的既有状态、审批、权限及敏感附件读取规则未改变。

六.6、修改前后对比

项目 修改前 修改后
合同名称、类型、开始日、结束日、状态 必填 可空
其余 7 个合同业务字段 可空 仍可空
changeReason 新增、更新、删除均必填 继续必填
expectedUpdateTime 更新、删除必填 更新、删除可选;提供过期值仍拒绝
结算信息字段模型 新建与独立账户新增复用同一 VO 不变,编辑页继续复用既有账户接口

六.7、影响评估

  • 是否破坏向后兼容: 否;原先完整载荷继续有效,旧客户端继续发送版本也有效。
  • 前端是否必须同步上线: 是;需要增加合同/结算 table、允许合同业务控件为空,并保留 changeReason 必填。
  • 前端 workaround 清理点: 删除合同业务字段的前端强制必填;不要删除 changeReason 校验。

七、不影响范围

  • 不修改供应商新建、更新、提交请求中的废弃 contracts 聚合字段语义。
  • 不修改结算账户字段、审批、状态机、默认账户、权限或接口路径。
  • 不修改供应商主体的 changeReason、expectedUpdateTime 必填规则。
  • 不新增业务错误码、Gateway 路由、Redis、MQ 或 Nacos 配置。

八、测试环境已验证

  • 合同信息整体省略不影响供应商草稿创建;空业务字段合同可登记并由详情读回。
  • 合同可在不传 expectedUpdateTime 时补全、清空和删除;传入过期版本返回并发失败且零写入。
  • 新增、更新、删除缺少 changeReason 均失败且零写入。
  • 编辑供应商后合同和初始结算账户摘要保持;结算读取入口可正常访问。
  • TEST 验收产生的供应商草稿、合同和初始账户已软删除,并通过详情与结算入口读回确认不可见。

十、相关文档

  • Issue: #6654
  • PR: #6665
  • 合并提交: 65ac86e9312b22dd2fc7313049a693fdb0ccad59

关联 / 联系人

  • 后端负责人: @lc
  • 前端状态: 已消费(frontend_status: verified)。页面「资质→合同→结算」排列现状已符(资质/合同在基本信息区、结算独立账户 Tab),DetailModal 合同表格对全 null 已 EMPTY/renderStatusTag 兜底安全;实质改动在 SupplierContractEditModal——12 业务字段全可空(去 contractName/contractType/status/startDate 必填、status 不再默认 DRAFT)、日期仅两端同填校验、expectedUpdateTime 编辑仅拿到版本才携带,changeReason 仍必填。