docs: add fix-double-finalize-and-bindings-api implementation plan
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# LeAudit 设计文档目录
|
||||
|
||||
本目录包含 `leaudit-platform` 项目的核心架构设计文档。
|
||||
|
||||
## 文档索引
|
||||
|
||||
| 文档 | 内容 | 状态 |
|
||||
|------|------|------|
|
||||
| [document_schema_design.md](document_schema_design.md) | 文档域表结构设计 — 17 张 `leaudit_*` 表完整说明 | ✅ 已落地 |
|
||||
| [dsl_rule_schema_design.md](dsl_rule_schema_design.md) | DSL 规则域表结构 — 规则集/版本/绑定管理 | ✅ 核心表已落地 |
|
||||
| [processing_logic.md](processing_logic.md) | LeAudit 7 阶段处理流水线说明 | 📖 参考文档 |
|
||||
| [bridge_directory_design.md](bridge_directory_design.md) | Bridge 桥接层目录与职责设计 | ✅ 已落地 |
|
||||
| [infrastructure_redesign.md](infrastructure_redesign.md) | 基础设施重设计 — OSS/队列/缓存/区域隔离 | 📋 设计蓝图 |
|
||||
|
||||
## 快览
|
||||
|
||||
```
|
||||
leaudit-platform 核心链路:
|
||||
|
||||
用户上传文档
|
||||
→ leaudit_bridge pipeline (OCR→Extract→Evaluate→Rescue)
|
||||
→ StorageAdapter 写入 leaudit_* 表
|
||||
→ Controller→Service 读取结果返回前端
|
||||
|
||||
数据存储:
|
||||
PostgreSQL: 17 张 leaudit_* 表 (元数据 + 结果索引)
|
||||
MinIO OSS: bdocs/ + artifacts/ (文件真源)
|
||||
Redis: 队列 + 缓存 + 并发控制
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 迁移规范
|
||||
|
||||
从老项目 `docauditai` 迁移代码到新项目 `leaudit-platform` 时,严格遵守以下规范。
|
||||
|
||||
### 一、路由
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| **路由地址不变** | 前端不做任何修改,`POST /auth/login`、`POST /upload` 等全部保持原样 |
|
||||
| **编码按新规范** | 不用旧项目 `@router.post()` 裸写逻辑,改为 Controller → Service(接口+实现) → Model 分层 |
|
||||
| **响应格式统一** | 旧 `{success, data}` → 新 `Result[T]` 格式 `{code, message, data}` |
|
||||
| **去掉 V1/V2** | 旧项目 `app/routes/v2/` 的多版本路由全部废弃,只保留实际使用的路由 |
|
||||
|
||||
### 二、Service 层
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| **业务逻辑原封不动** | 核心判断逻辑、优先级、算法全部保持和旧项目一致 |
|
||||
| **只改数据访问方式** | `asyncpg.connect()` → SQLAlchemy `GetAsyncSession()` + `text()` |
|
||||
| **接口+实现分离** | 每个模块定义 `IXxxService` 抽象接口 + `XxxServiceImpl` 实现 |
|
||||
| **不顺手优化** | 已验证的业务逻辑不做"顺便改进",避免引入新 bug |
|
||||
|
||||
### 三、数据库
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| **全 `leaudit_*` 前缀** | 与旧 `docauditai` 库的表完全隔离,不混用 |
|
||||
| **新数据库独立** | `leaudit_platform@nas.7bm.co:54302`,不修改旧库结构 |
|
||||
| **所有表必有** | `create_time` + `update_time` + `delete_time` 三时间戳 |
|
||||
| **所有列必有中文注释** | `COMMENT ON COLUMN xxx IS '...'` |
|
||||
| **软删除** | 业务表不允许物理删除,统一用 `delete_time IS NULL` 过滤 |
|
||||
|
||||
### 四、配置
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| **TOML → os.environ → Pydantic Settings → module export** | 配置加载链:`app.toml` → 环境变量 → `_settings.py` → `from fastapi_admin.config import XXX` |
|
||||
| **不硬编码** | 禁止在代码中写死 host/port/key,一律走 config 模块 |
|
||||
| **app.toml 不入 git** | 含数据库密码、API Key,`.gitignore` 已排除 |
|
||||
|
||||
### 五、Bridge 导入
|
||||
|
||||
| 旧引用 | 新引用 |
|
||||
|-------|-------|
|
||||
| `from core.config import XXX` | `from fastapi_admin.config import XXX` |
|
||||
| `from core.postgrest.client import ...` | `from fastapi_common...database import GetAsyncSession` + `text()` |
|
||||
| `from core.logger import log` | `from fastapi_common.fastapi_common_logger import logger` |
|
||||
| `from core.celery_app_limited import celery_app` | P2 阶段集成 |
|
||||
| `from core.utils.instance_context import ...` | 已废弃(显式参数替代 os.environ 切换) |
|
||||
|
||||
### 六、文件路径
|
||||
|
||||
| 旧路径 | 新路径 |
|
||||
|-------|-------|
|
||||
| `app/routes/v2/` | `fastapi_modules/fastapi_leaudit/controllers/` |
|
||||
| `app/rbac/` | `fastapi_modules/fastapi_leaudit/services/impl/` |
|
||||
| `services/leaudit_bridge/` | `fastapi_modules/fastapi_leaudit/leaudit_bridge/` |
|
||||
| `core/config.py` | `fastapi_admin/config/` |
|
||||
| `core/storage/` | `fastapi_common/` (通用化) |
|
||||
|
||||
### 七、不允许做的事
|
||||
|
||||
- ❌ 在老 `docauditai` 基础上直接改代码
|
||||
- ❌ 在新项目中引用 `docauditai` 的任何模块
|
||||
- ❌ 在新项目中直接 `import leaudit` 内核(必须走 bridge)
|
||||
- ❌ 跳过 Controller→Service→Model 分层直接写路由逻辑
|
||||
- ❌ 数据库表不加中文注释
|
||||
- ❌ 硬编码配置值
|
||||
@@ -0,0 +1,128 @@
|
||||
# LeAudit Bridge 目录设计
|
||||
|
||||
## 1. 目标
|
||||
|
||||
`fastapi_modules/fastapi_leaudit/leaudit_bridge/` 是 `leaudit-platform` 和 `leaudit` 内核之间的唯一正式桥接层。
|
||||
|
||||
设计目标:
|
||||
|
||||
- 业务层不直接调用 `leaudit` 内核模块
|
||||
- 所有文档评查统一从 bridge 进入
|
||||
- bridge 负责输入映射、上下文构建、结果适配、结果落库
|
||||
- 后续替换或升级 `leaudit` 时,最大限度减少对业务层的影响
|
||||
|
||||
## 2. 目录结构
|
||||
|
||||
```text
|
||||
fastapi_modules/fastapi_leaudit/leaudit_bridge/
|
||||
├── __init__.py # 顶层入口:create_pipeline / is_leaudit_mode
|
||||
├── pipeline.py # 管线总入口:OCR → Extract → Evaluate → Persist
|
||||
├── ctx_builder.py # 构建 leaudit 执行上下文
|
||||
├── rules_loader.py # 规则文件加载与缓存
|
||||
├── client_factory.py # OCR/LLM/VLM 客户端工厂
|
||||
├── result_adapter.py # leaudit 结果 → 统一格式
|
||||
├── storage_adapter.py # 结果写入 leaudit_* 表(SQLAlchemy)
|
||||
├── tasks.py # Celery 异步任务入口
|
||||
├── ocr_bridge.py # OCR/VLM 桥接后处理
|
||||
└── case_number_extractor.py # 案件编号提取
|
||||
```
|
||||
|
||||
> 路径从老项目 `services/leaudit_bridge/` 变更为 `fastapi_modules/fastapi_leaudit/leaudit_bridge/`。
|
||||
|
||||
## 3. 文件职责说明
|
||||
|
||||
### `__init__.py`
|
||||
|
||||
- `is_leaudit_mode()` — 新平台始终返回 True
|
||||
- `create_pipeline(rules_path)` — 创建完整 LauditPipeline 实例
|
||||
- 使用 `DocNormalizationAdapter` 包裹 OCR 客户端
|
||||
- 构建 `RulesFileRegistry` 用于内容分类
|
||||
|
||||
### `pipeline.py`
|
||||
|
||||
核心入口 `LauditPipeline.run()`:
|
||||
|
||||
```
|
||||
document_id + file_path + rules_file
|
||||
→ OCR (含分类/分段/印章增强)
|
||||
→ 案件编号提取
|
||||
→ Extraction (dispatch_extract)
|
||||
→ 坐标解析 (resolve_bundle_positions)
|
||||
→ Phase 判定 (draft/executed)
|
||||
→ Evaluation (evaluate_extraction)
|
||||
→ StorageAdapter 落库
|
||||
```
|
||||
|
||||
### `ctx_builder.py`
|
||||
|
||||
把 leaudit-platform 文档对象映射成 leaudit 可执行上下文。
|
||||
|
||||
### `rules_loader.py`
|
||||
|
||||
- 本地 YAML 加载(`leaudit.dsl.loader.load_rules_file`)
|
||||
- 未来扩展:OSS 下载 + 缓存
|
||||
|
||||
### `client_factory.py`
|
||||
|
||||
统一创建 leaudit 运行所需依赖对象:
|
||||
|
||||
- `create_ocr_client()` → `ChandraOCRClient`
|
||||
- `create_llm_client()` → `OpenAICompatibleClient`
|
||||
- `create_vlm_client()` → `QwenVLMClient`
|
||||
|
||||
配置源:`fastapi_admin.config`(从 `app.toml` 加载)
|
||||
|
||||
### `result_adapter.py`
|
||||
|
||||
把 leaudit 的原始结果转换成统一消费结构,屏蔽内核对象细节变化。
|
||||
|
||||
### `storage_adapter.py`
|
||||
|
||||
将适配后的结果写入 `leaudit_*` 表:
|
||||
|
||||
- `update_document_status()` → `leaudit_documents.processing_status`
|
||||
- `save_ocr_result()` → `leaudit_artifacts`
|
||||
- `save_extraction_result()` → `leaudit_field_results`
|
||||
- `save_evaluation_results()` → `leaudit_rule_results` + `leaudit_audit_runs`
|
||||
|
||||
使用 SQLAlchemy `GetAsyncSession()` + `text()` 查询。
|
||||
|
||||
### `tasks.py`
|
||||
|
||||
Celery 异步任务入口。P2 阶段完成 Celery 集成。
|
||||
|
||||
- `leaudit_process_document()` — 主处理函数
|
||||
- `dispatch_leaudit_task()` — 任务分发(P2 改用 `.apply_async()`)
|
||||
- `_resolve_rules_path()` — 规则路径解析(config → DB → type_id 映射)
|
||||
|
||||
## 4. 调用边界约束
|
||||
|
||||
- Controller 层不直接调用 `leaudit` 内核模块
|
||||
- Service 层不直接调用 `leaudit` 内核模块
|
||||
- 只有 `leaudit_bridge/` 可以感知 `leaudit` 内核类型和对象
|
||||
- 所有外部调用统一经过 `pipeline.py` 或 `tasks.py`
|
||||
|
||||
## 5. 数据流
|
||||
|
||||
```text
|
||||
Controller / Service
|
||||
→ leaudit_bridge.tasks.dispatch_leaudit_task
|
||||
→ leaudit_bridge.pipeline.LauditPipeline.run
|
||||
→ ctx_builder / rules_loader / client_factory
|
||||
→ leaudit 引擎执行
|
||||
→ result_adapter
|
||||
→ storage_adapter
|
||||
→ leaudit_* 结果表
|
||||
```
|
||||
|
||||
## 6. 导入路径迁移
|
||||
|
||||
从老项目迁移时,所有 `from core.*` 已更新为:
|
||||
|
||||
| 旧引用 | 新引用 |
|
||||
|-------|-------|
|
||||
| `from core.config import ...` | `from fastapi_admin.config import ...` |
|
||||
| `from core.postgrest.client import ...` | SQLAlchemy `GetAsyncSession()` + `text()` |
|
||||
| `from core.logger import log` | `from fastapi_common.fastapi_common_logger import logger` |
|
||||
| `from core.celery_app_limited import celery_app` | P2 阶段集成 |
|
||||
| `from core.utils.instance_context import ...` | 已移除(显式参数替代环境变量切换) |
|
||||
@@ -0,0 +1,220 @@
|
||||
# LeAudit 文档域表结构设计
|
||||
|
||||
> **状态**: 已落地到 `leaudit_platform` 数据库(17 张表,全中文注释)
|
||||
> **设计基准**: `/home/wren-dev/Porject/leaudit/src/leaudit` 源码分析
|
||||
|
||||
## 1. 设计依据
|
||||
|
||||
本设计基于对 `leaudit` 源码的完整分析,对齐其真实的处理管线、数据模型和产出物类型。
|
||||
|
||||
### 1.1 LeAudit 处理管线(7 阶段)
|
||||
|
||||
```text
|
||||
文件输入
|
||||
→ Stage 1: Normalize(解析 → OCR → 分类 → 案卷分段 → 印章增强 → markdown 渲染)
|
||||
→ Stage 2: Rules Resolve(加载 RulesFile)
|
||||
→ Stage 3: Extract(字段抽取 → hydrate → multi-entity → derived → grounding)
|
||||
→ Stage 4: Detect Phase(draft / executed 判定)
|
||||
→ Stage 5: Evaluate(规则引擎 → 16 种 check 类型 → pass/fail/skip)
|
||||
→ Stage 6: Rescue(失败规则补救:review → agent → pass/fail 翻转)
|
||||
→ Stage 7: Finalize → Persist
|
||||
```
|
||||
|
||||
### 1.2 LeAudit 自身存储模型(仅供参考,不做 leaudit-platform 真相源)
|
||||
|
||||
LeAudit 有一套自己的 SQLAlchemy ORM 表(`storage/models/`)。**leaudit-platform 不使用 LeAudit 的这套存储**。bridge 层(`storage_adapter.py`)已经绕过它,直接将结果写入 leaudit-platform 的 `leaudit_*` 表。
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计原则
|
||||
|
||||
### 2.1 完全独立于业务表
|
||||
|
||||
所有 LeAudit 域表统一 `leaudit_*` 前缀,与老 `docauditai` 的 `documents` / `evaluation_points` 等表完全隔离。
|
||||
|
||||
### 2.2 数据库存索引,OSS 存真身
|
||||
|
||||
- OCR JSON、normalized JSON、markdown、页面图、裁切图 → OSS 文件
|
||||
- 数据库只存元数据、状态、OSS 地址
|
||||
|
||||
### 2.3 一次执行一个 Run
|
||||
|
||||
每次文档进入 LeAudit 处理链,生成一条 `leaudit_audit_runs` 记录。支持重跑、版本回放、模型对比。
|
||||
|
||||
### 2.4 结果与产物解耦
|
||||
|
||||
- 结果表(rule_results / field_results)→ "这次跑出了什么"
|
||||
- 产物表(artifacts)→ "这次跑的过程中生成了哪些文件"
|
||||
|
||||
---
|
||||
|
||||
## 3. 完整表清单
|
||||
|
||||
| # | 表名 | 用途 | 状态 |
|
||||
|---|------|------|------|
|
||||
| 1 | `leaudit_documents` | LeAudit 域文档镜像,关联业务文档 | ✅ 已创建 |
|
||||
| 2 | `leaudit_document_files` | 文档文件版本管理 | ✅ 已创建 |
|
||||
| 3 | `leaudit_audit_runs` | 每次处理执行的主索引记录 | ✅ 已创建 |
|
||||
| 4 | `leaudit_artifacts` | OCR/normalize/manifest/markdown/图片等文件产物索引 | ✅ 已创建 |
|
||||
| 5 | `leaudit_rule_results` | 规则级评查结果(逐条规则一行) | ✅ 已创建 |
|
||||
| 6 | `leaudit_field_results` | 字段级抽取结果 | ✅ 已创建 |
|
||||
| 7 | `leaudit_run_metrics` | 各阶段耗时与计数统计 | ✅ 已创建 |
|
||||
| 8 | `leaudit_run_errors` | 各阶段错误与诊断 | ✅ 已创建 |
|
||||
| 9 | `leaudit_rescue_outcomes` | Rescue 补救结果 | ✅ 已创建 |
|
||||
| 10 | `leaudit_rule_sets` | 规则集主表 | ✅ 已创建 |
|
||||
| 11 | `leaudit_rule_versions` | 规则版本表 | ✅ 已创建 |
|
||||
| 12 | `leaudit_rule_type_bindings` | 文档类型与规则集绑定 | ✅ 已创建 |
|
||||
| 13 | `jwt_tokens` | JWT 令牌管理 | ✅ 已创建 |
|
||||
| 14 | `leaudit_document_types` | 文档类型定义 | ✅ 已创建 |
|
||||
| 15 | `leaudit_entry_modules` | 入口模块/导航菜单 | ✅ 已创建 |
|
||||
| 16 | `leaudit_evaluation_point_groups` | 评查点规则组(PID 树形) | ✅ 已创建 |
|
||||
| 17 | `leaudit_evaluation_points` | 规则点/评查点元数据 | ✅ 已创建 |
|
||||
|
||||
> 所有表均含 `create_time` / `update_time` / `delete_time` 三时间戳 + 中文列注释。
|
||||
> 详细建表 SQL 见 `scripts/schema_v2_add_evaluation_tables.sql`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心表结构(文档执行域)
|
||||
|
||||
### 4.1 `leaudit_documents`
|
||||
|
||||
LeAudit 域文档镜像表。通过 `biz_document_id` 关联老系统 `documents.id`。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | bigint PK | 主键,自增 |
|
||||
| `biz_document_id` | bigint UNIQUE | 关联老业务 `documents.id` |
|
||||
| `type_id` | bigint | 文档类型 ID → `leaudit_document_types.id` |
|
||||
| `processing_status` | varchar(64) | waiting / running / completed / failed |
|
||||
| `current_run_id` | bigint | 最新有效 `leaudit_audit_runs.id` |
|
||||
| `create_time` / `update_time` / `delete_time` | timestamptz | 标准时间戳 |
|
||||
|
||||
### 4.2 `leaudit_document_files`
|
||||
|
||||
文档文件表。一份文档可以有多个物理文件版本。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | bigint PK | 主键 |
|
||||
| `document_id` | bigint FK | 关联 `leaudit_documents.id` |
|
||||
| `file_role` | varchar(64) | primary / attachment / scan / ocr_result |
|
||||
| `file_name` / `file_ext` / `mime_type` / `file_size` | — | 文件基本信息 |
|
||||
| `sha256` | varchar(64) | 文件 SHA256 |
|
||||
| `oss_url` | varchar(2048) | OSS 地址(真源) |
|
||||
| `local_path` | varchar(1024) | 本地临时路径 |
|
||||
| `is_active` | boolean | 当前生效文件 |
|
||||
| `created_by` | bigint | 上传者 |
|
||||
|
||||
### 4.3 `leaudit_audit_runs`
|
||||
|
||||
核心追踪表。每次进入 LeAudit 主链生成一条记录。
|
||||
|
||||
| 字段分类 | 字段 | 说明 |
|
||||
|---------|------|------|
|
||||
| **标识** | `id` / `document_id` / `document_file_id` / `run_no` | 主键 + 关联 + 序号 |
|
||||
| **触发** | `trigger_source` / `trigger_user_id` / `task_id` | 触发来源/人/Celery ID |
|
||||
| **状态** | `status` / `phase` | pending→running→completed/failed |
|
||||
| **规则溯源** | `rule_set_id` / `rule_version_id` / `rule_type_id` | 使用的规则版本 |
|
||||
| | `rule_source_oss_url` / `rule_source_sha256` / `rule_local_cache_path` | 规则文件定位 |
|
||||
| **模型快照** | `engine_version` / `llm_provider` / `llm_model` / `vlm_provider` / `vlm_model` / `ocr_provider` / `ocr_model` | 可追溯差异来源 |
|
||||
| **Rescue** | `rescue_mode` / `rescue_applied` | 补救配置与执行标记 |
|
||||
| **结果汇总** | `total_score` / `passed_count` / `failed_count` / `skipped_count` / `result_status` | 总量统计 |
|
||||
| **时间** | `started_at` / `finished_at` / `create_time` / `update_time` | 时间追踪 |
|
||||
|
||||
### 4.4 `leaudit_artifacts`
|
||||
|
||||
统一产物表。记录一次 run 过程中产生的所有文件型中间产物。
|
||||
|
||||
**artifact_type 枚举(20 种)**:`original_doc` / `normalized_doc` / `render_png` / `render_pdf` / `ocr_json` / `extract_json` / `evaluate_json` / `rescue_json` / `quality_report` / `diff_report` / `cross_review` / `merged_result` / `final_report` / `vlm_render` / `vlm_vis_page` / `vlm_vis_subdoc` / `vlm_vis_field` / `vlm_debug` / `rescue_debug` / `pipeline_log`
|
||||
|
||||
### 4.5 `leaudit_rule_results`
|
||||
|
||||
规则级结果表。对齐 LeAudit `EvaluationResult.rules` 的实际字段。
|
||||
|
||||
| 字段分类 | 关键字段 | 说明 |
|
||||
|---------|---------|------|
|
||||
| **归属** | `run_id` / `rule_version_id` / `document_id` | 三重定位 |
|
||||
| **规则标识** | `rule_id` / `rule_name` / `risk` / `score` | 规则基本信息 |
|
||||
| **结果** | `passed` / `status` / `skip_reason` / `confidence` | 判定结果 |
|
||||
| **消息** | `pass_message` / `fail_message` | 通过/失败文案 |
|
||||
| **细节** | `stages` (jsonb) / `extracted_fields` (jsonb) / `field_positions` (jsonb) / `rule_meta` (jsonb) | 结构化详情 |
|
||||
| **整改** | `remediation` (jsonb) / `rescue_applied` / `rescue_passed` | 补救信息 |
|
||||
| **兜底** | `result_payload` (jsonb) | 完整 RuleResult JSON 备份 |
|
||||
|
||||
### 4.6-4.9 辅助表
|
||||
|
||||
- **`leaudit_field_results`**: 字段级抽取结果(value_text / confidence / grounding_score / hard_failed 等)
|
||||
- **`leaudit_run_metrics`**: 各阶段耗时(ocr/normalize/extract/evaluate/rescue_seconds)+ 计数统计
|
||||
- **`leaudit_run_errors`**: 各阶段错误(stage / level / error_code / message / detail_json)
|
||||
- **`leaudit_rescue_outcomes`**: Rescue 补救结果(diagnosis / final_status / llm_calls / vlm_calls)
|
||||
|
||||
---
|
||||
|
||||
## 5. 规则管理域表
|
||||
|
||||
详见 [DSL 规则域表结构设计](dsl_rule_schema_design.md)。
|
||||
|
||||
| 表 | 用途 |
|
||||
|----|------|
|
||||
| `leaudit_rule_sets` | 规则集主表(rule_type / rule_name / current_version_id) |
|
||||
| `leaudit_rule_versions` | 规则版本(oss_url / file_sha256 / metadata_* 快照) |
|
||||
| `leaudit_rule_type_bindings` | 文档类型 ↔ 规则集绑定(binding_mode / priority) |
|
||||
|
||||
---
|
||||
|
||||
## 6. 评查点管理域表
|
||||
|
||||
| 表 | 用途 |
|
||||
|----|------|
|
||||
| `leaudit_evaluation_point_groups` | 评查点规则组(PID 树形层级) |
|
||||
| `leaudit_evaluation_points` | 规则点/评查点(code / score / scoring_config / references_laws) |
|
||||
| `leaudit_document_types` | 文档类型定义(code / classification_keywords / extraction_mode) |
|
||||
| `leaudit_entry_modules` | 入口模块(name / path / areas / icon_path) |
|
||||
|
||||
---
|
||||
|
||||
## 7. 表关系图
|
||||
|
||||
```text
|
||||
leaudit_entry_modules
|
||||
└── leaudit_document_types
|
||||
├── leaudit_documents ── biz_document_id → 老系统 documents
|
||||
│ ├── leaudit_document_files
|
||||
│ └── leaudit_audit_runs
|
||||
│ ├── leaudit_artifacts (N)
|
||||
│ ├── leaudit_run_metrics (1)
|
||||
│ ├── leaudit_run_errors (N)
|
||||
│ ├── leaudit_rule_results (N)
|
||||
│ ├── leaudit_field_results (N)
|
||||
│ └── leaudit_rescue_outcomes (N)
|
||||
└── leaudit_rule_type_bindings
|
||||
└── leaudit_rule_sets
|
||||
├── leaudit_rule_versions
|
||||
└── leaudit_evaluation_point_groups
|
||||
└── leaudit_evaluation_points
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. OSS 路径约定
|
||||
|
||||
详见 [基础设施重设计](infrastructure_redesign.md) 第一节。
|
||||
|
||||
```text
|
||||
# 业务文档
|
||||
bdocs/{region}/{type_code}/{doc_id}/{version}/{file_role}.{ext}
|
||||
|
||||
# 评查产物
|
||||
artifacts/{region}/{run_id}/{artifact_type}/{detail}.{ext}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 最终结论
|
||||
|
||||
- 所有表 `leaudit_*` 前缀,与老系统完全隔离
|
||||
- `leaudit_audit_runs` 是每次处理的唯一追踪单位
|
||||
- `leaudit_artifacts` 统一管理所有文件产物,数据库只存索引
|
||||
- `leaudit_rule_results` 粒度到逐条规则,结构与 LeAudit `RuleResult` 对齐
|
||||
- 数据库已全部建表完成,含中文注释和三时间戳
|
||||
@@ -0,0 +1,241 @@
|
||||
# LeAudit DSL 规则域表结构设计
|
||||
|
||||
> **状态**: 核心表已落地 `leaudit_platform` 数据库
|
||||
> **相关**: [文档域表设计](document_schema_design.md) | [Bridge 目录设计](bridge_directory_design.md)
|
||||
|
||||
## 1. 设计目标
|
||||
|
||||
- `LeAudit` 运行时仍然按 YAML 规则文件执行
|
||||
- 规则文件的持久真相源不再依赖固定本地目录
|
||||
- 数据库存储规则元数据、版本、状态、OSS 地址
|
||||
- YAML 正文作为文件产物放在 OSS
|
||||
- 运行时根据数据库当前激活版本下载到本地临时文件,再按 `LeAudit` 原逻辑加载
|
||||
- 支持规则编辑、校验、发布、回滚、审计
|
||||
|
||||
## 2. 核心原则
|
||||
|
||||
### 2.1 规则真相源是 "OSS 文件 + 数据库索引"
|
||||
|
||||
- 执行规则内容:YAML 文件
|
||||
- 持久位置:OSS
|
||||
- 数据库:记录元数据、版本、OSS 地址、激活状态
|
||||
|
||||
### 2.2 不让数据库直接变成规则执行解释器
|
||||
|
||||
数据库不替代 YAML/DSL 本身,不把规则拆碎存成"行式规则引擎配置"。
|
||||
|
||||
原因:
|
||||
|
||||
- 会偏离 `LeAudit` 原逻辑
|
||||
- 会抬高规则编辑和发布成本
|
||||
- 会让 DSL 的可读性和兼容性变差
|
||||
|
||||
### 2.3 规则编辑与规则执行分离
|
||||
|
||||
- 编辑阶段:编辑 YAML 文本、校验、上传 OSS、写版本记录
|
||||
- 执行阶段:查 DB 元数据、下 OSS 文件、本地临时加载执行
|
||||
|
||||
## 3. 已落地核心表
|
||||
|
||||
| # | 表名 | 用途 | 状态 |
|
||||
|---|------|------|------|
|
||||
| 1 | `leaudit_rule_sets` | 规则集主表 | ✅ |
|
||||
| 2 | `leaudit_rule_versions` | 规则版本表 | ✅ |
|
||||
| 3 | `leaudit_rule_type_bindings` | 文档类型与规则集绑定 | ✅ |
|
||||
| 4 | `leaudit_evaluation_point_groups` | 评查点规则组(PID 树形) | ✅ |
|
||||
| 5 | `leaudit_evaluation_points` | 规则点/评查点元数据 | ✅ |
|
||||
|
||||
待补充(后续阶段):
|
||||
|
||||
| # | 表名 | 用途 |
|
||||
|---|------|------|
|
||||
| 6 | `leaudit_rule_file_artifacts` | 规则文件产物(校验报告、导出包等) |
|
||||
| 7 | `leaudit_rule_publish_logs` | 规则发布/回滚审计日志 |
|
||||
| 8 | `leaudit_rule_validation_logs` | 规则校验日志 |
|
||||
|
||||
## 4. 核心表结构
|
||||
|
||||
### 4.1 `leaudit_rule_sets`
|
||||
|
||||
规则集主表,描述一个稳定的业务规则集实体。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | bigint PK | 主键 |
|
||||
| `rule_type` | varchar(128) | 业务规则类型编码,对应 DSL `metadata.type_id` |
|
||||
| `rule_name` | varchar(512) | 规则集名称 |
|
||||
| `domain_type` | varchar(64) | contract / admin_license / legal_doc |
|
||||
| `description` | text | 规则集描述 |
|
||||
| `entry_module` | varchar(64) | 对应业务入口模块标识 |
|
||||
| `current_version_id` | bigint | 当前激活版本 → `leaudit_rule_versions.id` |
|
||||
| `status` | varchar(32) | draft / active / deprecated / archived |
|
||||
| `is_builtin` | boolean | 是否内置规则集(内置不可删除) |
|
||||
| `owner_user_id` | bigint | 负责人 |
|
||||
| `create_time` / `update_time` / `delete_time` | timestamptz | 标准时间戳 |
|
||||
|
||||
### 4.2 `leaudit_rule_versions`
|
||||
|
||||
规则版本表,每次编辑/发布都生成一条新版本。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | bigint PK | 主键 |
|
||||
| `rule_set_id` | bigint FK | → `leaudit_rule_sets.id` |
|
||||
| `version_no` | varchar(64) | 语义版本号 |
|
||||
| `version_seq` | int | 顺序号(rule_set 内递增) |
|
||||
| `status` | varchar(32) | draft / published / deprecated / rollback |
|
||||
| `source_type` | varchar(32) | oss_yaml / local_yaml / db_snippet |
|
||||
| `dsl_format` | varchar(32) | yaml / json |
|
||||
| `oss_url` | varchar(2048) | YAML 文件 OSS 地址 |
|
||||
| `file_sha256` / `file_size` | — | 文件完整性 |
|
||||
| `metadata_type_id` / `metadata_name` / `metadata_version` | — | DSL metadata 快照 |
|
||||
| `change_note` | text | 变更说明 |
|
||||
| `editor_user_id` / `publisher_user_id` / `published_at` | — | 编辑/发布信息 |
|
||||
| `create_time` / `update_time` | timestamptz | 标准时间戳 |
|
||||
|
||||
### 4.3 `leaudit_rule_type_bindings`
|
||||
|
||||
文档类型和规则集的绑定表。
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | bigint PK | 主键 |
|
||||
| `doc_type_id` | bigint | → `leaudit_document_types.id` |
|
||||
| `doc_type_code` | varchar(128) | 文档类型编码(冗余快速匹配) |
|
||||
| `rule_set_id` | bigint FK | → `leaudit_rule_sets.id` |
|
||||
| `binding_mode` | varchar(32) | explicit / wildcard / fallback |
|
||||
| `priority` | int | 优先级(越大越高) |
|
||||
| `is_active` | boolean | 是否激活 |
|
||||
| `note` | text | 备注 |
|
||||
| `create_time` / `update_time` | timestamptz | 标准时间戳 |
|
||||
|
||||
## 5. 规则执行时序
|
||||
|
||||
### 5.1 运行时加载(当前流程)
|
||||
|
||||
```text
|
||||
文档类型确定
|
||||
→ leaudit_rule_type_bindings 查绑定
|
||||
→ leaudit_rule_sets.current_version_id
|
||||
→ leaudit_rule_versions.oss_url
|
||||
→ 下载 YAML 到本地临时文件
|
||||
→ leaudit.dsl.loader.load_rules_file()
|
||||
→ 进入执行链
|
||||
```
|
||||
|
||||
### 5.2 编辑保存(后续实现)
|
||||
|
||||
```text
|
||||
前端提交 YAML 文本
|
||||
→ YAML 语法校验
|
||||
→ LeAudit DSL 语义校验
|
||||
→ 上传 YAML 到 OSS
|
||||
→ 写 leaudit_rule_versions
|
||||
→ 写 leaudit_rule_validation_logs(可选)
|
||||
```
|
||||
|
||||
### 5.3 发布生效(后续实现)
|
||||
|
||||
```text
|
||||
选择某版本发布
|
||||
→ 更新 leaudit_rule_sets.current_version_id
|
||||
→ 写 leaudit_rule_publish_logs(可选)
|
||||
→ 清理/失效本地规则缓存
|
||||
```
|
||||
|
||||
## 6. 与 OSS 的配合方式
|
||||
|
||||
### 6.1 路径建议
|
||||
|
||||
规则文件必须版本化,不能固定覆盖同一路径:
|
||||
|
||||
```text
|
||||
oss://leaudit/rules/{rule_type}/{version_no}/rules.yaml
|
||||
oss://leaudit/rules/{rule_type}/{version_no}/validation_report.json
|
||||
```
|
||||
|
||||
### 6.2 为什么不能只用固定路径
|
||||
|
||||
如果始终覆盖同一个 `rules.yaml`:
|
||||
|
||||
- 难回滚
|
||||
- 难追踪历史执行结果对应的规则版本
|
||||
- 本地缓存容易脏
|
||||
- 无法做结果回放和审计
|
||||
|
||||
## 7. 种子数据:从本地 `rules/` 到 OSS + DB
|
||||
|
||||
当前项目 `rules/` 目录下已有 20+ 个类型目录、每个包含可用的 `rules.yaml`。需一次性导入。
|
||||
|
||||
### 7.1 初始化脚本逻辑
|
||||
|
||||
```python
|
||||
# 伪代码:把 rules/ 导入 OSS + DB
|
||||
for each rules_dir in rules/:
|
||||
yaml_text = read(rules_dir / "rules.yaml")
|
||||
rules_file = parse_rules_yaml_text(yaml_text)
|
||||
|
||||
version_no = rules_file.metadata.version
|
||||
oss_url = f"oss://leaudit/rules/{rules_dir.name}/{version_no}/rules.yaml"
|
||||
upload_to_oss(oss_url, yaml_text)
|
||||
|
||||
rule_set = upsert_rule_set(
|
||||
rule_type=rules_dir.name,
|
||||
rule_name=rules_file.metadata.name,
|
||||
domain_type=classify_domain(rules_file),
|
||||
status="active",
|
||||
is_builtin=True,
|
||||
)
|
||||
|
||||
version = insert_rule_version(
|
||||
rule_set_id=rule_set.id,
|
||||
version_no=version_no,
|
||||
oss_url=oss_url,
|
||||
...
|
||||
)
|
||||
|
||||
update_rule_set(rule_set.id, current_version_id=version.id)
|
||||
|
||||
# 绑定文档类型(如果已知)
|
||||
doc_type_id = resolve_doc_type_id(rules_dir.name)
|
||||
if doc_type_id:
|
||||
insert_type_binding(...)
|
||||
```
|
||||
|
||||
### 7.2 初始化后的本地 `rules/` 处置
|
||||
|
||||
**推荐方案**:保留为只读紧急回退备份,标记为不再接受编辑。如果 OSS 不可用或规则全损,本地至少有一套可用的规则副本。
|
||||
|
||||
### 7.3 `_TYPE_ID_RULES_MAP` 硬编码过渡
|
||||
|
||||
`leaudit_bridge/tasks.py` 中的硬编码映射:
|
||||
|
||||
```python
|
||||
_TYPE_ID_RULES_MAP: dict[int, str] = {
|
||||
3: "行政处罚",
|
||||
}
|
||||
```
|
||||
|
||||
过渡策略:
|
||||
|
||||
1. **阶段 A**:保留硬编码为 fallback,同时 `leaudit_rule_type_bindings` 表已有数据
|
||||
2. **阶段 B**:`_resolve_rules_path()` 先查 `leaudit_rule_type_bindings`,未命中 fallback 到硬编码
|
||||
3. **阶段 C**:所有绑定入库后,删除 `_TYPE_ID_RULES_MAP`
|
||||
|
||||
## 8. 推荐接口能力(后续实现)
|
||||
|
||||
- `GET /api/leaudit/rule-sets`
|
||||
- `GET /api/leaudit/rule-sets/{rule_type}`
|
||||
- `GET /api/leaudit/rule-sets/{rule_type}/versions`
|
||||
- `GET /api/leaudit/rule-versions/{version_id}/content`
|
||||
- `POST /api/leaudit/rule-sets/{rule_type}/validate`
|
||||
- `POST /api/leaudit/rule-sets/{rule_type}/versions`
|
||||
- `POST /api/leaudit/rule-sets/{rule_type}/publish`
|
||||
- `POST /api/leaudit/rule-sets/{rule_type}/rollback`
|
||||
|
||||
## 9. 最终结论
|
||||
|
||||
- 规则内容继续保持 YAML 形态
|
||||
- 持久真相源从固定本地文件改为 OSS 文件
|
||||
- 数据库存规则元数据、版本、状态和 OSS 地址
|
||||
- 运行时通过 "DB → OSS → 本地临时 YAML → LeAudit loader" 保持 `LeAudit` 原逻辑不变
|
||||
@@ -0,0 +1,459 @@
|
||||
# LeAudit Platform — 基础设施深度重设计方案
|
||||
|
||||
> 基于老项目 docauditai 深度逆向分析,对标新平台 leaudit-platform 重新规划。
|
||||
|
||||
---
|
||||
|
||||
## 一、文件存储 OSS 路径设计
|
||||
|
||||
### 1.1 老项目路径模式
|
||||
|
||||
```
|
||||
documents/{instance_name}/{doc_type_name}/{year}/{中文日期}/{doc_dir}/{filename}
|
||||
|
||||
实例: documents/mz/行政许可卷宗/2026/04月27日/采购合同_14时30分25秒/采购合同.pdf
|
||||
```
|
||||
|
||||
**老项目核心问题:**
|
||||
- 中文路径(区域名、文档类型名、日期)— URL 编码后不可读,程序解析困难
|
||||
- `instance_name` 用区域缩写(mz/yf/jy/cz/sj),耦合 `INSTANCE_NAME` 环境变量
|
||||
- 纯时间戳区分版本,无语义化版本号,查找历史版本全靠 DB 反查
|
||||
- 业务文档和评查产物混在一个路径空间,无类型区分
|
||||
- 无文件级权限元数据,拿到 presigned URL 即可访问
|
||||
|
||||
### 1.2 新平台路径设计
|
||||
|
||||
#### 两级路径体系
|
||||
|
||||
```
|
||||
┌── 业务文档 (Business Documents) ── 用户上传的原始文件
|
||||
│ bdocs/{region}/{type_code}/{doc_id}/{version}/{file_role}.{ext}
|
||||
│ bdocs/gd-mz/contract.entrust/10042/v1/primary.pdf
|
||||
│ bdocs/gd-mz/contract.entrust/10042/v1/attachment_a.docx
|
||||
│
|
||||
└── 评查产物 (Audit Artifacts) ── 引擎产出的中间/最终文件
|
||||
artifacts/{region}/{run_id}/{artifact_type}/{detail}.{ext}
|
||||
artifacts/gd-mz/5801/ocr_result/ocr.json
|
||||
artifacts/gd-mz/5801/render_png/page_003.png
|
||||
artifacts/gd-mz/5801/final_report/report.pdf
|
||||
```
|
||||
|
||||
#### 路径段规范
|
||||
|
||||
| 段 | 含义 | 格式 | 示例 |
|
||||
|---|---|---|---|
|
||||
| `bdocs` / `artifacts` | 顶层命名空间 | 固定 | `bdocs` = 业务文档, `artifacts` = 评查产物 |
|
||||
| `{region}` | 区域代码 | `{province}-{city}` | `gd-mz` (广东-梅州), `gd-yf` (云浮), `gd-jy` (揭阳), `gd-cz` (潮州), `gd-sj` (省级) |
|
||||
| `{type_code}` | 文档类型编码 | DSL type_id | `contract.entrust`, `admin_license.new` |
|
||||
| `{doc_id}` | 文档 ID | DB 主键 | `10042` |
|
||||
| `{version}` | 版本号 | `v{n}` | `v1`, `v2`, `v3` |
|
||||
| `{file_role}` | 文件角色 | 枚举 | `primary` / `attachment_a` / `scan` / `ocr_text` |
|
||||
| `{run_id}` | 评查运行 ID | DB 主键 | `5801` |
|
||||
| `{artifact_type}` | 产物类型 | 枚举(20种) | `ocr_result`, `extract_json`, `evaluate_json`, `final_report` |
|
||||
| `{detail}` | 产物详情 | 自由格式 | `page_003.png`, `rule_R001.json` |
|
||||
|
||||
#### 关键设计决策
|
||||
|
||||
1. **全英文路径** — 无 URL 编码问题,日志/调试可直接阅读
|
||||
2. **区域用 `{province}-{city}` 代码** — 比旧系统 `mz`/`yf` 更明确,未来跨省扩展无歧义
|
||||
3. **`doc_id` 入路径** — 路径即自描述,无需查 DB 即可知道文件归属
|
||||
4. **显式 `{version}` 段** — 版本号在路径中可见,支持 v1/v2/v3 并存
|
||||
5. **产物按 `run_id` 组织** — 一次评查的所有产物在同一目录下,清理/归档方便
|
||||
|
||||
---
|
||||
|
||||
## 二、同文件多版本机制
|
||||
|
||||
### 2.1 老项目做法
|
||||
|
||||
- 每次上传用时间戳生成新目录 → 物理隔离
|
||||
- DB 中按 `(name, type_id)` 分组,`create_time DESC` 取最新
|
||||
- 版本号在查询时动态计算,不存储
|
||||
- 旧版本永久保留,无清理策略
|
||||
|
||||
### 2.2 新平台版本设计
|
||||
|
||||
#### 版本存储模型
|
||||
|
||||
```
|
||||
bdocs/gd-mz/contract.entrust/10042/
|
||||
├── v1/primary.pdf ← 首次上传
|
||||
├── v2/primary.pdf ← 第二次上传(修正版)
|
||||
└── v3/primary.pdf ← 第三次上传(最终版)
|
||||
```
|
||||
|
||||
#### 版本元数据
|
||||
|
||||
在 `leaudit_document_files` 表中记录:
|
||||
|
||||
```python
|
||||
class LeauditDocumentFile(Base):
|
||||
document_id: int # 文档 ID
|
||||
version_no: int # 版本号 (1, 2, 3...)
|
||||
version_seq: str # 语义版本 "v1", "v2"
|
||||
file_role: str # primary / attachment / ...
|
||||
oss_url: str # 完整 OSS 路径
|
||||
sha256: str # 文件哈希
|
||||
is_current: bool # 是否当前活跃版本
|
||||
replaced_by_id: int # 被哪个新版本取代(版本链)
|
||||
upload_user_id: int # 上传者
|
||||
change_note: str # 变更说明
|
||||
```
|
||||
|
||||
#### 版本生命周期
|
||||
|
||||
```
|
||||
upload v1 → v1.is_current = True
|
||||
upload v2 → v1.is_current = False, v1.replaced_by_id = v2.id
|
||||
v2.is_current = True
|
||||
upload v3 → v2.is_current = False, v2.replaced_by_id = v3.id
|
||||
v3.is_current = True
|
||||
```
|
||||
|
||||
- 所有旧版本文件保留在 OSS(不物理删除)
|
||||
- 版本链可在前端展示为"历史版本"列表
|
||||
- 回滚 = 将指定旧版本标记为 `is_current = True`(无需复制文件)
|
||||
|
||||
#### 与评查运行的关联
|
||||
|
||||
每个 `leaudit_audit_runs` 记录锁定使用的版本:
|
||||
|
||||
```
|
||||
audit_runs.document_file_id → 指向具体版本的 leaudit_document_files.id
|
||||
```
|
||||
|
||||
这样即使文档后来更新到 v3,历史评查记录仍然精确指向当时的 v1 文件。
|
||||
|
||||
---
|
||||
|
||||
## 三、多地区文件查看权限 & 区域隔离
|
||||
|
||||
### 3.1 老项目做法
|
||||
|
||||
- 单一 bucket,路径前缀 `{instance_name}` 区分区域
|
||||
- 文件访问无权限校验(拿到 presigned URL 即访问)
|
||||
- 隔离依赖 `INSTANCE_NAME` 环境变量 → 只在 API 层有效
|
||||
|
||||
### 3.2 新平台权限模型
|
||||
|
||||
#### 三层权限控制
|
||||
|
||||
```
|
||||
Layer 1: 区域隔离 (Region Isolation)
|
||||
└── 用户所属区域决定可见文档范围
|
||||
省级 (gd-sj) 用户可看所有区域
|
||||
地市级 (gd-mz) 用户只能看本区域
|
||||
|
||||
Layer 2: 文件级权限 (Document-Level)
|
||||
└── 基于 RBAC 的文档访问控制
|
||||
document:read:own → 本人上传的
|
||||
document:read:all → 本区域全部的
|
||||
document:read:cross → 跨区域查看
|
||||
|
||||
Layer 3: 产物级权限 (Artifact-Level)
|
||||
└── 评查产物按 run_id 隔离
|
||||
产物继承其文档的权限策略
|
||||
临时产物 (rescue debug log) 仅内部系统可读
|
||||
```
|
||||
|
||||
#### 权限检查流程
|
||||
|
||||
```
|
||||
请求: GET /api/v2/documents/10042/files/v1/primary.pdf
|
||||
|
||||
1. JWT 解析 → 获取 user_id, user_role, user_region
|
||||
2. 区域检查: user_region == 'gd-sj' OR user_region == 文档的区域
|
||||
3. 权限检查: CheckPermission(user_id, "document:read:own")
|
||||
或通过 GRANT/DENY 通配符匹配
|
||||
4. 通过 → 生成 presigned URL (TTL 10分钟)
|
||||
5. 拒绝 → 返回 403
|
||||
```
|
||||
|
||||
#### 跨区域访问
|
||||
|
||||
```python
|
||||
# 省级用户发起跨区域评查
|
||||
POST /api/v2/documents/cross-review
|
||||
{
|
||||
"document_id": 10042, # gd-mz 的文档
|
||||
"reviewer_region": "gd-yf", # 让云浮审核员查看
|
||||
"permission": "document:read:cross"
|
||||
}
|
||||
→ 系统为 gd-yf 区域的审核员创建临时访问授权
|
||||
→ 记录到 leaudit_cross_access_logs
|
||||
→ 临时授权在评查完成后自动过期
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、队列机制重设计
|
||||
|
||||
### 4.1 老项目分析
|
||||
|
||||
**架构:**
|
||||
```
|
||||
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ API Server │────▶│ Redis Queue │────▶│ Celery Worker│
|
||||
│ (8000-8873) │ │ (单队列) │ │ (8线程, 4并发)│
|
||||
└─────────────┘ └──────────────┘ └──────────────┘
|
||||
│ │
|
||||
└── source_port ────────────────────────▶│ os.environ 切换
|
||||
│ 线程级隔离
|
||||
```
|
||||
|
||||
**关键机制:**
|
||||
- 所有区域共享一个 Redis 队列
|
||||
- `source_port` 参数 → worker 在任务执行时切换环境变量
|
||||
- Redis 信号量限制全局并发为 4
|
||||
- Thread pool (8 线程) → 4 个实际并发 + 4 个 I/O 等待
|
||||
|
||||
**问题:**
|
||||
- 环境变量切换是脆弱的状态管理(线程安全问题,需 threading.local 补偿)
|
||||
- 单一队列无优先级区分(紧急任务和批处理同队列)
|
||||
- 信号量修复依赖定时任务(有窗口期泄漏风险)
|
||||
|
||||
### 4.2 新平台队列设计
|
||||
|
||||
#### 多队列架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ Redis │
|
||||
│ │
|
||||
│ leaudit:queue:high (优先级高) │
|
||||
│ leaudit:queue:default (普通) │
|
||||
│ leaudit:queue:batch (批量/低优先级) │
|
||||
│ leaudit:queue:system (系统维护) │
|
||||
│ │
|
||||
│ leaudit:semaphore:global (并发控制) │
|
||||
│ leaudit:semaphore:vlm (VLM并发) │
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 任务路由(不再用 source_port)
|
||||
|
||||
```python
|
||||
# 新方案:在任务提交时直接带上下文,而非运行时切换环境变量
|
||||
@celery_app.task(
|
||||
bind=True,
|
||||
queue="leaudit:queue:default",
|
||||
time_limit=1800,
|
||||
soft_time_limit=1500,
|
||||
max_retries=3,
|
||||
default_retry_delay=60,
|
||||
)
|
||||
async def leaudit_process_document(
|
||||
self,
|
||||
document_id: int,
|
||||
run_id: int,
|
||||
region: str, # gd-mz, gd-yf... (替代 source_port)
|
||||
config: dict, # 运行时配置快照
|
||||
user_id: int | None = None,
|
||||
):
|
||||
"""文档评查任务 - 上下文通过参数传递,不依赖环境变量"""
|
||||
...
|
||||
```
|
||||
|
||||
**改进点:**
|
||||
1. **显式参数替代环境变量** — `region` + `config` 直接传参,线程安全,可测试
|
||||
2. **优先级队列** — 用户手动触发的走 high,API 自动触发的走 default,批量导入走 batch
|
||||
3. **去 source_port** — 不再需要 `set_instance_environment` / `restore_instance_environment` 这种脆弱模式
|
||||
4. **配置快照** — 任务创建时拍下完整配置(LLM model、OCR endpoint 等),即使配置后续变更也不影响已提交任务
|
||||
|
||||
#### 任务类型与路由
|
||||
|
||||
| 任务 | 队列 | 优先级 | 并发限制 | 超时 |
|
||||
|---|---|---|---|---|
|
||||
| 用户手动评查 | `high` | 8 | 全局 4 | 30min |
|
||||
| API 自动评查 | `default` | 5 | 全局 4 | 30min |
|
||||
| 批量导入 | `batch` | 3 | 全局 2 | 60min |
|
||||
| 交叉评查 | `default` | 5 | 全局 4 | 30min |
|
||||
| 信号量修复 | `system` | 10 | 无限制 | 10s |
|
||||
| 健康检查 | `system` | 10 | 无限制 | 5s |
|
||||
| 统计更新 | `batch` | 1 | 全局 1 | 5min |
|
||||
|
||||
#### 并发控制改进
|
||||
|
||||
```python
|
||||
# 新方案:上下文管理器 + 自动释放
|
||||
class TaskConcurrencyLimiter:
|
||||
"""基于 Redis 的并发限制器,使用 Lua 脚本保证原子性"""
|
||||
|
||||
async def acquire(self, semaphore_key: str, max_concurrency: int, timeout: float) -> str:
|
||||
"""原子获取许可 → 返回 permit_id"""
|
||||
...
|
||||
|
||||
async def release(self, semaphore_key: str, permit_id: str):
|
||||
"""原子释放许可"""
|
||||
...
|
||||
|
||||
@asynccontextmanager
|
||||
async def limit(self, semaphore_key: str, max_concurrency: int):
|
||||
permit_id = await self.acquire(semaphore_key, max_concurrency, timeout=300)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
await self.release(semaphore_key, permit_id)
|
||||
```
|
||||
|
||||
- Redis Lua 脚本替代 WATCH/MULTI/EXEC(减少乐观锁冲突)
|
||||
- 上下文管理器替代手动 acquire/release(防泄漏)
|
||||
- 无定时修复任务(Lua 原子操作 → 不会出现不一致状态)
|
||||
|
||||
---
|
||||
|
||||
## 五、缓存机制重设计
|
||||
|
||||
### 5.1 老项目分析
|
||||
|
||||
**三层 Redis 使用:**
|
||||
|
||||
| 层 | 用途 | 数据 | TTL |
|
||||
|---|---|---|---|
|
||||
| 权限缓存 | RBAC 鉴权加速 | user:permissions:*, rbac:routes:* | 5-30 min |
|
||||
| Token 黑名单 | JWT 吊销 | token:revoked:{jti} | 最长 24h |
|
||||
| 并发控制 | 信号量 | semaphore:* | 1800s 许可 TTL |
|
||||
|
||||
**问题:**
|
||||
- 5+ 个独立 Redis 连接(无统一连接池)
|
||||
- `fastapi_cache2` 已初始化但从未使用
|
||||
- 区域隔离依赖 Redis DB 号切换(配置复杂,易出错)
|
||||
- 无 API 响应缓存(每次请求都查 DB)
|
||||
- 统计缓存 TTL=20min,与权限 TTL 不一致
|
||||
|
||||
### 5.2 新平台缓存架构
|
||||
|
||||
#### 统一连接池
|
||||
|
||||
```python
|
||||
# fastapi_common/fastapi_common_cache/redis_pool.py
|
||||
class RedisPool:
|
||||
"""全局 Redis 连接池 — 所有模块共用"""
|
||||
|
||||
_instance: 'RedisPool' = None
|
||||
|
||||
def __init__(self, config: dict):
|
||||
# 异步连接池 (主)
|
||||
self.async_pool = aioredis.ConnectionPool(
|
||||
max_connections=50,
|
||||
socket_connect_timeout=5,
|
||||
socket_keepalive=True,
|
||||
retry_on_timeout=True,
|
||||
)
|
||||
# 同步客户端 (Celery worker 用)
|
||||
self.sync_client = redis.Redis(...)
|
||||
```
|
||||
|
||||
**关键决策:不再用 Redis DB 号做区域隔离** — 改用 key prefix:
|
||||
```
|
||||
# 旧: REDIS_DB=12 (gd-mz), REDIS_DB=13 (gd-yf)
|
||||
# 新: 所有区域共享 DB 0, 用 prefix 区分
|
||||
leaudit:gd-mz:permission:user:12345
|
||||
leaudit:gd-yf:permission:user:12345
|
||||
leaudit:gd-sj:cache:stats:homepage
|
||||
```
|
||||
|
||||
好处:统一监控、可在单次 SCAN 中查询跨区域数据、配置简单(只有一个 DB 号)。
|
||||
|
||||
#### 缓存分层策略
|
||||
|
||||
```
|
||||
L1: 进程内存缓存 (Python dict / lru_cache)
|
||||
├── 配置数据 (规则集列表、引擎版本) — 启动加载,手动刷新
|
||||
└── DSL 规则文件内容 — 本地文件缓存 + mtime 检测
|
||||
|
||||
L2: Redis 缓存 (TTL 驱动)
|
||||
├── 权限数据: 5 min TTL (变化频率低)
|
||||
├── Token 黑名单: 按 JWT exp 计算,最长 24h
|
||||
├── 统计聚合: 10-30 min TTL (计算开销大)
|
||||
└── API 响应: 1-5 min TTL (热点接口)
|
||||
|
||||
L3: 分布式锁
|
||||
└── 并发控制信号量 (Lua 原子操作)
|
||||
```
|
||||
|
||||
#### Cache Key 命名规范
|
||||
|
||||
```
|
||||
leaudit:{region}:{domain}:{entity_type}:{entity_id}
|
||||
└── 空 = 全局共享
|
||||
|
||||
# 权限
|
||||
leaudit:gd-mz:perm:user:12345 # 用户权限集
|
||||
leaudit:gd-mz:perm:user:12345:doc:read # 单条权限检查
|
||||
|
||||
# Token
|
||||
leaudit:global:token:revoked:{jti} # 全局共享
|
||||
|
||||
# 统计
|
||||
leaudit:gd-mz:stats:homepage:{user_id} # 首页统计
|
||||
leaudit:gd-sj:stats:daily:2026-04-27 # 日报
|
||||
|
||||
# 并发
|
||||
leaudit:global:sem:task:permits # 任务并发信号量
|
||||
leaudit:global:sem:vlm:permits # VLM 并发信号量
|
||||
|
||||
# API 响应缓存
|
||||
leaudit:gd-mz:api:documents:list:{hash} # 文档列表
|
||||
```
|
||||
|
||||
#### 缓存失效策略
|
||||
|
||||
| 触发器 | 清理动作 | 粒度 |
|
||||
|---|---|---|
|
||||
| 用户角色变更 | `SCAN leaudit:*:perm:user:{uid}` → DEL | 精确 |
|
||||
| 规则集发布新版本 | `DEL leaudit:*:perm:role:*` (所有角色) | 全局 |
|
||||
| Token 吊销 | `SETEX token:revoked:{jti} TTL 1` | 精确 |
|
||||
| 文档状态变更 | `DEL leaudit:{region}:stats:*` | 区域 |
|
||||
| 配置变更 | `DEL leaudit:global:config:*` | 全局 |
|
||||
|
||||
#### 不缓存的数据
|
||||
|
||||
- **评查结果详情** — 每次评查结果不同,缓存无意义
|
||||
- **文档文件内容** — 在 OSS,不在缓存
|
||||
- **实时队列状态** — 直接读 Redis List,不做额外缓存
|
||||
- **DSL 规则执行中间结果** — 一次性的,不需要缓存
|
||||
|
||||
---
|
||||
|
||||
## 六、整体基础设施拓扑
|
||||
|
||||
```
|
||||
┌──────────────────────────┐
|
||||
│ PostgreSQL │
|
||||
│ nas.7bm.co:54302 │
|
||||
│ leaudit_platform │
|
||||
│ (17 tables) │
|
||||
└──────────┬───────────────┘
|
||||
│
|
||||
┌──────────────┐ ┌──────────┴───────────────┐ ┌──────────────┐
|
||||
│ MinIO OSS │ │ FastAPI App │ │ Redis │
|
||||
│ │◀───│ (port 8000-8873) │───▶│ │
|
||||
│ bdocs/ │ │ │ │ Queue │
|
||||
│ artifacts/ │ │ Controller→Service→Model │ │ Cache │
|
||||
│ │ │ Bridge→LeAudit Engine │ │ Semaphore │
|
||||
└──────────────┘ └──────────────────────────┘ └──────────────┘
|
||||
│
|
||||
┌──────────┴───────────────┐
|
||||
│ Celery Worker(s) │
|
||||
│ (thread pool) │
|
||||
│ Pipeline run │
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、实施优先级
|
||||
|
||||
| 优先级 | 任务 | 依赖 |
|
||||
|---|---|---|
|
||||
| **P0** | 配置文件补充:Redis/OSS/LLM/VLM/OCR 真实值 | app.toml |
|
||||
| **P0** | OSS 路径工具类 `oss_path_utils.py` | 上述路径规范 |
|
||||
| **P0** | Redis 统一连接池 `redis_pool.py` | Redis 配置 |
|
||||
| **P1** | 文件上传流程适配新 OSS 路径 | oss_path_utils |
|
||||
| **P1** | Celery 任务路由改造(参数化替代 source_port) | Redis 连接池 |
|
||||
| **P1** | 并发控制器 Lua 脚本实现 | Redis |
|
||||
| **P1** | 权限缓存对接 PermissionService | Redis 缓存层 |
|
||||
| **P2** | API 响应缓存(热点接口) | Redis 缓存层 |
|
||||
| **P2** | 多版本文件管理 | 文件上传流程 |
|
||||
| **P3** | 跨区域访问审计日志 | 权限系统 |
|
||||
@@ -0,0 +1,153 @@
|
||||
# LeAudit 处理逻辑说明
|
||||
|
||||
> 描述 leaudit 引擎的完整流水线阶段,新老项目通用。
|
||||
|
||||
## 1. 主链概览
|
||||
|
||||
`leaudit` 的完整处理链不是单一 OCR 流程,而是一条完整评查流水线:
|
||||
|
||||
```text
|
||||
文件输入
|
||||
→ normalize
|
||||
→ rules resolve
|
||||
→ extract
|
||||
→ determine phase
|
||||
→ evaluate
|
||||
→ rescue
|
||||
→ persist / adapt result
|
||||
```
|
||||
|
||||
## 2. 各阶段说明
|
||||
|
||||
### 2.1 normalize
|
||||
|
||||
职责:
|
||||
|
||||
- 文档解析
|
||||
- OCR 或文本提取
|
||||
- 文档分类
|
||||
- 案卷分段
|
||||
- 印章/签名增强
|
||||
- 统一形成归一化结果对象
|
||||
|
||||
产出:
|
||||
|
||||
- `normalized_doc`
|
||||
- 原始文本
|
||||
- 分类结果
|
||||
- 视觉清单(VisualManifest)
|
||||
- 子文档结构
|
||||
|
||||
### 2.2 rules resolve
|
||||
|
||||
职责:
|
||||
|
||||
- 按输入文档类型或分类结果找到 DSL 规则文件
|
||||
- 解析成 `RulesFile`
|
||||
|
||||
leaudit-platform 做法:
|
||||
|
||||
- 由 bridge 层显式完成规则映射(`rules_loader.py`)
|
||||
- 规则从 OSS 下载到本地临时文件后加载
|
||||
- 支持 `leaudit_rule_type_bindings` 表查绑定 → `leaudit_rule_versions.oss_url` 下载
|
||||
|
||||
### 2.3 extract
|
||||
|
||||
职责:
|
||||
|
||||
- 按 schema 分组抽取字段
|
||||
- 进行 null-field retry / deep retry
|
||||
- hydrate 类型化
|
||||
- multi-entity 展开
|
||||
- derived 字段计算
|
||||
- post-hoc grounding
|
||||
- seal/signature 补充抽取
|
||||
|
||||
产出:
|
||||
|
||||
- 结构化字段
|
||||
- 多实体字段
|
||||
- derived 字段
|
||||
- 抽取错误列表
|
||||
|
||||
### 2.4 determine phase
|
||||
|
||||
职责:
|
||||
|
||||
- 识别文档属于 `draft` 还是 `executed`
|
||||
- 决定后续哪些规则参与执行
|
||||
|
||||
注意:
|
||||
|
||||
- `leaudit` 的策略是先用 `executed` 全量抽取,再反向判定 phase
|
||||
- 接入时不要外层再强行重复做另一套 phase 判定逻辑
|
||||
|
||||
### 2.5 evaluate
|
||||
|
||||
职责:
|
||||
|
||||
- 执行 DSL rules
|
||||
- 经过 phase filter、activate_if、confidence gate
|
||||
- 逐条 rule 输出 pass/fail/skip
|
||||
- 计算分数、失败原因、整改建议
|
||||
|
||||
产出:
|
||||
|
||||
- 文档级评查结果
|
||||
- 规则级结果列表
|
||||
|
||||
### 2.6 rescue
|
||||
|
||||
职责:
|
||||
|
||||
- 对失败规则做补救判定
|
||||
- 可能把失败翻转为通过
|
||||
|
||||
注意:
|
||||
|
||||
- rescue 后的最终聚合结果才是业务最终结果
|
||||
- 落库时要认最终结果,不要混用 pre-rescue 和 post-rescue 状态
|
||||
|
||||
## 3. Bridge 层的输入要求
|
||||
|
||||
bridge 层(`fastapi_modules/fastapi_leaudit/leaudit_bridge/`)至少要准备:
|
||||
|
||||
- `document_id`
|
||||
- 可读的本地文件路径
|
||||
- 对应 `RulesFile`
|
||||
- OCR/LLM/VLM 客户端
|
||||
- 结果落库适配器(`storage_adapter.py`)
|
||||
|
||||
## 4. Bridge 层的输出
|
||||
|
||||
统一整理出:
|
||||
|
||||
- 文档级总结果:`total_score` / `passed_count` / `failed_count` / `skipped_count` / `timing`
|
||||
- 规则级结果:`rule_id` / `rule_name` / `passed` / `status` / `score` / `risk` / `message` / `remediation`
|
||||
- 抽取结果:`extraction_fields` / `derived_fields` / `multi_entity`
|
||||
|
||||
## 5. 对接时的关键约束
|
||||
|
||||
### 文件路径
|
||||
|
||||
`leaudit` 许多逻辑默认依赖本地文件路径,因此 bridge 层必须保证:
|
||||
|
||||
- 文档能以本地文件形式供其读取
|
||||
- 如果文件在对象存储中,需先下载到临时路径
|
||||
|
||||
### 配置统一
|
||||
|
||||
不要让 `leaudit` 在项目里形成另一套独立配置真相源。
|
||||
|
||||
由项目统一注入:
|
||||
|
||||
- OCR 配置(`app.toml` → `fastapi_admin.config.OCR_*`)
|
||||
- LLM 配置(`app.toml` → `fastapi_admin.config.LLM_*`)
|
||||
- VLM 配置(`app.toml` → `fastapi_admin.config.VLM_*`)
|
||||
- 并发控制配置
|
||||
|
||||
### 存储隔离
|
||||
|
||||
- `leaudit` 内部计算
|
||||
- bridge 适配后写入 `leaudit_*` 统一结果表
|
||||
- 不使用 leaudit 自己的默认 `documents/extractions/evaluations` 表
|
||||
Reference in New Issue
Block a user