**本文引用的文件 **
本文件说明 AI 数据清洗链路的 REST 接口,覆盖:
/aiClean:AI 清洗任务提交、执行、撤销、行拒绝明细、动态表分页、模板向量索引状态与重建。/aiClean/synonym:表头同义词库的查询、新增、修改、删除、启用/停用。/cleanLog(由 CleanErrorLogController 暴露):清洗错误日志分页查询。这些接口共同支撑以下能力:
AI 清洗相关接口集中在三个控制器中:
AiCleanController:挂在 /aiClean,是独立于 /dm、/govern 的新链路。AiCleanSynonymController:挂在 /aiClean/synonym,管理评分归一用的词对。CleanErrorLogController:挂在 /cleanLog,提供清洗错误日志分页查询。
graph TB
Client["前端或外部调用方"] --> AC["AiCleanController<br/>/aiClean/*"]
Client --> ASC["AiCleanSynonymController<br/>/aiClean/synonym/*"]
Client --> CEL["CleanErrorLogController<br/>/cleanLog/list"]
AC --> ACS["AiCleanService"]
AC --> TVI["AiCleanTemplateVectorIndex"]
AC --> JOB["AiCleanJobMapper<br/>ai_clean_job"]
ASC --> ASS["AiCleanSynonymService"]
ASS --> DB["PostgreSQL<br/>ai_clean_synonym"]
CEL --> ELQ["CleanErrorLogQuery"]
ELQ --> ELM["CleanErrorLogMapper<br/>clean_error_log"]
subgraph "AI 清洗存储"
JOB
DB
ELM
end
图示来源
章节来源
| 组件 | 职责 |
|---|---|
AiCleanController |
暴露智能清洗任务提交、同步执行、任务列表、撤销、拒绝明细、动态表分页、模板向量索引状态与重建接口。 |
AiCleanSynonymController |
暴露同义词词条列表、创建、更新、删除、启用/停用接口。 |
CleanErrorLogController |
暴露清洗错误日志分页查询接口。 |
| 组件 | 职责 |
|---|---|
AiCleanService |
实现 S1→S7 主流程:探测结构、匹配模板、生成规则、校验置信度、走既有链路或写 AI 动态表、记录任务与拒绝明细。 |
AiCleanSynonymService |
管理 ai_clean_synonym 表,校验“同一归一化词全局唯一”“不做链式传递”,并在事务提交后重建全局缓存。 |
AiCleanTemplateVectorIndex |
把既有模板和 AI 模板的结构元数据嵌入为向量,写入 pgvector 表,供模板语义召回与 Agent 选表使用。 |
| 表 | 用途 |
|---|---|
ai_clean_template |
AI 模板定义,含中文名、物理表名、表头指纹、分类、版本、置信度等。 |
ai_clean_field |
AI 模板字段,决定 DuckDB 动态表 DDL 与前端展示列。 |
ai_clean_rule |
清洗函数链快照,以枚举 hint JSON 形式保存。 |
ai_clean_job |
单个 sheet/block 的清洗任务轨迹,含状态、出口、token 成本、行统计、失败原因。 |
ai_clean_batch |
智能清洗批次状态机:JUDGING → CLEANING_EXISTING → LOADING_AI → DONE/PARTIAL。 |
ai_clean_synonym |
表头同义词库,用于评分归一和绑定域映射。 |
clean_error_log |
清洗失败的错误日志,按 batchId/fileId/sheetId 等过滤。 |
章节来源
| HTTP 方法 | URL | 主要参数 | 响应体 | 说明 |
|---|---|---|---|---|
| POST | /aiClean/submit |
fileId |
Result<Integer> |
为未匹配模板的 sheet 建 AI 清洗任务,幂等。 |
| POST | /aiClean/run |
jobId |
Result<AiCleanJob> |
同步执行一个清洗任务。 |
| GET | /aiClean/jobs |
fileId(可选) |
Result<List<AiCleanJob>> |
当前案件下的任务列表。 |
| POST | /aiClean/revert |
jobId |
Result<Long> |
撤销该 job 在 AI 动态表中的数据。 |
| GET | /aiClean/job/rejections |
jobId、reason(可选)、page、size |
Result<Map<String,Object>> |
分页返回行级拒绝明细。 |
| GET | /aiClean/data/page |
aiTemplateId、orderKey(可选)、asc、page、size |
Result<Map<String,Object>> |
分页读取 AI 动态表数据。 |
| POST | /aiClean/refreshTree |
无或任意 body | Result<Void> |
刷新当前案件的 AI 治理树节点。 |
| GET | /aiClean/templateVector/status |
无 | Result<IndexStatus> |
查看向量索引是否可用、维度、文档数。 |
| POST | /aiClean/templateVector/rebuild |
无 | Result<Integer> |
全量重建模板向量索引。 |
| HTTP 方法 | URL | 请求体 | 响应体 | 说明 |
|---|---|---|---|---|
| GET | /aiClean/synonym/list |
无 | Result<List<AiCleanSynonym>> |
列出全部词条,含停用。 |
| POST | /aiClean/synonym/create |
AiCleanSynonym |
Result<AiCleanSynonym> |
新增词对。 |
| POST | /aiClean/synonym/update |
AiCleanSynonym |
Result<AiCleanSynonym> |
修改词对。 |
| POST | /aiClean/synonym/delete |
{id} |
Result<Void> |
删除词对。 |
| POST | /aiClean/synonym/toggle |
{id, status} |
Result<Void> |
启用或停用词条。 |
| HTTP 方法 | URL | 查询参数 | 响应体 | 说明 |
|---|---|---|---|---|
| GET | /cleanLog/list |
id、batchId、fileId、sheetId、page、limit |
Result<Page<CleanErrorLog>> |
分页查询清洗错误日志。 |
章节来源
为上传文件中尚未匹配到既有模板的 sheet 创建 AI 清洗任务。
/aiClean/submitfileId:必填,Long,表示已上传文件的根记录 id。Result<Integer>,表示新建的任务数量。行为:
pid=fileId 且未绑定模板的 sheet。新任务初始状态为 PENDING。
sequenceDiagram
participant FE as "前端"
participant Ctrl as "AiCleanController"
participant Svc as "AiCleanService"
participant Job as "ai_clean_job"
FE->>Ctrl: POST /aiClean/submit?fileId=xxx
Ctrl->>Svc: submitUnmatched(caseId, fileId)
Svc->>Job: 插入 PENDING 任务
Job-->>Svc: 成功或唯一键冲突
Svc-->>Ctrl: 新建任务数
Ctrl-->>FE: Result<Integer>
图示来源
调用示例
curl:
curl -X POST "http://host/aiClean/submit?fileId=123"
前端 FormData:
const fd = new FormData()
fd.append('fileId', '123')
fetch('/aiClean/submit', { method: 'POST', body: fd })
常见错误
fileId 不存在:不会创建任务,返回新建数为 0。章节来源
同步执行一个 AI 清洗任务。模型调用可能耗时数秒,批量场景应轮询任务列表而非逐个等待。
/aiClean/runjobId:必填,Long。Result<AiCleanJob>,包含状态、出口、置信度、token 成本、行统计、失败原因等。权限:内部通过 caseAccessGuard 校验任务归属当前案件。
sequenceDiagram
participant FE as "前端"
participant Ctrl as "AiCleanController"
participant Svc as "AiCleanService"
participant Job as "ai_clean_job"
FE->>Ctrl: POST /aiClean/run?jobId=xxx
Ctrl->>Svc: run(jobId)
Svc->>Svc: 探测结构、匹配模板、生成规则、校验
alt 命中既有模板
Svc->>Svc: 走既有 doClean
Svc->>Job: 标记 DISPATCHED_A 或 A2/A3
else 无模板
Svc->>Svc: 写 AI 动态表
Svc->>Job: 标记 LOADING/SUCCESS
end
Svc-->>Ctrl: AiCleanJob
Ctrl-->>FE: Result<AiCleanJob>
图示来源
调用示例
curl:
curl -X POST "http://host/aiClean/run?jobId=456"
失败处理
FAIL,并记录最多 900 字符的失败原因。章节来源
查询当前案件下的 AI 清洗任务列表,可按 fileId 过滤。
/aiClean/jobsfileId:可选,Long。Result<List<AiCleanJob>>。章节来源
撤销某个 job 对应的 AI 动态表数据。
/aiClean/revertjobId:必填,Long。Result<Long>。章节来源
分页获取某个 job 的行级失败明细。
/aiClean/job/rejectionsjobId:必填,Long。reason:可选,String,按拒绝原因过滤。page:默认 1。size:默认 20。Result<Map<String, Object>>,结构包含分页字段 records、total、size、current;每条记录包含行号、阻塞列、原因、原值预览、该行全部列错误信息。拒绝原因枚举
| 原因 | 含义 | 是否整行拒绝 |
|---|---|---|
| REQUIRED_EMPTY | 必填列为空 | 是 |
| REQUIRED_TYPE_INVALID | 必填列类型解析失败 | 是 |
| REQUIRED_FUNC_ERROR | 必填列清洗函数执行异常 | 是 |
| REQUIRED_LENGTH_OVERFLOW | 必填列超长或精度溢出 | 是 |
| OPTIONAL_TYPE_INVALID | 非必填列类型不合法,单元格置空 | 否 |
| OPTIONAL_FUNC_ERROR | 非必填列函数执行失败,单元格置空 | 否 |
| OPTIONAL_LENGTH_OVERFLOW | 非必填列超长或精度溢出,单元格置空 | 否 |
| WRITER_ERROR | 写入数据库失败 | 是 |
flowchart TD
Start["进入 rejections"] --> LoadJob["加载 job"]
LoadJob --> CheckCase["校验案件归属"]
CheckCase --> BuildQuery["构建分页查询"]
BuildQuery --> FilterReason{"是否指定 reason?"}
FilterReason -->|是| ApplyReason["按 AiRejectReason 过滤"]
FilterReason -->|否| SkipReason["不过滤"]
ApplyReason --> Page["分页取数"]
SkipReason --> Page
Page --> MapRows["映射为 records + total + size + current"]
MapRows --> Return["返回 Result<Map<String,Object>>"]
图示来源
调用示例
curl:
curl "http://host/aiClean/job/rejections?jobId=456&reason=REQUIRED_TYPE_INVALID&page=1&size=20"
章节来源
分页读取 AI 动态表数据,治理树上 id<0 的节点通常走此接口。
/aiClean/data/pageaiTemplateId:必填,Long,指向 ai_clean_template.id。orderKey:可选,String,排序列键。asc:默认 true,Boolean,升序或降序。page:默认 1。size:默认 20。Result<Map<String, Object>>,结构类似治理树分页,包含 records、total、size、current。章节来源
手动重建当前案件的 AI 治理节点,幂等。
/aiClean/refreshTreeResult<Void>。章节来源
查看 AI 模板向量索引状态。
/aiClean/templateVector/statusResult<IndexStatus>,可观测是否可用、使用的嵌入模型、向量维度、当前文档数。章节来源
全量重建 AI 模板向量索引。
/aiClean/templateVector/rebuildResult<Integer>,表示实际写入的文档数。章节来源
列出全部同义词词条,包括停用的,按标准词排序。
/aiClean/synonym/listResult<List<AiCleanSynonym>>。章节来源
新增词对。
/aiClean/synonym/createAiCleanSynonym,至少包含 stdWord、aliasWord、remark,可选 status。Result<AiCleanSynonym>。校验规则:
若无 createBy,自动填充当前用户。
flowchart TD
Start["接收 AiCleanSynonym"] --> Trim["去除首尾空白"]
Trim --> Validate["校验 stdWord/aliasWord 非空且不等于"]
Validate --> Unique{"全局唯一?"}
Unique -->|否| Error["抛出 ServerException"]
Unique -->|是| Insert["插入 ai_clean_synonym"]
Insert --> Refresh["事务提交后重建 GlobalCache"]
Refresh --> Return["返回 Result<AiCleanSynonym>"]
图示来源
调用示例
curl:
curl -X POST "http://host/aiClean/synonym/create" \
-H "Content-Type: application/json" \
-d '{"stdWord":"交易金额","aliasWord":"发生额","remark":"自定义映射"}'
前端 FormData:
const fd = new FormData()
fd.append('stdWord', '交易金额')
fd.append('aliasWord', '发生额')
fd.append('remark', '自定义映射')
fetch('/aiClean/synonym/create', { method: 'POST', body: fd })
章节来源
修改词对。
/aiClean/synonym/updateAiCleanSynonym,id 必填,其他字段按需更新。Result<AiCleanSynonym>。章节来源
删除词对。
/aiClean/synonym/delete{id}。Result<Void>。章节来源
启用或停用词条。
/aiClean/synonym/toggle{id, status}。Result<Void>。章节来源
分页查询清洗错误日志。
/cleanLog/listid:可选,Long,按日志主键过滤。batchId:可选,Long,按智能清洗批次过滤。fileId:可选,Long,按文件过滤。sheetId:可选,Long,按 sheet 过滤。page、limit:来自父类 Query,MyBatis-Plus 分页参数。Result<Page<CleanErrorLog>>。createDateTime 升序。实体字段
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Long | 主键。 |
| fileId | Long | 文件 id。 |
| sheetId | Long | sheet id。 |
| batchId | Long | 智能清洗批次 id。 |
| fileName | String | 文件名。 |
| sheetName | String | sheet 名称。 |
| content | String | 错误内容。 |
| createDateTime | LocalDateTime | 创建时间。 |
classDiagram
class CleanErrorLogController {
+list(query)
}
class CleanErrorLogQuery {
+id
+batchId
+fileId
+sheetId
}
class CleanErrorLog {
+id
+fileId
+sheetId
+batchId
+fileName
+sheetName
+content
+createDateTime
}
CleanErrorLogController --> CleanErrorLogQuery : "接收查询参数"
CleanErrorLogController --> CleanErrorLog : "返回分页结果"
图示来源
调用示例
curl:
curl "http://host/cleanLog/list?batchId=100&fileId=123&page=1&limit=20"
章节来源
AI 清洗主流程由 AiCleanService 驱动,大致分为:
doClean,写入业务表。ai_clean_{id}。通过 /aiClean/job/rejections 和 /cleanLog/list 暴露失败细节。
flowchart TD
Upload["上传 CSV/Excel"] --> Probe["结构探测<br/>SheetProbe"]
Probe --> Match["模板匹配<br/>HeaderScorer + HeaderFingerprint + LLM"]
Match --> Decision{"命中既有模板?"}
Decision -->|是| Existing["出口 A/A2/A3<br/>既有业务表或降级 AI 表"]
Decision -->|否| NewTpl["出口 B<br/>新建 AI 模板 + DuckDB 动态表"]
Existing --> Stats["记录任务统计"]
NewTpl --> Stats
Stats --> Rejects["记录行级拒绝"]
Rejects --> Log["写入 clean_error_log"]
图示来源
AiCleanTemplateVectorIndex 负责把既有模板和 AI 模板的结构元数据嵌入为向量,写入 pgvector 表。它的作用是:
search_table_schema 工具提供自然语言选表能力。/aiClean/templateVector/rebuild 手动触发。向量索引不可用时走 fail-open:不阻断模板指纹、打分、LLM 主链路。
graph TB
Tpl["ai_clean_template / table_info"] --> Embed["EmbeddingProvider"]
Embed --> PgVec["pgvector 表<br/>rag_tpl_d{dims}"]
PgVec --> Recall["模板候选召回"]
PgVec --> Agent["Agent 选表"]
图示来源
DuckDB 作为本地临时数据源,承担:
批量清洗:将清洗后的数据写入 AI 动态表或既有业务表。
graph TB
File["CSV/Excel 文件"] --> Loader["AiDuckDb / 数据加载器"]
Loader --> Schema["推断 DuckDB 表结构"]
Schema --> Verify["规则校验与置信度评估"]
Verify --> Writer["写入 AI 动态表或既有表"]
图示来源
章节来源
现象
MATCHING 或 NEED_REVIEW。排查步骤
/aiClean/templateVector/status,确认向量索引是否可用。/aiClean/templateVector/rebuild 重建。detail、confidence、model_name、llm_calls 字段,判断是否需要人工调整模板。章节来源
现象
REQUIRED_TYPE_INVALID、OPTIONAL_TYPE_INVALID、REQUIRED_LENGTH_OVERFLOW 等。排查步骤
/aiClean/job/rejections?jobId=xxx,查看具体行号和列错误。AiRejectReason.label() 理解中文错误描述。ai_clean_field 中的 field_type、field_len、required 是否与真实数据一致。章节来源
现象
FAIL,failReason 有失败原因。PARTIAL 或 FAILED。排查步骤
/aiClean/jobs 中该 job 的 status、exit_code、fail_reason。clean_error_log。章节来源
现象
/aiClean/templateVector/status 显示不可用。排查步骤
/aiClean/templateVector/status 查看 unavailable 原因。/aiClean/templateVector/rebuild 全量重建。章节来源
建议顺序
/aiClean/job/rejections 定位具体 job 的具体行。/cleanLog/list 按 batchId、fileId、sheetId 拉取更宽的错误日志。ai_clean_job 的状态、出口、token 成本和失败原因综合判断。/aiClean/synonym/list 是否缺少必要映射。章节来源
/aiClean/run 是同步接口,模型调用可能耗时数秒,批量场景应轮询 /aiClean/jobs。/aiClean/templateVector/rebuild 是全量重建,适合首次上线或换嵌入模型时使用。ai_clean_reject 表和 /aiClean/job/rejections 中。[本节为通用性能说明,不直接分析特定代码文件]
AI 数据清洗接口通过 /aiClean、/aiClean/synonym 和 /cleanLog 三条路径,分别承担智能清洗任务控制、表头同义词管理和清洗错误日志查询职责。系统以 PostgreSQL 作为持久化主库,以 DuckDB 作为临时计算引擎,以 pgvector 作为模板语义索引后端。对外暴露的分页字段统一遵循 records、total、size、current 约定,错误定位则依赖 AiCleanJob、ai_clean_batch、ai_clean_reject 和 clean_error_log 四类数据。