Files
ccdi/docs/superpowers/specs/2026-04-23-staff-family-enterprise-relation-design.md

545 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 员工亲属实体关联维护设计
## 1. 背景
当前信息维护中的员工实体关联维护模块,底层使用 `ccdi_staff_enterprise_relation` 表,页面、接口、导入能力均已完整存在,但现有语义以“员工本人”为主对象。
本次需求要求将该模块整体切换为“员工亲属与实体的关联关系维护”:
- 主对象从员工本人切换为员工亲属
- 员工亲属必须来源于员工亲属关系维护中已维护的亲属
- 新增和导入时仅允许使用员工亲属关系中状态为有效的亲属
- 列表需要展示亲属身份证、亲属名称、亲属关联的员工
- 现有导入能力同步切换为员工亲属实体关联导入
- 若亲属关系后续被改为无效,则该亲属名下已有实体关联自动更新为无效
本次设计按最短路径实施,不新增平行模块,不做兼容性方案,不额外扩展用户未提出的兜底逻辑。
## 2. 目标与范围
### 2.1 目标
在保留现有员工实体关联模块入口、表结构和基本交互骨架的前提下,将其业务语义改造为员工亲属实体关联维护,并保证新增、编辑、查询、详情、导入、失效联动链路闭环。
### 2.2 本次范围
- 改造员工实体关联模块的查询、详情、新增、编辑、导入语义
- 接入员工亲属关系表作为唯一合法亲属来源
- 页面支持按亲属身份证模糊搜索有效亲属并自动带出信息
- 列表和详情展示亲属名称及关联员工
- 员工亲属关系失效时,自动将对应实体关联更新为无效
### 2.3 非本次范围
- 不新增独立“员工亲属实体关联”新表
- 不迁移旧的员工本人实体关联历史数据
- 不设计旧数据兼容展示逻辑
- 不增加亲属关系失效后自动恢复实体关联为有效的反向联动
- 不扩展用户未提出的新查询条件、新统计能力或新菜单入口
## 3. 现状分析
### 3.1 现有实体关联模块
当前模块主要位置如下:
- 后端控制器:`ccdi-info-collection/.../controller/CcdiStaffEnterpriseRelationController.java`
- 后端服务:`ccdi-info-collection/.../service/impl/CcdiStaffEnterpriseRelationServiceImpl.java`
- 查询 Mapper`ccdi-info-collection/.../mapper/info/collection/CcdiStaffEnterpriseRelationMapper.xml`
- 前端页面:`ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue`
- 前端 API`ruoyi-ui/src/api/ccdiStaffEnterpriseRelation.js`
当前主表为 `ccdi_staff_enterprise_relation`,核心字段包括:
- `person_id`:当前语义为员工身份证号
- `social_credit_code`
- `enterprise_name`
- `relation_person_post`
- `status`
- `remark`
- `data_source`
当前列表通过 `ccdi_base_staff.id_card = person_id` 查询员工姓名。
### 3.2 现有员工亲属关系模块
员工亲属关系维护模块已完整存在,主要使用:
- 表:`ccdi_staff_fmy_relation`
- 员工身份证:`person_id`
- 亲属名称:`relation_name`
- 亲属身份证:`relation_cert_no`
- 员工亲属标记:`is_emp_family`
- 状态:`status`
该表已经能够稳定表达“某亲属属于哪个员工”,可以直接作为本次改造的唯一合法来源。
## 4. 方案对比
### 4.1 方案 A沿用现有表并切换业务语义
做法:
- 继续使用 `ccdi_staff_enterprise_relation`
-`person_id` 改为表示“亲属身份证号”
- 查询时通过亲属关系表回补亲属名称和关联员工
优点:
- 改动最小
- 现有增删改查和导入框架可复用
- 不需要新增表、菜单、权限和页面
缺点:
- `person_id` 字段命名不再直观表达“亲属”
### 4.2 方案 B沿用现有表并增加冗余字段
做法:
- 在原表上补充亲属名称、关联员工身份证、关联员工姓名等冗余字段
优点:
- 查询 SQL 简单
缺点:
- 引入冗余数据
- 与员工亲属关系、员工基础信息容易产生不一致
- 超出最短路径原则
### 4.3 方案 C新建员工亲属实体关联表
做法:
- 新建全新表结构与全新模块
优点:
- 语义最干净
缺点:
- 改造范围明显扩大
- 需要增加整套表、接口、页面和权限
- 不符合本次“最短路径实现”要求
### 4.4 选型结论
采用方案 A。
原因:
- 可以在现有模块上直接改造,成本最低
- 满足业务闭环,不需要额外兜底
- 与当前仓库已有的员工亲属关系维护能力天然衔接
## 5. 数据模型设计
## 5.1 主表沿用
继续使用 `ccdi_staff_enterprise_relation`,但字段语义调整如下:
| 字段 | 新语义 |
|------|--------|
| `person_id` | 亲属身份证号 |
| `social_credit_code` | 统一社会信用代码 |
| `enterprise_name` | 企业名称 |
| `relation_person_post` | 亲属在企业中的职务 |
| `status` | 关联关系状态 |
| `remark` | 补充说明 |
| `data_source` | 数据来源 |
本次不修改表结构,不新增字段。
## 5.2 查询回填链路
列表和详情展示使用以下关联链路:
1.`ccdi_staff_enterprise_relation` 为主表
2. 通过 `ccdi_staff_fmy_relation.relation_cert_no = ccdi_staff_enterprise_relation.person_id`
3. 并限定 `ccdi_staff_fmy_relation.is_emp_family = 1`
4.`ccdi_staff_fmy_relation` 获取:
- `relation_name` 作为亲属名称
- `person_id` 作为关联员工身份证
5. 再通过 `ccdi_base_staff.id_card = ccdi_staff_fmy_relation.person_id` 获取员工姓名
最终返回给前端的核心展示字段为:
- `personId`:亲属身份证
- `relationName`:亲属名称
- `staffPersonId`:关联员工身份证
- `staffPersonName`:关联员工姓名
现有 `personName` 字段不再承载员工姓名语义,避免前后端继续误用,改造后统一使用新的返回字段。
## 6. 业务规则
### 6.1 合法亲属来源
新增和导入时,亲属身份证号必须匹配到一条满足以下条件的员工亲属关系:
- `is_emp_family = 1`
- `status = 1`
- `relation_cert_no = person_id`
只要任一条件不满足,即判定该亲属不允许用于实体关联维护。
### 6.2 唯一性规则
业务唯一性继续沿用现有模式,但语义变更为:
- `亲属身份证号 + 统一社会信用代码` 唯一
新增与导入都必须校验该组合唯一,禁止重复录入。
### 6.3 编辑规则
编辑时延续现有“业务主键不可修改”的方式:
- 亲属身份证号不可修改
- 统一社会信用代码不可修改
只允许修改:
- 企业名称
- 关联人在企业的职务
- 状态
- 补充说明
### 6.4 亲属失效联动规则
当员工亲属关系模块中某条亲属关系状态从有效改为无效时:
- 自动将该亲属名下所有实体关联记录的 `status` 更新为 `0`
联动范围限定为:
- `ccdi_staff_enterprise_relation.person_id = ccdi_staff_fmy_relation.relation_cert_no`
本次仅实现“改为无效时自动同步为无效”的单向联动,不设计反向自动恢复为有效。
## 7. 后端设计
### 7.1 接口延用
继续沿用现有接口路径:
- `GET /ccdi/staffEnterpriseRelation/list`
- `GET /ccdi/staffEnterpriseRelation/{id}`
- `POST /ccdi/staffEnterpriseRelation`
- `PUT /ccdi/staffEnterpriseRelation`
- `DELETE /ccdi/staffEnterpriseRelation/{ids}`
- `POST /ccdi/staffEnterpriseRelation/importTemplate`
- `POST /ccdi/staffEnterpriseRelation/importData`
- `GET /ccdi/staffEnterpriseRelation/importStatus/{taskId}`
- `GET /ccdi/staffEnterpriseRelation/importFailures/{taskId}`
不新增平行接口组。
### 7.2 查询 DTO 与 VO
查询条件建议调整为围绕亲属语义:
- 亲属身份证号
- 亲属名称
- 关联员工
- 统一社会信用代码
- 企业名称
- 状态
VO 增加或切换为以下字段:
- `personId`
- `relationName`
- `staffPersonId`
- `staffPersonName`
- `relationPersonPost`
- `socialCreditCode`
- `enterpriseName`
- `status`
- `remark`
- `dataSource`
- `createTime`
- `updateTime`
- `createdBy`
- `updatedBy`
### 7.3 查询 SQL
列表和详情 SQL 改为从亲属关系表回补字段,示意逻辑如下:
```sql
SELECT
ser.id,
ser.person_id,
sfr.relation_name,
sfr.person_id AS staff_person_id,
bs.name AS staff_person_name,
ser.relation_person_post,
ser.social_credit_code,
ser.enterprise_name,
ser.status,
ser.remark,
ser.data_source,
ser.created_by,
ser.create_time,
ser.updated_by,
ser.update_time
FROM ccdi_staff_enterprise_relation ser
LEFT JOIN ccdi_staff_fmy_relation sfr
ON sfr.relation_cert_no = ser.person_id
AND sfr.is_emp_family = 1
LEFT JOIN ccdi_base_staff bs
ON bs.id_card = sfr.person_id
```
其中:
- 列表查询不额外过滤 `sfr.status = 1`
- 原因是历史记录需要正常展示,即使亲属后续失效,记录仍可查到,但实体关联状态会被联动改为无效
### 7.4 新增与导入校验
新增和导入都统一改为校验有效亲属关系:
1. 根据亲属身份证号查询 `ccdi_staff_fmy_relation`
2. 要求命中 `is_emp_family = 1 and status = 1`
3. 未命中则报错
4. 命中后继续校验 `亲属身份证号 + 统一社会信用代码` 唯一
### 7.5 亲属下拉搜索接口
前端新增/编辑弹窗需要按身份证号模糊搜索有效亲属。
实现方式:
- 在员工实体关联模块后端新增一个用于下拉搜索的查询接口
- 返回有效亲属列表
返回项建议包含:
- `relationCertNo`
- `relationName`
- `personId`
- `personName`
查询条件:
- `relation_cert_no` 模糊匹配输入值
- `is_emp_family = 1`
- `status = 1`
### 7.6 亲属失效联动落点
联动逻辑放在员工亲属关系保存链路中处理,即:
- `CcdiStaffFmyRelationServiceImpl.updateRelation`
处理规则:
1. 查询更新前旧记录
2. 判断旧状态是否为有效、更新后状态是否为无效
3. 若是,则按当前亲属身份证号批量更新实体关联表状态为无效
该联动需和亲属关系更新处于同一事务内,保证状态一致。
## 8. 前端设计
### 8.1 页面延用
继续使用:
- `ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue`
不新建新页面。
### 8.2 查询区
查询区改为亲属语义,保留或新增:
- 亲属身份证号
- 亲属名称
- 关联员工
- 统一社会信用代码
- 企业名称
- 状态
### 8.3 列表区
列表核心列调整为:
- 亲属身份证
- 亲属名称
- 关联员工
- 企业名称
- 关联人在企业的职务
- 状态
- 数据来源
- 创建时间
- 操作
其中“关联员工”展示建议为:
- `员工姓名(员工身份证号)`
这样可以避免用户只看到姓名无法定位员工。
### 8.4 新增弹窗
新增弹窗保留现有骨架,调整主录入方式:
- 身份证号输入改为“按亲属身份证模糊搜索”
- 下拉数据来源改为有效员工亲属
下拉项显示建议:
- 左侧:亲属身份证号
- 右侧:亲属名称 / 关联员工姓名
用户选中后,自动回填并只读展示:
- 亲属名称
- 关联员工
其余字段继续录入:
- 统一社会信用代码
- 企业名称
- 关联人在企业的职务
- 补充说明
新增时状态仍默认有效。
### 8.5 编辑弹窗
编辑弹窗延续现有模式:
- 亲属身份证号不可编辑
- 统一社会信用代码不可编辑
- 亲属名称和关联员工仅展示,不可改
### 8.6 详情弹窗
详情弹窗基础信息展示:
- 亲属身份证
- 亲属名称
- 关联员工
- 统一社会信用代码
- 企业名称
- 关联人在企业的职务
- 状态
- 数据来源
- 补充说明
- 审计信息
## 9. 导入设计
### 9.1 导入模板
继续沿用现有异步导入机制,但模板标题、文案和字段语义改为亲属口径。
模板标题改为:
- 员工亲属实体关联信息
模板中的“身份证号”字段实际表示:
- 亲属身份证号
### 9.2 导入校验规则
每行导入数据按以下顺序校验:
1. 基础字段非空与格式合法
2. 亲属身份证号必须存在于员工亲属关系中
3. 且该亲属必须为员工亲属、状态有效
4. `亲属身份证号 + 统一社会信用代码` 在库中不能重复
5. 导入文件内同组合不能重复
### 9.3 失败记录展示
失败记录弹窗字段调整为:
- 亲属身份证号
- 亲属名称
- 企业名称
- 统一社会信用代码
- 失败原因
失败原因示例:
- 亲属身份证号不存在于员工亲属关系中
- 亲属已失效,无法导入实体关联
- 亲属身份证号和统一社会信用代码组合已存在
### 9.4 导入结果提示
所有导入提示文案统一改为:
- 员工亲属实体关联导入
避免继续出现“员工实体关系”旧语义。
## 10. 错误处理
后端错误提示统一切换为亲属语义,示例:
- 所选身份证号不是有效的员工亲属,无法新增实体关联
- 亲属身份证号和统一社会信用代码组合已存在
- 亲属身份证号不存在于员工亲属关系中,或该亲属已无效
- 亲属身份证号不可修改
前端不新增特殊兜底逻辑,直接按现有错误提示机制回显后端返回信息。
## 11. 测试要点
### 11.1 列表与详情
- 列表正确展示亲属身份证、亲属名称、关联员工
- 详情页展示字段与列表语义一致
### 11.2 新增
- 输入亲属身份证可模糊搜索有效亲属
- 选中亲属后自动带出亲属名称和关联员工
- 无效亲属、非员工亲属、不存在的亲属身份证不能新增
- 重复的亲属身份证号 + 统一社会信用代码不能新增
### 11.3 编辑
- 亲属身份证号不可修改
- 统一社会信用代码不可修改
- 企业名称、职务、状态、备注可正常编辑
### 11.4 导入
- 有效亲属导入成功
- 亲属不存在导入失败
- 亲属无效导入失败
- 导入文件内重复失败
- 数据库内重复失败
- 失败记录文案与字段正确
### 11.5 失效联动
- 将员工亲属关系从有效改为无效后
- 该亲属名下已有实体关联自动更新为无效
- 列表仍能查到该记录,但状态为无效
## 12. 实施边界总结
本次改造严格限定为“在原员工实体关联模块上切换主对象为员工亲属”,核心边界如下:
- 不新建表
- 不新建页面
- 不迁移旧数据
- 不做兼容性兜底
- 不做反向恢复联动
- 所有合法性判断均以员工亲属关系表为准
在此边界内,前后端和导入链路均可形成完整闭环,并满足本次需求。