比较提交
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
fd76eea701 | ||
|
|
49f67134fd | ||
|
|
314c228654 | ||
|
|
3e8ea93caf | ||
|
|
33c0aa2c08 | ||
|
|
6831ec3128 | ||
|
|
42a16b78c6 | ||
|
|
257ecb1233 | ||
|
|
e9b52319c9 | ||
|
|
4060c65261 | ||
|
|
0fcf0801cf |
@@ -7,11 +7,11 @@ author: "lc(GIT)"
|
|||||||
change_type: "修改接口"
|
change_type: "修改接口"
|
||||||
backend_status: "deployed"
|
backend_status: "deployed"
|
||||||
gateway_status: "verified"
|
gateway_status: "verified"
|
||||||
frontend_status: "pending"
|
frontend_status: "verified"
|
||||||
frontend_owner: ""
|
frontend_owner: "mmg"
|
||||||
frontend_ref: ""
|
frontend_ref: "25fb4b27"
|
||||||
target_release: ""
|
target_release: ""
|
||||||
verified_at: ""
|
verified_at: "2026-08-25"
|
||||||
status_note: "PR #6342 与补充修复 PR #6349 均已合并 dev-v3,TEST 最终部署提交为 af3a7bae5。真实 SUPER_ADMIN 经 Gateway 已验证显式永久有效、395042 冲突零写入、历史省略兼容和数据清理;未认证请求仍返回标准 401 业务响应。管理端尚待适配,frontend_status 保持 pending。"
|
status_note: "PR #6342 与补充修复 PR #6349 均已合并 dev-v3,TEST 最终部署提交为 af3a7bae5。真实 SUPER_ADMIN 经 Gateway 已验证显式永久有效、395042 冲突零写入、历史省略兼容和数据清理;未认证请求仍返回标准 401 业务响应。管理端尚待适配,frontend_status 保持 pending。"
|
||||||
updated_at: "2026-08-25"
|
updated_at: "2026-08-25"
|
||||||
base: "dev-v3"
|
base: "dev-v3"
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "6343"
|
||||||
|
title: "供应商新建修改允许供应商类型为空"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "lc(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "verified"
|
||||||
|
frontend_owner: "mmg"
|
||||||
|
frontend_ref: "d84c6c25"
|
||||||
|
target_release: ""
|
||||||
|
verified_at: "2026-08-26"
|
||||||
|
status_note: "PR #6385 已合并 dev-v3,合并提交 b8d99f58 已部署到 TEST。供应商创建草稿的 types 可省略、传 null 或空数组;更新省略 types 保持原关系,显式空数组清空无资源占用的类型;注册提交仍至少需要一个类型。"
|
||||||
|
updated_at: "2026-08-26"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 供应商新建修改允许供应商类型为空
|
||||||
|
|
||||||
|
供应商草稿阶段不再强制选择供应商类型。创建草稿可不传类型,编辑草稿也可显式清空全部未被资源占用的类型;注册提交的完整性门禁保持不变。
|
||||||
|
|
||||||
|
## 变更接口
|
||||||
|
|
||||||
|
| 方法 | 路径 | 行为变化 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | `/admin/supplier/items/add` | `types` 省略、为 `null` 或空数组时均可创建无类型草稿;非空时继续校验最多 15 项、生效字典值和唯一性 |
|
||||||
|
| PUT | `/admin/supplier/items/{supplierId}/update` | 省略 `types` 时保持原类型关系;显式传空数组时清空全部无资源占用的类型;非空快照继续执行原校验 |
|
||||||
|
| POST | `/admin/supplier/items/{supplierId}/submit` | 行为不变:没有供应商类型时仍拒绝提交注册 |
|
||||||
|
| GET | `/admin/supplier/items/{supplierId}/basic-info/view` | 无类型供应商的 `types` 返回空数组 |
|
||||||
|
|
||||||
|
字段名和 JSON 类型没有变化。`types` 非空时仍提交对象数组,类型值来自供应商类型生效字典。
|
||||||
|
|
||||||
|
## 校验与错误语义
|
||||||
|
|
||||||
|
- 创建无类型草稿仅放宽草稿保存,不放宽注册提交、审批或状态机。
|
||||||
|
- 更新显式清空前仍检查资源关系占用;存在有效资源关系的类型不能被移除,失败时主体、类型关系和审计保持零变化。
|
||||||
|
- 无类型草稿提交继续返回既有业务错误码 `395008`,草稿状态和并发版本不变。
|
||||||
|
- 非空类型继续校验最多 15 项、生效字典值、重复值和主类型约束。
|
||||||
|
- 业务失败可能仍使用 HTTP 200,客户端必须同时检查统一响应的 `code`、`success`、`message` 和 `data`。
|
||||||
|
|
||||||
|
## 兼容性与前端事项
|
||||||
|
|
||||||
|
- 管理端请求字段和响应结构不变,现有非空类型流程无需调整,因此无前端源码变更。
|
||||||
|
- 无类型供应商仍可被列表和详情读取;客户端应兼容 `types: []`。
|
||||||
|
- 不新增数据库 migration,不修改配置、Gateway、Redis、MQ、Nacos、Feign 或跨服务写入。
|
||||||
|
- 本次不自动修改历史数据,也不绕过既有权限、数据范围、软删除、资源占用和审计规则。
|
||||||
|
|
||||||
|
## 验证证据
|
||||||
|
|
||||||
|
- 合并后独立审计:#6343 相关跨层定向 153 项零失败;Resource 最新目标分支全量 2111 项零失败、38 项仓库既有条件跳过;`git diff --check` 通过。
|
||||||
|
- TEST 运行态:服务器后端仓库为 `dev-v3` 的 `1a16a5aec`,包含合并提交 `b8d99f58c`;`hl-resource-service` 的 8082、8182 双实例均监听。
|
||||||
|
- 真实 Gateway 正向:`types` 省略、`null`、空数组分别成功创建三个 `DRAFT`,详情均返回空类型数组;带一个生效类型的草稿创建成功。
|
||||||
|
- 更新语义:省略 `types` 后原一项类型保持不变,显式 `types: []` 后详情返回空数组。
|
||||||
|
- 状态门禁:无类型草稿提交返回 `395008`,状态仍为 `DRAFT`、版本不变,确认零写入。
|
||||||
|
- 权限门禁:未登录请求返回业务码 401,真实 `CUSTOMIZER` 返回 `395002`,两者均通过列表回读确认零写入;正向使用真实 `SUPER_ADMIN` 身份。
|
||||||
|
- 清理:四条成功创建的 TEST 草稿全部删除;逐条详情返回 `395001`,按随机全名查询均为零条,没有留下测试业务数据。
|
||||||
|
|
||||||
|
## 撤回
|
||||||
|
|
||||||
|
1. 从最新 `dev-v3` 创建回退分支,执行 `git revert -m 1 --no-edit b8d99f58c84daa12ef50d68bcc3090f38f57a22d`,经独立 PR 合入。
|
||||||
|
2. 通过 Deploy Panel API 重新构建并滚动部署 `hl-resource-service`,复核 8082、8182 双实例和 Nacos 健康状态。
|
||||||
|
3. 无数据库、配置、Redis 或 MQ 变更,不执行 DDL、DML、缓存清理或消息补偿;不要回退后续无关工单的提交或 migration。
|
||||||
|
4. 回退前让调用方恢复创建时至少提交一个生效类型,并停止更新时发送 `types: []`;更新不改类型时继续省略该字段。
|
||||||
|
5. 回退后经 Gateway 复测非空类型创建、更新省略保持、无类型创建拒绝、无类型提交拒绝、资源占用移除保护和越权零写入。
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
- **Issue**: [#6343](https://git.1814.love:8443/wx/HL/issues/6343)
|
||||||
|
- **PR**: [#6385](https://git.1814.love:8443/wx/HL/pulls/6385)
|
||||||
|
- **功能提交**: [d625f0448](https://git.1814.love:8443/wx/HL/commit/d625f04489655e11982b9baeeb45d74ba4f7c0e1)
|
||||||
|
- **合并提交**: [b8d99f58c](https://git.1814.love:8443/wx/HL/commit/b8d99f58c84daa12ef50d68bcc3090f38f57a22d)
|
||||||
|
- **后端负责人**: @lc
|
||||||
@@ -0,0 +1,369 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
@@ -0,0 +1,392 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "6397"
|
||||||
|
title: "供应商注册合同聚合信息"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "lc(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "verified"
|
||||||
|
frontend_owner: "mmg"
|
||||||
|
frontend_ref: "f2a4200d"
|
||||||
|
target_release: ""
|
||||||
|
verified_at: "2026-08-26"
|
||||||
|
status_note: "PR #6405 已合并 dev-v3;Deploy Panel 任务 af3f205b 成功发布 Resource 双实例,合同接口已完成 23 项真实 Gateway 验收。TEST 工作副本存在服务器本地提交导致精确提交回读仍被阻塞,后端工单保持开启;本条只交接已经实测存在的接口契约。"
|
||||||
|
updated_at: "2026-08-26"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🔧 供应商注册合同聚合信息
|
||||||
|
|
||||||
|
供应商创建草稿、提交注册和基础信息详情现统一支持合同完整快照。管理端应把“合同信息”放在“资质证照”之后、现有账户区域之前,并把原“初始账户”展示标题改为“结算信息”。
|
||||||
|
|
||||||
|
展示名调整不改变接口字段:原请求字段仍为 `initialAccounts`,不得改成 `settlementInfo` 或其他名称。
|
||||||
|
|
||||||
|
## 变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变化 |
|
||||||
|
|---:|---|---|---|---|
|
||||||
|
| 1 | 创建供应商注册草稿 | POST | `/admin/supplier/items/add` | 请求可选增加 `contracts` 完整集合 |
|
||||||
|
| 2 | 提交供应商注册 | POST | `/admin/supplier/items/{supplierId}/submit` | 请求可选增加 `contracts` 完整快照 |
|
||||||
|
| 3 | 查询供应商基本信息 | GET | `/admin/supplier/items/{supplierId}/basic-info/view` | 响应增加 `contracts` 列表 |
|
||||||
|
|
||||||
|
统一响应均为 `Result<T>`。业务失败可能仍为 HTTP 200,调用方必须同时判断 `code`、`success`、`message` 和 `data`。
|
||||||
|
|
||||||
|
## 公共合同字段
|
||||||
|
|
||||||
|
### 请求字段 `contracts[]`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 创建必填 | 提交既有项必填 | 约束与说明 |
|
||||||
|
|---|---|---:|---:|---|
|
||||||
|
| `contractId` | String | 否,且禁止传入 | 是 | 正整数 ID 字符串;必须属于当前供应商,同一快照不得重复 |
|
||||||
|
| `contractName` | String | 是 | 是 | 非空白,最长 500 字符 |
|
||||||
|
| `contractNo` | String | 否 | 否 | 最长 100 字符 |
|
||||||
|
| `contractType` | String | 是 | 是 | `FRAME`、`SINGLE_TRIP`、`PURCHASE` |
|
||||||
|
| `signDate` | String | 否 | 否 | `yyyy-MM-dd` |
|
||||||
|
| `startDate` | String | 是 | 是 | `yyyy-MM-dd` |
|
||||||
|
| `endDate` | String | 是 | 是 | `yyyy-MM-dd`,不得早于 `startDate` |
|
||||||
|
| `amount` | Number | 否 | 否 | 大于等于 0,最多 10 位整数和 2 位小数 |
|
||||||
|
| `pricingMode` | String | 否 | 否 | 计价方式说明,最长 100 字符 |
|
||||||
|
| `settleCycle` | String | 否 | 否 | 结算周期说明,最长 32 字符 |
|
||||||
|
| `status` | String | 是 | 是 | 写接口允许 `DRAFT`、`ACTIVE`、`EXPIRED` |
|
||||||
|
| `scanFileUrl` | String | 否 | 否 | 合同扫描件永久地址,最长 1000 字符 |
|
||||||
|
| `remark` | String | 否 | 否 | 最长 500 字符 |
|
||||||
|
|
||||||
|
### 响应字段 `contracts[]`
|
||||||
|
|
||||||
|
详情返回上述全部业务字段,并额外返回:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `contractId` | String | 合同 ID,始终按字符串处理,不得转 JavaScript Number |
|
||||||
|
| `updateTime` | String | 合同当前版本,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||||
|
|
||||||
|
历史数据可能返回只读状态 `TERMINATED`;创建和提交请求不得发送该状态。
|
||||||
|
|
||||||
|
## 1. 创建供应商注册草稿
|
||||||
|
|
||||||
|
`POST /admin/supplier/items/add`
|
||||||
|
|
||||||
|
### 使用场景与边界
|
||||||
|
|
||||||
|
- `contracts` 可省略、为 `null` 或空数组,旧客户端行为不变。
|
||||||
|
- 非空时最多 100 项,合同与供应商主体、资质和 `initialAccounts` 一起成功或一起失败。
|
||||||
|
- 创建请求中的每个合同都是新合同,禁止携带 `contractId`。
|
||||||
|
- 写入仍要求现有供应商创建权限;仅可信 `FINANCE`、`SUPER_ADMIN` 且拥有对应平台权限的身份可执行。
|
||||||
|
|
||||||
|
### 典型请求
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /admin/supplier/items/add
|
||||||
|
Authorization: Bearer <admin-token>
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fullName": "示例供应商有限公司",
|
||||||
|
"shortName": "示例供应商",
|
||||||
|
"taxNo": "91350211M000100Y46",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"typeCode": "SCENIC"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mainCooperation": "景区资源合作",
|
||||||
|
"licenseImageUrl": "https://files.example.com/license.png",
|
||||||
|
"qualifications": [
|
||||||
|
{
|
||||||
|
"qualType": "BUSINESS_LICENSE",
|
||||||
|
"certNo": "LIC-2026-001",
|
||||||
|
"imageUrl": "https://files.example.com/license.png",
|
||||||
|
"permanentValid": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contracts": [
|
||||||
|
{
|
||||||
|
"contractName": "2026 年度框架合同",
|
||||||
|
"contractNo": "HT-2026-001",
|
||||||
|
"contractType": "FRAME",
|
||||||
|
"signDate": "2026-08-26",
|
||||||
|
"startDate": "2026-09-01",
|
||||||
|
"endDate": "2027-08-31",
|
||||||
|
"amount": 1200.50,
|
||||||
|
"pricingMode": "按团结算",
|
||||||
|
"settleCycle": "MONTHLY",
|
||||||
|
"status": "DRAFT",
|
||||||
|
"scanFileUrl": "https://files.example.com/contracts/HT-2026-001.pdf",
|
||||||
|
"remark": "年度合作"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"initialAccounts": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 成功响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"supplierId": "2090300000000063970",
|
||||||
|
"supplierNo": null,
|
||||||
|
"status": "DRAFT",
|
||||||
|
"onboardingStage": "PROFILE_DRAFT",
|
||||||
|
"initialAccounts": [],
|
||||||
|
"updateTime": "2026-08-26 11:10:00"
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 失败响应:创建携带合同 ID
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"message": "创建草稿不能携带合同ID",
|
||||||
|
"data": null,
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
失败时不会留下供应商主体或部分合同。
|
||||||
|
|
||||||
|
## 2. 提交供应商注册
|
||||||
|
|
||||||
|
`POST /admin/supplier/items/{supplierId}/submit`
|
||||||
|
|
||||||
|
### 使用场景与快照语义
|
||||||
|
|
||||||
|
- `contracts` 省略或为 `null`:本次不处理合同,保留草稿当前合同。
|
||||||
|
- `contracts: []`:明确清空当前全部合同。
|
||||||
|
- 非空数组:作为完整快照;带 `contractId` 的项覆盖当前合同,不带 ID 的项新增,当前已有但数组中遗漏的合同删除。
|
||||||
|
- 带 ID 的合同必须属于路径中的供应商;不属于当前供应商、重复 ID、非法枚举、负金额或日期逆序均失败。
|
||||||
|
- `expectedUpdateTime` 仍是供应商聚合并发版本;发生并发修改时调用方应刷新详情后重新组装完整表单。
|
||||||
|
- 合同快照会进入本次审批资料,但提交注册不会自动改写合同自身的 `status`。
|
||||||
|
|
||||||
|
### 典型请求
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /admin/supplier/items/2090300000000063970/submit
|
||||||
|
Authorization: Bearer <admin-token>
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fullName": "示例供应商有限公司",
|
||||||
|
"shortName": "示例供应商",
|
||||||
|
"taxNo": "91350211M000100Y46",
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"typeCode": "SCENIC"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mainCooperation": "景区资源合作",
|
||||||
|
"licenseImageUrl": "https://files.example.com/license.png",
|
||||||
|
"qualifications": [
|
||||||
|
{
|
||||||
|
"qualType": "BUSINESS_LICENSE",
|
||||||
|
"certNo": "LIC-2026-001",
|
||||||
|
"imageUrl": "https://files.example.com/license.png",
|
||||||
|
"permanentValid": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contracts": [
|
||||||
|
{
|
||||||
|
"contractId": "2090300000000063971",
|
||||||
|
"contractName": "2026 年度框架合同",
|
||||||
|
"contractNo": "HT-2026-001",
|
||||||
|
"contractType": "FRAME",
|
||||||
|
"signDate": "2026-08-26",
|
||||||
|
"startDate": "2026-09-01",
|
||||||
|
"endDate": "2027-08-31",
|
||||||
|
"amount": 1200.50,
|
||||||
|
"pricingMode": "按团结算",
|
||||||
|
"settleCycle": "MONTHLY",
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"scanFileUrl": "https://files.example.com/contracts/HT-2026-001.pdf",
|
||||||
|
"remark": "提交审批"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"initialAccounts": [],
|
||||||
|
"expectedUpdateTime": "2026-08-26 11:10:00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 成功响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"approvalLogId": "2090300000000063972",
|
||||||
|
"requestNo": "SUP-REQ-7b8c9d00112233445566778899aabbccddeeff00112233445566778899aabbcc",
|
||||||
|
"provider": "LOCAL_AUTO",
|
||||||
|
"approvalStatus": "APPROVED",
|
||||||
|
"spNo": null,
|
||||||
|
"spStatus": null,
|
||||||
|
"syncStatus": "APPLIED",
|
||||||
|
"submittedAt": "2026-08-26 11:11:00",
|
||||||
|
"finishedAt": "2026-08-26 11:11:00"
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 失败响应:合同不属于当前供应商
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 400,
|
||||||
|
"message": "合同不属于当前供应商",
|
||||||
|
"data": null,
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
该失败会回滚本次提交表单中的主体、资质、合同和审计变化,供应商仍保持原状态和原版本。
|
||||||
|
|
||||||
|
## 3. 查询供应商基本信息
|
||||||
|
|
||||||
|
`GET /admin/supplier/items/{supplierId}/basic-info/view`
|
||||||
|
|
||||||
|
### 请求
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /admin/supplier/items/2090300000000063970/basic-info/view
|
||||||
|
Authorization: Bearer <admin-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
无请求体。读取继续要求可信读角色和 `supplier:view` 平台权限。
|
||||||
|
|
||||||
|
### 成功响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"supplierId": "2090300000000063970",
|
||||||
|
"supplierNo": null,
|
||||||
|
"fullName": "示例供应商有限公司",
|
||||||
|
"shortName": "示例供应商",
|
||||||
|
"tax_no": "9135**********0Y46",
|
||||||
|
"legalRepresentative": null,
|
||||||
|
"legalRepresentativeIdNoMask": null,
|
||||||
|
"legalRepresentativeIdCardFrontUrl": null,
|
||||||
|
"legalRepresentativeIdCardBackUrl": null,
|
||||||
|
"contactPhoneMask": null,
|
||||||
|
"establishDate": null,
|
||||||
|
"registeredCapital": null,
|
||||||
|
"businessScope": null,
|
||||||
|
"staffScale": null,
|
||||||
|
"mainCooperation": "景区资源合作",
|
||||||
|
"status": "DRAFT",
|
||||||
|
"creditLevel": "B",
|
||||||
|
"totalScore": null,
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"typeCode": "SCENIC",
|
||||||
|
"typeName": "景区"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contacts": [],
|
||||||
|
"qualifications": [
|
||||||
|
{
|
||||||
|
"qualificationId": "2090300000000063973",
|
||||||
|
"qualType": "BUSINESS_LICENSE",
|
||||||
|
"qualTypeName": "营业执照",
|
||||||
|
"certNoMask": "LI*********01",
|
||||||
|
"imageUrl": "https://files.example.com/license.png",
|
||||||
|
"expiryDate": null,
|
||||||
|
"permanentValid": true,
|
||||||
|
"daysUntilExpiry": null,
|
||||||
|
"validityStatus": "VALID",
|
||||||
|
"validityStatusName": "有效",
|
||||||
|
"isRequired": false,
|
||||||
|
"expired": false,
|
||||||
|
"updateTime": "2026-08-26 11:10:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contracts": [
|
||||||
|
{
|
||||||
|
"contractId": "2090300000000063971",
|
||||||
|
"contractName": "2026 年度框架合同",
|
||||||
|
"contractNo": "HT-2026-001",
|
||||||
|
"contractType": "FRAME",
|
||||||
|
"signDate": "2026-08-26",
|
||||||
|
"startDate": "2026-09-01",
|
||||||
|
"endDate": "2027-08-31",
|
||||||
|
"amount": 1200.50,
|
||||||
|
"pricingMode": "按团结算",
|
||||||
|
"settleCycle": "MONTHLY",
|
||||||
|
"status": "DRAFT",
|
||||||
|
"scanFileUrl": "https://files.example.com/contracts/HT-2026-001.pdf",
|
||||||
|
"remark": "年度合作",
|
||||||
|
"updateTime": "2026-08-26 11:10:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updateTime": "2026-08-26 11:10:00"
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
合同按 `contractId` 升序返回,只包含当前有效合同。没有合同时返回空数组 `[]`,不返回 `null`。
|
||||||
|
|
||||||
|
### 常见失败
|
||||||
|
|
||||||
|
| 场景 | `code` | 前端处理 |
|
||||||
|
|---|---:|---|
|
||||||
|
| 未登录或 Token 失效 | `401` | 跳转登录,不展示空详情 |
|
||||||
|
| 可信角色或 `supplier:view` 平台权限不足 | `403` | 展示无权限状态 |
|
||||||
|
| 供应商不存在或已删除 | `395001` | 返回列表并刷新 |
|
||||||
|
|
||||||
|
## 修改前后对比
|
||||||
|
|
||||||
|
| 场景 | 修改前 | 修改后 |
|
||||||
|
|---|---|---|
|
||||||
|
| 创建草稿 | 请求不能携带合同 | 可选携带完整 `contracts`,与草稿一起成功或失败 |
|
||||||
|
| 提交注册 | 提交表单不能维护合同 | 可省略保留、空数组清空或提交完整合同快照 |
|
||||||
|
| 基础信息 | 不返回合同列表 | 返回完整 `contracts[]` 及字符串 ID、版本 |
|
||||||
|
| 账户区域标题 | 页面显示“初始账户” | 页面应显示“结算信息”,接口字段仍为 `initialAccounts` |
|
||||||
|
| 页面区块顺序 | 资质后直接进入账户区域 | 资质证照 → 合同信息 → 结算信息 |
|
||||||
|
|
||||||
|
## 兼容性与管理端接入事项
|
||||||
|
|
||||||
|
1. 在“资质证照”区域之后新增“合同信息”表格,在合同之后显示原账户表格。
|
||||||
|
2. 原账户表格标题改为“结算信息”;所有请求和响应继续使用 `initialAccounts`,不要改字段名。
|
||||||
|
3. 创建草稿时合同为完整新项,不发送 `contractId`;编辑后提交时,既有合同必须原样带回字符串 `contractId`。
|
||||||
|
4. 提交表单是完整快照。用户明确删除全部合同时发送 `contracts: []`;未加载合同或不处理合同时省略字段,不要误发空数组。
|
||||||
|
5. 所有 ID 均作为字符串保存、比较和回传,不经过 Number 转换。
|
||||||
|
6. 本次不新增接口路径、权限点或业务错误码;旧客户端省略 `contracts` 时继续可用。
|
||||||
|
7. 管理端源码不在本后端工单中修改,前端状态保持 `pending`,直至完成页签、标题和表格接入并提供前端引用。
|
||||||
|
|
||||||
|
## TEST 验证证据
|
||||||
|
|
||||||
|
- 自动化:供应商定向测试 115 项通过;`hl-resource-service` 全量 2,111 项,0 失败、0 错误,38 项条件跳过;`hl-verify` 与差异检查通过。
|
||||||
|
- 部署:Deploy Panel API 任务 `af3f205b` 终态 `success`、退出码 0、`has_build_error=false`,未发现 Maven、编译或滚动发布错误。
|
||||||
|
- 健康:`hl-resource-service` 的 8082、8182 双实例运行;Nacos `test` 命名空间两实例均 `healthy=true`、`enabled=true`。
|
||||||
|
- 真实 Gateway:23 项断言通过,覆盖合同随草稿创建、详情完整回显、字符串 ID、创建携带 ID 失败、普通 ADMIN 越权、提交外部合同 ID 完整回滚、日期逆序零写入和旧客户端省略 `contracts`。
|
||||||
|
- 清理:两个临时 DRAFT 均通过业务删除接口软删除并回读为不存在;仅保留不可逆的 CREATE/DELETE 操作审计。
|
||||||
|
- 环境限制:部署前锁定 `origin/dev-v3=1a16a5aec7d0b95ec87e6fb222581060a3135984`,但面板 Git API 回读到服务器本地短提交 `6f7d3ca78`,Gitea 无法解析该对象。接口行为已真实验证,后端工单仍等待测试环境恢复精确远端提交后复验,不能据此宣称最终交付完成。
|
||||||
|
|
||||||
|
## 撤回
|
||||||
|
|
||||||
|
1. 从最新 `dev-v3` 创建回退分支,执行 `git revert -m 1 --no-edit 1a16a5aec7d0b95ec87e6fb222581060a3135984`,经独立 PR 合入。
|
||||||
|
2. 重新滚动部署 `hl-resource-service`;无需执行数据库结构、配置、Redis 或 MQ 恢复。
|
||||||
|
3. 管理端停止发送和读取 `contracts`,恢复原页面结构;`initialAccounts` 契约始终不变。
|
||||||
|
4. 已保存的合同资料保留,不做破坏性批量清理;回退后旧客户端继续按省略 `contracts` 的路径工作。
|
||||||
|
5. 经 Gateway 复测创建、提交、基础信息、未认证、越权、非法合同 ID、失败零写入和旧客户端兼容,并确认双实例与 Nacos 健康。
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
- **Issue**: [#6397](https://git.1814.love:8443/wx/HL/issues/6397)
|
||||||
|
- **PR**: [#6405](https://git.1814.love:8443/wx/HL/pulls/6405)
|
||||||
|
- **合并提交**: [1a16a5aec](https://git.1814.love:8443/wx/HL/commit/1a16a5aec7d0b95ec87e6fb222581060a3135984)
|
||||||
|
- **后端负责人**: @lc
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "6406"
|
||||||
|
title: "开票申请按税号自动回填企业工商信息"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "wx(GIT)"
|
||||||
|
change_type: "新增接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "verified"
|
||||||
|
frontend_owner: "mmg"
|
||||||
|
frontend_ref: "080ea8df"
|
||||||
|
target_release: ""
|
||||||
|
verified_at: "2026-08-26"
|
||||||
|
status_note: "PR #6415 已合并 dev-v3 并部署测试服,经网关 API 实测 companyInfo 与 lastInvoiceTitle 两段往返一致,非法税号返回 581526"
|
||||||
|
updated_at: "2026-08-26"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🔍 开票申请:按税号自动回填企业工商信息
|
||||||
|
|
||||||
|
开票申请填写页面现支持按税号自动回填企业工商信息。后端对接第三方企业信息查询服务(元典开放平台),并同时返回本系统同税号最近一条历史开票抬头,两段数据并列返回、由前端自行决定展示与回填优先级,后端不做合并取舍。
|
||||||
|
|
||||||
|
> **PR**: [#6415](https://git.1814.love:8443/wx/HL/pulls/6415) | **服务**: hl-order-service-v3 | **作者**: wx | **更新时间**: 2026-08-26
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 开票企业信息自动回填查询 | GET | `/v3/admin/invoice/company-info` | 新增 | 按税号查两段回填数据 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、接口详情
|
||||||
|
|
||||||
|
### 1. 开票企业信息自动回填查询 `GET /v3/admin/invoice/company-info`
|
||||||
|
|
||||||
|
**入参**: `taxNo`(Query 参数)
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| taxNo | Query | String | ✅ | 非空;去空格后长度 15 或 18 | 统一社会信用代码(或旧 15 位注册号) |
|
||||||
|
|
||||||
|
**出参 `Result<CompanyInfoRespVO>`**:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| companyInfo | Object 或 null | 元典第三方工商照面;查不到/第三方异常/全 key 耗尽时为 null |
|
||||||
|
| lastInvoiceTitle | Object 或 null | 本系统同税号最近一条非作废发票的历史抬头;无历史时为 null |
|
||||||
|
|
||||||
|
**companyInfo 字段**(元典工商照面):
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| titleName | String | 企业名称 |
|
||||||
|
| taxNo | String | 统一社会信用代码 |
|
||||||
|
| legalPersonName | String | 法定代表人 |
|
||||||
|
| registAddress | String | 注册地址 |
|
||||||
|
| regStatus | String | 经营状态(存续/注销/吊销等,由第三方返回) |
|
||||||
|
|
||||||
|
**lastInvoiceTitle 字段**(本系统历史抬头):
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| titleName | String | 开票抬头 |
|
||||||
|
| bankName | String | 开户行 |
|
||||||
|
| bankAccount | String | 银行账号 |
|
||||||
|
| registAddress | String | 注册地址 |
|
||||||
|
| registPhone | String | 注册电话 |
|
||||||
|
| email | String | 邮箱 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /v3/admin/invoice/company-info?taxNo=91110000802100433B
|
||||||
|
Authorization: Bearer <admin-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 成功响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"companyInfo": {
|
||||||
|
"titleName": "北京百度网讯科技有限公司",
|
||||||
|
"taxNo": "91110000802100433B",
|
||||||
|
"registAddress": "北京市海淀区上地十街10号百度大厦2层",
|
||||||
|
"legalPersonName": "梁志祥",
|
||||||
|
"regStatus": "存续"
|
||||||
|
},
|
||||||
|
"lastInvoiceTitle": {
|
||||||
|
"titleName": "北京百度网讯科技有限公司",
|
||||||
|
"bankName": "招商银行北京分行",
|
||||||
|
"bankAccount": "110900100011110",
|
||||||
|
"registAddress": "北京市海淀区上地十街10号百度大厦2层",
|
||||||
|
"registPhone": "010-59928888",
|
||||||
|
"email": "invoice@baidu.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 降级响应示例(任一段查不到为 null,互不阻塞)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"companyInfo": null,
|
||||||
|
"lastInvoiceTitle": null
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 非法税号错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 581526,
|
||||||
|
"message": "税号格式不正确(统一社会信用代码须为 15 或 18 位)",
|
||||||
|
"data": null,
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、边界行为
|
||||||
|
|
||||||
|
- 未登录 → 401(网关拦截)
|
||||||
|
- 税号为空 / 去空格后非 15 或 18 位 → `581526` 参数错误
|
||||||
|
- 元典查无该企业 → `companyInfo = null`,`lastInvoiceTitle` 照常返回
|
||||||
|
- 元典接口超时 / 异常 / 全部 key 不可用 → 降级 `companyInfo = null`,不影响 `lastInvoiceTitle`
|
||||||
|
- 本系统无同税号历史发票 → `lastInvoiceTitle = null`
|
||||||
|
- 税号含空格 → 后端自动去空格后再查询,不报错
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、不影响范围(显式声明)
|
||||||
|
|
||||||
|
- **仅新增**:本查询接口为纯只读旁路查询
|
||||||
|
- **零影响**:
|
||||||
|
- 开票申请提交接口(`/v3/admin/order/{orderId}/invoice/apply`)行为不变
|
||||||
|
- 开票流程、开票记录写入逻辑
|
||||||
|
- 既有发票列表/详情查询
|
||||||
|
- 订单、订单核心、房务、车务模块
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、测试环境已验证
|
||||||
|
|
||||||
|
网关实测(`https://api.test.1814.love:9443`):
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /v3/admin/invoice/company-info?taxNo=91110000802100433B
|
||||||
|
→ 200 + companyInfo(北京百度网讯科技有限公司/梁志祥/存续) ✓
|
||||||
|
→ 200 + lastInvoiceTitle(招商银行北京分行/110900100011110/invoice@baidu.com) ✓
|
||||||
|
GET /v3/admin/invoice/company-info?taxNo=123
|
||||||
|
→ 581526 税号格式不正确 ✓
|
||||||
|
未带 Authorization 头
|
||||||
|
→ 401 缺少有效的 Authorization 头 ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- 关联 Issue: [wx/HL#6406](https://git.1814.love:8443/wx/HL/issues/6406)
|
||||||
|
- 关联 PR: [wx/HL#6415](https://git.1814.love:8443/wx/HL/pulls/6415)
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
### 链接
|
||||||
|
|
||||||
|
- **Issue**: [#6406](https://git.1814.love:8443/wx/HL/issues/6406)
|
||||||
|
- **PR**: [#6415](https://git.1814.love:8443/wx/HL/pulls/6415)
|
||||||
|
- **Merge commit**: [f2c6e9fef53315a458b79f71747ee3494c420694](https://git.1814.love:8443/wx/HL/commit/f2c6e9fef53315a458b79f71747ee3494c420694)
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- **后端负责人**: @wx
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "6409"
|
||||||
|
title: "行政区划响应 ID 固定为字符串"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "lc(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "not_required"
|
||||||
|
frontend_owner: ""
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: ""
|
||||||
|
status_note: "PR #6412 已合并 dev-v3;hl-user-service 已以提交 4b31612a 部署 TEST,create、children、path 经真实 Gateway 验证全部 Long ID 固定为 JSON string,空层级保持 null。"
|
||||||
|
updated_at: "2026-08-26"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 行政区划响应 ID 固定为字符串
|
||||||
|
|
||||||
|
行政区划创建、单级联动和父链回显接口中的 Long ID 现在始终按 JSON 字符串返回,避免小整数因全局安全整数规则被输出为 JSON number。请求参数、权限、业务校验和错误码不变。
|
||||||
|
|
||||||
|
## 变更接口
|
||||||
|
|
||||||
|
| 方法 | 路径 | 响应变化 |
|
||||||
|
|---|---|---|
|
||||||
|
| POST | `/admin/region` | 成功响应 `data` 从 JSON number 固定为 string |
|
||||||
|
| GET | `/admin/region/children` | `parentId`、`defaultId`、`items[].id`、`items[].parentId` 固定为 string;空值保持 `null` |
|
||||||
|
| GET | `/admin/region/{id}/path` | `selectedId`、`provinceId`、`prefectureId`、`countyId` 及 `path` 节点 ID 固定为 string;缺失层级保持 `null` |
|
||||||
|
|
||||||
|
更新接口 `PUT /admin/region/{id}` 的请求和响应不变。所有请求路径、Query/Path 参数仍按原 Long 语义解析。
|
||||||
|
|
||||||
|
## 兼容性与错误语义
|
||||||
|
|
||||||
|
- 这是已发布接口 JSON 类型的契约修正,不新增字段,也不改变字段名称。
|
||||||
|
- 行政区划 Redis 缓存可同时反序列化旧 JSON number 与新 JSON string,滚动部署期间兼容。
|
||||||
|
- 根层 `parentId`、无默认节点的 `defaultId` 和父链中不存在的层级继续返回 `null`,不会变成字符串 `"null"`。
|
||||||
|
- 未认证请求继续返回业务码 `401`;非法正整数参数、缺失节点和非法节点类型继续使用既有错误语义,失败路径零业务写入。
|
||||||
|
- 无数据库 migration、配置、Gateway、MQ、缓存键或跨服务契约变更;无需修改前端源码,调用方按既定字符串 ID 契约消费即可。
|
||||||
|
- 统一响应可能以 HTTP 200 承载业务失败,客户端必须同时检查 `code`、`success`、`message` 和 `data`。
|
||||||
|
|
||||||
|
## TEST 验证证据
|
||||||
|
|
||||||
|
- PR #6412 合并提交为 `8494028aa2dfbb4e39147c09695898d247045d4c`;TEST 目标提交 `4b31612aeba5c0d3ac690cc6af15b66290bd8b48` 包含该合并提交。
|
||||||
|
- Deploy Panel API 任务 `b10dd2ed` 成功执行 `/opt/hulalv/scripts/deploy-backend.sh`;User 的 8081、8181 双实例和 Nacos 两个 healthy/enabled 实例通过,任务期 9 个可验证采样均有可用实例,0 个故障采样。
|
||||||
|
- 真实 TEST 管理员经 Gateway 验证 create、children、path:创建 ID、联动列表 ID、父链 ID 和对应 Redis 缓存 ID 均为字符串,根节点和缺失层级空值保持 `null`。
|
||||||
|
- 未认证请求返回 `401`;非法父节点、缺失节点、空创建和旧非法节点类型均按既有错误返回,并确认失败路径零写入。
|
||||||
|
- 成功创建和 CAS 更新后,精确删除本次创建的 1 行;数据库总量恢复且测试命名空间为 0。4 个相关 Redis key 按基线值及绝对过期时间恢复,验收会话已注销;操作审计按系统约定保留。
|
||||||
|
- 本地验证:Controller/Cache 定向 22 项通过;User 全量 3644 项通过、0 失败、0 错误,8 项条件跳过;`hl-verify` 与 `git diff --check` 通过。
|
||||||
|
|
||||||
|
## 撤回
|
||||||
|
|
||||||
|
1. 从最新 `dev-v3` 创建独立回退分支,执行 `git revert -m 1 --no-edit 8494028aa2dfbb4e39147c09695898d247045d4c`,经评审 PR 合入;不要回退后续无关提交。
|
||||||
|
2. 通过 Deploy Panel API 对回退目标执行新鲜预检和显式部署 `hl-user-service`,复核双实例、Nacos、日志和精确提交。
|
||||||
|
3. 本修复无数据库、配置或 MQ 变更,不执行 DDL、DML 或消息补偿。缓存新旧表示均可读取,通常无需清理;确需强制恢复旧表示时,仅在停止行政区划刷新后精确处理 `hl:user:region:v1:` 命名空间并由回退版本预热。
|
||||||
|
4. 撤回前确认调用方可重新接受 JSON number ID;撤回后经 Gateway 复测 create、children、path、空层级、未认证和失败零写入。
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
- **Issue**: [#6409](https://git.1814.love:8443/wx/HL/issues/6409)
|
||||||
|
- **PR**: [#6412](https://git.1814.love:8443/wx/HL/pulls/6412)
|
||||||
|
- **合并提交**: [8494028aa](https://git.1814.love:8443/wx/HL/commit/8494028aa2dfbb4e39147c09695898d247045d4c)
|
||||||
|
- **后端负责人**: @lc
|
||||||
在新工单中引用
屏蔽一个用户