# 员工资产导入与实体库自动补入修复设计 ## 1. 背景 本设计用于修复以下后端问题: 1. 【员工信息维护】双 Sheet 导入时,`员工资产信息` Sheet 只能通过数据库已有员工反查归属,不能识别同一个导入文件中刚成功导入的员工。 2. 【员工亲属关系维护】双 Sheet 导入时,`亲属资产信息` Sheet 只能通过数据库已有亲属关系反查归属,不能识别同一个导入文件中刚成功导入的亲属关系。 3. 关联业务自动补入实体库能力在当前主工作区未完整接回,员工亲属、信贷客户、中介、供应商四类业务成功关联后,缺失企业需要统一补入 `ccdi_enterprise_base_info`。 本次设计只涉及后端。不调整前端页面结构、上传入口、模板样式和前端轮询字段。 ## 2. 目标 - 双 Sheet 导入时,主 Sheet 与资产 Sheet 在后端按业务依赖顺序执行。 - 资产 Sheet 可关联数据库已有主数据,也可关联同一文件中本轮主 Sheet 成功导入的数据。 - 主 Sheet 失败行不能作为资产归属依据。 - 继续返回当前前端已支持的两个任务 ID: - 员工信息维护:`staffTaskId`、`assetTaskId` - 员工亲属关系维护:`relationTaskId`、`assetTaskId` - 恢复统一 `EnterpriseAutoFillService`,并接入员工亲属、信贷客户、中介、供应商四类业务。 - 实体库自动补入只插入缺失企业,不更新已存在实体。 ## 3. 不在本次范围 - 不改前端上传入口、轮询逻辑、失败记录展示和模板下载样式。 - 不合并双 Sheet 导入任务 ID。 - 不改变员工、亲属关系、资产、实体关联现有字段校验规则。 - 不改变实体库手工新增、编辑、导入的既有业务规则。 - 不增加兜底来源、降级来源或兼容性分支。 ## 4. 总体架构 本次后端设计分为两条链路。 ### 4.1 双 Sheet 导入编排 `/ccdi/baseStaff/importData` 和 `/ccdi/staffFmyRelation/importData` 保持原接口、原模板、原返回字段。 Controller 继续负责读取两个 Sheet,但不再分别提交两个彼此独立的异步任务。服务层新增提交编排方法: - 员工信息维护提交方法接收 `List` 和 `List`。 - 员工亲属关系维护提交方法接收 `List` 和 `List`。 提交方法按实际存在的 Sheet 初始化对应任务状态,并启动一个后台编排任务。编排任务内部按顺序执行: 1. 主 Sheet 校验与插入。 2. 收集本轮主 Sheet 成功上下文。 3. 资产 Sheet 校验与插入。 ### 4.2 实体库自动补入 新增或恢复统一内部服务: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java` 该服务是关联业务补入实体库的唯一后端入口。四类业务在业务校验通过、业务记录落库前调用: - 员工亲属实体关联:`EMP_RELATION` - 信贷客户实体关联:`CREDIT_CUSTOMER` - 中介实体关联:`INTERMEDIARY` - 招投标供应商:`SUPPLIER` 实体库缺失时最小插入;已存在时不更新。 供应商来源需要同步新增后端枚举 `EnterpriseSource.SUPPLIER("SUPPLIER", "供应商")`,并补充 `/ccdi/enum/enterpriseSource` 枚举接口测试。前端继续通过现有枚举接口展示来源,不需要调整页面结构。 ## 5. 员工信息维护双 Sheet 导入设计 ### 5.1 当前问题 当前 `/ccdi/baseStaff/importData` 分别读取 `员工信息` 和 `员工资产信息`,然后分别调用: - `baseStaffService.importBaseStaff(staffList)` - `baseStaffAssetImportService.importAssetInfo(assetList)` 两个任务互相独立。员工资产导入只通过数据库 `ccdi_base_staff.id_card` 查找归属,无法稳定看到同一文件中刚导入成功的员工。 ### 5.2 设计方案 新增员工信息维护双 Sheet 后端编排能力。 提交阶段: 1. Controller 读取两个 Sheet。 2. 若两个 Sheet 都无数据,仍返回“至少需要一条数据”。 3. 若存在员工 Sheet,生成并初始化 `staffTaskId`。 4. 若存在员工资产 Sheet,生成并初始化 `assetTaskId`。 5. 返回当前前端兼容的 `BaseStaffImportSubmitResultVO`。 6. 后台启动一个编排任务。 编排阶段: 1. 先执行员工主数据导入。 2. 沿用现有员工必填、身份证号、部门、员工 ID、身份证号重复校验。 3. 批量插入员工成功记录。 4. 收集本轮成功员工的 `idCard`。 5. 更新员工任务状态和失败记录。 6. 再执行员工资产导入。 7. 员工资产归属候选来源为: - 数据库已有 `ccdi_base_staff.id_card` - 本轮员工 Sheet 成功导入的 `idCard` 8. 员工资产落库继续保持: - `family_id = 员工身份证号` - `person_id = 员工身份证号` 9. 员工资产继续保持重复校验:`personId + assetMainType + assetSubType + assetName`。 ### 5.3 任务状态 - 只填员工 Sheet:只返回 `staffTaskId`。 - 只填员工资产 Sheet:只返回 `assetTaskId`,只按数据库已有员工校验。 - 只填员工资产 Sheet 是正常导入场景,不因 `员工信息` Sheet 为空或未填写而拦截。 - 两个 Sheet 都填:返回 `staffTaskId` 和 `assetTaskId`,后台保证员工先处理、资产后处理。 - 员工主 Sheet 部分成功时,员工资产只能使用成功员工上下文。 - 员工主 Sheet 全部失败时,员工资产仍执行,但只能命中数据库已有员工。 ## 6. 员工亲属关系维护双 Sheet 导入设计 ### 6.1 当前问题 当前 `/ccdi/staffFmyRelation/importData` 分别读取 `员工亲属关系信息` 和 `亲属资产信息`,然后分别调用: - `relationService.importRelation(relationList)` - `assetInfoImportService.importAssetInfo(assetList)` 两个任务互相独立。亲属资产导入只通过数据库 `ccdi_staff_fmy_relation.relation_cert_no` 查找归属,无法稳定看到同一文件中刚导入成功的亲属关系。 ### 6.2 设计方案 新增员工亲属关系维护双 Sheet 后端编排能力。 提交阶段: 1. Controller 读取两个 Sheet。 2. 若两个 Sheet 都无数据,仍返回“至少需要一条数据”。 3. 若存在亲属关系 Sheet,生成并初始化 `relationTaskId`。 4. 若存在亲属资产 Sheet,生成并初始化 `assetTaskId`。 5. 返回当前前端兼容的 `StaffFmyRelationImportSubmitResultVO`。 6. 后台启动一个编排任务。 编排阶段: 1. 先执行亲属关系导入。 2. 沿用现有亲属关系必填、员工身份证号存在性、关系人证件号、重复组合校验。 3. 批量插入亲属关系成功记录。 4. 收集本轮成功亲属关系映射: - `relationCertNo` 作为资产 Sheet 的 `personId` - `personId` 作为资产落库的 `familyId` 5. 更新亲属关系任务状态和失败记录。 6. 再执行亲属资产导入。 7. 亲属资产归属候选来源为: - 数据库已有员工亲属关系,沿用现有 owner 查询条件 - 本轮亲属关系 Sheet 成功导入的员工亲属关系 8. 亲属资产落库继续保持: - `family_id = 员工身份证号` - `person_id = 亲属身份证号` ### 6.3 任务状态 - 只填亲属关系 Sheet:只返回 `relationTaskId`。 - 只填亲属资产 Sheet:只返回 `assetTaskId`,只按数据库已有员工亲属关系校验,并沿用现有 owner 查询条件。 - 只填亲属资产 Sheet 是正常导入场景,不因 `员工亲属关系信息` Sheet 为空或未填写而拦截。 - 两个 Sheet 都填:返回 `relationTaskId` 和 `assetTaskId`,后台保证亲属关系先处理、资产后处理。 - 亲属关系主 Sheet 部分成功时,亲属资产只能使用成功亲属关系上下文。 - 亲属关系主 Sheet 全部失败时,亲属资产仍执行,但只能命中数据库已有员工亲属关系。 ## 7. 实体库自动补入设计 ### 7.1 服务职责 `EnterpriseAutoFillService` 只负责一件事:对关联业务成功记录中的企业,确保实体库存在对应统一社会信用代码。 服务接口: - `ensureExists(EnterpriseFillItem item)` - `ensureExistsBatch(List items)` `EnterpriseFillItem` 字段: - `socialCreditCode` - `enterpriseName` - `entSource` - `dataSource` - `userName` ### 7.2 插入规则 1. 按 `socialCreditCode` 去重,同批次同一统一社会信用代码只处理一次。 2. 批量查询 `ccdi_enterprise_base_info` 已存在记录。 3. 已存在实体不更新。 4. 缺失实体最小插入: - `social_credit_code = 统一社会信用代码` - `enterprise_name = 企业名称;中介实体关联缺失实体时允许为 NULL` - `ent_source = 来源` - `data_source = MANUAL 或 IMPORT` - `risk_level = 来源规则值` - `created_by/updated_by = 当前用户` 5. 中介实体关联缺失实体时不要求提供机构名称,补入实体的 `enterprise_name` 可以为 `NULL`。 6. 中介来源 `INTERMEDIARY` 写 `risk_level = 1`。 7. 员工亲属、信贷客户、供应商来源写 `risk_level = NULL`。 8. 并发导致主键重复时,按“已存在实体”处理。 9. 其他数据库异常抛出,让当前业务事务失败。 ### 7.3 四类业务接入点 员工亲属实体关联: - 手工新增:校验有效亲属和组合不重复后、插入关联表前补入。 - 导入:只对校验成功并即将插入的记录批量补入。 - 来源:`EMP_RELATION` - 数据来源:手工新增为 `MANUAL`,导入为 `IMPORT` - 风险等级:`NULL` 信贷客户实体关联: - 手工新增:校验通过后、插入关联表前补入。 - 导入:只对校验成功并即将插入的记录批量补入。 - 来源:`CREDIT_CUSTOMER` - 数据来源:手工新增为 `MANUAL`,导入为 `IMPORT` - 风险等级:`NULL` 中介实体关联: - 手工新增:字段校验和重复关系校验通过后,取消或替换“实体库必须已存在”校验,先补入实体库,再插入关联关系。 - 导入:字段校验和重复关系校验通过后,取消或替换“实体库必须已存在”失败条件,只对即将插入的成功记录批量补入。 - 来源:`INTERMEDIARY` - 数据来源:手工新增为 `MANUAL`,导入为 `IMPORT` - 风险等级:`1` 招投标供应商: - 手工新增或保存招投标主信息时,只对 `supplierUscc` 非空且通过现有格式校验的供应商补入。 - 导入:只对成功采购事项中 `supplierUscc` 非空且通过现有格式校验的供应商批量补入,失败采购事项不补入。 - 导入中 `supplierUscc` 为空的供应商保持现有保存规则,但不补入实体库。 - 来源:`SUPPLIER` - 数据来源:手工新增为 `MANUAL`,导入为 `IMPORT` - 风险等级:`NULL` ## 8. 错误处理与边界 ### 8.1 双 Sheet 导入边界 - 主 Sheet 失败行不能进入资产归属候选。 - 主 Sheet 文件内重复行不能进入资产归属候选。 - 主 Sheet 数据库重复行不能进入资产归属候选。 - 主 Sheet 为空但资产 Sheet 有数据时,资产 Sheet 必须按现有独立资产导入规则正常执行。 - 资产 Sheet 仍按自身失败记录任务记录 `sheetName`、`rowNum`、`errorMessage`。 - 员工资产找不到员工时继续报“员工资产导入仅支持员工本人证件号”。 - 亲属资产找不到归属时继续报“未找到亲属资产归属员工”。 - 亲属资产命中多个归属时继续报“亲属资产归属员工不唯一”。 - 亲属资产数据库归属查询条件沿用当前实现,不因本次编排新增额外状态过滤。 ### 8.2 自动补入边界 - 只处理业务校验成功、即将落库的记录。 - 失败业务行不能产生实体库记录。 - 已存在实体不更新,避免覆盖人工维护数据。 - 不增加默认企业名称、默认来源、默认风险等级等兜底逻辑。 - 不改变实体库导入的严格新增规则。 - 中介实体关联的“实体库必须已存在”校验需要被自动补入替换,否则缺失实体无法进入补入链路。 - 中介实体关联不新增机构名称入参;缺失实体补入时允许 `enterprise_name = NULL`。 - 供应商自动补入只处理非空统一社会信用代码,空统一社会信用代码不改变原导入保存规则。 ## 9. 测试设计 ### 9.1 单元测试 员工信息维护双 Sheet: - 同一模板中新员工和员工资产同时导入,资产引用新员工身份证号,资产成功。 - 员工 Sheet 行校验失败,资产引用该身份证号且数据库不存在,资产失败。 - 员工信息 Sheet 为空或未填写,员工资产 Sheet 引用数据库已有员工时,资产成功导入。 - 只导员工资产,数据库已有员工时成功。 - 只导员工资产,数据库无员工时失败。 - 资产重复命中数据库或当前文件重复时失败。 员工亲属关系维护双 Sheet: - 同一模板中新亲属关系和亲属资产同时导入,资产引用新亲属证件号,资产成功。 - 亲属关系 Sheet 行校验失败,资产引用该亲属证件号且数据库不存在,资产失败。 - 员工亲属关系信息 Sheet 为空或未填写,亲属资产 Sheet 引用数据库已有员工亲属关系时,资产成功导入。 - 只导亲属资产,数据库已有唯一员工亲属关系时成功。 - 只导亲属资产,数据库不存在亲属关系时失败。 - 同一亲属证件号命中多个员工归属时失败。 实体库自动补入: - 已存在实体不插入、不更新。 - 缺失实体插入最小记录。 - 同批多个成功行引用同一统一社会信用代码,只补入一次。 - 中介来源写 `riskLevel=1`。 - 员工亲属、信贷客户、供应商来源写 `riskLevel=null`。 - 后端枚举接口返回 `SUPPLIER/供应商`。 - 并发重复主键按已存在处理。 四类业务接入: - 手工新增成功时调用自动补入。 - 导入成功行进入自动补入集合。 - 导入失败行不进入自动补入集合。 ### 9.2 接口验证 1. 下载真实 `/ccdi/baseStaff/importTemplate` 模板,构造 `员工信息` + `员工资产信息` 同文件导入,上传 `/ccdi/baseStaff/importData`,轮询两个任务并查询员工、资产落库。 2. 下载真实 `/ccdi/staffFmyRelation/importTemplate` 模板,构造 `员工亲属关系信息` + `亲属资产信息` 同文件导入,上传 `/ccdi/staffFmyRelation/importData`,轮询两个任务并查询亲属关系、资产落库。 3. 分别调用员工亲属、信贷客户、中介、供应商新增或导入接口,验证实体库自动补入来源、数据来源和风险等级。 4. 涉及中文清理 SQL 或验证 SQL 时使用 `bin/mysql_utf8_exec.sh`。 ### 9.3 页面验证 完成后必须进入真实业务页面验证,不打开 prototype: 1. 【员工信息维护】下载模板,填写员工和员工资产,上传后检查任务状态、失败记录、列表和详情资产。 2. 【员工亲属关系维护】下载模板,填写亲属关系和亲属资产,上传后检查任务状态、失败记录、详情资产。 3. 四类实体自动补入完成后,进入【实体库管理】按统一社会信用代码查询。 4. 测试结束后清理本轮新增员工、亲属关系、资产、实体关联和自动补入实体库数据。 5. 测试结束后关闭测试过程中启动的前后端进程。 ## 10. 实施顺序 1. 提炼员工主数据导入执行方法,返回成功员工上下文和失败记录。 2. 提炼员工资产导入执行方法,支持额外员工归属上下文。 3. 新增员工信息维护双 Sheet 编排方法并接入 Controller。 4. 提炼亲属关系导入执行方法,返回成功亲属关系上下文和失败记录。 5. 提炼亲属资产导入执行方法,支持额外亲属关系归属上下文。 6. 新增员工亲属关系维护双 Sheet 编排方法并接入 Controller。 7. 恢复或新增 `EnterpriseAutoFillService`。 8. 接入员工亲属、信贷客户、中介、供应商四类自动补入。 9. 补充单元测试。 10. 执行接口验证和真实页面验证。 11. 新增实施记录到 `docs/reports/implementation/`。 ## 11. 风险与控制 - 不能继续由 Controller 分别提交两个独立异步任务,否则资产任务执行顺序仍不确定。 - 不能把主 Sheet 失败行放入资产归属上下文,否则会产生脏资产。 - 不能更新已存在实体库记录,否则会覆盖人工维护的企业名称、风险等级和来源。 - 自动补入必须在业务记录落库前执行,并与业务事务保持一致。 - 四类自动补入必须只处理成功业务行,失败行不能污染实体库。