--- schema: "hl-changelog/v2" ticket: "6391" title: "供应商法人证件号与身份证正反面" consumer: "admin" author: "lc(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "74b3d8bf" target_release: "" verified_at: "2026-08-26" status_note: "PR #6401 已合并 dev-v3 并部署 TEST;供应商创建、更新、提交新增法人身份证号及正反面永久地址,详情仅返回脱敏证件号。旧客户端省略三字段时保持兼容,前端待接入输入与双面上传。" updated_at: "2026-08-26" base: "dev-v3" --- # 🔧 供应商法人证件号与身份证正反面 供应商“基本信息-法定代表人”新增身份证号、人像面和国徽面三个字段。身份证图片继续使用既有文件上传能力,本次接口只接收上传完成后的永久 HTTPS 地址,不新增上传或 OCR 接口。 ## 变更接口 | 方法 | 路径 | 权限 | 变化 | |---|---|---|---| | POST | `/admin/supplier/items/add` | `supplier:create` | 创建草稿可保存三个法人证件字段 | | PUT | `/admin/supplier/items/{supplierId}/update` | `supplier:update` | 增量更新三个法人证件字段 | | POST | `/admin/supplier/items/{supplierId}/submit` | `supplier:update`、`supplier:approval:submit` | 完整注册表单可提交三个法人证件字段 | | GET | `/admin/supplier/items/{supplierId}/basic-info/view` | `supplier:view` | 返回脱敏证件号及身份证正反面地址 | ## 通用字段与校验 | 字段 | 类型 | 必填 | 规则 | |---|---|---|---| | `legalRepresentativeIdNo` | string | 条件必填 | 18 位中国大陆居民身份证号;校验长度、出生日期、顺序码及校验位;末位小写 `x` 会规范为大写 `X` | | `legalRepresentativeIdCardFrontUrl` | string | 条件必填 | 身份证人像面永久地址,最长 1000 字符;必须为公网 HTTPS 地址,且不能含账号密码、查询串或片段 | | `legalRepresentativeIdCardBackUrl` | string | 条件必填 | 身份证国徽面永久地址,规则同人像面 | 三个字段必须“全部省略”或“同时提供”: - 全部省略:兼容旧客户端和没有法人证件数据的存量供应商。 - 任意一个有值:三个字段必须同时有值,不能只保存证件号或单面图片。 - 法定代表人可能对应多个供应商,证件号不作为供应商之间的唯一键。 - 写接口的成功响应不回传证件号;需要展示时调用详情接口。 - 统一响应可能以 HTTP 200 承载业务失败,必须同时判断 `code`、`success` 和 `data`。 ## 1. 创建供应商草稿 ### 使用场景 在新建供应商基本信息时,同时保存法定代表人身份证号及正反面扫描件地址。 ### 请求 ```http POST /admin/supplier/items/add Authorization: Bearer <管理员令牌> Content-Type: application/json ``` 本次新增的三个字段遵循上方通用规则;创建草稿的既有最低必填字段仍为 `fullName`、`taxNo`、`mainCooperation`。 典型成功请求: ```json { "fullName": "法人证件联调示例供应商", "taxNo": "L6391EXAMPLE001", "legalRepresentative": "示例法人", "legalRepresentativeIdNo": "11010519491231002x", "legalRepresentativeIdCardFrontUrl": "https://files.example.com/supplier/id/front.jpg", "legalRepresentativeIdCardBackUrl": "https://files.example.com/supplier/id/back.jpg", "mainCooperation": "旅游资源供应" } ``` 典型成功响应: ```json { "code": 200, "message": "成功", "success": true, "data": { "supplierId": "1900000000000000001", "supplierNo": null, "status": "DRAFT", "onboardingStage": "PROFILE_DRAFT", "initialAccounts": [], "updateTime": "2026-08-26 11:40:00" } } ``` 兼容边界:旧客户端可完全省略三个新字段,其余请求保持原样。 异常请求(仅传人像面): ```json { "fullName": "法人证件联调示例供应商", "taxNo": "L6391EXAMPLE001", "legalRepresentativeIdCardFrontUrl": "https://files.example.com/supplier/id/front.jpg", "mainCooperation": "旅游资源供应" } ``` ```json { "code": 400, "message": "法定代表人证件号、人像面和国徽面必须同时提供", "success": false, "data": null } ``` 失败不会创建供应商草稿。 ## 2. 更新供应商 ### 使用场景 在供应商基本信息编辑页补录或替换完整的法人证件三字段。 ### 请求 ```http PUT /admin/supplier/items/1900000000000000001/update Authorization: Bearer <管理员令牌> Content-Type: application/json ``` `changeReason` 和 `expectedUpdateTime` 沿用原接口必填约束;三个法人证件字段作为一组增量字段处理。典型成功请求: ```json { "legalRepresentative": "示例法人", "legalRepresentativeIdNo": "11010519491231002X", "legalRepresentativeIdCardFrontUrl": "https://files.example.com/supplier/id/front-v2.jpg", "legalRepresentativeIdCardBackUrl": "https://files.example.com/supplier/id/back-v2.jpg", "changeReason": "补录法人身份证扫描件", "expectedUpdateTime": "2026-08-26 11:40:00" } ``` 典型成功响应: ```json { "code": 200, "message": "成功", "success": true, "data": { "supplierId": "1900000000000000001", "supplierNo": null, "status": "DRAFT", "onboardingStage": "PROFILE_DRAFT", "initialAccounts": [], "updateTime": "2026-08-26 11:42:00" } } ``` 兼容边界:三个新字段全部省略时,不修改现有法人证件数据。若请求中出现任一新字段,服务端会与当前值合并后再次检查三字段是否完整。 异常请求(身份证校验位错误): ```json { "legalRepresentativeIdNo": "110105194912310021", "legalRepresentativeIdCardFrontUrl": "https://files.example.com/supplier/id/front-v2.jpg", "legalRepresentativeIdCardBackUrl": "https://files.example.com/supplier/id/back-v2.jpg", "changeReason": "补录法人身份证扫描件", "expectedUpdateTime": "2026-08-26 11:40:00" } ``` ```json { "code": 400, "message": "法定代表人证件号的日期或校验位不合法", "success": false, "data": null } ``` 失败时主体版本、法人证件数据和变更记录均保持原状。 ## 3. 提交供应商注册审批 ### 使用场景 提交草稿的完整注册表单时,将法人证件三字段一并纳入审批内容。 ### 请求 ```http POST /admin/supplier/items/1900000000000000001/submit Authorization: Bearer <管理员令牌> Content-Type: application/json ``` 本接口继续要求完整注册表单和当前 `expectedUpdateTime`。典型成功请求: ```json { "fullName": "法人证件联调示例供应商", "taxNo": "L6391EXAMPLE001", "legalRepresentative": "示例法人", "legalRepresentativeIdNo": "11010519491231002X", "legalRepresentativeIdCardFrontUrl": "https://files.example.com/supplier/id/front.jpg", "legalRepresentativeIdCardBackUrl": "https://files.example.com/supplier/id/back.jpg", "mainCooperation": "旅游资源供应", "expectedUpdateTime": "2026-08-26 11:42:00" } ``` 典型成功响应: ```json { "code": 200, "message": "成功", "success": true, "data": { "approvalLogId": "1900000000000000101", "requestNo": "SUP-REQ-20260826-0001", "provider": "LOCAL_AUTO", "approvalStatus": "APPROVED", "spNo": null, "spStatus": null, "syncStatus": "APPLIED", "submittedAt": "2026-08-26 11:43:00", "finishedAt": "2026-08-26 11:43:00" } } ``` 兼容边界:没有法人证件数据的旧草稿仍可按旧请求提交;若提交法人证件,则三字段必须完整。 异常请求(图片地址带查询串): ```json { "fullName": "法人证件联调示例供应商", "taxNo": "L6391EXAMPLE001", "legalRepresentativeIdNo": "11010519491231002X", "legalRepresentativeIdCardFrontUrl": "https://files.example.com/supplier/id/front.jpg?token=temporary", "legalRepresentativeIdCardBackUrl": "https://files.example.com/supplier/id/back.jpg", "mainCooperation": "旅游资源供应", "expectedUpdateTime": "2026-08-26 11:42:00" } ``` ```json { "code": 400, "message": "法人证件人像面地址不在允许范围", "success": false, "data": null } ``` 失败时不会创建审批或改变供应商状态。 ## 4. 查询供应商基本信息 ### 使用场景 编辑页回显法人证件信息。证件号只返回掩码,不能用于恢复原文或再次提交;图片地址可用于有权限页面的预览。 ### 请求 ```http GET /admin/supplier/items/1900000000000000001/basic-info/view Authorization: Bearer <管理员令牌> ``` 无请求体。 典型成功响应: ```json { "code": 200, "message": "成功", "success": true, "data": { "supplierId": "1900000000000000001", "supplierNo": null, "fullName": "法人证件联调示例供应商", "shortName": null, "tax_no": "L639****E001", "legalRepresentative": "示例法人", "legalRepresentativeIdNoMask": "110105********002X", "legalRepresentativeIdCardFrontUrl": "https://files.example.com/supplier/id/front.jpg", "legalRepresentativeIdCardBackUrl": "https://files.example.com/supplier/id/back.jpg", "contactPhoneMask": null, "establishDate": null, "registeredCapital": null, "businessScope": null, "staffScale": null, "mainCooperation": "旅游资源供应", "status": "DRAFT", "creditLevel": "B", "totalScore": null, "types": [], "contacts": [], "qualifications": [], "updateTime": "2026-08-26 11:42:00" } } ``` 兼容边界:存量供应商没有法人证件数据时,三个响应字段均为 `null`。响应中不存在 `legalRepresentativeIdNo` 明文字段。 未认证示例: ```json { "code": 401, "message": "未认证或登录已失效", "success": false, "data": null } ``` ## 错误与前端处理 | 响应码 | 触发条件 | 前端处理 | |---:|---|---| | `400` | 身份证不是 18 位、出生日期/顺序码/校验位错误、三字段不完整、图片地址不符合规则 | 保留表单并定位到法人证件区域;不要自动重试 | | `401` | 未登录或 Gateway 认证失效 | 进入统一重新登录流程 | | `403` | 角色、功能权限或数据范围不足 | 隐藏无权操作并展示统一无权限提示 | | `395014` | `expectedUpdateTime` 已过期 | 重新读取详情,提示用户确认后再提交 | 本次不新增业务错误码;参数失败继续使用统一 `400`。 ## 前端改造清单 - 在“新建/编辑供应商-基本信息-法定代表人”后增加身份证号输入框、人像面上传和国徽面上传。 - 上传完成后提交永久 HTTPS 地址;不要提交临时签名 URL、查询参数 URL、Base64 或文件二进制。 - 前端可做 18 位长度和末位 `X/x` 预校验,但最终以服务端日期及校验位结果为准。 - 三字段联动必填;旧数据三个字段均为空时允许继续按原流程操作。 - 详情只展示 `legalRepresentativeIdNoMask`;不得寻找或缓存身份证号明文。 - 写成功后如需回显,重新调用详情接口,不要从写响应读取新字段。 ## 验证证据 - 自动化:最新 `dev-v3` 的 Resource 全量测试 2111 项通过、0 失败、0 错误,38 项既有条件跳过;法人证件聚焦测试 89 项通过。 - TEST:Deploy Panel 任务 `118fcff1` 将目标提交 `de615b49e3ecef4be13bd6bc78b3100d08ef0bd2` 部署到双实例;该提交包含 #6391 合并提交 `b909c8f7dfd73712886a33b574c72030dde89d4f`,服务与 Nacos 健康检查通过。 - 真实 Gateway:7 组场景通过,覆盖合法创建、错误校验位零写入、详情脱敏与图片回显、未认证拒绝、非法更新零写入、旧客户端省略字段兼容和迁移状态。 - 清理:本轮验收草稿已通过业务删除接口软删除并保留正常删除审计,不遗留可用测试供应商。 ## 撤回 1. 从最新 `dev-v3` 创建回退分支,revert #6401 合并提交并经独立 PR 合入。 2. 重新部署 `hl-resource-service`;新增的可空数据结构保留,不执行破坏性删除。 3. 旧客户端、存量供应商和已保存的安全数据保持兼容;无需恢复配置、Redis 或 MQ。 4. 经 Gateway 重跑旧请求、合法/非法证件号、三字段完整性、详情脱敏和失败零写入检查。 ## 关联 / 联系人 - **Issue**: [#6391](https://git.1814.love:8443/wx/HL/issues/6391) - **PR**: [#6401](https://git.1814.love:8443/wx/HL/pulls/6401) - **合并提交**: [b909c8f7d](https://git.1814.love:8443/wx/HL/commit/b909c8f7dfd73712886a33b574c72030dde89d4f) - **后端负责人**: @lc