# 征信维护设计文档 ## 1. 背景 当前仓库已经具备两项征信解析基础能力: - `ccdi-lsfx` 已提供 `POST /lsfx/credit/parse` 征信解析联调接口,可接收 HTML 并返回 `lx_header`、`lx_debt`、`lx_publictype` - `lsfx-mock-server` 已支持从征信 HTML 中解析员工姓名和身份证号,能够稳定返回本地联调 payload 本次需求是在“信息维护”菜单下新增“征信维护”菜单,支持批量导入员工征信 HTML 文件并提交上传。系统需要调用现有征信解析接口完成数据解析,并把返回结果批量落库。页面同时要展示当前已维护的员工征信信息,并支持删除现有征信数据。 ## 2. 已确认约束 - 菜单挂在“信息维护”下,页面为独立菜单,不挂到员工维护弹窗内 - 页面主列表按员工维度展示摘要,一行一个员工 - 批量上传按员工维度原子化处理,一个员工失败不影响其他员工 - 负债明细表名固定为 `ccdi_debts_info` - 负债信息按照 `assets/征信解析/ccdi_debts_info.xlsx` 设计 - 负面信息按照接口字段单独设计一张表 - 每个员工只保留最新的征信信息 - 列表操作列支持删除现有征信信息 - 不新增征信主表,主列表通过明细表实时聚合 ## 3. 目标 - 在“信息维护”下新增“征信维护”菜单与页面 - 支持一次上传多个 `.html/.htm` 征信文件 - 上传后逐文件调用征信解析接口 - 按员工身份证号归户,并按“最新征信优先”规则覆盖写入 - 在页面展示员工维度的征信摘要列表 - 在详情中展示负债明细和负面信息 - 支持按员工删除当前已维护的征信数据 ## 4. 非目标 - 不改造现有 `POST /lsfx/credit/parse` 的协议语义 - 不新增异步任务、轮询状态页或上传历史模块 - 不保存多版本征信记录 - 不保存原始 HTML 文件或原始 payload JSON - 不增加说明书之外的补丁性兜底方案 ## 5. 方案对比 ### 5.1 方案 A:三张业务表,增加征信主表 新增征信主表保存员工最新摘要,再配合负债表和负面信息表。 优点: - 列表查询简单 - 聚合逻辑更少 缺点: - 引入额外主表,超出本次最短路径 - 同一份摘要数据会与明细形成重复维护 ### 5.2 方案 B:仅保留两张业务表,列表实时聚合 只建设 `ccdi_debts_info` 与负面信息表,主列表按员工维度实时聚合展示。 优点: - 满足“负债表固定命名”和“负面信息单独建表” - 不引入额外主表,路径最短 - 删除和覆盖逻辑仍然清晰,按员工清空两张表即可 缺点: - 列表查询需要做员工维度聚合 ### 5.3 方案 C:两张业务表加原始 payload 存档 在方案 B 基础上额外保存原始 payload 或原始 HTML。 优点: - 排查解析问题时信息更全 缺点: - 增加冗余与维护成本 - 超出当前需求 ## 6. 最终方案 采用方案 B:仅新增 `ccdi_debts_info` 和 `ccdi_credit_negative_info` 两张业务表,列表按员工维度实时聚合,不新增征信主表。 选择原因: - 满足“每个员工只保留最新征信信息”的要求 - 满足“负债信息按 Excel 设计、负面信息单独建表”的要求 - 不额外增加主表,保持最短路径实现 - 删除逻辑直接按员工清理两张业务表,语义明确 ## 7. 总体架构 业务链路如下: `征信维护页面 -> 征信维护业务接口 -> 征信维护服务 -> /lsfx/credit/parse -> 征信解析服务` 职责拆分如下: - 征信维护页面 - 选择多个 HTML 文件并提交上传 - 展示员工维度征信摘要列表 - 打开详情弹窗查看负债明细与负面信息 - 删除员工当前征信数据 - 征信维护业务接口 - 对外提供上传、列表、详情、删除接口 - 不直接把前端耦合到 `ccdi-lsfx` - 征信维护服务 - 逐文件调用征信解析接口 - 根据员工身份证号归户 - 校验“是否为最新征信” - 单员工事务覆盖写入两张业务表 - `ccdi-lsfx` - 保持现有征信解析客户端能力 - 继续作为外部征信解析接入边界 ## 8. 数据模型设计 ### 8.1 负债明细表 `ccdi_debts_info` 一条记录表示一条负债明细,字段按 `assets/征信解析/ccdi_debts_info.xlsx` 设计: - `debt_id`:`BIGINT` 主键,自增 - `person_id`:`VARCHAR(18)`,员工身份证号 - `person_name`:`VARCHAR(100)`,员工姓名 - `query_date`:`DATE`,征信查询日期 - `debt_main_type`:`VARCHAR(50)`,负债大类 - `debt_sub_type`:`VARCHAR(50)`,负债小类 - `creditor_type`:`VARCHAR(50)`,债权人类型 - `debt_name`:`VARCHAR(100)`,负债名称 - `principal_balance`:`DECIMAL(18,2)`,负债本金余额 - `debt_total_amount`:`DECIMAL(18,2)`,负债总额 - `debt_status`:`VARCHAR(20)`,负债状态 - `create_by`:`VARCHAR(64)` - `create_time`:`DATETIME` - `update_by`:`VARCHAR(64)` - `update_time`:`DATETIME` 建议索引: - `idx_person_id(person_id)` - `idx_query_date(query_date)` - `idx_person_query_date(person_id, query_date)` ### 8.2 负面信息表 `ccdi_credit_negative_info` 一条记录表示一个员工当前最新征信对应的负面信息汇总: - `negative_id`:`BIGINT` 主键,自增 - `person_id`:`VARCHAR(18)`,员工身份证号 - `person_name`:`VARCHAR(100)`,员工姓名 - `query_date`:`DATE`,征信查询日期 - `civil_cnt`:`INT`,民事案件笔数 - `enforce_cnt`:`INT`,强制执行笔数 - `adm_cnt`:`INT`,行政处罚笔数 - `civil_lmt`:`DECIMAL(18,2)`,民事案件金额 - `enforce_lmt`:`DECIMAL(18,2)`,强制执行金额 - `adm_lmt`:`DECIMAL(18,2)`,行政处罚金额 - `create_by`:`VARCHAR(64)` - `create_time`:`DATETIME` - `update_by`:`VARCHAR(64)` - `update_time`:`DATETIME` 建议索引: - `uk_person_id(person_id)` 唯一索引 ## 9. 解析字段映射设计 ### 9.1 归户字段 从解析结果 `lx_header` 中读取: - `query_cert_no` -> 员工身份证号 `person_id` - `query_cust_name` -> 员工姓名 `person_name` - `report_time` -> 征信查询日期 `query_date` `query_cert_no` 作为员工唯一归户标识,并校验该身份证号必须存在于 `ccdi_base_staff`。 ### 9.2 `lx_debt` 到 `ccdi_debts_info` 的映射 `lx_debt` 当前为 7 组固定字段,每组映射成一条负债明细: | 接口前缀 | 负债大类 | 负债小类 | 债权人类型 | 负债名称 | | --- | --- | --- | --- | --- | | `uncle_bank_house` | 银行 | 住房贷款 | 银行 | 未结清银行住房贷款 | | `uncle_bank_car` | 银行 | 汽车贷款 | 银行 | 未结清银行汽车贷款 | | `uncle_bank_manage` | 银行 | 经营贷款 | 银行 | 未结清银行经营贷款 | | `uncle_bank_consume` | 银行 | 消费贷款 | 银行 | 未结清银行消费贷款 | | `uncle_bank_other` | 银行 | 其他贷款 | 银行 | 未结清银行其他贷款 | | `uncle_not_bank` | 非银 | 非银行贷款 | 非银 | 未结清非银行贷款 | | `uncle_credit_cart` | 银行 | 信用卡 | 银行 | 未结清信用卡 | 字段映射规则: - `*_bal` -> `principal_balance` - `*_lmt` -> `debt_total_amount` - `*_state` -> `debt_status` 落库过滤规则: - 当某组 `principal_balance`、`debt_total_amount` 都为空或 `0`,且 `debt_status` 为空时,不生成明细记录 - 其余情况生成一条明细 ### 9.3 `lx_publictype` 到 `ccdi_credit_negative_info` 的映射 按接口字段直接映射: - `civil_cnt` -> `civil_cnt` - `enforce_cnt` -> `enforce_cnt` - `adm_cnt` -> `adm_cnt` - `civil_lmt` -> `civil_lmt` - `enforce_lmt` -> `enforce_lmt` - `adm_lmt` -> `adm_lmt` ## 10. 最新征信判定与覆盖策略 系统只保留员工最新征信,规则如下: - 以 `person_id` 作为员工唯一标识 - 以 `query_date` 作为征信最新时间判定字段 - 员工上传新文件时: - 若库中无现有征信数据,直接写入 - 若新 `query_date` 大于等于现有 `query_date`,覆盖旧数据 - 若新 `query_date` 小于现有 `query_date`,该员工本次文件判定为失败,不覆盖 单员工覆盖写入步骤: 1. 删除 `ccdi_debts_info` 中该员工旧记录 2. 删除 `ccdi_credit_negative_info` 中该员工旧记录 3. 插入本次解析后的最新负债明细 4. 插入本次解析后的最新负面信息 以上 4 步置于同一事务中,保证员工维度原子化。 ## 11. 页面设计 ### 11.1 菜单 在“信息维护”下新增菜单: - 菜单名称:`征信维护` - 路由:`creditInfo` - 组件:`ccdiCreditInfo/index` - 权限前缀:`ccdi:creditInfo:*` ### 11.2 顶部操作区 - 批量上传征信 HTML 按钮 - 查询条件: - 员工姓名 - 柜员号 - 身份证号 - 是否已维护征信 上传弹窗要求: - 仅允许选择 `.html/.htm` - 支持一次选择多个文件 - 提交后调用业务上传接口 - 接口返回后直接展示成功/失败汇总及失败清单 ### 11.3 主列表 主列表按员工一行展示,字段建议: - 员工姓名 - 柜员号 - 身份证号 - 所属部门 - 最近征信查询日期 - 负债笔数 - 负债总额 - 民事案件笔数 - 强制执行笔数 - 行政处罚笔数 - 操作 聚合规则: - 基础信息来自 `ccdi_base_staff` - 最近征信查询日期、负债笔数、负债总额来自 `ccdi_debts_info` - 三类负面计数来自 `ccdi_credit_negative_info` - 任一业务表存在数据,即视为“已维护征信” ### 11.4 详情弹窗 详情弹窗分为两部分: - 摘要区域 - 员工姓名 - 身份证号 - 征信查询日期 - 负债总额 - 负债笔数 - 民事案件笔数 - 强制执行笔数 - 行政处罚笔数 - 明细区域 - 负债信息表格 - 负面信息汇总卡片 负债表格字段: - 负债大类 - 负债小类 - 债权人类型 - 负债名称 - 负债本金余额 - 负债总额 - 负债状态 负面信息展示字段: - 民事案件笔数 / 金额 - 强制执行笔数 / 金额 - 行政处罚笔数 / 金额 ### 11.5 删除交互 列表点击删除时,提示: `确认删除该员工当前已维护的征信信息吗?` 确认后删除该员工在两张业务表中的全部数据。 ## 12. 接口设计 新增业务接口,前端不直接调用 `/lsfx/credit/parse`: ### 12.1 批量上传 - 方法:`POST` - 路径:`/ccdi/creditInfo/upload` - 入参:多个 HTML 文件 - 返回: - 总文件数 - 成功数 - 失败数 - 失败明细列表 失败明细建议字段: - `fileName` - `personId` - `personName` - `reason` ### 12.2 列表分页查询 - 方法:`GET` - 路径:`/ccdi/creditInfo/list` - 功能:分页查询员工征信摘要列表 ### 12.3 详情查询 - 方法:`GET` - 路径:`/ccdi/creditInfo/{personId}` - 功能:查询员工征信摘要、负债明细和负面信息 ### 12.4 删除 - 方法:`DELETE` - 路径:`/ccdi/creditInfo/{personId}` - 功能:删除员工当前征信数据 ## 13. 后端设计 建议改动集中在 `ccdi-info-collection` 模块,新增独立征信维护资源。 建议新增: - `controller/CcdiCreditInfoController.java` - `service/ICcdiCreditInfoService.java` - `service/impl/CcdiCreditInfoServiceImpl.java` - `domain/CcdiCreditNegativeInfo.java` - `domain/dto/CreditInfoUploadResultDTO.java` - `domain/vo/CreditInfoListVO.java` - `domain/vo/CreditInfoDetailVO.java` - `mapper/CcdiCreditNegativeInfoMapper.java` - `mapper/CcdiCreditInfoQueryMapper.java` - `resources/mapper/...` 职责说明: - `CcdiCreditInfoController` - 暴露上传、列表、详情、删除接口 - `CcdiCreditInfoService` - 协调解析、归户、最新记录校验、覆盖写入 - `CcdiCreditNegativeInfoMapper` - 维护负面信息表增删查 - `CcdiCreditInfoQueryMapper` - 承担员工维度列表聚合查询和详情聚合查询 ## 14. 异常处理 上传过程中逐文件处理,失败不影响其他员工成功: - 文件后缀非法:失败原因为“文件格式不支持” - 解析接口调用失败:失败原因为“征信解析失败” - 解析成功但身份证号为空:失败原因为“未解析到员工身份证号” - 身份证号未匹配到员工:失败原因为“未匹配到员工信息” - 上传征信日期早于当前最新记录:失败原因为“上传征信日期早于当前已维护最新记录” - 员工覆盖写入事务失败:失败原因为“征信数据保存失败” 删除接口按幂等处理: - 即使员工当前无征信数据,也返回删除成功 ## 15. 测试方案 ### 15.1 后端测试 - 上传服务测试 - 单文件解析成功后正确写入两张业务表 - 同员工新日期文件覆盖旧数据 - 同员工旧日期文件被拒绝覆盖 - 一个员工失败不影响其他员工成功 - 删除后两张表数据均被清空 - 聚合查询测试 - 只有负债信息 - 只有负面信息 - 两者都有 - 两者都没有 - 分页下聚合字段正确 - 控制器测试 - 批量上传成功 - 混合成功/失败上传 - 详情查询成功/无数据 - 删除接口幂等 ### 15.2 前端验证 - 上传多个 HTML 文件并查看结果提示 - 列表按员工维度展示聚合摘要 - 详情弹窗正确显示负债表和负面信息 - 删除后该员工征信摘要即时消失 - 前端生产构建通过 ## 16. 文档与脚本产出要求 本次实施阶段需同步补充: - 后端实施计划:`docs/plans/backend/` - 前端实施计划:`docs/plans/frontend/` - SQL 脚本:`sql/` 或 `sql/migration/` - 实施记录:`docs/reports/implementation/` 文档落点需遵循仓库目录规范,避免写入错误目录。