# 员工资产信息维护设计

## 背景
员工信息维护页面当前仅支持维护员工基础信息，不支持维护员工名下资产信息。现需在现有员工维护页面中补齐资产信息的新增、编辑、删除、详情展示和导入能力，并与现有员工导入交互保持一致。

本次设计基于 `2026-03-12` 确认了以下业务约束：

- 资产表保留字段名 `person_id`
- 资产表新增 `family_id`
- `family_id` 存归属员工身份证号
- `person_id` 存资产实际持有人身份证号
- 员工本人资产：`family_id = person_id = 员工身份证号`
- 员工亲属资产：`family_id = 员工身份证号`，`person_id = 亲属身份证号`
- `asset_id` 改为数据库自增主键，不出现在导入模板中
- 资产导入模板不要求填写 `family_id`
- 导入员工资产信息时，系统根据 `person_id` 自动填充归属员工的 `id_card` 到 `family_id`
- 资产导入失败记录入口需与员工导入失败记录入口明确区分，按钮文案为“查看员工资产导入失败记录”

## 目标
- 在员工信息维护页新增员工资产信息维护能力
- 在员工新增和编辑弹窗中支持员工资产信息的添加、编辑、删除
- 在员工详情弹窗中展示该员工全部资产信息
- 在员工列表页新增“导入资产信息”入口，并复用现有异步导入交互
- 删除员工时同步删除该员工全部资产信息

## 非目标
- 不新增独立的“员工资产信息管理”菜单页面
- 不在资产维护中暴露 `asset_id`
- 不允许用户在前端手工填写 `person_id`
- 不改造现有员工列表查询条件和分页结构

## 现状
当前员工维护能力主要集中在以下位置：

- 前端页面：`ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
- 前端接口：`ruoyi-ui/src/api/ccdiBaseStaff.js`
- 后端控制器：`ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiBaseStaffController.java`
- 后端服务：`ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java`
- 导入服务：`ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java`

现有员工导入采用异步任务 + Redis 状态轮询 + 失败记录分页查询的模式，前端使用本地存储保存最近一次导入任务信息。

## 方案对比

### 方案一：员工页内嵌资产子表聚合维护
- 员工新增、编辑、详情接口返回员工主信息和资产列表聚合结果
- 员工弹窗内部直接维护资产子表
- 资产导入使用独立接口，但交互完全复用员工导入模式

优点：
- 最符合当前需求和现有页面使用习惯
- 用户一次打开员工弹窗即可维护完整信息
- 删除员工时做事务级联最直接

缺点：
- 员工 DTO、VO、Service、前端表单都需要联动调整

### 方案二：资产作为独立模块实现，员工页只嵌入调用
- 后端和前端都按独立资源建设资产模块
- 员工页通过额外接口拉取并嵌入展示

优点：
- 模块边界更清晰
- 未来扩展独立资产菜单更容易

缺点：
- 本次需求明显超出必要范围
- 页面状态和接口交互更复杂

### 方案三：员工页只加资产查看和导入，编辑改为二级弹窗
- 员工弹窗中通过二级弹窗维护资产

优点：
- 对现有员工表单侵入较小

缺点：
- 交互割裂
- 不符合“新增和编辑弹窗中支持添加、编辑、删除”的要求

## 最终方案
采用方案一：在员工信息维护页内嵌资产信息子表，员工接口作为聚合接口返回和保存资产列表；资产导入保持独立入口和独立后端接口，但沿用现有员工导入的上传、异步处理、结果通知、失败记录查询交互。

## 数据模型设计

### 数据表
新增 `ccdi_asset_info` 表，字段来源于 `assets/资产信息表.csv`，并做以下调整：

- `asset_id`：`BIGINT` 自增主键
- `family_id`：`VARCHAR(18)`，保存归属员工身份证号，关联 `ccdi_base_staff.id_card`
- `person_id`：`VARCHAR(18)`，保存资产实际持有人身份证号
- 审计字段沿用当前项目规范，由后端自动填充

建议字段如下：

- `asset_id`
- `family_id`
- `person_id`
- `asset_main_type`
- `asset_sub_type`
- `asset_name`
- `ownership_ratio`
- `purchase_eval_date`
- `original_value`
- `current_value`
- `valuation_date`
- `asset_status`
- `remarks`
- `create_by`
- `create_time`
- `update_by`
- `update_time`

建议索引：

- `idx_family_id(family_id)`
- `idx_person_id(person_id)`
- `idx_asset_main_type(asset_main_type)`

## 后端设计

### 新增资产信息模块对象
在 `ccdi-info-collection` 中新增：

- `domain/CcdiAssetInfo.java`
- `domain/dto/CcdiAssetInfoDTO.java`
- `domain/vo/CcdiAssetInfoVO.java`
- `domain/excel/CcdiAssetInfoExcel.java`
- `domain/vo/AssetImportFailureVO.java`
- `mapper/CcdiAssetInfoMapper.java`
- `service/ICcdiAssetInfoService.java`
- `service/ICcdiAssetInfoImportService.java`
- `service/impl/CcdiAssetInfoServiceImpl.java`
- `service/impl/CcdiAssetInfoImportServiceImpl.java`
- `controller/CcdiAssetInfoController.java`
- `resources/mapper/info/collection/CcdiAssetInfoMapper.xml`

### 扩展员工聚合接口
扩展现有员工 DTO 和 VO：

- `CcdiBaseStaffAddDTO.assetInfoList`
- `CcdiBaseStaffEditDTO.assetInfoList`
- `CcdiBaseStaffVO.assetInfoList`

后端聚合规则：

- 查询员工详情时，按员工 `id_card` 查询 `family_id = 员工身份证号` 的全部资产并组装到 `assetInfoList`
- 新增员工时，先保存员工，再以员工 `id_card` 回填资产列表中的 `family_id`
- 编辑员工时，更新员工主信息，再按员工身份证号重建以 `family_id` 归户的资产列表
- 删除员工时，先按 `family_id` 删除资产再删员工，整个过程置于同一事务内

### 编辑时的资产处理策略
编辑员工时不要求前端传递资产行状态，直接按“当前完整列表”覆盖：

- 获取编辑前员工旧身份证号
- 更新员工主信息
- 如果身份证号变更，按旧身份证号删除 `family_id = 旧身份证号` 的旧资产
- 按当前最新身份证号删除 `family_id = 当前身份证号` 的对应资产
- 当前员工本人资产设置 `family_id = person_id = 当前员工身份证号`
- 当前员工亲属资产设置 `family_id = 当前员工身份证号`，`person_id = 资产实际持有人身份证号`
- 批量插入最新资产列表

该策略实现简单，能直接覆盖资产新增、编辑、删除三种变化。

### 资产导入接口
新增独立资产导入接口：

- `POST /ccdi/assetInfo/importTemplate`
- `POST /ccdi/assetInfo/importData`
- `GET /ccdi/assetInfo/importStatus/{taskId}`
- `GET /ccdi/assetInfo/importFailures/{taskId}`

导入交互沿用现有员工导入设计：

- 异步任务执行
- Redis 保存任务状态
- 失败记录单独缓存
- 前端轮询状态并显示通知

### 校验规则

#### 员工保存时
- 员工基础信息仍沿用现有校验规则
- `assetInfoList` 中空行自动过滤
- 资产必填项：`personId`、`assetMainType`、`assetSubType`、`assetName`、`currentValue`、`assetStatus`
- 数值和日期格式必须合法
- 后端强制回填 `family_id = 员工 id_card`
- 当 `personId = 员工 id_card` 时，视为员工本人资产
- 当 `personId != 员工 id_card` 时，视为员工亲属资产

#### 资产导入时
- 模板中仅要求填写 `person_id`，不要求填写 `family_id`
- 若 `person_id` 能匹配员工 `id_card`，视为员工本人资产，自动填充 `family_id = 该员工 id_card`
- 若 `person_id` 不能匹配员工 `id_card`，则继续匹配员工亲属关系中的亲属身份证号，匹配成功后自动填充对应员工的 `id_card` 到 `family_id`
- 若 `person_id` 同时匹配到多个员工家庭，则导入失败，原因标记为“资产归属员工不唯一”
- 若 `person_id` 在员工和员工亲属关系中均无法匹配，则导入失败
- 模板中不包含 `asset_id`
- 允许同一员工导入多条资产
- 失败记录仅返回失败数据，不返回成功数据

## 前端设计

### 列表页
在 `ruoyi-ui/src/views/ccdiBaseStaff/index.vue` 的按钮区新增：

- “导入资产信息”按钮
- “查看员工资产导入失败记录”按钮

资产导入相关状态全部独立维护：

- 独立弹窗状态
- 独立轮询定时器
- 独立任务 ID
- 独立 localStorage key
- 独立失败记录弹窗和分页状态

### 新增和编辑弹窗
在现有员工弹窗的“基本信息”下方新增“资产信息”分区，采用可编辑子表形式。

子表字段：

- 资产实际持有人身份证号
- 资产大类
- 资产小类
- 资产名称
- 产权占比
- 购买/评估日期
- 资产原值
- 当前估值
- 估值截止日期
- 资产状态
- 备注
- 操作

交互规则：

- 分区右侧提供“新增资产”按钮
- 每行支持删除
- 编辑时回显 `assetInfoList`
- 前端不展示 `asset_id`、`family_id`
- 前端允许填写 `personId`，用于表示资产实际持有人身份证号
- 当 `personId` 与当前员工身份证号一致时，视为本人资产；不一致时，视为亲属资产

### 详情弹窗
在“员工详情”弹窗中新增“资产信息”区域，使用只读表格展示全部资产。

若无资产数据，显示“暂无资产信息”空状态。

详情表格建议增加：

- `personId`：资产实际持有人身份证号
- `ownerType`：本人 / 亲属

## 数据流

### 员工新增
前端提交员工基础信息和 `assetInfoList`，后端保存员工后按员工 `id_card` 为每条资产回填 `family_id`，再批量保存资产。

### 员工编辑
前端拉取员工详情回显基础信息和资产列表，用户修改后提交完整列表，后端按最新员工身份证号重建 `family_id = 员工身份证号` 的资产明细。

### 员工详情
后端查询员工主信息，再按 `family_id = 员工身份证号` 查询资产列表并一并返回。

### 员工删除
后端先按 `family_id = 员工身份证号` 删除资产，再删除员工主记录。

### 资产导入
前端上传资产 Excel，后端根据 `person_id` 自动识别归属员工并回填 `family_id`，异步校验并批量插入资产数据，失败记录通过独立入口查看。

## 异常处理
- 资产导入时，若 `person_id` 无法匹配员工本人或员工亲属，记录失败原因
- 资产导入时，若 `person_id` 对应多个员工家庭，记录归属不唯一失败原因
- 员工编辑时若身份证号变更，必须同步处理旧资产清理和新资产重建
- 员工删除和员工编辑资产重建均使用事务，防止主从数据不一致
- 前端提示文案中明确区分“员工导入”和“员工资产导入”

## 验收标准
- 员工列表页新增“导入资产信息”按钮
- 资产导入失败时显示“查看员工资产导入失败记录”按钮
- 员工新增和编辑弹窗中可添加、编辑、删除资产信息
- 员工详情弹窗中可查看该员工全部资产信息
- 删除员工后，该员工资产信息同步删除
- 资产导入模板不包含 `asset_id`
- 资产导入模板不填写 `family_id`
- 资产导入使用 `person_id` 识别资产实际持有人，并自动回填归属员工的 `family_id`
