--- schema: "hl-changelog/v2" ticket: "6739" title: "供应商三级地址保存与回显" consumer: "admin" author: "lc(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "5f35aafd" target_release: "v2.1" verified_at: "2026-08-30" status_note: "PR #6744 已合并 dev-v3,合并提交 2d24b329 已部署 TEST。前端实证 #6350 拼接单字符串 address 且 002 不回显与新契约相反,属 required。已实现:三级联动与详细地址独立成字段(countyId+address),RegionCascader 新增 getRegionPath 受控回显,编辑表单回填入快照增量比对、详情页 countyId 反查省市区名。前端 commit 5f35aafd 已推 v2.1。当前状态:已消费(frontend_status: verified)。" updated_at: "2026-08-30" base: "dev-v3" --- # 供应商三级地址保存与回显 `countyId` 保存三级联动的最下级区县 ID,`address` 只保存详细地址,例如“某某路 1 号”。前端不要再使用 `addressId`。 ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---:|---|---|---|---|---| | 1 | 新增供应商草稿 | POST | `/admin/supplier/items/add` | 请求字段 | 可传 `countyId`、`address` | | 2 | 修改供应商 | PUT | `/admin/supplier/items/{supplierId}/update` | 请求字段 | 可更新 `countyId`、`address` | | 3 | 提交供应商 | POST | `/admin/supplier/items/{supplierId}/submit` | 请求字段 | 完整表单可传 `countyId`、`address` | | 4 | 查询供应商基本信息 | GET | `/admin/supplier/items/{supplierId}/basic-info/view` | 响应字段 | 顶层返回三级 ID 与 `address` | ## 三、接口详情 ### 1. 新增供应商草稿 `POST /admin/supplier/items/add` **VO**: `SupplierDraftSaveReqVO / SupplierWriteRespVO` #### 使用场景 新增草稿时同时保存区县选择和详细地址。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | `countyId` | Body | String | 否 | 正整数 | 三级联动最后一级区县 ID | | `address` | Body | String | 否 | 最长 500 字符 | 详细地址,不拼接省市区名称 | | `fullName` / `taxNo` / `mainCooperation` | Body | String | 是 | 沿用既有规则 | 其他新增必填字段 | #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | `data.supplierId` | String | 新供应商 ID | | `data.updateTime` | String | 后续修改使用的并发版本 | #### 请求示例 ```json {"fullName":"示例旅行服务有限公司","taxNo":"91350211M000100Y46","countyId":"376","address":"某某路1号","mainCooperation":"景区合作"} ``` #### 响应示例 ```json {"code":200,"message":"成功","success":true,"data":{"supplierId":"2093966831894056961","status":"DRAFT","updateTime":"2026-08-30 15:39:31"}} ``` #### 空数据 / 降级响应 省略 `countyId` 或 `address` 时按 `null` 保存;依赖或业务校验失败时不产生部分写入。 #### 错误响应 ```json {"code":400,"message":"区县ID必须为正数","success":false,"data":null} ``` #### 业务边界 - 有三级联动选择结果时只传最后一级 `countyId`。 - `addressId` 不是本接口字段。 ### 2. 修改供应商 `PUT /admin/supplier/items/{supplierId}/update` **VO**: `SupplierUpdateReqVO / SupplierWriteRespVO` #### 使用场景 编辑页修改区县或详细地址。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | `supplierId` | Path | String | 是 | 正整数 | 供应商 ID | | `countyId` | Body | String | 否 | 正整数 | 新的最下级区县 ID | | `address` | Body | String | 否 | 最长 500 字符 | 新的详细地址 | | `expectedUpdateTime` | Body | String | 是 | `yyyy-MM-dd HH:mm:ss` | 详情最新并发版本 | #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | `data.supplierId` | String | 供应商 ID | | `data.updateTime` | String | 保存后的新并发版本 | #### 请求示例 ```json {"countyId":"377","address":"某某路2号","expectedUpdateTime":"2026-08-30 15:39:31"} ``` #### 响应示例 ```json {"code":200,"message":"成功","success":true,"data":{"supplierId":"2093966831894056961","status":"DRAFT","updateTime":"2026-08-30 15:39:42"}} ``` #### 空数据 / 降级响应 省略 `countyId` 或 `address` 表示该字段不修改;失败时原值和版本保持不变。 #### 错误响应 ```json {"code":400,"message":"区县ID必须为正数","success":false,"data":null} ``` #### 业务边界 - 修改成功后使用响应中的新 `updateTime` 重新查询详情。 - 非正数 `countyId` 在写入前拒绝。 ### 3. 提交供应商 `POST /admin/supplier/items/{supplierId}/submit` **VO**: `SupplierSubmitReqVO / SupplierApprovalCommandRespVO` #### 使用场景 提交完整供应商表单进入审批时一并保存区县和详细地址。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | `supplierId` | Path | String | 是 | 正整数 | 草稿供应商 ID | | `countyId` | Body | String | 否 | 正整数 | 最下级区县 ID | | `address` | Body | String | 否 | 最长 500 字符 | 详细地址 | | `expectedUpdateTime` | Body | String | 是 | `yyyy-MM-dd HH:mm:ss` | 当前并发版本 | #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | `data.approvalLogId` | String | 审批记录 ID | | `data.approvalStatus` | String | 审批状态 | #### 请求示例 ```json {"fullName":"示例旅行服务有限公司","taxNo":"91350211M000100Y46","countyId":"377","address":"某某路2号","mainCooperation":"景区合作","expectedUpdateTime":"2026-08-30 15:39:42"} ``` #### 响应示例 ```json {"code":200,"message":"成功","success":true,"data":{"approvalLogId":"2093967000000000001","approvalStatus":"APPROVED"}} ``` #### 空数据 / 降级响应 `countyId`、`address` 为空时按完整表单既有规则处理;提交失败不推进审批状态。 #### 错误响应 ```json {"code":400,"message":"区县ID必须为正数","success":false,"data":null} ``` #### 业务边界 - 仍须提交完整表单和最新 `expectedUpdateTime`。 - 本次不改变既有审批、幂等和状态门禁。 ### 4. 查询供应商基本信息 `GET /admin/supplier/items/{supplierId}/basic-info/view` **VO**: `SupplierBasicInfoRespVO` #### 使用场景 编辑页读取三级联动默认值和详细地址。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | `supplierId` | Path | String | 是 | 正整数 | 供应商 ID | #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | `data.provinceId` | String/null | 省级 ID | | `data.prefectureId` | String/null | 市级 ID | | `data.countyId` | String/null | 已保存的最下级区县 ID | | `data.address` | String/null | 已保存的详细地址原值 | #### 请求示例 ```http GET /admin/supplier/items/2093966831894056961/basic-info/view Authorization: Bearer ``` #### 响应示例 ```json {"code":200,"message":"成功","success":true,"data":{"supplierId":"2093966831894056961","provinceId":"1","prefectureId":"35","countyId":"377","address":"某某路2号","updateTime":"2026-08-30 15:39:42"}} ``` #### 空数据 / 降级响应 历史供应商没有 `countyId` 时三个行政区划 ID 均为 `null`,`address` 仍按历史值返回。 #### 错误响应 ```json {"code":395001,"message":"供应商不存在","success":false,"data":null} ``` #### 业务边界 - 三个行政区划 ID 均按 JSON 字符串返回。 - 行政区划父链不可用或不完整时失败关闭,不伪造 ID。 ## 四、契约约束与正确调用方式 1. 用 `/admin/region/children` 逐级选择,将最后一级 ID 作为 `countyId`,门牌内容单独放入 `address`。 2. 编辑页按 `provinceId → prefectureId → countyId` 设置默认选中,并用 `address` 初始化详细地址输入框。 3. 前端字段统一命名为 `countyId`;保存后使用新版本重新查询详情。 ## 五、数据库行为 - 写接口成功后,后续详情查询返回已保存的 `countyId` 和 `address`。 - 非正数 `countyId` 校验失败时不修改原值或并发版本。 - 历史供应商不自动补行政区划 ID。 ## 六、边界行为 - 未认证返回业务码 `401`;供应商不存在或已删除返回 `395001`。 - 业务失败可能仍使用 HTTP 200,前端须同时判断 `code` 与 `success`。 - `address` 不包含省市区中文文本;省市区展示由三级 ID 对应的选项生成。 ## 六.6、修改前后对比 | 项目 | 修改前 | 修改后 | |---|---|---| | 写请求 | 仅保存详细地址 | 可同时保存最下级 `countyId` 与详细地址 | | 详情回显 | 仅有 `address` | 顶层增加三个行政区划 ID,继续返回 `address` | ## 六.7、影响评估 - **是否破坏向后兼容**:否,历史空值继续返回 `null`。 - **前端是否必须同步上线**:是,需要传 `countyId` 并消费三级回显字段。 - **前端 workaround 清理点**:移除 `addressId` 映射和自行解析完整中文地址的逻辑。 ## 七、不影响范围 - 不改变供应商权限、状态机、审批、并发和错误码。 - 不改变行政区划联动接口契约,也不影响其他资源模块。 ## 八、测试环境已验证 - 新增与修改后可回读对应 `countyId`、`address`,详情三个行政区划 ID 均为字符串。 - 非正数 `countyId` 返回 `400` 且原数据不变;未认证返回 `401`;验收草稿已清理。 ## 十、相关文档 - [Issue #6739](https://git.1814.love:8443/wx/HL/issues/6739) - [PR #6744](https://git.1814.love:8443/wx/HL/pulls/6744) ## 关联 / 联系人 - **合并提交**: [2d24b3290](https://git.1814.love:8443/wx/HL/commit/2d24b32908bffb0e7ac95fd31b33cdfe56ae69f5) - **后端负责人**: @lc - **当前状态**: 已消费(`frontend_status: verified`)。RegionCascader 新增受控回显(watch value→getRegionPath 反查父链逐级预载,零 emit,停用节点占位);EditModal 提交体改 countyId+address 独立字段、fillForm 回填+快照增量比对(改动才携带、清空都不携带),删拼接逻辑;DetailModal 地址改 countyId 反查省市区名+address。spec 84 例绿,commit 5f35aafd 已推 v2.1。