11 条有业务交付改判 verified(#6397/6903/6904/6905/6950/6979/6986/7013/7029/7036/7066,owner=mmg+对应业务 commit ref+交付日 verified_at); 6 条实证零改动改判 not_required(#6014/6016/6140/6938/6842/7087,仅翻 frontend_status 不填 owner/ref)。 #5935 挂起待后端补字段,保持 pending 不动。sync-log 均已记账。
12 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 | 6842 | 供应商合同修改与删除接口 | admin | lc(GIT) | 修改接口 | deployed | verified | not_required | 汇总现有合同修改、删除契约;前端按完整替换、必填原因及可选合同版本接入。既有 TEST 业务实测及本次只读复核范围见正文。 | 2026-09-06 | dev-v3 |
供应商合同:修改与删除接口
影响范围:管理后台供应商合同编辑、删除。当前状态:后端已部署;前端待核对接入。
更新为整份替换,15 个业务字段均可空;修改、删除都必须提交 changeReason。expectedUpdateTime 可省略,传入时必须使用合同自身版本。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 修改供应商合同 | PUT | /admin/supplier/items/{supplierId}/contracts/{contractId}/update |
现有契约说明 | 完整替换一份合同 |
| 2 | 删除供应商合同 | DELETE | /admin/supplier/items/{supplierId}/contracts/{contractId}/del |
现有契约说明 | 携带 JSON 原因软删除一份合同 |
三、接口详情
1. 修改供应商合同 PUT /admin/supplier/items/{supplierId}/contracts/{contractId}/update
VO: SupplierContractUpdateReqVO / SupplierContractRespVO
使用场景
编辑供应商下已登记的单份合同。提交应保留的全部业务字段;未提交字段按空值覆盖。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
supplierId |
Path | String | 是 | 正整数 ID | 所属供应商 |
contractId |
Path | String | 是 | 正整数 ID | 目标合同 |
changeReason |
Body | String | 是 | 去空白后非空,最长 500 字符 | 本次修改原因 |
expectedUpdateTime |
Body | String/null | 否 | yyyy-MM-dd HH:mm:ss |
合同 updateTime,传入即校验 |
contractName |
Body | String/null | 否 | 最长 500 字符 | 合同名称 |
contractNo |
Body | String/null | 否 | 最长 100 字符 | 合同编号 |
contractType |
Body | String/null | 否 | FRAME / SINGLE_TRIP / PURCHASE |
合同类型 |
signDate |
Body | String/null | 否 | yyyy-MM-dd |
签署日期 |
startDate |
Body | String/null | 否 | yyyy-MM-dd |
有效期开始 |
endDate |
Body | String/null | 否 | 两端有值时不得早于 startDate |
有效期结束 |
businessLine |
Body | String/null | 否 | 最长 100 字符 | 业务线自由文本 |
relatedMainContract |
Body | String/null | 否 | 最长 100 字符 | 关联主合同引用 |
autoRenew |
Body | Boolean/null | 否 | true / false / null |
自动续约 / 不续约 / 未登记 |
amount |
Body | String/null | 否 | 非负,最多 10 位整数、2 位小数 | 金额,如 "1200.50" |
pricingMode |
Body | String/null | 否 | 最长 100 字符 | 计价方式 |
settleCycle |
Body | String/null | 否 | 最长 32 字符 | 结算周期自由文本 |
status |
Body | String/null | 否 | DRAFT / ACTIVE / SIGNED / EXPIRED |
合同状态 |
scanFileUrl |
Body | String/null | 否 | 最长 1000 字符 | 一个合同附件的永久地址 |
remark |
Body | String/null | 否 | 最长 500 字符 | 合同备注 |
出参
| 字段 | 类型 | 说明 |
|---|---|---|
code / message / success |
Integer / String / Boolean | 成功为 200、成功、true |
data.contractId |
String | 合同 ID |
data.contractName |
String/null | 合同名称 |
data.contractNo |
String/null | 合同编号 |
data.contractType |
String/null | 合同类型 |
data.signDate |
String/null | 签署日期 |
data.startDate / data.endDate |
String/null | 有效期,yyyy-MM-dd |
data.businessLine |
String/null | 业务线 |
data.relatedMainContract |
String/null | 关联主合同 |
data.autoRenew |
Boolean/null | 自动续约标记,false 与 null 含义不同 |
data.amount |
String/null | 金额;无金额为 null |
data.pricingMode |
String/null | 计价方式 |
data.settleCycle |
String/null | 结算周期 |
data.status |
String/null | 合同状态;历史读取可能出现 TERMINATED |
data.scanFileUrl |
String/null | 合同附件地址 |
data.remark |
String/null | 合同备注 |
data.updateTime |
String | 合同新版本,yyyy-MM-dd HH:mm:ss |
请求示例
以下 ID、附件地址和时间均为示例值。
PUT /admin/supplier/items/2095000000000000001/contracts/2095000000000000010/update
Authorization: Bearer <当前有效凭证>
Content-Type: application/json
{
"changeReason": "调整合同有效期",
"expectedUpdateTime": "2026-09-06 10:00:00",
"contractName": "年度服务合同",
"contractNo": "HT-2026-001",
"contractType": "FRAME",
"signDate": "2026-09-01",
"startDate": "2026-09-01",
"endDate": "2027-09-30",
"businessLine": "旅行服务",
"relatedMainContract": null,
"autoRenew": false,
"amount": "1200.50",
"pricingMode": "按团结算",
"settleCycle": "月结",
"status": "SIGNED",
"scanFileUrl": "https://files.example.com/contracts/demo.pdf",
"remark": null
}
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"contractId": "2095000000000000010",
"contractName": "年度服务合同",
"contractNo": "HT-2026-001",
"contractType": "FRAME",
"signDate": "2026-09-01",
"startDate": "2026-09-01",
"endDate": "2027-09-30",
"businessLine": "旅行服务",
"relatedMainContract": null,
"autoRenew": false,
"amount": "1200.50",
"pricingMode": "按团结算",
"settleCycle": "月结",
"status": "SIGNED",
"scanFileUrl": "https://files.example.com/contracts/demo.pdf",
"remark": null,
"updateTime": "2026-09-06 10:00:01"
}
}
空数据 / 降级响应
可空业务字段省略或传 null 均清空;可选文本空白也规范化为空。只传 changeReason 会尝试清空全部业务字段,不表示仅改原因。成功返回合同对象,无变化时返回业务错误。
错误响应
合同版本过期:
{"code":395014,"message":"数据已被他人修改,请刷新后重试","success":false,"data":null}
400:缺少原因或格式错误;395002:无写权限;395051:合同不存在、已删除或归属不符;395054:日期倒置;395057:无实际变化;395031:供应商已归档;395005:主体企微审批未结束或状态不允许。
业务边界
- 要求
FINANCE或SUPER_ADMIN且具有supplier:update;ADMIN被拒绝。 - 合同必须属于路径供应商,供应商不能已归档,主体企微审批必须已结束。
- 完整替换包含页面隐藏字段;需保留的字段应原样提交。版本取合同自身,不使用供应商主体版本。
2. 删除供应商合同 DELETE /admin/supplier/items/{supplierId}/contracts/{contractId}/del
VO: SupplierContractDeleteReqVO / Result<Void>
使用场景
删除一份已登记合同。DELETE 请求须携带 JSON 请求体。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
supplierId |
Path | String | 是 | 正整数 ID | 所属供应商 |
contractId |
Path | String | 是 | 正整数 ID | 目标合同 |
changeReason |
Body | String | 是 | 去空白后非空,最长 500 字符 | 本次删除原因 |
expectedUpdateTime |
Body | String/null | 否 | yyyy-MM-dd HH:mm:ss |
合同当前版本,提供时必须匹配 |
出参
| 字段 | 类型 | 说明 |
|---|---|---|
code |
Integer | 成功为 200 |
message |
String | 成功为 成功 |
success |
Boolean | 成功为 true |
data |
null | 无合同对象 |
请求示例
DELETE /admin/supplier/items/2095000000000000001/contracts/2095000000000000010/del
Authorization: Bearer <当前有效凭证>
Content-Type: application/json
{
"changeReason": "合同重复登记",
"expectedUpdateTime": "2026-09-06 10:00:01"
}
响应示例
{"code":200,"message":"成功","success":true,"data":null}
空数据 / 降级响应
成功的 data: null 为正常结果,删除后从详情合同列表中移除该项。
错误响应
缺少删除原因:
{"code":400,"message":"变更原因不能为空","success":false,"data":null}
395002:无写权限;395051:合同不存在、已删除或归属不符;395014:版本过期;395031:供应商已归档;395005:主体企微审批未结束或状态不允许。
业务边界
- 要求
FINANCE或SUPER_ADMIN且具有supplier:update;供应商已归档或主体企微审批未结束时拒绝。 - 删除为软删除,不删除供应商或账户;没有合同恢复接口。
- 前端请求库需将原因放进 DELETE 的 JSON body;不能只拼 Query 参数或只传 ID。
四、契约约束与正确调用方式
从 GET /admin/supplier/items/{supplierId}/basic-info/view 的 data.contracts[] 读取合同、contractId 和 updateTime。修改时提交应保留的全部字段;修改成功用新版本继续操作,删除成功刷新列表。版本冲突重新读取后再编辑。日期按 GMT+8 日历日期传 yyyy-MM-dd。
五、数据库行为
更新只替换目标合同并刷新合同版本;删除后有效详情不再返回该合同,历史记录保留。供应商主体版本不因合同写入改变。参数、权限、归属或版本校验失败时不产生合同变更。
六、边界行为
未登录或登录失效按认证失败处理;供应商不存在返回 395001。业务错误可能随 HTTP 200 返回,应检查 code 和 success。金额 1200.5 与 1200.50 视为相同值,仅改变金额格式不会构成实际变更。
六.5、枚举 / 数据字典
contractType
| 值 | 中文 | 说明 |
|---|---|---|
FRAME |
框架合同 | 可写 |
SINGLE_TRIP |
单团单合同 | 可写 |
PURCHASE |
采购合同 | 可写 |
null |
未登记 | 可写 |
status
| 值 | 中文 | 说明 |
|---|---|---|
DRAFT |
草稿 | 可写 |
ACTIVE |
生效中 | 可写 |
SIGNED |
已签约 | 可写 |
EXPIRED |
已过期 | 可写 |
TERMINATED |
已终止 | 仅历史回显,不可提交 |
null |
未登记 | 可写 |
六.6、修改前后对比
本次为既有契约汇总,后端无新增变更。
| 字段 / 行为 | 早期 #6544 说明 | 当前契约 |
|---|---|---|
| 合同业务字段 | 部分必填 | 全部可空;含业务线、关联主合同、自动续约 |
expectedUpdateTime |
必填 | 可选,提供时校验合同版本 |
status |
未列出 SIGNED |
支持已签约 SIGNED |
| 修改 / 删除 | 独立接口 | 路径保持不变;原因仍必填 |
六.7、影响评估
本次没有新增兼容性变化,无需与后端同步上线。前端接入现有修改、删除按钮时使用上述请求体,避免只传变化字段导致清空、丢弃 DELETE body 或误用主体版本。
七、不影响范围
本次仅说明合同维护入口;供应商主体保存和账户维护继续使用各自接口。
八、测试环境已验证
- 既有业务实测:#6654 已覆盖合同补全、清空、删除、缺原因及版本失败零写入;#6842 已覆盖扩展字段保存回读、
SIGNED、null、false和日期错误零写入。 - 本次于 2026-09-06 通过 TEST Gateway 只读核对 Resource Swagger:合同 PUT、DELETE 路径存在,更新的 15 个业务字段、可选版本与必填原因均已发布;同步核对最新
dev-v3。此次未重复执行共享环境业务写操作。