# 员工资产导入与员工亲属实体自动补入后端解决方案

## 背景

本方案针对以下两个已验证问题：

1. 【员工信息维护】和【员工亲属关系维护】使用双 Sheet 模板导入时，第二个 Sheet 的资产信息只按数据库已有主数据反查归属，不能识别同一个导入文件中刚导入的员工或亲属关系，导致提示“未找到资产归属员工”等错误。
2. 【员工亲属实体关联】新增或导入关系人与企业关联后，只写入员工亲属实体关联表，没有同步生成实体库数据，导致企业名称和统一社会信用代码不能自动落入【实体库管理】。

本次解决方案只涉及后端，不调整前端页面结构、上传入口和模板样式。现有前端已经能接收员工任务 ID 与资产任务 ID，并分别轮询失败记录，因此后端需要保持现有返回字段兼容。

## 目标

- 双 Sheet 导入时，主 Sheet 和资产 Sheet 按同一个文件内的业务依赖顺序处理。
- 资产 Sheet 可以关联到数据库已存在的员工或亲属主数据，也可以关联到同一文件中本轮成功导入的员工或亲属主数据。
- 主 Sheet 校验失败的数据不能作为资产归属依据。
- 员工亲属实体关联新增和导入成功后，缺失的企业自动补入实体库。
- 实体库自动补入只插入缺失企业，不更新已存在实体。

## 方案一：员工信息维护双 Sheet 导入

### 当前问题链路

- `/ccdi/baseStaff/importData` 当前分别读取 `员工信息` 与 `员工资产信息` 两个 Sheet。
- 有员工数据时调用 `baseStaffService.importBaseStaff(staffList)`，有资产数据时调用 `baseStaffAssetImportService.importAssetInfo(assetList)`。
- 两个任务独立异步执行，员工资产任务只通过 `ccdi_base_staff.id_card` 查询数据库已有员工。
- 当模板中员工和资产同时首次导入时，资产任务无法稳定看到本轮员工导入结果。

### 改造策略

保留接口路径、模板名称、返回结构和前端轮询模型，新增后端双 Sheet 编排能力。

1. 在员工导入服务中增加双 Sheet 提交方法：
   - 输入：`List<CcdiBaseStaffExcel> staffList`、`List<CcdiBaseStaffAssetInfoExcel> assetList`
   - 输出：`BaseStaffImportSubmitResultVO`
   - 有员工 Sheet 时生成 `staffTaskId`
   - 有资产 Sheet 时生成 `assetTaskId`

2. 新增一个统一异步编排方法，按顺序执行：
   - 初始化员工导入任务状态
   - 执行员工主数据校验和批量插入
   - 收集本轮员工导入成功的 `idCard`
   - 更新员工任务状态和失败记录
   - 初始化员工资产导入任务状态
   - 执行员工资产导入
   - 员工资产归属候选来源为：
     - 数据库已有 `ccdi_base_staff.id_card`
     - 本轮员工 Sheet 成功导入的 `idCard`
   - 更新员工资产任务状态和失败记录

3. 员工资产导入逻辑调整：
   - 保留“员工资产只允许员工本人身份证号”的业务规则。
   - 保留重复校验规则：`personId + assetMainType + assetSubType + assetName`。
   - `personId` 既可以命中数据库已有员工，也可以命中本轮成功导入员工。
   - 只命中员工 Sheet 失败行、且数据库中也不存在该身份证号时，资产行进入失败记录。

4. `/ccdi/baseStaff/importData` 改为调用新的双 Sheet 提交方法，不再由 Controller 分别提交两个互相独立的异步任务。

### 结果状态

- 只填员工 Sheet：只返回 `staffTaskId`，行为保持不变。
- 只填员工资产 Sheet：只返回 `assetTaskId`，按数据库已有员工校验。
- 两个 Sheet 都填写：返回 `staffTaskId` 与 `assetTaskId`，但后端在同一个编排任务中先处理员工再处理资产。

## 方案二：员工亲属关系维护双 Sheet 导入

### 当前问题链路

- `/ccdi/staffFmyRelation/importData` 当前分别读取 `员工亲属关系信息` 与 `亲属资产信息` 两个 Sheet。
- 有亲属关系数据时调用 `relationService.importRelation(relationList)`，有亲属资产数据时调用 `assetInfoImportService.importAssetInfo(assetList)`。
- 亲属资产导入只通过 `ccdi_staff_fmy_relation.relation_cert_no` 查询数据库已有亲属关系。
- 当模板中亲属关系和亲属资产同时首次导入时，资产任务无法稳定看到本轮亲属关系导入结果。

### 改造策略

保留接口路径、模板名称、返回结构和前端轮询模型，新增员工亲属关系双 Sheet 编排能力。

1. 在员工亲属关系导入服务中增加双 Sheet 提交方法：
   - 输入：`List<CcdiStaffFmyRelationExcel> relationList`、`List<CcdiAssetInfoExcel> assetList`
   - 输出：`StaffFmyRelationImportSubmitResultVO`
   - 有亲属关系 Sheet 时生成 `relationTaskId`
   - 有亲属资产 Sheet 时生成 `assetTaskId`

2. 新增一个统一异步编排方法，按顺序执行：
   - 初始化亲属关系导入任务状态
   - 执行亲属关系校验和批量插入
   - 收集本轮成功导入且有效的亲属关系映射：
     - `relationCertNo` 作为资产 Sheet 的 `personId`
     - `personId` 作为资产落库的 `familyId`
   - 更新亲属关系任务状态和失败记录
   - 初始化亲属资产导入任务状态
   - 执行亲属资产导入
   - 亲属资产归属候选来源为：
     - 数据库已有有效员工亲属关系
     - 本轮亲属关系 Sheet 成功导入的有效员工亲属关系
   - 更新亲属资产任务状态和失败记录

3. 亲属资产导入逻辑调整：
   - 保留 `family_id = 员工身份证号`、`person_id = 亲属身份证号` 的落库规则。
   - `personId` 命中唯一亲属关系时导入成功。
   - `personId` 未命中数据库和本轮成功亲属关系时，失败原因为“未找到亲属资产归属员工”。
   - `personId` 命中多个员工归属时，失败原因为“亲属资产归属员工不唯一”。
   - 只命中亲属关系 Sheet 失败行、且数据库中也不存在有效亲属关系时，资产行进入失败记录。

4. `/ccdi/staffFmyRelation/importData` 改为调用新的双 Sheet 提交方法，不再由 Controller 分别提交两个互相独立的异步任务。

### 结果状态

- 只填亲属关系 Sheet：只返回 `relationTaskId`，行为保持不变。
- 只填亲属资产 Sheet：只返回 `assetTaskId`，按数据库已有亲属关系校验。
- 两个 Sheet 都填写：返回 `relationTaskId` 与 `assetTaskId`，但后端在同一个编排任务中先处理亲属关系再处理亲属资产。

## 方案三：员工亲属实体关联自动补入实体库

### 当前问题链路

- 员工亲属实体关联新增只写入 `ccdi_staff_enterprise_relation`。
- 员工亲属实体关联导入成功行也只写入 `ccdi_staff_enterprise_relation`。
- 当前源码中没有可复用的实体库自动补入服务实现。

### 改造策略

新增后端内部服务 `EnterpriseAutoFillService`，统一处理缺失企业的最小插入。

1. 新增服务文件：
   - `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java`

2. 服务方法：
   - `ensureExists(EnterpriseFillItem item)`
   - `ensureExistsBatch(List<EnterpriseFillItem> items)`

3. `EnterpriseFillItem` 字段：
   - `socialCreditCode`
   - `enterpriseName`
   - `entSource`
   - `dataSource`
   - `userName`

4. 插入规则：
   - 按 `socialCreditCode` 去重。
   - 先批量查询 `ccdi_enterprise_base_info` 已存在记录。
   - 已存在实体不更新。
   - 缺失实体最小插入：
     - `social_credit_code = 统一社会信用代码`
     - `enterprise_name = 企业名称`
     - `ent_source = EMP_RELATION`
     - `data_source = MANUAL` 或 `IMPORT`
     - `risk_level = NULL`
     - `create_by/update_by = 当前用户`
   - 不引入额外兜底字段，不改变实体库手工新增规则。

5. 接入员工亲属实体关联新增：
   - 在 `CcdiStaffEnterpriseRelationServiceImpl#insertRelation` 中，亲属有效性校验和组合查重通过后，写关联表前调用：
     - `entSource = EnterpriseSource.EMP_RELATION.getCode()`
     - `dataSource = DataSource.MANUAL.getCode()`

6. 接入员工亲属实体关联导入：
   - 在 `CcdiStaffEnterpriseRelationImportServiceImpl#importRelationAsync` 中，只对校验成功并即将插入的 `newRecords` 组装补入列表。
   - 批量写关联表前调用 `ensureExistsBatch`。
   - 失败行不进入实体库补入集合。
   - `dataSource = DataSource.IMPORT.getCode()`。

## 测试方案

### 单元测试

1. 员工信息维护双 Sheet：
   - 同一模板中员工 Sheet 新增员工，员工资产 Sheet 使用该员工身份证号，资产应导入成功。
   - 员工 Sheet 行校验失败，资产 Sheet 使用该失败员工身份证号且数据库不存在，资产应失败。
   - 只导资产 Sheet，数据库已有员工时资产成功。
   - 只导资产 Sheet，数据库无员工时资产失败。
   - 资产重复命中数据库或当前文件重复时失败。

2. 员工亲属关系维护双 Sheet：
   - 同一模板中亲属关系 Sheet 新增亲属，亲属资产 Sheet 使用该亲属证件号，资产应导入成功，并落库 `family_id = 员工身份证号`。
   - 亲属关系 Sheet 行校验失败，亲属资产 Sheet 使用该失败亲属证件号且数据库不存在，资产应失败。
   - 只导亲属资产 Sheet，数据库已有唯一有效亲属关系时资产成功。
   - 只导亲属资产 Sheet，数据库不存在亲属关系时资产失败。
   - 同一亲属证件号命中多个员工归属时资产失败。

3. 员工亲属实体关联自动补入：
   - 手工新增员工亲属实体关联成功时，缺失企业自动插入实体库，来源为 `EMP_RELATION`，数据来源为 `MANUAL`。
   - 手工新增时实体库已存在该统一社会信用代码，不更新实体库旧记录。
   - 导入成功行自动插入实体库，来源为 `EMP_RELATION`，数据来源为 `IMPORT`。
   - 导入失败行不插入实体库。
   - 同一批多个成功行引用同一统一社会信用代码，只补入一次。

### 接口验证

1. 调用 `/ccdi/baseStaff/importTemplate` 下载真实模板，构造：
   - `员工信息` 新员工
   - `员工资产信息` 引用该员工身份证号
   - 上传 `/ccdi/baseStaff/importData`
   - 轮询员工任务与资产任务，确认都成功
   - 查询 `ccdi_base_staff` 与 `ccdi_asset_info`，确认员工和资产落库

2. 调用 `/ccdi/staffFmyRelation/importTemplate` 下载真实模板，构造：
   - `员工亲属关系信息` 新亲属
   - `亲属资产信息` 引用该亲属证件号
   - 上传 `/ccdi/staffFmyRelation/importData`
   - 轮询亲属关系任务与亲属资产任务，确认都成功
   - 查询 `ccdi_staff_fmy_relation` 与 `ccdi_asset_info`，确认亲属关系和资产落库

3. 调用员工亲属实体关联新增接口：
   - 新增前确认 `ccdi_enterprise_base_info` 不存在该统一社会信用代码
   - 新增关联成功后查询实体库，确认自动生成企业记录
   - 校验 `ent_source = EMP_RELATION`

### 页面验证

完成后需要使用真实业务页面验证：

1. 【员工信息维护】页面下载模板，按模板填写员工和员工资产，上传后检查任务状态、失败记录和列表详情。
2. 【员工亲属关系维护】页面下载模板，按模板填写亲属关系和亲属资产，上传后检查任务状态、失败记录和详情资产列表。
3. 【员工亲属实体关联】页面新增关系人与企业关联后，进入【实体库管理】查询该统一社会信用代码，确认企业已自动生成且来源显示为员工关系人。

测试结束后清理本轮新增员工、亲属关系、资产、员工亲属实体关联和自动补入实体库数据，并关闭测试过程中启动的前后端进程。

## 实施顺序

1. 抽取员工导入和亲属关系导入的可复用执行方法，返回成功主数据上下文和失败记录。
2. 新增员工信息维护双 Sheet 后端编排方法，接入 Controller。
3. 新增员工亲属关系维护双 Sheet 后端编排方法，接入 Controller。
4. 新增 `EnterpriseAutoFillService`。
5. 员工亲属实体关联新增链路接入实体库自动补入。
6. 员工亲属实体关联导入链路接入实体库自动补入。
7. 补充单元测试。
8. 执行接口验证和真实页面验证。
9. 新增实施记录到 `docs/reports/implementation/`。

## 风险点

- 双 Sheet 导入不能继续使用两个互相独立的异步任务，否则仍然存在主数据与资产任务执行顺序不确定的问题。
- 资产归属上下文必须只使用“数据库已有数据”和“本轮主 Sheet 成功数据”，不能把失败主数据作为资产归属。
- 实体库自动补入不能更新已存在企业，避免覆盖人工维护的企业名称、风险等级和来源信息。
- 员工亲属实体关联自动补入必须只处理成功行，失败行不能产生实体库记录。
