文件
hl-api-changelog/changelogs-v2/2026-08/25_6304_供应商资质增加永久有效与到期剩余天数-修改接口-管理后台.md
T
lc e1b0a667cd
changelog-filename-gate / validate (push) Successful in 2s
changelog-filename-gate / validate (pull_request) Successful in 2s
docs(api): 发布供应商资质有效期契约
2026-08-25 16:23:36 +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 6304 供应商资质增加永久有效与到期剩余天数 admin lc(GIT) 修改接口 deployed verified pending PR #6342 与补充修复 PR #6349 均已合并 dev-v3,TEST 最终部署提交为 af3a7bae5。真实 SUPER_ADMIN 经 Gateway 已验证显式永久有效、395042 冲突零写入、历史省略兼容和数据清理;未认证请求仍返回标准 401 业务响应。管理端尚待适配,frontend_status 保持 pending。 2026-08-25 dev-v3

供应商资质增加永久有效与到期剩余天数

供应商资质的有效期输入改为显式的“永久有效”契约;非永久有效资质必须提交到期日期。详情按上海时区自然日返回有符号剩余天数,便于页面直接展示“剩余 N 天”“今日到期”或“已过期 N 天”。

服务: hl-resource-service PR: #6342、补充修复 #6349 Issue: #6304、补充修复 #6347 日期: 2026-08-25 影响范围: 管理端供应商新建、编辑、提交审批和基本信息详情的资质证照区域

⚠️ 关键变化

  • 资质写入字段新增 permanentValid;true 时不得提交 expiryDate,false 时必须提交 expiryDate。
  • 详情资质项新增 daysUntilExpiry;永久有效时返回 null,有限期按 Asia/Shanghai 自然日返回正数、零或负数。
  • 既有只读字段 isRequired 仍表示“该供应商类型是否要求此资质”,没有改义,也不能作为永久有效标识。

一、变更接口清单

# 接口 方法 路径 变化
1 创建供应商注册草稿 POST /admin/supplier/items/add qualifications[].permanentValid 可写
2 更新供应商 PUT /admin/supplier/items/{supplierId}/update qualifications[].permanentValid 可写,并支持增量保留语义
3 提交供应商注册审批 POST /admin/supplier/items/{supplierId}/submit qualifications[].permanentValid 可写
4 查询供应商基本信息 GET /admin/supplier/items/{supplierId}/basic-info/view qualifications[] 新增 daysUntilExpiry

统一响应仍为 Result<T>。业务校验失败可能仍返回 HTTP 200,调用方必须同时判断 code、success 和 data。

二、字段契约

2.1 写入字段

字段 位置 JSON 类型 必填 说明
permanentValid qualifications[] Boolean 条件必填 true 表示永久有效,false 表示存在到期日;历史调用方可省略并触发兼容推导
expiryDate qualifications[] String / null 条件必填 格式 yyyy-MM-dd;仅 permanentValid=false 时必须有值

合法组合:

场景 permanentValid expiryDate 结果
永久有效 true 省略或 null 接受,到期日为空
有到期日 false yyyy-MM-dd 接受,保存该到期日
历史永久请求 省略 省略或 null 接受,兼容推导为 true
历史有限期请求 省略 yyyy-MM-dd 接受,兼容推导为 false
矛盾组合 true yyyy-MM-dd 拒绝,业务码 395042
缺少到期日 false 省略或 null 拒绝,业务码 395042

更新既有资质时还有以下增量规则:

  • permanentValid 与 expiryDate 均省略:保留当前有效期,不做修改。
  • 仅提交非空 expiryDate:兼容推导 permanentValid=false。
  • 显式提交 permanentValid=true 且不提交到期日:切换为永久有效并清空原到期日。
  • 新增资质且两字段均省略:按历史兼容口径创建为永久有效。

2.2 详情响应字段

字段 JSON 类型 空值 说明
permanentValid Boolean 不为空 由 expiryDate 是否为空动态推导
daysUntilExpiry Integer / null 永久有效时为 null 到期日相对上海业务当日的有符号自然日差
expired Boolean 不为空 仅当 daysUntilExpiry < 0 时为 true
isRequired Boolean 不为空 历史只读兼容字段;继续表示供应商类型规则是否要求该资质

以 2026-08-25(上海日期)为例:

expiryDate permanentValid daysUntilExpiry expired
null true null false
2026-08-26 false 1 false
2026-08-25 false 0 false
2026-08-24 false -1 true

三、接口详情

3.1 创建供应商注册草稿 POST /admin/supplier/items/add

使用场景:新建供应商草稿时保存资质证照。permanentValid 位于每个 qualifications[] 项内。

典型请求:

{
  "fullName": "示例酒店管理有限公司",
  "shortName": "示例酒店",
  "taxNo": "91110108MA00000001",
  "types": [
    {
      "typeCode": "HOTEL"
    }
  ],
  "mainCooperation": "酒店住宿服务",
  "qualifications": [
    {
      "qualType": "BUSINESS_LICENSE",
      "certNo": "LIC-2026-0001",
      "imageUrl": "https://files.example.test/supplier/license-1.jpg",
      "permanentValid": false,
      "expiryDate": "2027-08-25"
    }
  ]
}

成功响应:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "supplierId": "2092152294748352514",
    "supplierNo": null,
    "status": "DRAFT",
    "onboardingStage": "PROFILE_DRAFT",
    "initialAccounts": [],
    "updateTime": "2026-08-25 15:21:00"
  }
}

永久有效请求只需将资质项改为:

{
  "qualType": "BUSINESS_LICENSE",
  "certNo": "LIC-2026-PERM",
  "imageUrl": "https://files.example.test/supplier/license-perm.jpg",
  "permanentValid": true,
  "expiryDate": null
}

3.2 更新供应商 PUT /admin/supplier/items/{supplierId}/update

使用场景:编辑既有资质或切换永久有效状态。qualifications 一旦传入,仍代表资质集合的完整当前快照;既有项必须携带 qualificationId 和该项的 expectedUpdateTime。

将有限期资质切换为永久有效:

{
  "qualifications": [
    {
      "qualificationId": "2092152300000000001",
      "qualType": "BUSINESS_LICENSE",
      "certNo": "LIC-2026-0001",
      "imageUrl": "https://files.example.test/supplier/license-1.jpg",
      "permanentValid": true,
      "expiryDate": null,
      "expectedUpdateTime": "2026-08-25 15:21:00"
    }
  ],
  "changeReason": "营业执照变更为永久有效",
  "expectedUpdateTime": "2026-08-25 15:21:00"
}

成功响应:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "supplierId": "2092152294748352514",
    "supplierNo": null,
    "status": "DRAFT",
    "onboardingStage": "PROFILE_DRAFT",
    "initialAccounts": [],
    "updateTime": "2026-08-25 15:35:00"
  }
}

若本次不修改既有资质的有效期,可同时省略该项的 permanentValid 和 expiryDate;不要回传未经确认的默认值。

3.3 提交供应商注册审批 POST /admin/supplier/items/{supplierId}/submit

使用场景:提交完整供应商表单。资质有效期组合规则与创建草稿完全一致。

典型请求:

{
  "fullName": "示例酒店管理有限公司",
  "shortName": "示例酒店",
  "taxNo": "91110108MA00000001",
  "types": [
    {
      "typeCode": "HOTEL"
    }
  ],
  "mainCooperation": "酒店住宿服务",
  "licenseImageUrl": "https://files.example.test/supplier/license-1.jpg",
  "qualifications": [
    {
      "qualificationId": "2092152300000000001",
      "qualType": "BUSINESS_LICENSE",
      "certNo": "LIC-2026-0001",
      "imageUrl": "https://files.example.test/supplier/license-1.jpg",
      "permanentValid": false,
      "expiryDate": "2027-08-25"
    }
  ],
  "expectedUpdateTime": "2026-08-25 15:35:00"
}

成功响应:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "approvalLogId": "2092152400000000001",
    "requestNo": "SUP-PROFILE-20260825-0001",
    "provider": "LOCAL_AUTO",
    "approvalStatus": "APPROVED",
    "spNo": null,
    "spStatus": null,
    "syncStatus": "APPLIED",
    "submittedAt": "2026-08-25 15:36:00",
    "finishedAt": "2026-08-25 15:36:00"
  }
}

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

使用场景:展示供应商详情的资质证照。请求需要有效的管理端 Authorization,无请求体。

GET /admin/supplier/items/2092152294748352514/basic-info/view
Authorization: Bearer <有效管理端令牌>

有限期资质成功响应(假设上海日期为 2026-08-25):

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "supplierId": "2092152294748352514",
    "supplierNo": null,
    "fullName": "示例酒店管理有限公司",
    "shortName": "示例酒店",
    "tax_no": "9111********0001",
    "legalRepresentative": null,
    "contactPhoneMask": null,
    "establishDate": null,
    "registeredCapital": null,
    "businessScope": null,
    "staffScale": null,
    "mainCooperation": "酒店住宿服务",
    "status": "DRAFT",
    "creditLevel": "B",
    "totalScore": null,
    "types": [
      {
        "typeCode": "HOTEL",
        "typeName": "酒店"
      }
    ],
    "contacts": [],
    "qualifications": [
      {
        "qualificationId": "2092152300000000001",
        "qualType": "BUSINESS_LICENSE",
        "qualTypeName": "营业执照",
        "certNoMask": "LIC****0001",
        "imageUrl": "https://files.example.test/supplier/license-1.jpg",
        "expiryDate": "2026-08-26",
        "permanentValid": false,
        "daysUntilExpiry": 1,
        "validityStatus": "VALID",
        "validityStatusName": "有效",
        "isRequired": true,
        "expired": false,
        "updateTime": "2026-08-25 15:21:00"
      }
    ],
    "updateTime": "2026-08-25 15:21:00"
  }
}

永久有效资质的差异字段为:

{
  "expiryDate": null,
  "permanentValid": true,
  "daysUntilExpiry": null,
  "validityStatus": "VALID",
  "validityStatusName": "有效",
  "expired": false
}

四、错误响应

新增业务错误码:

业务码 消息 触发条件 写入结果
395042 永久有效标记与到期日期不一致 permanentValid=true 且到期日非空,或 permanentValid=false 且到期日为空 整笔请求失败,零写入

异常请求片段:

{
  "qualifications": [
    {
      "qualType": "BUSINESS_LICENSE",
      "permanentValid": true,
      "expiryDate": "2027-08-25"
    }
  ]
}

错误响应:

{
  "code": 395042,
  "message": "永久有效标记与到期日期不一致",
  "success": false,
  "data": null
}

既有错误码继续有效,包括资质类型无效、证照编号无效、必备资质缺失、资质过期和并发版本冲突。

五、修改前后对比

字段级对比

字段 修改前 修改后
写入 qualifications[].permanentValid 无显式写入契约,只能由到期日是否为空隐式表达 可显式提交;省略时仍兼容历史请求
响应 qualifications[].daysUntilExpiry 无 有限期返回有符号整数,永久有效返回 null
响应 qualifications[].isRequired 供应商类型是否要求此资质 语义不变,继续只读返回

行为级对比

行为 修改前 修改后
永久有效输入 仅通过不传到期日表达 优先显式提交 permanentValid=true;旧请求仍兼容
非永久有效输入 提交到期日 提交 permanentValid=false 与到期日
���期提示 调用方自行按日期计算 直接读取 daysUntilExpiry,无需自行处理时区和跨日

六、管理端接入事项

  1. 将资质证照区域的“是否必备”编辑控件改为“永久有效”;提交字段必须使用 permanentValid,不得复用或改写 isRequired。
  2. permanentValid=true 时清空并禁用 expiryDate;false 时要求用户选择到期日。
  3. 非永久有效时在到期日期附近显示后端返回的 daysUntilExpiry:正数表示剩余天数,零表示今日到期,负数表示已过期天数。
  4. 永久有效时不显示到期天数。
  5. 编辑既有数据以详情返回的 permanentValid 回显;不要从 isRequired 推导。

七、兼容性与影响评估

  • 是否破坏向后兼容: 否。旧客户端省略 permanentValid 时仍按 expiryDate 兼容推导。
  • 前端是否必须同步上线: 是。当前界面尚未提交新字段,也未展示剩余天数。
  • 前端 workaround 清理点: 若页面存在本地到期天数计算,改为直接消费 daysUntilExpiry。
  • 不新增接口、Gateway 路由、权限点、数据库迁移、配置、Redis 或 MQ 契约。
  • 不改变供应商状态机、审批语义、资质必备规则、证照编号脱敏、软删除和数据范围。

八、TEST 验证证据

  • 原实现 PR #6342 与补充修复 PR #6349 均已合并;远端精确部署分支 deploy/6347-supplier-qualification-null-expiry 的 HEAD 与最终 dev-v3 合并提交 af3a7bae5f3d19b97a7ad52daf31bb8fd18d5676 完全一致,TEST 双实例健康。
  • 真实 SUPER_ADMIN 经 Gateway 将既有有限期资质切换为永久有效后,更新成功;重新查询返回 expiryDate=null、permanentValid=true、daysUntilExpiry=null、expired=false、validityStatus=VALID。
  • 同一资质提交 permanentValid=true 与非空 expiryDate 时返回业务码 395042、消息“永久有效标记与到期日期不一致”;失败前后供应商和资质的 updateTime 均保持 2026-08-25 16:19:20,证明零写入。
  • 历史客户端省略 permanentValid、仅提交 expiryDate=2026-08-26 时更新成功;详情兼容返回 permanentValid=false、daysUntilExpiry=1、expired=false。
  • 本工单创建的 DRAFT 测试供应商已通过业务删除接口清理;按精确测试名称分页查询 total=0,按原 ID 查询返回 395001 供应商不存在。
  • 未认证访问供应商入口返回业务码 401、success=false、data=null,证明请求仍经 Gateway 认证门禁。
  • 原实现定向 80 项零失败、Resource 全量 2058 项零失败(38 项条件跳过);补充修复定向 46 项零失败、Resource 全量 2059 项零失败(38 项条件跳过)。离线 HTML 自检通过,文档仓库 npm test 46 项零失败。
  • 管理端适配不属于本后端工单,frontend_status 保持 pending,不把后端契约验证误记为前端已完成。

九、撤回

  1. 从最新 dev-v3 创建回退分支,先执行 git revert -m 1 --no-edit af3a7bae5f3d19b97a7ad52daf31bb8fd18d5676 撤回补充修复,再执行 git revert -m 1 --no-edit c5b1a84aea6b1c7beb7a626131376355008e662c 撤回原实现;分别审查并经独立 PR 合入。
  2. 重新构建并滚动部署 hl-resource-service;本次无数据库、配置、Redis 或 MQ 变更,无需执行 DDL、DML 或数据恢复。
  3. 回退前确认管理端不再提交 permanentValid 或读取 daysUntilExpiry;旧字段 expiryDate、expired 和 isRequired 以及存量数据无需迁移。
  4. 回退后经 Gateway 复测草稿创建、更新、提交、详情、未认证和永久/有限期边界,并确认资质必备规则与证照脱敏保持原行为。

关联 / 联系人