diff --git a/changelogs-v2/2026-08/24_6258_供应商联系人默认标识与唯一性逻辑-修改接口-管理后台.md b/changelogs-v2/2026-08/24_6258_供应商联系人默认标识与唯一性逻辑-修改接口-管理后台.md new file mode 100644 index 00000000..7362de67 --- /dev/null +++ b/changelogs-v2/2026-08/24_6258_供应商联系人默认标识与唯一性逻辑-修改接口-管理后台.md @@ -0,0 +1,96 @@ +--- +schema: "hl-changelog/v2" +ticket: "6258" +title: "供应商联系人默认标识与唯一性逻辑" +consumer: "admin" +author: "lc(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "2026-08-24" +status_note: "PR #6264 已合并 dev-v3,合并提交 5c27bee4f 已在 TEST Gateway 验证。供应商联系人请求和详情响应新增 isPrimary;服务端在聚合锁与本地事务内保证同一供应商至多一个默认联系人,并兼容旧请求省略字段。" +updated_at: "2026-08-24" +base: "dev-v3" +--- + +# 供应商联系人默认标识与唯一性逻辑 + +供应商注册和资料维护链路为联系人增加默认标识 `isPrimary`。字段为可空 Boolean;服务端根据创建、完整提交和增量更新的不同语义处理省略值,并保证一个供应商最多只有一个默认联系人。 + +## 变更接口 + +| 方法 | 路径 | 行为变化 | +|---|---|---| +| POST | `/admin/supplier/items/add` | 请求 `contacts[].isPrimary` 可选;省略按 `false` 保存;显式提交多个 `true` 时整次创建失败且零写入 | +| PUT | `/admin/supplier/items/{supplierId}/update` | 已有联系人省略 `isPrimary` 时保持原值;新增联系人省略时按 `false`;显式将另一联系人设为 `true` 时在同一事务内清除原默认联系人 | +| POST | `/admin/supplier/items/{supplierId}/submit` | 完整联系人快照支持 `isPrimary`;省略按 `false`;显式提交多个 `true` 时在主体、联系人、审计和审批写入前失败 | +| GET | `/admin/supplier/items/{supplierId}/basic-info/view` | 响应 `contacts[]` 新增 Boolean 字段 `isPrimary`;联系电话仍只返回脱敏字段 `contactPhoneMask` | + +请求和响应示例: + +```json +{ + "contacts": [ + { + "contactId": "1234567890123456789", + "contactName": "联系人甲", + "contactPhone": "13800000000", + "contactRole": "BIZ", + "isPrimary": true, + "expectedUpdateTime": "2026-08-24 17:30:00" + } + ] +} +``` + +`Long` 类型的 `contactId` 继续按字符串传输,时间格式保持 `yyyy-MM-dd HH:mm:ss`。 + +## 字段语义与兼容性 + +- 创建草稿和完整提交:`isPrimary=true` 表示默认联系人;`false`、`null` 或省略均按非默认联系人处理。 +- 增量更新已有联系人:省略 `isPrimary` 表示不修改该联系人当前默认状态;显式 `false` 表示取消默认。 +- 增量更新新增联系人:省略 `isPrimary` 按 `false` 保存。 +- 显式设置新的默认联系人时,服务端锁定当前联系人集合,在同一聚合事务内清除原默认联系人并设置新值。 +- 允许没有默认联系人;取消或删除默认联系人后不会自动选举其他联系人。 +- 旧客户端不传 `isPrimary` 时,创建、更新和提交请求保持兼容。 +- 不新增或修改数据库 migration;复用既有 `supplier_contact.is_primary` 列。 + +## 校验与错误语义 + +- 单次请求显式包含两个及以上 `isPrimary=true` 时返回业务码 `400`,消息为 `同一供应商只能设置一个默认联系人`。 +- 重复默认校验在业务写入前执行;失败请求不会修改供应商版本、联系人、审计记录或审批状态。 +- 写权限保持不变:`FINANCE` 和 `SUPER_ADMIN` 可写,`ADMIN` 返回业务码 `395002`、消息 `无权执行该供应商写操作`。 +- 业务失败可能仍使用 HTTP 200,客户端必须同时检查统一响应的 `code`、`success`、`message` 和 `data`。 + +## 未变化范围 + +- 不新增接口、Gateway 路由、菜单权限、角色、状态机或审批节点。 +- 不改变联系人电话加密存储、脱敏输出、软删除、审计、聚合锁、幂等与乐观版本语义。 +- 不修改配置、Redis Key 或 MQ 契约。 +- 本工单仅交付后端;管理端需按本记录接入 `isPrimary` 字段和默认联系人交互。 + +## 验证证据 + +- 自动化:定向 37 项零失败;Resource 全量 1941 项零失败、38 项仓库既有条件跳过;Gateway 8 项零失败;合并后独立审计再跑权限、事务和契约相关 55 项零失败。 +- TEST Gateway:35 个真实认证断言通过,覆盖创建单默认、切换默认、旧请求省略字段、显式取消后零默认、创建/更新/提交多个默认失败且零写入、电话脱敏和 ADMIN 越权拒绝。 +- TEST 数据库:两个有效联系人切换后统计为 `2:1`,显式取消后统计为 `2:0`。 +- 部署证明:当前远端 `dev-v3` 包含合并提交 `5c27bee4f`,TEST Gateway 已实际回显并执行新增 `isPrimary` 行为。 +- 清理:所有合成供应商草稿均已软删除,按测试前缀查询有效数据为 0;测试账号当前角色已恢复为 `SUPER_ADMIN`,此前登录失败计数已通过管理接口清理。 + +## 撤回 + +1. 从最新 `dev-v3` 创建回退分支,执行 `git revert -m 1 --no-edit 5c27bee4f5d76d85c9c58727c05b330bc06ab608`,经独立 PR 合入。 +2. 重新构建并滚动部署 `hl-resource-service`;不执行 DDL 或 DML,不恢复配置、Redis 或 MQ。 +3. 回退代码会忽略既有 `supplier_contact.is_primary` 值;旧请求继续兼容,依赖新字段的管理端应同步停止使用 `isPrimary`。 +4. 经 Gateway 复测创建、更新、提交和详情四个既有接口,并确认联系人电话仍脱敏、草稿删除与权限门禁正常。 + +## 关联 / 联系人 + +- **Issue**: [#6258](https://git.1814.love:8443/wx/HL/issues/6258) +- **PR**: [#6264](https://git.1814.love:8443/wx/HL/pulls/6264) +- **合并提交**: [5c27bee4f](https://git.1814.love:8443/wx/HL/commit/5c27bee4f5d76d85c9c58727c05b330bc06ab608) +- **后端负责人**: @lc