# 员工亲属关系维护功能设计文档

## 一、项目概述

### 1.1 功能描述
开发员工亲属关系维护功能，实现对员工家庭成员信息的新增、修改、删除、查询、导入、导出等完整的CRUD操作。

### 1.2 技术栈
- **后端**: Spring Boot 3.5.8 + MyBatis Plus 3.5.10 + SpringDoc
- **前端**: Vue 2.6.12 + Element UI 2.15.14
- **数据库**: MySQL 8.2.0
- **Excel处理**: EasyExcel
- **缓存**: Redis
- **异步处理**: Spring @Async

### 1.3 参考标准
本功能完全参考**采购交易管理**模块的设计与实现，确保代码风格、交互方式、技术实现的一致性。

---

## 二、数据库设计

### 2.1 主表结构

**表名**: `ccdi_staff_fmy_relation`（员工家庭关系表）

| 字段序号 | 字段名 | 类型 | 默认值 | 是否可为空 | 主键 | 注释 |
|---------|--------|------|--------|-----------|------|------|
| 1 | id | BIGINT | - | 否 | 是 | 主键，自增 |
| 2 | person_id | VARCHAR | - | 否 | - | 员工身份证号 |
| 3 | relation_type | VARCHAR | - | 否 | - | 关系类型（配偶、子女、父母、兄弟姐妹等） |
| 4 | relation_name | VARCHAR | - | 否 | - | 关系人姓名 |
| 5 | gender | CHAR | - | 是 | - | 性别：M/F/O |
| 6 | birth_date | DATE | - | 是 | - | 关系人出生日期 |
| 7 | relation_cert_type | VARCHAR | - | 否 | - | 证件类型（必填） |
| 8 | relation_cert_no | VARCHAR | - | 否 | - | 证件号码（必填） |
| 9 | mobile_phone1 | VARCHAR | - | 是 | - | 手机号码1 |
| 10 | mobile_phone2 | VARCHAR | - | 是 | - | 手机号码2 |
| 11 | wechat_no1 | VARCHAR | - | 是 | - | 微信号1 |
| 12 | wechat_no2 | VARCHAR | - | 是 | - | 微信号2 |
| 13 | wechat_no3 | VARCHAR | - | 是 | - | 微信号3 |
| 14 | contact_address | VARCHAR | - | 是 | - | 详细联系地址 |
| 15 | relation_desc | VARCHAR | - | 是 | - | 关系详细描述 |
| 16 | status | INT | 1 | 否 | - | 状态：0-无效，1-有效 |
| 17 | effective_date | DATETIME | - | 是 | - | 关系生效日期 |
| 18 | invalid_date | DATETIME | - | 是 | - | 关系失效日期 |
| 19 | remark | TEXT | - | 是 | - | 备注信息 |
| 20 | data_source | VARCHAR(50) | - | 是 | - | 数据来源：MANUAL/SYSTEM/IMPORT/API |
| 21 | is_emp_family | TINYINT(1) | 1 | 否 | - | 是否员工家庭关系：固定为1 |
| 22 | is_cust_family | TINYINT(1) | 0 | 否 | - | 是否信贷客户家庭关系：固定为0 |
| 23 | created_by | VARCHAR | - | 否 | - | 记录创建人 |
| 24 | updated_by | VARCHAR | - | 是 | - | 记录更新人 |
| 25 | create_time | DATETIME | - | 否 | - | 记录创建时间 |
| 26 | update_time | DATETIME | - | 是 | - | 记录更新时间 |

### 2.2 关联关系

```
ccdi_staff_fmy_relation.person_id → ccdi_base_staff.id_card (外键关联)
```

### 2.3 唯一键设计

**唯一键 = 员工身份证号 + 关系人身份证号**

- 格式：`{personId}_{relationCertNo}`
- 示例：`110101199001011234_110101199001015678`
- 用于导入时的重复性校验

---

## 三、后端设计

### 3.1 模块命名

**模块名称**: `StaffFamilyRelation`（员工亲属关系）

**包路径**: `com.ruoyi.ccdi`

### 3.2 类结构设计

#### 3.2.1 实体类（Entity）

**CcdiStaffFmyRelation.java**
```java
@Data
@TableName("ccdi_staff_fmy_relation")
public class CcdiStaffFmyRelation implements Serializable {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String personId;
    private String relationType;
    private String relationName;
    private String gender;
    private Date birthDate;
    private String relationCertType;
    private String relationCertNo;
    private String mobilePhone1;
    private String mobilePhone2;
    private String wechatNo1;
    private String wechatNo2;
    private String wechatNo3;
    private String contactAddress;
    private String relationDesc;
    private Integer status;
    private Date effectiveDate;
    private Date invalidDate;
    private String remark;
    private String dataSource;
    private Boolean isEmpFamily;
    private Boolean isCustFamily;

    @TableField(fill = FieldFill.INSERT)
    private String createdBy;

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updatedBy;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}
```

#### 3.2.2 DTO设计

**CcdiStaffFmyRelationAddDTO.java**（新增DTO）
- 必填字段：
  - `personId` - 员工身份证号
  - `relationType` - 关系类型
  - `relationName` - 关系人姓名
  - `relationCertType` - 证件类型
  - `relationCertNo` - 证件号码
- 可选字段：其他所有字段
- 校验规则：
  - 身份证号格式验证（18位）
  - 手机号格式验证（11位）
  - 性别值验证（M/F/O）
  - 字段长度验证

**CcdiStaffFmyRelationEditDTO.java**（编辑DTO）
- 包含所有字段（除审计字段）
- 同样的校验规则

**CcdiStaffFmyRelationQueryDTO.java**（查询DTO）
- 查询条件：
  - `personId` - 员工身份证号（精确）
  - `personName` - 员工姓名（模糊）
  - `relationType` - 关系类型
  - `relationName` - 关系人姓名（模糊）
  - `status` - 状态
  - `dataSource` - 数据来源
  - `effectiveDateStart/End` - 生效日期范围

#### 3.2.3 VO设计

**CcdiStaffFmyRelationVO.java**（列表/详情VO）
- 包含所有展示字段
- 扩展字段：
  - `personName` - 员工姓名（关联查询）
  - `genderName` - 性别名称（转换）
  - `statusName` - 状态名称（转换）
  - `dataSourceName` - 数据来源名称

**CcdiStaffFmyRelationExcel.java**（Excel导入导出）
- 使用`@DictDropdown`注解添加字典下拉框：
  - `relationType` → `ccdi_relation_type`
  - `gender` → `ccdi_indiv_gender`
  - `relationCertType` → `ccdi_certificate_type`
- 使用`@Required`注解标记必填字段
- 字段索引：0-16

**StaffFmyRelationImportFailureVO.java**（导入失败记录）
- `rowNum` - 行号
- `personId` - 员工身份证号
- `relationName` - 关系人姓名
- `errorMessage` - 错误信息

#### 3.2.4 Mapper层

**CcdiStaffFmyRelationMapper.java**
```java
@Mapper
public interface CcdiStaffFmyRelationMapper extends BaseMapper<CcdiStaffFmyRelation> {
    Page<CcdiStaffFmyRelationVO> selectRelationPage(
        Page<CcdiStaffFmyRelationVO> page,
        @Param("query") CcdiStaffFmyRelationQueryDTO queryDTO
    );

    List<CcdiStaffFmyRelationExcel> selectRelationListForExport(
        @Param("query") CcdiStaffFmyRelationQueryDTO queryDTO
    );
}
```

**XML映射要点**：
- LEFT JOIN `ccdi_base_staff`获取员工姓名
- WHERE条件：`is_emp_family = 1`
- 支持多条件动态查询
- 按创建时间倒序排列

#### 3.2.5 Service层

**ICcdiStaffFmyRelationService.java**（主服务接口）
```java
public interface ICcdiStaffFmyRelationService {
    List<CcdiStaffFmyRelationVO> selectRelationList(CcdiStaffFmyRelationQueryDTO queryDTO);
    Page<CcdiStaffFmyRelationVO> selectRelationPage(Page<CcdiStaffFmyRelationVO> page, CcdiStaffFmyRelationQueryDTO queryDTO);
    List<CcdiStaffFmyRelationExcel> selectRelationListForExport(CcdiStaffFmyRelationQueryDTO queryDTO);
    CcdiStaffFmyRelationVO selectRelationById(Long id);
    int insertRelation(CcdiStaffFmyRelationAddDTO addDTO);
    int updateRelation(CcdiStaffFmyRelationEditDTO editDTO);
    int deleteRelationByIds(Long[] ids);
    String importRelation(List<CcdiStaffFmyRelationExcel> excelList);
}
```

**ICcdiStaffFmyRelationImportService.java**（导入服务接口）
```java
public interface ICcdiStaffFmyRelationImportService {
    void importRelationAsync(List<CcdiStaffFmyRelationExcel> excelList, String taskId, String userName);
    ImportStatusVO getImportStatus(String taskId);
    List<StaffFmyRelationImportFailureVO> getImportFailures(String taskId);
}
```

#### 3.2.6 Controller层

**CcdiStaffFmyRelationController.java**

**接口清单**：

| 接口路径 | 方法 | 功能 | 权限标识 |
|---------|------|------|---------|
| /ccdi/staffFmyRelation/list | GET | 查询列表 | ccdi:staffFmyRelation:list |
| /ccdi/staffFmyRelation/{id} | GET | 查询详情 | ccdi:staffFmyRelation:query |
| /ccdi/staffFmyRelation | POST | 新增 | ccdi:staffFmyRelation:add |
| /ccdi/staffFmyRelation | PUT | 修改 | ccdi:staffFmyRelation:edit |
| /ccdi/staffFmyRelation/{ids} | DELETE | 删除 | ccdi:staffFmyRelation:remove |
| /ccdi/staffFmyRelation/export | POST | 导出 | ccdi:staffFmyRelation:export |
| /ccdi/staffFmyRelation/importTemplate | POST | 下载模板 | - |
| /ccdi/staffFmyRelation/importData | POST | 导入 | ccdi:staffFmyRelation:import |
| /ccdi/staffFmyRelation/importStatus/{taskId} | GET | 导入状态 | ccdi:staffFmyRelation:import |
| /ccdi/staffFmyRelation/importFailures/{taskId} | GET | 失败记录 | ccdi:staffFmyRelation:import |

### 3.3 异步导入机制

#### 3.3.1 导入流程

```
┌─────────────┐
│ 前端上传文件 │
└──────┬──────┘
       │
       ▼
┌──────────────────────────────────┐
│ Controller.importData()          │
│ 1. 解析Excel为Excel列表          │
│ 2. 生成UUID任务ID                │
│ 3. 初始化Redis状态=PROCESSING    │
│ 4. 调用@Async异步方法            │
│ 5. 立即返回任务ID                │
└──────┬───────────────────────────┘
       │
       ▼ 立即返回
┌──────────────────────────────────┐
│ { taskId, status: "PROCESSING" } │
└──────┬───────────────────────────┘
       │
       │ 前端开始轮询（2秒/次，最多150次）
       ▼
┌──────────────────────────────────┐
│ @Async异步线程                   │
│ importRelationAsync()            │
│ 1. 遍历Excel数据                 │
│ 2. 验证每条数据                  │
│ 3. 唯一键校验（去重）            │
│ 4. 分类：成功/失败               │
│ 5. 批量插入成功数据(500条/批)    │
│ 6. 失败记录存入Redis(7天)        │
│ 7. 更新Redis状态=SUCCESS/PARTIAL │
└──────────────────────────────────┘
```

#### 3.3.2 Redis存储结构

**状态存储**（Hash）：
```
Key: import:staffFmyRelation:{taskId}
Fields:
  - taskId: 任务ID
  - status: PROCESSING/SUCCESS/PARTIAL_SUCCESS
  - totalCount: 总数
  - successCount: 成功数
  - failureCount: 失败数
  - progress: 进度（0-100）
  - startTime: 开始时间戳
  - endTime: 结束时间戳
  - message: 状态消息
TTL: 7天
```

**失败记录存储**（List）：
```
Key: import:staffFmyRelation:{taskId}:failures
Value: JSON数组，包含所有失败记录
TTL: 7天
```

#### 3.3.3 唯一键校验

**唯一键 = 员工身份证号 + 关系人身份证号**

- 用于检测Excel文件内部的重复记录
- 重复时跳过并记录到失败列表
- 错误提示：`员工[xxx]与关系人身份证号[xxx]的关系在导入文件中重复`

#### 3.3.4 数据验证规则

**必填字段验证**：
1. 员工身份证号 - 非空 + 18位格式
2. 关系类型 - 非空
3. 关系人姓名 - 非空
4. 证件类型 - 非空
5. 证件号码 - 非空

**格式验证**：
- 身份证号：`^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$`
- 手机号：`^1[3-9]\\d{9}$`
- 性别：`^[MFO]$`

**长度验证**：
- 姓名：≤100字符
- 证件类型/号码：≤50字符
- 地址/描述/备注：≤500字符

---

## 四、前端设计

### 4.1 目录结构

```
ruoyi-ui/src/
├── api/
│   └── ccdiStaffFmyRelation.js     # API接口定义
└── views/
    └── ccdiStaffFmyRelation/
        └── index.vue                # 主页面组件
```

### 4.2 API接口设计

**ccdiStaffFmyRelation.js**
```javascript
// 查询列表
export function listRelation(query)
export function getRelation(id)
export function addRelation(data)
export function updateRelation(data)
export function delRelation(ids)
export function exportRelation(query)
export function importTemplate()
export function importData(file)
export function getImportStatus(taskId)
export function getImportFailures(taskId, pageNum, pageSize)
export function getStaffList(query)  // 获取员工列表
```

### 4.3 页面功能设计

#### 4.3.1 列表页面

**查询条件**：
- 员工：下拉选择器（支持远程搜索）
- 关系类型：下拉选择
- 关系人姓名：文本输入
- 搜索/重置按钮

**操作按钮**：
- 新增
- 导入
- 导出
- 查看导入失败记录（有失败数据时显示）

**列表列**：
- 选择框
- 员工姓名
- 员工身份证号
- 关系类型
- 关系人姓名
- 性别
- 联系电话
- 联系地址
- 状态（标签显示）
- 创建时间
- 操作（详情/编辑/删除）

#### 4.3.2 新增/编辑表单

**表单分组**：

1. **基本信息**
   - 员工：下拉选择器（远程搜索，显示"姓名 (身份证号)"）
   - 关系类型：下拉选择
   - 关系人姓名：文本输入
   - 性别：下拉选择（男/女/其他）
   - 出生日期：日期选择

2. **证件信息**
   - 证件类型：文本输入
   - 证件号码：文本输入

3. **联系方式**
   - 手机号码1：文本输入
   - 手机号码2：文本输入
   - 微信号1/2/3：文本输入
   - 联系地址：文本域

4. **其他信息**
   - 关系描述：文本域
   - 生效日期：日期选择
   - 失效日期：日期选择
   - 备注：文本域

**表单验证**：
- 必填字段标记
- 格式验证（手机号、身份证号）
- 长度限制

#### 4.3.3 详情页面

使用`el-descriptions`组件展示所有字段信息，分组显示：
- 基本信息
- 证件信息
- 联系方式
- 其他信息
- 审计信息

#### 4.3.4 导入功能

**导入对话框**：
- 拖拽上传
- 仅支持.xlsx/.xls格式
- 下载模板链接
- 上传后立即返回任务ID

**导入结果轮询**：
- 每2秒查询一次状态
- 最多轮询150次（5分钟）
- 完成后显示通知：
  - 全部成功：绿色通知，显示总数
  - 部分失败：橙色通知，显示成功/失败数
  - 显示"查看导入失败记录"按钮

**失败记录对话框**：
- 显示导入统计信息
- 表格展示失败记录：
  - 行号
  - 员工身份证号
  - 关系人姓名
  - 失败原因
- 支持分页
- 清除历史记录按钮

#### 4.3.5 localStorage持久化

**存储内容**：
```javascript
{
  taskId: "uuid",
  status: "SUCCESS/PARTIAL_SUCCESS",
  hasFailures: true/false,
  totalCount: 100,
  successCount: 95,
  failureCount: 5,
  saveTime: 1707456000000
}
```

**Key名称**：
```
staff_fmy_relation_import_last_task
```

**功能**：
- 页面刷新后恢复导入状态
- 显示"查看导入失败记录"按钮
- 鼠标悬停显示上次导入信息
- 7天自动过期

### 4.4 员工选择器

**远程搜索**：
- 输入关键词调用`/ccdi/baseStaff/list`
- 显示格式：`姓名 (身份证号)`
- 返回值：身份证号

---

## 五、与采购交易管理对比验证

### 5.1 架构对比

| 对比项 | 采购交易管理 | 员工亲属关系 | 一致性 |
|-------|-------------|-------------|--------|
| 模块命名 | PurchaseTransaction | StaffFmyRelation | ✅ 遵循规范 |
| 包结构 | com.ruoyi.ccdi | com.ruoyi.ccdi | ✅ 一致 |
| 分层架构 | Controller-Service-Mapper | Controller-Service-Mapper | ✅ 一致 |
| DTO/VO分离 | 是 | 是 | ✅ 一致 |
| MyBatis Plus | 使用 | 使用 | ✅ 一致 |

### 5.2 Controller接口对比

| 接口功能 | 采购交易 | 员工亲属关系 | 一致性 |
|---------|---------|-------------|--------|
| 查询列表 | /list | /list | ✅ 一致 |
| 查询详情 | /{id} | /{id} | ✅ 一致 |
| 新增 | POST | POST | ✅ 一致 |
| 修改 | PUT | PUT | ✅ 一致 |
| 删除 | /{ids} | /{ids} | ✅ 一致 |
| 导出 | /export | /export | ✅ 一致 |
| 下载模板 | /importTemplate | /importTemplate | ✅ 一致 |
| 导入数据 | /importData | /importData | ✅ 一致 |
| 导入状态 | /importStatus/{id} | /importStatus/{id} | ✅ 一致 |
| 失败记录 | /importFailures/{id} | /importFailures/{id} | ✅ 一致 |

### 5.3 异步导入对比

| 对比项 | 采购交易 | 员工亲属关系 | 一致性 |
|-------|---------|-------------|--------|
| 异步注解 | @Async | @Async | ✅ 一致 |
| 状态存储 | Redis Hash | Redis Hash | ✅ 一致 |
| 失败记录存储 | Redis List | Redis List | ✅ 一致 |
| 过期时间 | 7天 | 7天 | ✅ 一致 |
| 批量插入大小 | 500条/批 | 500条/批 | ✅ 一致 |
| 唯一键校验 | 采购事项ID | 员工身份证号+关系人身份证号 | ✅ 逻辑一致 |
| 数据验证 | validateTransactionData() | validateRelationData() | ✅ 结构一致 |

### 5.4 前端交互对比

| 对比项 | 采购交易 | 员工亲属关系 | 一致性 |
|-------|---------|-------------|--------|
| 轮询间隔 | 2秒 | 2秒 | ✅ 一致 |
| 最大轮询次数 | 150次 | 150次 | ✅ 一致 |
| localStorage Key | purchase_transaction_import_last_task | staff_fmy_relation_import_last_task | ✅ 命名规范一致 |
| 失败记录展示 | 表格+分页 | 表格+分页 | ✅ 一致 |
| 导入结果通知 | $notify | $notify | ✅ 一致 |
| 清除历史记录 | 有 | 有 | ✅ 一致 |

### 5.5 Excel功能对比

| 对比项 | 采购交易 | 员工亲属关系 | 一致性 |
|-------|---------|-------------|--------|
| @Required注解 | 使用 | 使用 | ✅ 一致 |
| @DictDropdown注解 | 未使用 | 使用 | ⚠️ 员工亲属关系增强 |
| 导出功能 | 有 | 有 | ✅ 一致 |
| 模板下载 | 有 | 有 | ✅ 一致 |

### 5.6 差异说明

**员工亲属关系的增强**：
1. ✅ 使用`@DictDropdown`注解添加Excel字典下拉框
   - 关系类型、性别、证件类型
   - 提升用户体验，减少录入错误

**业务逻辑差异**：
1. 唯一键设计
   - 采购交易：单一主键（采购事项ID）
   - 员工亲属关系：组合键（员工身份证号+关系人身份证号）
   - 原因：符合业务实际

2. 查询条件
   - 采购交易：项目名称、标的物名称、申请人
   - 员工亲属关系：员工、关系类型、关系人姓名
   - 原因：业务场景不同

### 5.7 对比结论

✅ **员工亲属关系维护功能的设计与实现完全遵循采购交易管理的标准**

- 代码结构一致
- 接口风格一致
- 异步导入机制一致
- 前端交互一致
- 技术栈一致

**唯一差异**：使用了`@DictDropdown`注解增强Excel导入体验，属于技术优化。

---

## 六、实施计划

### 6.1 开发任务清单

#### 后端开发
1. ✅ 创建实体类 `CcdiStaffFmyRelation.java`
2. ✅ 创建DTO类（Add/Edit/Query）
3. ✅ 创建VO类（List/Detail/ImportFailure）
4. ✅ 创建Excel类 `CcdiStaffFmyRelationExcel.java`
5. ✅ 创建Mapper接口和XML映射
6. ✅ 创建Service接口和实现类
7. ✅ 创建ImportService接口和实现类
8. ✅ 创建Controller控制器
9. ✅ 配置权限标识

#### 前端开发
1. ✅ 创建API接口文件 `ccdiStaffFmyRelation.js`
2. ✅ 创建主页面组件 `index.vue`
3. ✅ 实现查询列表功能
4. ✅ 实现新增/编辑功能
5. ✅ 实现详情查看功能
6. ✅ 实现删除功能
7. ✅ 实现导入功能（含异步轮询）
8. ✅ 实现导出功能
9. ✅ 实现员工选择器（远程搜索）

#### 系统配置
1. ⏳ 配置菜单权限
2. ⏳ 配置字典数据（关系类型、性别、证件类型）
3. ⏳ 分配角色权限
4. ⏳ 配置按钮权限

### 6.2 测试计划

#### 单元测试
- Service层数据验证
- Mapper层SQL查询
- 导入重复校验

#### 功能测试
- CRUD基本操作
- 导入导出功能
- 异步导入状态查询
- 失败记录查看

#### 集成测试
- 前后端联调
- 权限控制测试
- Excel模板测试

#### 性能测试
- 大数据量导入（1000+条）
- 并发导入测试
- 分页查询性能

### 6.3 部署清单

1. 数据库表创建（如不存在）
2. 后端代码部署
3. 前端代码部署
4. 菜单权限配置
5. 字典数据初始化
6. 功能测试验证

---

## 七、附录

### 7.1 字典配置

**需要配置的字典类型**：
- `ccdi_relation_type`：关系类型（配偶、子女、父母、兄弟姐妹、其他）
- `ccdi_indiv_gender`：性别（男、女、其他）
- `ccdi_certificate_type`：证件类型（身份证、护照、军官证等）

### 7.2 权限配置

**菜单标识**：`ccdi:staffFmyRelation`

**权限标识清单**：
- `ccdi:staffFmyRelation:list` - 查询列表
- `ccdi:staffFmyRelation:query` - 查询详情
- `ccdi:staffFmyRelation:add` - 新增
- `ccdi:staffFmyRelation:edit` - 修改
- `ccdi:staffFmyRelation:remove` - 删除
- `ccdi:staffFmyRelation:export` - 导出
- `ccdi:staffFmyRelation:import` - 导入

### 7.3 API文档生成

使用SpringDoc自动生成Swagger文档：
- 访问地址：`/swagger-ui/index.html`
- 接口分组：员工亲属关系管理
- 所有接口包含完整的参数说明和响应示例

---

## 八、设计总结

本设计方案完全遵循若依框架规范和采购交易管理的实现标准，确保：

✅ **代码一致性**：命名规范、包结构、分层架构完全一致
✅ **技术一致性**：技术栈、组件选型、实现方式完全一致
✅ **交互一致性**：前端交互、导入导出、异步处理完全一致
✅ **功能完整性**：CRUD、导入导出、权限控制一应俱全

该设计方案可以直接进入开发实施阶段，开发完成后将与采购交易管理功能进行最终对比验证，确保实现效果完全一致。
