# 关联业务自动补入实体库设计

## 1. 背景

当前信息维护中多条业务链路都会录入或导入统一社会信用代码和企业名称，但实体库 `ccdi_enterprise_base_info` 不一定已经存在对应企业。现状中部分链路只保存业务关联表，部分链路会因为实体库不存在而失败，导致“关联业务已经知道企业信息，但实体库没有沉淀”的数据断点。

本次需求要求以下模块在新建、导入时，如果要关联的实体在系统实体库不存在，则自动将不存在的实体添加到实体库中：

- 员工亲属实体关联
- 中介库与实体关联
- 信贷客户实体关联
- 招投标信息维护

本次设计按最短路径实现，不增加兼容性分支，不扩展用户未提出的兜底或降级逻辑。

## 2. 目标与范围

### 2.1 目标

在保留现有页面交互和业务校验规则的前提下，为上述四类业务的新建和导入链路补齐实体库自动沉淀能力，保证业务数据成功落库时，对应实体也已存在于实体库。

### 2.2 本次范围

- 新增后端内部实体库自动补全能力
- 改造员工亲属实体关联新建、导入链路
- 改造中介实体关联新建、导入链路
- 改造信贷客户实体关联新建、导入链路
- 改造招投标信息维护新建、导入链路中的供应商实体补入
- 新增企业来源 `SUPPLIER=供应商`
- 保证中介库管理新增、导入实体时风险等级默认为高风险
- 补充对应后端、前端与测试文档

### 2.3 非本次范围

- 不修改实体库主键规则，仍以统一社会信用代码作为实体唯一标识
- 不在实体库已存在时覆盖企业名称、来源、风险等级、数据来源等字段
- 不从外部接口拉取企业工商详情
- 不为没有统一社会信用代码的招投标供应商创建实体库记录
- 不新增用户确认弹窗或前端交互分支
- 不修改关联表与招投标主从表结构

## 3. 现状分析

### 3.1 实体库

实体库表为 `ccdi_enterprise_base_info`，主键为 `social_credit_code`。核心字段包括：

- `social_credit_code`
- `enterprise_name`
- `risk_level`
- `ent_source`
- `data_source`
- `created_by`
- `updated_by`

当前企业来源枚举包含：

- `GENERAL`：一般企业
- `EMP_RELATION`：员工关系人
- `CREDIT_CUSTOMER`：信贷客户
- `INTERMEDIARY`：中介
- `BOTH`：兼有

当前缺少供应商来源，需要新增 `SUPPLIER`。

### 3.2 各业务链路

员工亲属实体关联：

- 新建、导入会校验有效员工亲属
- 业务关联表保存统一社会信用代码和企业名称
- 当前不会补入实体库

中介实体关联：

- 新建时要求关联机构必须已存在
- 导入时当前会因“统一社会信用代码不存在于系统机构表”失败
- 本次需要改为实体库缺失时自动补入

信贷客户实体关联：

- 新建、导入保存统一社会信用代码和企业名称
- 当前不会补入实体库

招投标信息维护：

- 新建、导入维护供应商明细
- 供应商明细包含供应商名称和统一信用代码
- 当前不会补入实体库

## 4. 实现方案

### 4.1 新增后端内部实体库自动补全服务

本次采用单一路径实现：

- 新增一个内部复用能力，统一接收统一社会信用代码、企业名称、企业来源、数据来源和操作人
- 各业务 Service 在成功落业务数据前调用
- 已存在则不处理，不存在则插入最小实体记录

采用该方案的原因：

- 规则集中，避免四处重复
- 新建与导入可复用同一口径
- 后续新增业务来源时扩展点明确
- 需要各业务链路接入该内部服务
- 满足最短路径实现
- 业务规则集中可测
- 不改变前端交互
- 可以明确保证已存在实体不被覆盖

## 5. 数据规则

### 5.1 实体识别规则

自动补入实体库时，只按统一社会信用代码判断实体是否存在。

- 若 `ccdi_enterprise_base_info.social_credit_code` 已存在：不更新实体库任何字段
- 若不存在：新增一条最小实体记录

### 5.2 最小实体记录字段

自动补入时写入以下字段：

| 字段 | 规则 |
|------|------|
| `social_credit_code` | 来源业务中的统一社会信用代码 |
| `enterprise_name` | 来源业务中的企业名称或供应商名称 |
| `ent_source` | 按业务来源映射 |
| `risk_level` | 仅中介来源默认为 `1`，其他来源按 `NULL` 落库 |
| `data_source` | 新建为 `MANUAL`，导入为 `IMPORT` |
| `created_by` | 当前操作人 |
| `updated_by` | 当前操作人 |

其他实体字段保持为空。自动补入能力必须使用独立插入路径，不复用实体库管理手工新增中要求风险等级必填的校验逻辑；员工亲属、信贷客户、供应商三类自动补入记录必须显式保证 `risk_level` 按 `NULL` 落库，不能吃到历史表默认低风险值。

### 5.3 企业来源映射

| 触发业务 | `ent_source` | `risk_level` |
|----------|--------------|--------------|
| 员工亲属实体关联 | `EMP_RELATION` | 空 |
| 中介实体关联 | `INTERMEDIARY` | `1` |
| 中介库管理新增实体 | `INTERMEDIARY` | `1` |
| 中介库管理导入实体 | `INTERMEDIARY` | `1` |
| 信贷客户实体关联 | `CREDIT_CUSTOMER` | 空 |
| 招投标供应商 | `SUPPLIER` | 空 |

需要新增企业来源枚举：

- `SUPPLIER("SUPPLIER", "供应商")`

企业来源选项由现有 `EnterpriseSource` 枚举接口下发，实体库管理页面应通过该接口正常显示和筛选“供应商”。

### 5.4 同批重复规则

导入场景中，如果同一批成功数据内同一个统一社会信用代码出现多次，且实体库当前不存在：

- 只补入一次实体库
- 使用首次有效出现的企业名称或供应商名称
- 后续相同统一社会信用代码不覆盖已补入实体

## 6. 业务链路设计

### 6.1 员工亲属实体关联

新建：

1. 校验亲属身份证号必须是有效员工亲属
2. 校验亲属身份证号和统一社会信用代码组合唯一
3. 调用实体库自动补全，来源为 `EMP_RELATION`，数据来源为 `MANUAL`
4. 插入员工亲属实体关联

导入：

1. 保持现有每行基础校验、有效亲属校验、重复组合校验
2. 仅对校验成功行收集实体信息
3. 批量自动补入缺失实体，来源为 `EMP_RELATION`，数据来源为 `IMPORT`
4. 批量插入员工亲属实体关联成功行
5. 校验失败行不补入实体库

### 6.2 中介实体关联

新建：

1. 校验中介本人存在
2. 不再将“关联机构不存在”作为失败条件
3. 校验中介和统一社会信用代码组合唯一
4. 调用实体库自动补全，来源为 `INTERMEDIARY`，风险等级为 `1`，数据来源为 `MANUAL`
5. 插入中介实体关联

导入：

1. 保持中介本人存在、字段格式、重复组合等校验
2. 取消“统一社会信用代码不存在于系统机构表”失败条件
3. 对校验成功行收集实体信息
4. 批量自动补入缺失实体，来源为 `INTERMEDIARY`，风险等级为 `1`，数据来源为 `IMPORT`
5. 批量插入中介实体关联成功行

中介库管理新增、导入实体：

- 新增实体时继续直接写实体库，风险等级默认为 `1`
- 导入实体时成功记录的风险等级默认为 `1`

### 6.3 信贷客户实体关联

新建：

1. 校验身份证号和统一社会信用代码组合唯一
2. 调用实体库自动补全，来源为 `CREDIT_CUSTOMER`，数据来源为 `MANUAL`
3. 插入信贷客户实体关联

导入：

1. 保持现有字段格式、重复组合等校验
2. 对校验成功行收集实体信息
3. 批量自动补入缺失实体，来源为 `CREDIT_CUSTOMER`，数据来源为 `IMPORT`
4. 批量插入信贷客户实体关联成功行

### 6.4 招投标信息维护

新建：

1. 保持采购事项 ID 唯一性校验
2. 保持供应商唯一中标、重复供应商等校验
3. 从供应商明细中收集 `supplierUscc` 不为空的供应商
4. 调用实体库自动补全，来源为 `SUPPLIER`，数据来源为 `MANUAL`
5. 插入招投标主信息和供应商明细

导入：

1. 保持双 Sheet 主从关系、主信息字段、供应商字段、重复供应商等校验
2. 仅对校验成功的采购事项收集供应商实体信息
3. 只处理供应商统一信用代码不为空的供应商
4. 批量自动补入缺失实体，来源为 `SUPPLIER`，数据来源为 `IMPORT`
5. 批量插入招投标主信息和供应商明细

没有统一信用代码的供应商不补实体库，也不因此失败。

## 7. 事务与并发

### 7.1 新建事务

新建场景中，实体库补全和业务数据插入在同一事务内执行。任一环节失败，本次新建整体回滚。

### 7.2 导入事务

导入场景保持现有异步任务机制。每个导入任务在成功数据批量落库前先执行实体库补全，再写业务表。若实体补全失败，对应成功候选数据不得静默写入业务表。

### 7.3 并发重复

如果两个请求同时补入同一统一社会信用代码：

- 查询时不存在但插入时遇到主键重复，应按“实体已存在”处理
- 不覆盖并发方已写入的实体字段
- 不影响当前业务数据继续落库

## 8. 异常处理

保留现有业务校验错误：

- 字段必填失败
- 统一社会信用代码格式错误
- 身份证号格式错误
- 亲属不存在或无效
- 中介本人不存在
- 重复关联组合
- 重复供应商
- 招投标主从 Sheet 关系异常

不再作为失败原因：

- 中介实体关联中的“统一社会信用代码不存在于系统机构表”

自动补入实体库时，企业名称为空或超过长度不单独新增兜底规则，沿用各业务现有字段校验结果。

## 9. 前端与枚举

前端不新增交互。

需要同步新增或调整：

- 后端 `EnterpriseSource` 枚举新增 `SUPPLIER("SUPPLIER", "供应商")`
- 现有企业来源枚举接口应返回供应商选项
- 实体库管理页面企业来源下拉、列表、详情中的枚举展示应能正确显示“供应商”

## 10. 测试设计

### 10.1 后端测试

需要覆盖：

- 员工亲属实体关联新建：实体库无记录时补入 `EMP_RELATION`，风险为空
- 员工亲属实体关联导入：成功行补入实体库，失败行不补
- 中介实体关联新建：实体库无记录时补入 `INTERMEDIARY`，风险为 `1`
- 中介实体关联导入：原实体不存在场景由失败改为成功
- 中介库管理新增实体：未填写风险等级时写入 `riskLevel=1`
- 中介库管理导入实体：成功记录写入 `riskLevel=1`
- 信贷客户实体关联新建：实体库无记录时补入 `CREDIT_CUSTOMER`，风险为空
- 信贷客户实体关联导入：成功行补入实体库，失败行不补
- 招投标新建：供应商统一信用代码存在时补入 `SUPPLIER`，风险为空
- 招投标导入：成功采购事项的供应商补入实体库，失败采购事项不补
- 已存在实体不覆盖原有名称、来源、风险等级、数据来源
- 同批重复统一社会信用代码只补一次，首次名称生效
- 并发或插入重复时按已存在处理
- 员工亲属、信贷客户、招投标供应商自动补入记录的 `risk_level` 为 `NULL`

### 10.2 前端测试

需要覆盖：

- 实体库管理企业来源筛选项包含“供应商”
- 实体库列表和详情能够显示供应商来源
- 招投标页面新建供应商后，实体库可查询到自动补入的供应商实体

完成页面功能开发后，需要使用 Playwright 打开真实业务页面验证，不使用原型页面。

### 10.3 导入测试

导入测试必须进入真实业务页面执行：

- 下载当前页面模板
- 基于模板生成测试文件
- 覆盖成功导入、失败行不补实体、混合成功失败、同批重复统一社会信用代码等场景
- 测试结束后清理本轮成功写入的测试数据和导入任务缓存

## 11. 实施文档要求

本次功能涉及后端服务、企业来源枚举和前端枚举展示，因此后续实施计划需要拆分：

- 后端实施计划：`docs/plans/backend/`
- 前端实施计划：`docs/plans/frontend/`

实现完成后需要新增实施记录：

- `docs/reports/implementation/`

实施记录需包含修改内容、影响范围和验证情况。
