Files
leaudit-platform-backend/docs/leaudit/SYSTEM_OVERVIEW.md
T
2026-04-29 11:48:50 +08:00

498 lines
18 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.
# LeAudit Platform — 系统现状总览
> 最后更新:2026-04-28
## 一、目标架构
补充文档:
- 并发与重试参数:`docs/leaudit/并发与重试参数说明.md`
```
┌─ API ───────────────────────────────────────────────────────────┐
│ AuditController (/audit) RuleController (/rule-sets) │
│ DocumentController (/upload) │
│ POST /run 触发评查 GET / 规则集列表 │
│ POST /upload 上传建档/可选自动评查 │
│ GET /run/:id 查询状态 GET /{type}/versions 版本列表 │
│ GET /result/:id 查询结果 GET /versions/:id/content 正文│
│ POST /{type}/validate 校验 │
│ POST /{type}/versions 创建 │
│ POST /{type}/publish 发布 │
│ POST /{type}/rollback 回滚 │
│ GET /bindings 绑定列表 │
│ POST /{type}/bindings 创建绑定│
│ PUT /bindings/{id} 更新绑定│
│ DELETE /bindings/{id} 删除绑定│
├─ Service ───────────────────────────────────────────────────────┤
│ AuditServiceImpl RuleServiceImpl + OssServiceImpl│
├─ Bridge ────────────────────────────────────────────────────────┤
│ FileSourceResolver RuleVersionResolver RulesLoader │
│ AuditCtxBuilder AuditServiceFactory NativeRunner │
│ StorageAdapter ResultAdapter RuleValidator │
│ tasks.py (dispatch_leaudit_task) │
├─ leaudit (不改) ────────────────────────────────────────────────│
│ AuditCtx → AuditService.audit() → OCR/Normalize/Extract/ │
│ Evaluate/Rescue → 填充 ctx.normalized_doc/extraction/ │
│ evaluation/fallback_tasks/timing/extraction_errors │
├─ 存储 ──────────────────────────────────────────────────────────┤
│ OSS (MinIO) PostgreSQL │
│ bdocs/{region}/{type}/{doc_id}/ leaudit_documents │
│ rules/{rule_type}/{version_no}/ leaudit_document_files │
│ artifacts/{region}/{run_id}/ leaudit_audit_runs │
│ leaudit_rule_sets │
│ leaudit_rule_versions │
│ leaudit_rule_type_bindings │
│ leaudit_rule_results │
│ leaudit_field_results │
│ leaudit_run_metrics │
│ leaudit_run_errors │
│ leaudit_rescue_outcomes │
│ leaudit_artifacts │
└─────────────────────────────────────────────────────────────────┘
```
## 二、两条核心数据流
### 流 A:规则生命周期
```
编辑 YAML
→ POST /rule-sets/{type}/validate → RuleValidator (YAML语法 + DSL语义)
→ POST /rule-sets/{type}/versions → 上传 OSS + INSERT leaudit_rule_versions
→ POST /rule-sets/{type}/publish → UPDATE leaudit_rule_sets.current_version_id
→ POST /rule-sets/{type}/bindings → INSERT leaudit_rule_type_bindings
→ 新 run 自动绑定新版本到对应文档类型
```
### 流 B:评查执行
```
POST /audit/run { documentId }
→ AuditServiceImpl.Run()
1. 查 leaudit_documents + leaudit_document_files
2. 查 leaudit_rule_type_bindings → rule_set_id + rule_version_id
3. INSERT leaudit_audit_runs (rule_version_id, rule_source_oss_url, sha256)
4. FileSourceResolver → 下载文档 bytes
5. dispatch_leaudit_task()
┌─ RuleVersionResolver → OSS 下载规则 YAML → SHA256 校验
├─ RulesLoader → RulesFile
├─ NativeRunner.run() → AuditCtx → AuditService.audit()
└─ NativeRunner.persist_result()
├─ save_ocr_result() → leaudit_artifacts
├─ save_extraction_result() → leaudit_field_results
├─ save_evaluation_results() → leaudit_rule_results + 分数
├─ save_run_errors() → leaudit_run_errors
├─ save_rescue_outcomes() → leaudit_rescue_outcomes
├─ save_run_metrics() → leaudit_run_metrics
└─ finalize_run() → 终态 (result_status/finished_at)
```
## 三、模块完成度
### M1OSS 基础设施 — 100%
| 文件 | 状态 | 说明 |
|---|---|---|
| `fastapi_common/fastapi_common_storage/oss_client.py` | 已完成 | 上传/下载/Presign/ObjectExists |
| `fastapi_common/fastapi_common_storage/oss_path_utils.py` | 已完成 | BuildBusinessDocKey/ArtifactKey/RuleYamlKey |
| `fastapi_admin/config/_settings.py` | 已完成 | OSS_ENDPOINT/ACCESS_KEY/SECRET_KEY/BUCKET/REGION/USE_SSL/PRESIGN_EXPIRE |
| `fastapi_admin/config/__init__.pyi` | 已完成 | 类型声明 |
### M2:规则管理后端 — 100%
| 文件 | 状态 | 说明 |
|---|---|---|
| `controllers/ruleController.py` | 已完成 | 11 个端点(含绑定管理) |
| `services/ruleService.py` | 已完成 | IRuleService 接口 |
| `services/impl/ruleServiceImpl.py` | 已完成 | 规则 CRUD + 绑定 CRUD |
| `leaudit_bridge/ruleValidator.py` | 已完成 | YAML 语法 + DSL 语义校验 |
| `domian/Dto/ruleVersionCreateDto.py` | 已完成 | 创建版本 DTO |
| `domian/Dto/ruleValidateDto.py` | 已完成 | 校验 DTO |
| `domian/Dto/rulePublishDto.py` | 已完成 | 发布/回滚 DTO |
| `domian/Dto/ruleBindingDto.py` | 已完成 | 绑定 DTO |
| `domian/vo/ruleVo.py` | 已完成 | RuleSetVO/VersionVO/ContentVO/ValidationVO/BindingVO |
### M3:执行链与持久化 — ~95%
| 文件 | 状态 | 说明 |
|---|---|---|
| `leaudit_bridge/auditCtxBuilder.py` | 已完成 | 构建原生 AuditCtx |
| `leaudit_bridge/auditServiceFactory.py` | 已完成 | 创建 AuditService |
| `leaudit_bridge/nativeRunner.py` | 已完成 | 原生执行入口 + persist_result |
| `leaudit_bridge/fileSourceResolver.py` | 已完成 | 文档来源解析(接 OSS) |
| `leaudit_bridge/ruleVersionResolver.py` | 已完成 | 规则版本解析(含 SHA256) |
| `leaudit_bridge/storage_adapter.py` | 已完成 | 全部持久化方法(已修双重 finalize) |
| `leaudit_bridge/tasks.py` | 已完成 | 任务入口 + 失败处理 |
| `leaudit_bridge/pipeline.py` | 待退役 | 旧管线(已被 nativeRunner 替代) |
| `leaudit_bridge/client_factory.py` | 90% | OCR/LLM/VLM 客户端工厂 |
### M4:全流程联调 — ~25%
| 项目 | 状态 |
|---|---|
| 上传入口梳理 | 已完成 |
| 上传后自动触发评查 | 已完成最小链路(`POST /upload` + `autoRun` |
| 结果查询展示 | 已补运行/规则/字段/errors/rescue/metrics/artifacts 查询 |
| 联调样例准备 | 待做 |
| E2E 验证 | 待做 |
## 四、持久化执行顺序与终态
`persist_result()` 严格按以下顺序执行,`finalize_run` 在最后是唯一的终态写入者:
```
save_ocr_result()
save_extraction_result()
save_evaluation_results() ← 只写分数/计数,不写 finished_at
save_run_errors()
save_rescue_outcomes()
save_run_metrics()
finalize_run() ← 唯一写 result_status / finished_at / rescue_applied / phase
```
**终态来源(直接从 AuditCtx 读取,不另存平行状态):**
| 平台表 | 来源 |
|---|---|
| `leaudit_run_metrics` | `ctx.timing` |
| `leaudit_run_errors` | `ctx.extraction_errors` + `ctx.extraction.all_errors` |
| `leaudit_rescue_outcomes` | `ctx.fallback_tasks` |
| `leaudit_audit_runs.result_status` | 综合 `ctx.fallback_tasks` + `ctx.evaluation` 推导 |
| `leaudit_audit_runs.finished_at` | `finalize_run` 写入 now() |
| `leaudit_audit_runs.rescue_applied` | `bool(ctx.fallback_tasks)` |
## 四点五、最新补充:上传建档入口
当前仓库已补最小上传入口:
```text
POST /upload (multipart/form-data)
file
typeId / typeCode
region=default
fileRole=primary
createdBy?
autoRun=false
speed=normal|urgent
```
执行链:
```text
Upload
-> DocumentServiceImpl.Upload()
-> create leaudit_documents
-> 旧 active 文件失效
-> 上传原始文件到 OSS:
bdocs/{region}/{type_code}/{document_id}/v{n}/{file_role}.{ext}
-> INSERT leaudit_document_files
-> autoRun=true 时直接调用 AuditServiceImpl.Run()
```
说明:
- `leaudit_documents` 现阶段是平台内部文档主表,不再依赖旧系统 `documents.id`
- 每次前端上传都会新建一条 `leaudit_documents`
- `speed`
- `normal` -> `leaudit.normal`
- `urgent` -> `leaudit.urgent`
## 四点六、最新补充:结果查询视图
当前 `GetRunStatus()` / `GetResult()` 已不再只返回 run 主表摘要。
- `GetRunStatus()` 当前已返回:
- `documentFileId`
- `resultStatus`
- `ruleSetId` / `ruleVersionId` / `ruleTypeId`
- `rescueApplied`
- `skippedCount`
- `GetResult()` 当前已可聚合查询:
- `leaudit_rule_results`
- `leaudit_field_results`
- `leaudit_run_errors`
- `leaudit_rescue_outcomes`
- `leaudit_run_metrics`
- `leaudit_artifacts`
建议联调方式:
- 上传后从 `run.runId` 轮询 `GET /audit/run/{runId}`
- 完成后调用 `GET /audit/result/{runId}`
- worker 日志里会明确打印:
- 已投递到哪个队列
- worker 实际消费的是 `urgent` 还是 `normal`
## 四点七、关键关系图:DocumentType -> Binding -> RuleSet -> RuleVersion
这一段是当前规则执行链里最关键的一层路由关系。
很多时候容易把这四张表混在一起,但它们职责其实完全不同:
- `leaudit_document_types`:定义“系统里有哪些文档类型”
- `leaudit_rule_type_bindings`:定义“某个文档类型该用哪套规则集”
- `leaudit_rule_sets`:定义“某类规则集合本身”
- `leaudit_rule_versions`:定义“某套规则的具体版本快照”
### 关系图
```text
┌────────────────────────────┐
│ leaudit_document_types │
│----------------------------│
│ id │
│ code │ <- 文档类型编码,如 contract.entrust
│ name │
│ extraction_mode │
│ is_enabled │
└──────────────┬─────────────┘
│ 1:N
┌────────────────────────────┐
│ leaudit_rule_type_bindings │
│----------------------------│
│ id │
│ doc_type_id │ -> leaudit_document_types.id
│ doc_type_code │ -> 冗余编码,便于快速排查
│ rule_set_id │ -> leaudit_rule_sets.id
│ binding_mode │ -> explicit / wildcard / fallback
│ priority │ -> 多条命中时按优先级选
│ is_active │
│ region │ -> default / mz / yf / ...
└──────────────┬─────────────┘
│ N:1
┌────────────────────────────┐
│ leaudit_rule_sets │
│----------------------------│
│ id │
│ rule_type │ <- 与 DSL metadata.type_id 对齐
│ rule_name │
│ current_version_id │ -> 当前生效版本
│ status │
└──────────────┬─────────────┘
│ 1:N
┌────────────────────────────┐
│ leaudit_rule_versions │
│----------------------------│
│ id │
│ rule_set_id │ -> leaudit_rule_sets.id
│ version_no │
│ status │
│ oss_url │ -> rules/.../rules.yaml
│ file_sha256 │
│ metadata_type_id │
│ published_at │
└────────────────────────────┘
```
### 执行时怎么走
真正运行评查时,不是直接“拿一个 rule_type 去找 YAML”,而是按下面这条链走:
```text
leaudit_documents.type_id
-> leaudit_document_types.id
-> leaudit_rule_type_bindings.doc_type_id
-> leaudit_rule_type_bindings.rule_set_id
-> leaudit_rule_sets.current_version_id
-> leaudit_rule_versions.id
-> leaudit_rule_versions.oss_url
-> 下载对应 rules.yaml
-> NativeRunner / AuditService.audit(ctx)
```
### 每张表到底解决什么问题
#### 1. `leaudit_document_types`
它解决的是:
```text
“这份文档属于什么类型?”
```
例如:
```text
id = 2
code = contract.entrust
name = 通用委托合同
```
那一条 `leaudit_documents` 记录里如果:
```text
type_id = 2
```
就表示这份文档被平台认定为“通用委托合同”。
它本质上是平台的“文档分类字典表”。
#### 2. `leaudit_rule_type_bindings`
它解决的是:
```text
“这种文档类型,应该使用哪套规则集?”
```
例如:
```text
doc_type_id = 2
rule_set_id = 22
region = default
binding_mode = explicit
priority = 100
```
意思就是:
- 文档类型 `2`(通用委托合同)
-`default` 区域
- 显式绑定到规则集 `22`
这张表本质上是“规则路由表”。
它不能省略,因为后续扩展会依赖它支持:
- 同一种文档类型在不同地区走不同规则
- 同一种文档类型绑定多套规则并通过优先级决策
- fallback / wildcard 兜底规则
#### 3. `leaudit_rule_sets`
它解决的是:
```text
“系统里存在什么规则集合?”
```
例如:
```text
id = 22
rule_type = contract.entrust
rule_name = 通用委托合同
current_version_id = 2
```
这表示:
- 系统里有一套“通用委托合同”规则集
- 当前生效版本是 `2`
注意:
- `rule_set` 表示“规则集合”的稳定身份
- 它不是某个具体 YAML 版本文件
#### 4. `leaudit_rule_versions`
它解决的是:
```text
“这套规则当前/历史具体长什么样?”
```
例如:
```text
id = 2
rule_set_id = 22
version_no = 2.0
oss_url = rules/contract.entrust/2.0/rules.yaml
status = published
```
这表示:
- 规则集 `22`
-`2.0` 版本
- 对应 OSS 上某个 `rules.yaml`
这层是规则版本治理的核心,支持:
- 发布
- 回滚
- 历史追溯
- 运行时精确复盘
### 为什么一定要拆成四层
如果不拆,后面会遇到很多问题:
- 文档类型和规则类型耦合死,无法独立演进
- 没法优雅支持一类文档多套规则
- 没法支持地区差异
- 没法做发布 / 回滚 / 历史 run 溯源
拆成四层以后,结构会更稳定:
- `document_type` 负责“分类”
- `binding` 负责“路由”
- `rule_set` 负责“规则身份”
- `rule_version` 负责“规则版本快照”
### 当前初始化策略
基于当前数据库里已有的 `leaudit_rule_sets`,当前最合理的初始化策略是:
```text
rule_set.rule_type -> document_type.code
rule_set.rule_name -> document_type.name
document_type.id -> binding.doc_type_id
rule_set.id -> binding.rule_set_id
rule_set.current_version_id -> 当前执行版本
```
也就是说,先用现有 `rule_set` 反向补齐:
- `leaudit_document_types`
- `leaudit_rule_type_bindings`
这是当前最稳、也最方便后续扩展的做法。
## 五、数据库表关系
```
leaudit_document_types
└── leaudit_rule_type_bindings (doc_type_id → rule_set_id)
└── leaudit_rule_sets (current_version_id)
└── leaudit_rule_versions (oss_url, sha256)
└── leaudit_audit_runs (rule_version_id, rule_source_oss_url)
├── leaudit_rule_results
├── leaudit_field_results
├── leaudit_run_metrics
├── leaudit_run_errors
├── leaudit_rescue_outcomes
└── leaudit_artifacts
```
## 六、关键设计原则
1. **leaudit 核心不改** — 所有定制在 bridge 层,leaudit 保持纯 Python 包
2. **只读 AuditCtx** — 执行完只从 ctx 上读取,不自己模拟 stage
3. **规则版本溯源** — 每个 run 绑定具体 `rule_version_id`,老 run 不受新发布影响
4. **OSS 真源 + DB 索引** — YAML 正文存 OSS,元数据索引存 PostgreSQL
5. **终态单点写入**`finalize_run` 是 run 主表终态的唯一写入者
6. **独立 session 提交** — 每个 `save_*` 独立会话,按"先子表后主表"顺序保证最终一致性
## 七、已知待补项
| 缺口 | 优先级 |
|---|---|
| `run_metrics.llm_call_count` / `vlm_call_count` 仍为空 | P1(可从 RescueTask 汇总) |
| `ctx.timing` 缺 normalize/rescue 独立 key | P2(等 leaudit 原生补) |
| 文档状态更新逻辑分散在两处 | P1(应收敛到 StorageAdapter |
| Celery 异步化 | P2(当前同步可跑通,生产需异步) |
| `_TYPE_ID_RULES_MAP` 硬编码兜底 | P2(等 bindings 全覆盖后移除) |
| M4 E2E 联调 | P0(下个里程碑) |