18 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 | 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[].contactIdqualifications[].qualificationIdcontracts[].contractIdevaluations[].evaluationIdevaluations[].orderIdevaluations[].resourceIdevaluations[].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 |
本次没有新增业务错误码;既有权限、供应商不存在、未注册、必备资质、乐观锁和审批状态错误继续使用原错误码。
管理端接入事项
- 新建、编辑和提交表单按本文在前端同步限制长度、数字格式、范围、小数位和枚举,避免用户提交后才看到后端错误。
- 银行账号输入可保留空格或连字符用于展示,但提交前应确认去除分隔符后为 8 至 32 位数字;不要使用 JavaScript
Number保存账号。 - 税率必须保留
%;根据invoiceType联动必填和清空,根据settleMode联动accountPeriod。 - 所有子表业务 ID 使用字符串保存和提交。新增项省略 ID,不能传
null;禁止用Number(...)转换雪花 ID。 - 合同金额和评分使用十进制定点输入,分别限制两位小数和对应范围。
- 接口失败时保留用户表单,展示后端
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 副作用,未生成需要清理的重复确认令牌。
撤回
- 从最新
dev-v3创建回退分支,执行git revert -m 1 --no-edit 52a61a88c906395d5cafd72cbda2390ad31a02a3,经新的独立 PR 合入。 - 重新构建并滚动部署
hl-resource-service,确认部署提交不再包含本工单变更。 - 无数据库 migration、数据改写、配置、Redis、MQ 或跨服务契约变更,不需要执行 DDL、DML、缓存清理、消息补偿或配置恢复;现有业务和审计数据保持不变。
- 回退后接口路径、字段名和成功响应结构仍不变,但输入接受范围恢复为本工单前的宽松口径;管理端若已上线严格校验可继续保持,避免重新接受明显非法值。
- 经 Gateway 重跑合法新增、修改、提交、独立账户、未认证、失败零写入和脱敏读取用例,并确认 Nacos 双实例健康、服务日志无新增错误。
- 若撤回本 Changelog,以新的文档提交删除本文件或标记撤回,不重写已发布历史。
关联 / 联系人
链接
联系人
- 后端负责人: @lc