Files
ccdi/docs/design/2026-03-23-credit-info-maintenance-design.md
2026-03-23 21:24:09 +08:00

463 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 征信维护设计文档
## 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/`
文档落点需遵循仓库目录规范,避免写入错误目录。