@@ -0,0 +1,157 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "6275"
|
||||
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: ""
|
||||
status_note: "PR #6276 已合并 dev-v3,合并提交 f7b442b67 已由部署任务 #785ce882 发布到 TEST 并经 Gateway 验证。非空联系人快照现在始终归一为且仅为一个默认联系人;联系人角色按 sup_content_role 当前 ACTIVE 字典值校验。管理端待将角色/职务改为动态下拉并适配新的默认联系人规则。"
|
||||
updated_at: "2026-08-24"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# 供应商联系人默认规则与角色字典校验
|
||||
|
||||
供应商创建、草稿更新和注册提交统一收紧联系人契约:联系人列表非空时,服务端保证最终恰好一个默认联系人;联系人角色必须使用字典 `sup_content_role` 当前启用项的 `dictValue`。本记录覆盖 #6258 中“允许没有默认联系人、取消或删除后不自动选举”的旧语义。
|
||||
|
||||
## 变更接口
|
||||
|
||||
| 方法 | 路径 | 行为变化 |
|
||||
|---|---|---|
|
||||
| POST | `/admin/supplier/items/add` | 非空 `contacts` 未显式选择默认项时,服务端将请求第一项设为默认;联系人角色按生效字典校验 |
|
||||
| PUT | `/admin/supplier/items/{supplierId}/update` | 非空联系人快照最终未保留或选中默认项时,请求第一项自动成为默认;显式提交的角色按生效字典校验 |
|
||||
| POST | `/admin/supplier/items/{supplierId}/submit` | 完整联系人快照执行相同的唯一默认归一和角色字典校验;失败发生在主体、联系人和审批写入前 |
|
||||
| GET | `/admin/supplier/items/{supplierId}/basic-info/view` | 返回服务端归一后的 `contacts[].isPrimary` 和已保存的 `contacts[].contactRole`;电话仍仅返回 `contactPhoneMask` |
|
||||
|
||||
管理端角色下拉继续使用既有通用字典接口,该接口本工单未修改:
|
||||
|
||||
| 方法 | 路径 | 用途 |
|
||||
|---|---|---|
|
||||
| GET | `/admin/dict/data/sup_content_role` | 获取联系人角色选项;展示 `dictLabel`,提交 `dictValue`,仅使用 `status=ACTIVE` 的项 |
|
||||
|
||||
业务失败可能仍返回 HTTP 200,调用方必须同时判断统一响应中的 `code`、`success`、`message` 和 `data`。
|
||||
|
||||
## 默认联系人规则
|
||||
|
||||
- `contacts` 为非空列表时,保存后必须且只会有一个 `isPrimary=true`。
|
||||
- 只有一个联系人时,即使请求传 `false`、`null` 或省略 `isPrimary`,该联系人也会成为默认联系人。
|
||||
- 多个联系人且没有任何项传 `true` 时,按请求数组顺序将第一项设为默认联系人。
|
||||
- 恰好一个联系人传 `true` 时保留该选择。
|
||||
- 两个及以上联系人传 `true` 时返回业务码 `400`、消息 `同一供应商只能设置一个默认联系人`,整次请求零写入。
|
||||
- 草稿增量更新中,已有联系人省略 `isPrimary` 时优先保留原默认状态;显式传 `false` 表示取消。若删除或取消原默认联系人后没有其他默认项,则请求列表第一项自动成为默认。
|
||||
- 增量更新的 `contacts=null` 或省略字段表示联系人不变;`contacts=[]` 表示清空联系人,空列表允许零默认联系人。
|
||||
- 客户端应在提交后重新读取详情,以服务端返回的 `isPrimary` 为最终状态。
|
||||
|
||||
创建或完整提交示例(两个联系人均未选择默认):
|
||||
|
||||
```json
|
||||
{
|
||||
"contacts": [
|
||||
{
|
||||
"contactName": "联系人甲",
|
||||
"contactPhone": "13800000001",
|
||||
"contactRole": "contentBus",
|
||||
"isPrimary": false
|
||||
},
|
||||
{
|
||||
"contactName": "联系人乙",
|
||||
"contactPhone": "13800000002",
|
||||
"contactRole": "contentMoney",
|
||||
"isPrimary": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
保存后详情中的第一项为默认联系人:
|
||||
|
||||
```json
|
||||
{
|
||||
"contacts": [
|
||||
{
|
||||
"contactName": "联系人甲",
|
||||
"contactPhoneMask": "138****0001",
|
||||
"contactRole": "contentBus",
|
||||
"isPrimary": true
|
||||
},
|
||||
{
|
||||
"contactName": "联系人乙",
|
||||
"contactPhoneMask": "138****0002",
|
||||
"contactRole": "contentMoney",
|
||||
"isPrimary": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`Long` 类型的 `contactId` 继续按字符串传输;更新已有联系人仍需携带详情返回的 `expectedUpdateTime`。
|
||||
|
||||
## 联系人角色字典规则
|
||||
|
||||
- 创建和注册提交的每个联系人都必须提供非空 `contactRole`,值必须精确匹配 `sup_content_role` 当前 `status=ACTIVE` 项的 `dictValue`。
|
||||
- 草稿增量更新已有联系人时,省略 `contactRole` 表示保持原值,不会因为本次无关更新而额外依赖字典;新增联系人或显式修改角色时必须提交生效的 `dictValue`。
|
||||
- 空白、未知或已停用值返回业务码 `400`、消息 `联系人角色不合法或已停用`,失败请求不修改供应商或联系人。
|
||||
- 字典服务异常、响应失败、空数据或没有启用项时失败关闭,返回“联系人角色字典暂不可用,请稍后重试”,不降级为硬编码选项。
|
||||
- TEST 当前可见项为 `contentBus`(业务人员)和 `contentMoney`(财务人员),仅用于联调示例;客户端必须动态读取字典,不能将这些值固化在代码中。
|
||||
|
||||
字典响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"dictType": "sup_content_role",
|
||||
"dictLabel": "业务人员",
|
||||
"dictValue": "contentBus",
|
||||
"sortOrder": 1,
|
||||
"status": "ACTIVE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 管理端接入事项
|
||||
|
||||
1. “角色/职务”字段改为下拉框,进入联系人页面时请求 `/admin/dict/data/sup_content_role`,展示 `dictLabel`、提交 `dictValue`,过滤非 `ACTIVE` 项。
|
||||
2. 单联系人场景将该联系人呈现为默认;多联系人可选择一个默认联系人,但不可同时选择多个。未选择时服务端会把请求第一项设为默认。
|
||||
3. 新增、删除、拖动或调整联系人顺序后,提交数组顺序应与界面顺序一致;保存成功后重新读取详情并以服务端返回值刷新默认标识。
|
||||
4. 更新已有联系人时,未修改角色可省略 `contactRole`;新增联系人必须提交从字典选择的角色值。
|
||||
|
||||
## 未变化范围
|
||||
|
||||
- 不新增接口、Gateway 路由、数据库 migration、字典数据、菜单权限、角色、状态机或审批节点。
|
||||
- 不改变联系人电话加密存储、脱敏输出、软删除、审计、聚合锁、幂等与乐观版本语义。
|
||||
- 不修改配置、Redis Key 或 MQ 契约,也不回填历史联系人数据。
|
||||
- 本工单只交付后端;管理端代码未在后端仓库修改,前端状态保持 `pending` 直至完成上述接入并提供验证提交。
|
||||
|
||||
## 验证证据
|
||||
|
||||
- 自动化:默认联系人写入 16 项、应用服务 14 项、角色字典 11 项、聚合校验 6 项,共 47 项定向测试零失败;Resource 全量 1966 项零失败,38 项仓库既有条件跳过。
|
||||
- 合并后独立审计:在目标提交上重新执行 47 项定向测试,零失败且未发现确定性缺口。
|
||||
- TEST 部署:部署任务 #785ce882 成功,`hl-resource-service` 的 8082、8182 实例滚动重启并恢复健康;部署服务器 HEAD `1f2e0ac6d` 包含目标合并提交 `f7b442b67`。
|
||||
- TEST Gateway:使用真实管理员会话验证单联系人自动默认、多联系人无选择时第一项默认、删除默认后重新选举、角色省略保持原值、清空联系人、重复默认与非法角色零写入、提交失败零审批副作用;未认证请求返回业务码 `401`。
|
||||
- 清理:合成供应商草稿已通过既有删除接口清理,精确查询结果为 0;未产生外部文件、Redis、MQ 或配置副作用。
|
||||
|
||||
## 撤回
|
||||
|
||||
1. 从最新 `dev-v3` 创建回退分支,执行 `git revert -m 1 --no-edit f7b442b67f4d989fac6b29a771331ca6d66148e3`,经独立 PR 合入。
|
||||
2. 重新构建并滚动部署 `hl-resource-service`;本次没有数据库、字典数据、配置、Redis 或 MQ 变更,无需执行 DDL、DML 或数据恢复。
|
||||
3. 回退后恢复 #6258 的旧语义:允许联系人列表非空但没有默认联系人,角色不再由后端按 `sup_content_role` 动态校验;接口路径和字段结构不变。
|
||||
4. 经 Gateway 复测创建、更新、提交和详情接口,确认回退后的默认联系人语义、角色传值、电话脱敏、权限和失败零写入符合目标版本。
|
||||
5. 若管理端已上线本次交互,应同步停止依赖“服务端必定选出一个默认联系人”的保证;字典下拉仍可继续使用既有通用字典接口,但需按回退版本确认提交兼容性。
|
||||
|
||||
## 关联 / 联系人
|
||||
|
||||
- **Issue**: [#6275](https://git.1814.love:8443/wx/HL/issues/6275)
|
||||
- **PR**: [#6276](https://git.1814.love:8443/wx/HL/pulls/6276)
|
||||
- **合并提交**: [f7b442b67](https://git.1814.love:8443/wx/HL/commit/f7b442b67f4d989fac6b29a771331ca6d66148e3)
|
||||
- **后端负责人**: @lc
|
||||
在新工单中引用
屏蔽一个用户