
# 统一数据库架构设计文档 (Unified Database Schema)

| 文档版本 | V1.0 |
| :--- | :--- |
| **最后更新** | 2025-12-09 |
| **适用范围** | UDC-M (模块一), QPES (模块二), KES (模块三) |
| **技术选型** | PostgreSQL 14+, SQLModel (ORM) |

---

## 1. 设计概述

本数据库旨在支撑“文档解析 -> 题目提取 -> 知识增强”的全链路数据流转。

### 1.1 核心实体关系 (ERD 简述)
*   **Documents (1) : (N) Conversion Tasks**
    *   一个文档可以被多次转换（如转 MD, 转 PDF）。
*   **Documents (1) : (N) Questions**
    *   一个文档包含多道题目。
*   **Documents (1) : (N) Extraction Tasks**
    *   一个文档可以被多次提取（如调整 Prompt 后重试）。

### 1.2 关键数据类型
*   **UUID**: 所有核心业务主键使用 UUID，方便分布式生成和迁移。
*   **JSONB**: 充分利用 PostgreSQL 的 JSONB 能力存储非结构化数据（选项、知识点数组、图片链接），支持索引查询。

---

## 2. 详细表结构定义

### 2.1 基础资产层 (Module 1: UDC-M)

#### Table: `documents` (文档资产表)
这是系统的根表，记录所有上传的原始文件。

```sql
CREATE TABLE documents (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    
    -- 文件元数据
    filename VARCHAR(255) NOT NULL,        -- 原始文件名
    file_hash VARCHAR(64) NOT NULL UNIQUE, -- SHA256，用于秒传去重
    file_size BIGINT,                      -- 文件大小 (Bytes)
    file_type VARCHAR(16),                 -- 扩展名: pdf, docx, xlsx
    storage_path VARCHAR(512) NOT NULL,    -- MinIO 永久存储路径 (udc-raw/...)
    
    -- 业务元数据
    tags JSONB,                            -- 标签: {"subject": "math", "year": 2024}
    uploader_id BIGINT,                    -- 上传用户ID
    
    -- 转换状态缓存 (指向最新的 Markdown 结果)
    markdown_status VARCHAR(32) DEFAULT 'none', -- none, processing, success, failed
    markdown_url VARCHAR(512),        -- 解析后的 Markdown MinIO 地址
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_docs_hash ON documents(file_hash);
CREATE INDEX idx_docs_created ON documents(created_at);
```

#### Table: `conversion_tasks` (转换流水记录)
记录 UDC 服务的每次转换操作。

```sql
CREATE TABLE conversion_tasks (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    document_id UUID REFERENCES documents(id) ON DELETE CASCADE,
    
    target_format VARCHAR(16) NOT NULL,    -- markdown, pdf, docx
    output_mode VARCHAR(16) DEFAULT 'url', -- url, archive
    
    status VARCHAR(32) NOT NULL,           -- queued, processing, success, failed
    result_url VARCHAR(512),               -- 结果文件下载地址
    error_msg TEXT,                        -- 错误日志
    config_snapshot(JSONB),                -- 记录当时转换时的参数（如 template_id, force_ocr, chunk_size 等），方便排错。
    
    -- 性能指标
    worker_node VARCHAR(64),               -- 处理该任务的 Worker 节点名
    process_duration_ms INT,               -- 处理耗时
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    completed_at TIMESTAMP
);
```

---

### 2.2 题目数据层 (Module 2 & 3: QPES & KES)

#### Table: `questions` (核心题库表)
这张表是 Module 2 (Insert) 和 Module 3 (Update) 的协作点。

```sql
CREATE TABLE questions (
    id BIGSERIAL PRIMARY KEY,              -- 题目自增ID (用于对外展示更短)
    uid UUID DEFAULT gen_random_uuid(),    -- 内部唯一标识
    
    -- 关联
    document_id UUID REFERENCES documents(id) ON DELETE CASCADE,
    extraction_task_id UUID,               -- 关联是哪次任务提取出来的
    question_seq INT,                      -- 题目在原卷中的顺序
    
    -- ==========================================
    -- Module 2 写入 (内容提取)
    -- ==========================================
    question_type VARCHAR(32),             -- choice, fill, essay, unknown
    
    content_md TEXT NOT NULL,              -- 题干 (Markdown + LaTeX + MinIO URL)
    options_json JSONB,                    -- 选项 (仅选择题): [{"label":"A", "text":"..."}, ...]
    answer_md TEXT,                        -- 参考答案
    analysis_md TEXT,                      -- 解析
    
    image_urls JSONB,                      -- 冗余字段: ["http://minio.../1.jpg"] 方便索引包含图片的题
    content_hash VARCHAR(64),              -- MD5(content_md)，用于文档内或跨文档去重
    
    -- ==========================================
    -- Module 3 写入 (知识增强)
    -- ==========================================
    -- 机器自动提取部分
    problem_summary TEXT,                  -- 摘要
    knowledges JSONB,                      -- ["知识点1", "知识点2"]
    methods JSONB,                         -- ["方法1", "方法2"]
    
    -- 人工审核部分 (应用层展示以此为准)
    updated_knowledges JSONB,
    updated_methods JSONB,
    
    -- 状态流转
    enrich_status VARCHAR(32) DEFAULT 'pending', -- pending -> processing -> done / failed
    enrich_error TEXT,
    
    is_reviewed BOOLEAN DEFAULT FALSE,     -- 是否人工审核过
    reviewer_id BIGINT,
    reviewed_at TIMESTAMP,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 索引
CREATE INDEX idx_q_doc ON questions(document_id);
CREATE INDEX idx_q_enrich_status ON questions(enrich_status) WHERE enrich_status = 'pending'; -- 任务队列轮询优化
CREATE INDEX idx_q_knowledges ON questions USING GIN (knowledges); -- 支持 JSON 数组搜索
CREATE INDEX idx_q_hash ON questions(content_hash);
CREATE INDEX idx_q_is_reviewed ON questions(is_reviewed); -- (方便 KES 过滤保护)。
```

#### Table: `extraction_tasks` (提取任务记录)
记录 LLM 提取的消耗。

```sql
CREATE TABLE extraction_tasks (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    document_id UUID REFERENCES documents(id),
    
    model_name VARCHAR(64),                -- e.g., deepseek-v3
    chunk_strategy VARCHAR(64),            -- e.g., sliding-window-3000
    
    total_tokens INT,                      -- 消耗 Token 数
    questions_count INT,                   -- 提取出的题目数量
    
    status VARCHAR(32),                    -- processing, success, failed
    error_log TEXT,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

---

### 2.3 辅助字典层 (Module 3 Optional)

#### Table: `knowledge_tree` (知识点树 - 选做)
用于规范化 Module 3 的打标结果，避免 AI 生成“勾股定理”和“毕达哥拉斯定理”这种同义词导致检索分裂。

```sql
CREATE TABLE knowledge_tree (
    id SERIAL PRIMARY KEY,
    name VARCHAR(128) NOT NULL,
    parent_id INT REFERENCES knowledge_tree(id),
    subject VARCHAR(32),                   -- math, physics
    level VARCHAR(32),                     -- primary, middle, high
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

---

## 3. 数据流转状态机 (State Machine)

### 3.1 `documents.latest_parse_status`
*   `none`: 刚上传，未处理。
*   `processing`: UDC 正在转换中。
*   `success`: 转换完成，`latest_result_url` 有值（Markdown可用）。
*   `failed`: 转换失败。

### 3.2 `questions.enrich_status`
*   `pending`: 题目刚被 Module 2 插入，等待打标。
*   `processing`: Module 3 正在调用 API。
*   `done`: 机器打标成功。
*   `failed`: 外部 API 调用失败（需重试）。

---

## 4. 开发注意事项

1.  **JSONB 兼容性**: 在 Python 中使用 SQLModel 定义 JSONB 字段时，需要使用 `sa_column=Column(JSON)`。
2.  **事务控制**:
    *   Module 2 在写入 `questions` 时，建议开启事务：如果一道题写入失败，整个 Batch 回滚，或者记录错误日志但不中断其他题目。
3.  **并发锁**:
    *   Module 3 在领取任务时（`SELECT * FROM questions WHERE enrich_status='pending' LIMIT 10`），建议使用 `FOR UPDATE SKIP LOCKED`，防止多个 Worker 抢同一个题目造成重复调用 API 浪费钱。

---

现在，你的三个 PRD 只需要专注于**业务逻辑**和**接口定义**，数据库部分直接指向这份文档即可。这样不仅结构清晰，而且保证了技术实现的一致性。