文件
hl-api-changelog/changelogs-v2/2026-08/25_6312_供应商敏感字段与数值格式校验收紧-修改接口-管理后台.md
T
2026-08-25 14:27:33 +08:00

18 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 6312 供应商敏感字段与数值格式校验收紧 admin lc(GIT) 修改接口 deployed verified verified mmg 187b7c3b 2026-08-25 PR #6322 已合并 dev-v3,合并提交 52a61a88 已由部署任务 b5d27d2b 发布到 TEST。供应商新增、修改、注册提交和独立新增收款账户现统一校验税号、电话、证照编号、银行账号、税率、金额、评分、枚举及业务 ID;接口路径、字段名和成功响应结构不变。 2026-08-25 dev-v3

供应商敏感字段与数值格式校验收紧

供应商新增、修改、注册提交和独立新增收款账户的输入契约现统一收紧。管理端必须在提交前按本文校验税号、电话、证照编号、银行账号、税率、金额、评分和枚举,并将供应商子表的 Long ID 作为规范正整数字符串发送。

本次没有新增、删除或重命名字段,也没有改变成功响应结构;但以前可能被接受的错误格式现在会返回参数错误,因此属于请求兼容性收紧。

🔧 关键变化

项目 修改前 修改后
敏感字段格式 部分入口仅校验非空或宽松长度,Controller 与 Service 口径可能不一致 Controller 与 Service 使用同一业务口径,绕过 Controller 也不能写入非法值
银行账号 展示格式和规范化后的数字长度缺少完整门禁 去除空格、连字符后必须为 8 至 32 位数字
税率、金额、评分 个别入口可进入后续流程后才失败 请求入口即校验范围和小数位
子表业务 ID null、数字令牌、零或溢出值可能被错误解释为新增或类型错误 只有字段省略表示新增;显式 ID 必须是规范正整数字符串
枚举 DTO 与直接 Service 调用的口径可能不一致 人员规模、账户、结算、发票、合同类型和合同状态均双层校验

业务失败可能仍返回 HTTP 200。管理端必须同时判断统一响应中的 code、success、message 和 data。

变更接口清单

# 方法 路径 权限 变化
1 POST /admin/supplier/items/add supplier:create 收紧主体、联系人、资质和初始账户输入
2 PUT /admin/supplier/items/{supplierId}/update supplier:update 收紧主体、子表 ID、合同和评价输入
3 POST /admin/supplier/items/{supplierId}/submit supplier:update + supplier:approval:submit 完整注册表单复用与新增一致的字段规则
4 POST /admin/supplier/items/{supplierId}/bank-accounts/add supplier:account:manage + supplier:approval:submit 独立账户复用初始账户的账号、结算和开票规则

通用字段规则

主体、联系人和资质

字段 JSON 类型 空值语义 当前约束
taxNo String 新增、提交必填;修改时省略表示不改 原始文本 6 至 64 位,只允许字母、数字、空格和连字符;服务端移除空格、连字符并转大写后仍须为 6 至 64 位字母或数字;可识别的统一社会信用代码、居民身份证同时校验日期或校验位
contactPhone String / null 公司电话可省略 原始文本 7 至 20 位;去除空格、括号、连字符及 +86/0086 后,须为中国大陆手机号、带 0 的座机或 400/800 服务号
contacts[].contactPhone String 新增、完整提交的联系人必填;修改既有联系人时省略表示不改 与公司电话使用同一规范化和号码类型规则
qualifications[].certNo String / null 草稿可空;提交是否必填继续按资质规则判断 最大 128 个 UTF-8 字节;超过长度直接拒绝,读取仍只返回脱敏值
staffScale String / null 省略表示不改或不设置 仅允许 LT50、R50_200、R200_500、GT500,区分大小写

电话示例:

输入 结果
+86 138-0013-8000 接受,按境内手机号规范化
010-12345678 接受,按境内座机规范化
1234567 拒绝,消息为 公司联系电话格式不合法 或 联系人电话格式不合法
13800ABC000 拒绝,电话不能包含字母

收款账户、结算和开票

initialAccounts[] 与独立账户接口 accounts[] 使用同一规则:

字段 必填 当前约束
accountType 是 CORPORATE(对公)或 PERSONAL(对私)
bankName 是 非空,最大 500 字符
bankBranch 否 最大 500 字符
accountNo 是 只允许数字、空格和连字符;去除展示分隔符后必须为 8 至 32 位数字
settleMode 否 PREPAY、MONTHLY、SINGLE
accountPeriod 条件必填 只有 MONTHLY 必须填写;其他结算方式必须为空
invoiceType 否 SPECIAL、NORMAL、NONE
taxRate 条件必填 SPECIAL/NORMAL 必填,NONE 必须为空;格式为 0% 至 100%,最多两位小数

合法税率包括 0%、6%、6.5%、6.50%、100%、100.00%。5.123%、100.01%、缺少 % 或负数均拒绝。

合同、评价和枚举

字段 当前约束
contracts[].amount 0 至 9999999999.99,最多两位小数
contracts[].contractType FRAME、SINGLE_TRIP、PURCHASE
contracts[].status DRAFT、ACTIVE、EXPIRED
evaluations[].score 0 至 5,最多两位小数

枚举均区分大小写。空值是否表示“不修改”继续由对应修改字段语义决定;显式提交未知值一律拒绝。

路径和子表业务 ID

路径中的 {supplierId} 必须大于 0。JSON 中以下可选子表 ID 使用同一严格规则:

  • contacts[].contactId
  • qualifications[].qualificationId
  • contracts[].contractId
  • evaluations[].evaluationId
  • evaluations[].orderId
  • evaluations[].resourceId
  • evaluations[].evaluator
发送值 结果
字段省略 接受,表示新增子项或未建立可选关联
"1" 至 "9223372036854775807" 接受,必须是 1 至 19 位且在 Java Long 正整数范围内
null、""、"0"、"-1" 拒绝
"01"、" 1" 拒绝,不允许前导零或空白
1 拒绝,数字 JSON 令牌不能替代字符串
"9223372036854775808" 或 20 位以上数字 拒绝,超出范围或长度

管理端更新已有子项时,应原样回传详情接口返回的字符串 ID;新增子项必须省略 ID 字段,不能传 null。

接口详情

1. 创建供应商草稿 POST /admin/supplier/items/add

请求头:

Authorization: Bearer <accessToken>
Content-Type: application/json

典型合法请求:

{
  "fullName": "示例旅行服务有限公司",
  "shortName": "示例旅行",
  "taxNo": "91350211M000100Y46",
  "contactPhone": "010-12345678",
  "staffScale": "R50_200",
  "mainCooperation": "住宿与地接服务",
  "types": [
    { "typeCode": "HOTEL" }
  ],
  "contacts": [
    {
      "contactName": "示例联系人",
      "contactPhone": "+86 138-0013-8000",
      "contactRole": "contentBus",
      "isPrimary": true
    }
  ],
  "initialAccounts": [
    {
      "accountType": "CORPORATE",
      "bankName": "示例银行",
      "accountNo": "6222 0212 3456 7890",
      "settleMode": "SINGLE",
      "invoiceType": "SPECIAL",
      "taxRate": "6.00%"
    }
  ]
}

成功响应结构不变:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "supplierId": "2092000000000000001",
    "supplierNo": null,
    "status": "DRAFT",
    "onboardingStage": "PROFILE_DRAFT",
    "initialAccounts": [
      {
        "accountId": "2092000000000000002",
        "accountNoMask": "6222********7890",
        "status": "DRAFT"
      }
    ],
    "updateTime": "2026-08-25 12:35:00"
  }
}

无效税号、电话、证照编号或初始账户会在供应商、联系人、账户和审批写入前失败。

2. 修改供应商 PUT /admin/supplier/items/{supplierId}/update

请求头:

Authorization: Bearer <accessToken>
Content-Type: application/json

更新已有联系人时,contactId 必须使用字符串;新增联系人则省略该字段:

{
  "contactPhone": "+86 138-0013-8000",
  "staffScale": "R200_500",
  "contacts": [
    {
      "contactId": "2092000000000000010",
      "contactPhone": "010-87654321",
      "expectedUpdateTime": "2026-08-25 12:35:00"
    },
    {
      "contactName": "新增联系人",
      "contactPhone": "13900139000",
      "contactRole": "contentMoney",
      "isPrimary": false
    }
  ],
  "contracts": [
    {
      "contractName": "年度合作框架",
      "contractType": "FRAME",
      "amount": 1000000.50,
      "status": "DRAFT"
    }
  ],
  "evaluations": [
    {
      "orderId": "2092000000000000020",
      "score": 4.75,
      "content": "履约正常"
    }
  ],
  "changeReason": "更新联系方式与合作资料",
  "expectedUpdateTime": "2026-08-25 12:35:00"
}

成功响应仍为 Result<SupplierWriteRespVO>:

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

任一子项 ID、金额、评分或枚举非法时,整个更新失败,既有主体与子表快照不变。

3. 提交供应商注册 POST /admin/supplier/items/{supplierId}/submit

请求头:

Authorization: Bearer <accessToken>
Content-Type: application/json

提交接口接收完整注册表单,主体、联系人、资质和 initialAccounts 复用创建接口的全部规则,并要求 expectedUpdateTime:

{
  "fullName": "示例旅行服务有限公司",
  "taxNo": "91350211M000100Y46",
  "contactPhone": "010-12345678",
  "mainCooperation": "住宿与地接服务",
  "types": [
    { "typeCode": "HOTEL" }
  ],
  "contacts": [
    {
      "contactName": "示例联系人",
      "contactPhone": "13800138000",
      "contactRole": "contentBus",
      "isPrimary": true
    }
  ],
  "qualifications": [
    {
      "qualType": "BUSINESS_LICENSE",
      "certNo": "LIC-2026-000001",
      "imageUrl": "https://files.example.com/supplier/license.png"
    }
  ],
  "expectedUpdateTime": "2026-08-25 12:40:00"
}

成功响应结构不变:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "approvalLogId": "2092000000000000030",
    "requestNo": "SUPPLIER-PROFILE-EXAMPLE",
    "provider": "LOCAL_AUTO",
    "approvalStatus": "APPROVED",
    "spNo": null,
    "spStatus": null,
    "syncStatus": "APPLIED",
    "submittedAt": "2026-08-25 12:41:00",
    "finishedAt": "2026-08-25 12:41:00"
  }
}

格式错误会在创建审批事实前失败;必备资质、并发版本、重复主体和状态机规则保持既有语义。

4. 独立新增收款账户 POST /admin/supplier/items/{supplierId}/bank-accounts/add

请求头:

Authorization: Bearer <accessToken>
Content-Type: application/json

请求示例:

{
  "accounts": [
    {
      "accountType": "CORPORATE",
      "bankName": "示例银行",
      "bankBranch": "示例支行",
      "accountNo": "6222-0212-3456-7890",
      "settleMode": "MONTHLY",
      "accountPeriod": "月结30天",
      "invoiceType": "NORMAL",
      "taxRate": "6%",
      "proofFileUrls": [
        "https://files.example.com/supplier/account-proof.png"
      ]
    }
  ]
}

成功响应仍为按请求顺序返回的数组:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": [
    {
      "accountId": "2092000000000000040",
      "approvalLogId": "2092000000000000041",
      "requestNo": "SUPPLIER-ACCOUNT-EXAMPLE",
      "provider": "LOCAL_AUTO",
      "approvalStatus": "APPROVED",
      "spNo": null,
      "spStatus": null,
      "syncStatus": "APPLIED",
      "accountStatus": "ACTIVE",
      "isDefault": "NO",
      "submittedAt": "2026-08-25 12:45:00",
      "finishedAt": "2026-08-25 12:45:00"
    }
  ]
}

账号、结算或开票组合非法时整批在账户和审批写入前失败。供应商尚未完成注册时,合法格式仍返回既有业务错误 395010(请先完成供应商注册)。

典型错误响应

字段格式失败统一使用业务码 400,例如:

{
  "code": 400,
  "message": "收款账号规范化后必须为8到32位数字",
  "success": false,
  "data": null
}
场景 code 典型消息
{supplierId} 为 0 400 供应商ID必须为正数
路径 ID 超出 Long 范围 400 参数 supplierId 格式错误,请检查后重试
可识别的统一社会信用代码校验位错误 400 主体证件号的统一社会信用代码校验位不合法
公司或联系人电话不符合境内号码规则 400 公司联系电话格式不合法 / 联系人电话格式不合法
子表 ID 显式为 null、空、数字令牌、零或溢出 400 请求数据格式错误:字段 [...] 格式错误
账号规范化后少于 8 位或超过 32 位 400 收款账号规范化后必须为8到32位数字
税率超过 100% 或多于两位小数 400 税率必须为0%至100%且最多两位小数
合同金额超过范围或多于两位小数 400 合同金额最多10位整数和2位小数
评价分数超过 5 或多于两位小数 400 评分不能大于5 / 评分最多1位整数和2位小数
未认证 401 统一认证失败;HTTP 状态可能仍为 200

本次没有新增业务错误码;既有权限、供应商不存在、未注册、必备资质、乐观锁和审批状态错误继续使用原错误码。

管理端接入事项

  1. 新建、编辑和提交表单按本文在前端同步限制长度、数字格式、范围、小数位和枚举,避免用户提交后才看到后端错误。
  2. 银行账号输入可保留空格或连字符用于展示,但提交前应确认去除分隔符后为 8 至 32 位数字;不要使用 JavaScript Number 保存账号。
  3. 税率必须保留 %;根据 invoiceType 联动必填和清空,根据 settleMode 联动 accountPeriod。
  4. 所有子表业务 ID 使用字符串保存和提交。新增项省略 ID,不能传 null;禁止用 Number(...) 转换雪花 ID。
  5. 合同金额和评分使用十进制定点输入,分别限制两位小数和对应范围。
  6. 接口失败时保留用户表单,展示后端 message;不能只判断 HTTP 状态。

影响评估与未变化范围

  • 是否破坏向后兼容:对合法请求兼容;对以前可能被接受的非法格式不兼容。
  • 前端是否需要同步:需要。请求字段和响应结构未变,但表单校验、ID 序列化及结算/开票联动应对齐。
  • 历史数据:不迁移、不批量重写;存量数据读取不受影响,下次显式修改相关字段时按新规则校验。
  • 权限与状态机:不变,仍使用既有管理端角色、平台权限、乐观版本、聚合锁、幂等、审批、审计和软删除规则。
  • 基础设施:无数据库 migration、配置、Redis Key、MQ Topic、Feign 或 Gateway 路由变化。
  • 响应与脱敏:成功响应结构不变;税号、电话、证照号和账号读取仍只返回脱敏值。

验证证据

  • 自动化:12 个供应商聚焦测试类全部通过;Resource 及依赖模块全量 2018 项测试零失败、零错误,38 项仓库既有条件跳过,13 个 Maven 模块构建成功。
  • 独立审查:合并提交的补丁等价、字段边界、Controller/Service 入口一致性和失败零副作用测试均通过,未发现确定性缺口。
  • TEST 部署:部署任务 b5d27d2b 成功,hl-resource-service 的 8182、8082 双实例依次启动;服务器运行提交确认包含合并提交 52a61a88c,Nacos 两实例均为 healthy、enabled。
  • 真实 Gateway:使用真实有效 SUPER_ADMIN 登录完成 35 项验收,覆盖未认证、路径 ID、税号、公司电话、联系人电话、证照长度、初始/独立账户、税率、子表 ID、合同金额、评价分数、枚举、合法创建、脱敏读取、合法更新和业务状态失败。
  • 零写入:合法草稿建立后的数据库快照为 1 个主体、1 个类型、1 个联系人、1 个初始账户、0 个审批;全部失败用例执行后快照完全一致。
  • 清理:合成草稿通过既有删除接口成功清理,活动主体、类型、联系人、账户、评价和审批均为 0;按审计要求保留 3 条主体变更事实和 2 条账户变更事实,不含真实个人或企业数据。
  • 运行健康:验收后 8082、8182 仍健康,Gateway 可达,两实例本次启动日志 ERROR=0;未触发文件、配置或 MQ 副作用,未生成需要清理的重复确认令牌。

撤回

  1. 从最新 dev-v3 创建回退分支,执行 git revert -m 1 --no-edit 52a61a88c906395d5cafd72cbda2390ad31a02a3,经新的独立 PR 合入。
  2. 重新构建并滚动部署 hl-resource-service,确认部署提交不再包含本工单变更。
  3. 无数据库 migration、数据改写、配置、Redis、MQ 或跨服务契约变更,不需要执行 DDL、DML、缓存清理、消息补偿或配置恢复;现有业务和审计数据保持不变。
  4. 回退后接口路径、字段名和成功响应结构仍不变,但输入接受范围恢复为本工单前的宽松口径;管理端若已上线严格校验可继续保持,避免重新接受明显非法值。
  5. 经 Gateway 重跑合法新增、修改、提交、独立账户、未认证、失败零写入和脱敏读取用例,并确认 Nacos 双实例健康、服务日志无新增错误。
  6. 若撤回本 Changelog,以新的文档提交删除本文件或标记撤回,不重写已发布历史。

关联 / 联系人

链接

联系人

  • 后端负责人: @lc