348 行
18 KiB
Markdown
348 行
18 KiB
Markdown
---
|
||
schema: "hl-changelog/v2"
|
||
ticket: "6476"
|
||
title: "供应商详情补齐地址备注并统一表单校验"
|
||
consumer: "admin"
|
||
author: "lc(GIT)"
|
||
change_type: "修改接口"
|
||
backend_status: "deployed"
|
||
gateway_status: "verified"
|
||
frontend_status: "verified"
|
||
frontend_owner: "mmg"
|
||
frontend_ref: "6635a2ae"
|
||
target_release: "v2.1"
|
||
verified_at: "2026-08-27"
|
||
status_note: "PR #6517 已合并 dev-v3(合并提交 a952a04c);hl-resource-service 已随 dev-v3 精确提交 b269e5fd 由 Deploy Panel 任务 938ca09e 发布 TEST。真实 TEST 身份已验证 address/remark 创建、详情、更新回显,创建/更新同口径校验、权限失败零写入及测试数据清理。"
|
||
updated_at: "2026-08-27"
|
||
base: "dev-v3"
|
||
---
|
||
|
||
# 供应商管理:详情补齐地址备注并统一表单校验
|
||
|
||
供应商详情接口现在完整返回主体的 `address` 和 `remark`,管理端可用同一份详情快照初始化查看页与编辑表单。创建、提交和更新原本已有的业务字段与校验未增加新的必填项;本工单用契约测试和真实 TEST 验收固定三条写链路的一致口径。
|
||
|
||
本次没有新增接口路径、权限点或业务错误码。
|
||
|
||
## 一、背景
|
||
|
||
创建和更新请求已经接受地址、备注等主体字段,但基础信息详情此前没有回传 `address`、`remark`,导致管理端打开编辑页时无法完整还原已保存表单。同时,创建、提交和更新分属不同请求模型,消费方需要一个明确、可验证的共同校验口径。
|
||
|
||
本次处理后:
|
||
|
||
- 详情响应补齐主体地址与内部备注。
|
||
- 创建、提交、更新对共同主体字段、类型、联系人、资质和合同继续执行同一业务规则。
|
||
- 更新只额外要求变更原因、主体并发版本,以及被修改子项的 ID/版本。
|
||
- #6436/#6499 已交付的授权完整值语义保持不变,不重新引入脱敏值或“留空表示保留旧敏感值”的旧约定。
|
||
|
||
## 二、变更接口清单
|
||
|
||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||
|---:|---|---|---|---|---|
|
||
| 1 | 查询供应商基本信息 | GET | `/admin/supplier/items/{supplierId}/basic-info/view` | 响应新增字段 | 根对象新增 `address`、`remark`,用于完整初始化查看与编辑表单 |
|
||
|
||
创建草稿、提交注册和更新资料的路径、请求字段及错误码没有结构性变化,因此不作为新增接口列入上表;其稳定调用规则在第四节集中说明。
|
||
|
||
## 三、接口详情
|
||
|
||
### 1. 查询供应商基本信息 `GET /admin/supplier/items/{supplierId}/basic-info/view`
|
||
|
||
**VO**: `SupplierBasicInfoRespVO`
|
||
|
||
#### 使用场景
|
||
|
||
管理端进入供应商详情或编辑页时调用。响应是主体、类型、联系人、资质、合同及并发版本的完整快照;编辑页应直接使用其中的 `address`、`remark` 和 `updateTime`,不得用本地空值覆盖。
|
||
|
||
#### 入参
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|---|---|---|---:|---|---|
|
||
| `supplierId` | Path | String | 是 | 正整数 ID 字符串 | 目标供应商;不得转为 JavaScript Number |
|
||
|
||
#### 出参 `Result<SupplierBasicInfoRespVO>`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| `supplierId` | String | 供应商 ID |
|
||
| `fullName` / `shortName` | String/null | 供应商全称与简称 |
|
||
| `tax_no` | String | 完整主体证件号;JSON 字段名保持 `tax_no` |
|
||
| `legalRepresentative` | String/null | 法定代表人姓名 |
|
||
| `legalRepresentativeIdNo` | String/null | 完整法人居民身份证号 |
|
||
| `legalRepresentativeIdCardFrontUrl` / `legalRepresentativeIdCardBackUrl` | String/null | 法人证件正反面永久文件地址 |
|
||
| `contactPhone` | String/null | 完整公司联系电话 |
|
||
| `establishDate` | String/null | 成立日期,格式 `yyyy-MM-dd` |
|
||
| `registeredCapital` / `businessScope` | String/null | 注册资本与经营范围 |
|
||
| `address` | String/null | 本次新增回显:注册地址或经营地址 |
|
||
| `staffScale` | String/null | 人员规模字典值 |
|
||
| `mainCooperation` | String | 主要合作内容 |
|
||
| `remark` | String/null | 本次新增回显:供应商主体内部备注 |
|
||
| `types` | Array | 类型完整集合;每项包含 `isPrimary` |
|
||
| `primaryTypeCode` / `primaryTypeName` | String/null | 当前主类型 |
|
||
| `contacts` / `qualifications` / `contracts` | Array | 联系人、资质和合同完整集合;现有项包含字符串 ID 与 `updateTime` |
|
||
| `status` | String | 当前供应商状态 |
|
||
| `updateTime` | String | 主体并发版本,格式 `yyyy-MM-dd HH:mm:ss` |
|
||
|
||
#### 请求示例
|
||
|
||
```http
|
||
GET /admin/supplier/items/2092800000000000001/basic-info/view
|
||
Authorization: Bearer <admin-token>
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"success": true,
|
||
"data": {
|
||
"supplierId": "2092800000000000001",
|
||
"fullName": "示例旅行服务有限公司",
|
||
"shortName": "示例旅行",
|
||
"tax_no": "91350211M000100Y46",
|
||
"legalRepresentative": "示例法人",
|
||
"legalRepresentativeIdNo": "11010519491231002X",
|
||
"legalRepresentativeIdNoMask": "11010519491231002X",
|
||
"legalRepresentativeIdCardFrontUrl": "https://files.example.com/supplier/id-front.jpg",
|
||
"legalRepresentativeIdCardBackUrl": "https://files.example.com/supplier/id-back.jpg",
|
||
"contactPhone": "13800138000",
|
||
"contactPhoneMask": "13800138000",
|
||
"establishDate": "2020-01-01",
|
||
"registeredCapital": "100万元",
|
||
"businessScope": "境内旅游服务",
|
||
"address": "福建省厦门市示例路 1 号",
|
||
"staffScale": "LT50",
|
||
"mainCooperation": "酒店与景区资源合作",
|
||
"remark": "重点合作供应商",
|
||
"types": [{"typeCode": "HOTEL", "typeName": "酒店", "isPrimary": true}],
|
||
"primaryTypeCode": "HOTEL",
|
||
"primaryTypeName": "酒店",
|
||
"contacts": [],
|
||
"qualifications": [],
|
||
"contracts": [],
|
||
"status": "DRAFT",
|
||
"updateTime": "2026-08-27 15:30:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
历史供应商未填写地址或备注时,字段明确返回 `null`;无类型、联系人、资质或合同时,相应集合返回空数组,不把缺失数据当成接口异常:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"success": true,
|
||
"data": {
|
||
"supplierId": "2092800000000000001",
|
||
"address": null,
|
||
"remark": null,
|
||
"types": [],
|
||
"primaryTypeCode": null,
|
||
"primaryTypeName": null,
|
||
"contacts": [],
|
||
"qualifications": [],
|
||
"contracts": [],
|
||
"updateTime": "2026-08-27 15:30:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
供应商不存在、已删除或超出数据范围时不返回空详情:
|
||
|
||
```json
|
||
{
|
||
"code": 395001,
|
||
"message": "供应商不存在",
|
||
"success": false,
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 要求可信管理身份、`supplier:view` 平台权限和供应商数据范围;登录态不能代替业务权限。
|
||
- `address`、`remark` 属于响应的向后兼容增加;旧客户端忽略未知字段即可继续运行。
|
||
- `legalRepresentativeIdNoMask`、`contactPhoneMask` 等废弃兼容别名继续与完整值字段同值;新代码使用无 `Mask` 字段。
|
||
- Long ID 一律按字符串保存、比较和传输。
|
||
- 本接口只读,不修改主体版本、缓存或审批状态。
|
||
|
||
## 四、契约约束与正确调用方式
|
||
|
||
以下三个既有写接口没有新增路径或字段,但共同业务口径由本工单固定:
|
||
|
||
- 创建草稿:`POST /admin/supplier/items/add`
|
||
- 提交注册:`POST /admin/supplier/items/{supplierId}/submit`
|
||
- 更新资料:`PUT /admin/supplier/items/{supplierId}/update`
|
||
|
||
### 共同主体字段规则
|
||
|
||
| 字段 | 创建/提交 | 更新 | 统一约束 |
|
||
|---|---|---|---|
|
||
| `fullName` | 必填 | 可选 | 最长 500 字符 |
|
||
| `shortName` | 可选 | 可选 | 最长 300 字符 |
|
||
| `taxNo` | 必填 | 可选,且仅允许在状态规则内修改 | 6 至 64 位字母、数字或展示分隔符;身份证/统一社会信用代码还校验日期或校验位 |
|
||
| `legalRepresentative` | 可选 | 可选 | 最长 500 字符 |
|
||
| `legalRepresentativeIdNo` | 可选 | 可选 | 18 位合法居民身份证号;与正反面地址按完整三字段组合校验 |
|
||
| `legalRepresentativeIdCardFrontUrl` / `legalRepresentativeIdCardBackUrl` | 可选 | 可选 | 公网 HTTPS 永久文件地址,单项最长 1000 字符 |
|
||
| `contactPhone` | 可选 | 可选 | 7 至 20 位合法电话字符 |
|
||
| `establishDate` | 可选 | 可选 | `yyyy-MM-dd`,不得晚于当前日期 |
|
||
| `registeredCapital` | 可选 | 可选 | 最长 50 字符 |
|
||
| `businessScope` | 可选 | 可选 | 最长 500 字符 |
|
||
| `address` | 可选 | 可选 | 最长 500 字符;创建和更新同口径 |
|
||
| `staffScale` | 可选 | 可选 | `LT50`、`R50_200`、`R200_500`、`GT500` |
|
||
| `mainCooperation` | 必填 | 可选 | 创建/提交不能为空白;更新传值时按同一业务含义保存 |
|
||
| `licenseImageUrl` | 可选 | 可选 | 最长 500 字符,并与营业执照资质归一化 |
|
||
| `remark` | 可选 | 可选 | 供应商主体内部备注,不等同于审批意见或 `changeReason` |
|
||
|
||
集合上限与语义保持不变:`types` 最多 15 项;`contacts`、`qualifications`、`contracts` 各最多 100 项;`initialAccounts` 最多 1 项。非空类型必须来自生效字典且不得重复,非空联系人集合必须形成唯一默认联系人,资质和合同继续执行类型、日期、金额、归属和完整快照校验。
|
||
|
||
更新场景额外要求:
|
||
|
||
- `changeReason` 必填、非空白、最长 500 字符,并进入业务审计。
|
||
- `expectedUpdateTime` 必须等于详情最新 `updateTime`。
|
||
- 已有联系人、资质、合同随完整快照更新时,必须带回对应字符串 ID 和子项 `updateTime`。
|
||
|
||
### ✅ 正确 / ❌ 错误 payload 对照
|
||
|
||
| 场景 | payload / 结果 |
|
||
|---|---|
|
||
| ✅ 更新地址和备注 | `{"address":"福建省厦门市示例路 2 号","remark":"地址已确认","changeReason":"更新地址和备注","expectedUpdateTime":"2026-08-27 15:30:00"}` |
|
||
| ✅ 只更新备注 | `{"remark":"新的内部备注","changeReason":"补充内部备注","expectedUpdateTime":"2026-08-27 15:30:00"}` |
|
||
| ❌ 地址超过 500 字符 | 创建、更新均返回 `400`,零写入 |
|
||
| ❌ 成立日期晚于当前日期 | 创建、更新均返回 `400`,零写入 |
|
||
| ❌ 更新缺少 `changeReason` | 返回 `400`,零写入 |
|
||
| ❌ 使用旧 `expectedUpdateTime` | 返回 `395014`,零写入;必须刷新详情后由用户确认 |
|
||
|
||
### 正确更新顺序
|
||
|
||
1. 先查询详情,保存字符串 ID、子项版本和根对象 `updateTime`。
|
||
2. 用户提交时只发送实际变更字段,并附非空 `changeReason`、最新 `expectedUpdateTime`。
|
||
3. 更新成功后采用响应里的新版本;需要完整表单时重新查询详情。
|
||
4. 遇到 `395014` 先刷新,不得自动用旧表单覆盖他人修改。
|
||
|
||
## 五、数据库行为
|
||
|
||
- 成功创建、提交或更新时,主体与本次携带的类型、联系人、资质、合同及业务审计按聚合事务一起成功。
|
||
- 地址和备注保存后,详情读取与变更审计使用同一业务值;`remark` 不会被提升为审批意见或变更原因。
|
||
- 请求校验、权限、状态、归属或并发版本失败时零写入,主体 `updateTime` 不变化。
|
||
- 本次没有数据库结构变更、Flyway migration 或历史数据回填;旧记录的地址/备注原值保持不变。
|
||
- 不新增 Redis、MQ、Feign 或跨服务副作用。
|
||
|
||
## 六、边界行为
|
||
|
||
- 未登录或 Token 失效:`401`,不把无认证响应当作正向验收。
|
||
- ADMIN 等无写权限角色调用创建、提交或更新:`395002`,零写入;已有详情仍按读取权限返回。
|
||
- 供应商不存在或已删除:`395001`。
|
||
- 并发版本过期:`395014`,调用方刷新详情后重提。
|
||
- 地址超过 500 字符、未来成立日期、必填字段缺失或组合不完整:`400`,零写入。
|
||
- 历史 `address`、`remark` 为空:详情返回 `null`,不报错、不猜测默认值。
|
||
- `ARCHIVED` 等不可修改状态继续由既有状态门禁拒绝更新。
|
||
- 业务失败可能仍使用 HTTP 200,调用方必须同时判断响应体 `code`、`success`、`data`。
|
||
|
||
## 六.5、枚举 / 数据字典
|
||
|
||
### `staffScale`(供应商人员规模稳定值)
|
||
|
||
**所属字段**: `SupplierDraftSaveReqVO.staffScale / SupplierUpdateReqVO.staffScale / SupplierBasicInfoRespVO.staffScale` | **类型**: `String`
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|---|---|---|
|
||
| `LT50` | 50 人以下 | 小于 50 人 |
|
||
| `R50_200` | 50 至 200 人 | 50 至 200 人档 |
|
||
| `R200_500` | 200 至 500 人 | 200 至 500 人档 |
|
||
| `GT500` | 500 人以上 | 大于 500 人 |
|
||
|
||
`types[].typeCode`、`contacts[].contactRole`、`qualifications[].qualType` 继续从对应生效数据字典读取,不在客户端硬编码展示名。
|
||
|
||
## 六.6、修改前后对比
|
||
|
||
### 字段级对比
|
||
|
||
| 字段 | 改前 | 改后 |
|
||
|---|---|---|
|
||
| 详情根对象 `address` | 未返回,已保存值无法用于表单回填 | 返回保存的地址;未填写为 `null` |
|
||
| 详情根对象 `remark` | 未返回,已保存值无法用于表单回填 | 返回主体内部备注;未填写为 `null` |
|
||
| 创建/提交/更新请求字段 | 已存在 | 无新增字段、无新增必填项 |
|
||
|
||
### 行为级对比
|
||
|
||
| 行为 | 改前 | 改后 |
|
||
|---|---|---|
|
||
| 打开编辑页 | 详情缺少地址和备注,表单初始化不完整 | 一次详情调用可完整初始化主体字段 |
|
||
| 共同字段校验 | 实现存在,但缺少跨创建/更新契约锁定 | 自动化和 TEST 验收固定创建/更新同口径 |
|
||
| 完整敏感值回显 | 按 #6436/#6499 返回授权完整值 | 保持不变 |
|
||
|
||
## 六.7、影响评估
|
||
|
||
- **是否破坏向后兼容**: 否。响应新增两个可空字段,旧客户端可忽略;请求无新增必填项。
|
||
- **前端是否必须同步上线**: 为闭合编辑体验需要接入 `address`、`remark`,但不构成旧客户端继续运行的硬门禁。
|
||
- **前端 workaround 清理点**: 删除编辑页对地址、备注的本地空值兜底,改为使用详情真实值。
|
||
- **QA 重点**: 新建后打开详情、更新后刷新详情、地址长度 500/501、今天/未来成立日期、ADMIN 越权、旧版本并发冲突。
|
||
|
||
## 七、不影响范围
|
||
|
||
- **仅影响**: 管理后台供应商详情/编辑表单的主体地址、内部备注回填,以及已有写接口校验口径的契约固定。
|
||
- **零影响**:
|
||
- Gateway 路由和认证级别。
|
||
- 供应商生命周期状态机、审批、收款账户、证明附件、资源关系及归档语义。
|
||
- 数据库结构、历史数据、Nacos 配置、Redis、MQ、Feign 和其他服务。
|
||
- 小程序、C 端、Web、H5、桌面端接口。
|
||
- #6436/#6499 的授权完整值和废弃 `*Mask` 兼容语义。
|
||
|
||
## 八、测试环境已验证
|
||
|
||
- 本地自动化:供应商定向测试 49 项通过;最新 `dev-v3` 上 `hl-resource-service` Reactor 全量 2,153 项,0 失败、0 错误、38 项条件跳过。
|
||
- 部署:Deploy Panel API 任务 `938ca09e` 终态 `success`;目标与实际提交均为 `b269e5fdca2e1d29e0336314a909885e9d1f9d16`,包含本工单合并提交 `a952a04c`。
|
||
- 采样:部署任务期 8 个有效采样;每个采样至少 2 个运行进程、2 个健康启用 Nacos 实例,零观测不可用采样。该证据只证明采样点,不代表采样间绝对连续。
|
||
- 真实 Gateway:SUPER_ADMIN 创建完整表单后,详情精确回显 `address`、`remark`、完整授权字段、主类型及联系人/资质/合同 ID 和版本;更新后 API、数据库主体与更新审计一致。
|
||
- 失败路径:创建/更新的地址超长均返回 `400`;创建/更新的未来成立日期均返回 `400`;ADMIN 更新返回 `395002`;所有失败均验证零写入、版本不变。
|
||
- 清理:临时供应商先经业务删除接口软删除,确认主体已删除且活动子项为 0;随后按本次唯一标记精确清理 8 行测试数据,17 张关联表回读,剩余供应商 0 行;测试会话已注销并验证失效。
|
||
|
||
## 九、相关历史 PR
|
||
|
||
| PR | Issue | 说明 | 是否仍有效 |
|
||
|---|---|---|---|
|
||
| #6478 / #6483 / #6486 | #6436 | 供应商授权完整值、主类型及同日修正 | ✅ 有效,本次保持 |
|
||
| #6517 | #6476 | 详情补齐地址备注并固定表单校验契约 | ✅ 最新 |
|
||
|
||
## 十、相关文档
|
||
|
||
- 关联 Issue:[#6476](https://git.1814.love:8443/wx/HL/issues/6476)
|
||
- 关联 PR:[#6517](https://git.1814.love:8443/wx/HL/pulls/6517)
|
||
- 相关完整值契约:`changelogs-v2/2026-08/27_6436_供应商敏感字段完整回显与主类型-修改接口-管理后台.md`
|
||
- 管理端接入:读取详情 `address`、`remark`,更新时保留最新 `expectedUpdateTime`;前端引用待回填。
|
||
|
||
## 撤回
|
||
|
||
1. 从最新 `dev-v3` 创建独立回退分支,执行:
|
||
|
||
```bash
|
||
git revert -m 1 --no-edit a952a04cf80f24d6c50cbec4b992c82f8580beba
|
||
```
|
||
|
||
2. 运行供应商定向测试、`hl-resource-service` 全量测试和差异检查,经独立 PR 合入。
|
||
3. 使用同一 Deploy Panel 两阶段客户端,仅滚动部署 `hl-resource-service`,并绑定批准回退分支的精确提交。
|
||
4. 管理端停止依赖详情响应中的 `address`、`remark`,再撤回本 Changelog;既有请求字段和完整值契约保持不变。
|
||
5. 无数据库、配置、Redis 或 MQ 恢复步骤;已保存的地址和备注数据保留,不做破坏性回填或删除。
|
||
6. 撤回后经 Gateway 复测详情、创建、更新、未认证、越权、并发冲突和失败零写入,并确认 Resource 双实例与 Nacos 健康。
|
||
|
||
## 关联 / 联系人
|
||
|
||
### 链接
|
||
|
||
- **Issue**: [#6476](https://git.1814.love:8443/wx/HL/issues/6476)
|
||
- **PR**: [#6517](https://git.1814.love:8443/wx/HL/pulls/6517)
|
||
- **实现提交**: [`3cd07d27`](https://git.1814.love:8443/wx/HL/commit/3cd07d27dc876f17af1751ec7ef9de2ddbd71949)
|
||
- **Merge commit**: [`a952a04c`](https://git.1814.love:8443/wx/HL/commit/a952a04cf80f24d6c50cbec4b992c82f8580beba)
|
||
- **TEST 目标提交**: [`b269e5fd`](https://git.1814.love:8443/wx/HL/commit/b269e5fdca2e1d29e0336314a909885e9d1f9d16)
|
||
|
||
### 联系人
|
||
|
||
- **后端负责人**: @lc
|
||
- **管理端负责人**: @mmg(`frontend_status: pending`)
|