# 知识库模块 - 数据模型文档

> **版本**: v1.0  
> **最后更新**: 2026-02-10  
> **维护者**: 后端团队

> 🚧 Draft：本文件为最小占位。当前数据模型以 Prisma schema 为准（`backend/prisma/schema/platform_knowledge.prisma`）。`05-data-model.md` 记录 SharePoint 同步策略，待后续合并整理。

---

## 📋 概述

### Schema 信息

- **Schema 名称**: `platform_knowledge`（待确认）
- **业务域**: 知识库索引与协作数据
- **核心表数量**: 待补充

### 设计原则

- 索引与文件分离：文件在 SharePoint，索引在 KB
- 权限优先：权限过滤必须在检索前生效
- 可追溯：文档来源、版本、更新时间可查询
- 检索能力外置：检索/向量由 RAGFlow 统一管理，KB 仅维护映射关系

> 说明：原 `document_chunks` 已移除，分块与索引由 RAGFlow 托管。

---

## 🗄️ 表结构设计

### 表 1: knowledge_articles（原生文章）

#### 基本信息

- **表名**: `platform_knowledge.knowledge_articles`
- **说明**: 存储知识库原生文章内容（单人编辑）
- **主键**: `id`
- **外键**: `created_by` → `platform_iam.users.id`

#### 表结构

```sql
CREATE TABLE platform_knowledge.knowledge_articles (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  title VARCHAR(200) NOT NULL,
  content TEXT NOT NULL,
  status knowledge_article_status NOT NULL DEFAULT 'DRAFT',
  created_by UUID NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  deleted_at TIMESTAMPTZ
);
```

#### 字段详细说明

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `id` | UUID | 是 | gen_random_uuid() | 主键 |
| `title` | VARCHAR(200) | 是 | - | 文章标题 |
| `content` | TEXT | 是 | - | ProseMirror 文档 JSON（序列化字符串） |
| `status` | ENUM | 是 | DRAFT | 文章状态 |
| `created_by` | UUID | 是 | - | 创建人 |
| `created_at` | TIMESTAMPTZ | 是 | NOW() | 创建时间 |
| `updated_at` | TIMESTAMPTZ | 是 | NOW() | 更新时间 |
| `deleted_at` | TIMESTAMPTZ | 否 | NULL | 软删除 |

#### 枚举值定义

`knowledge_article_status`:

| 值 | 说明 |
|----|------|
| `DRAFT` | 草稿 |
| `PUBLISHED` | 已发布 |
| `ARCHIVED` | 已归档 |

---

### 表 1.1: sp_document_index（SharePoint 文档索引）

#### 基本信息

- **表名**: `platform_knowledge.sp_document_index`
- **说明**: 存储 SharePoint 文档元数据索引（文件本体仍在 SharePoint）
- **主键**: `id`
- **唯一约束**: `sp_item_id + sp_drive_id`

#### 表结构（节选）

```sql
CREATE TABLE platform_knowledge.sp_document_index (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  sp_item_id VARCHAR(255) NOT NULL,
  sp_drive_id VARCHAR(255) NOT NULL,
  sp_site_id VARCHAR(255) NOT NULL,
  title VARCHAR(500) NOT NULL,
  web_url TEXT NOT NULL,
  file_type VARCHAR(100),
  file_extension VARCHAR(20),
  size BIGINT,
  sp_etag VARCHAR(255),
  sp_modified_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

#### 字段详细说明（新增/关键字段）

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `sp_etag` | VARCHAR(255) | 否 | NULL | SharePoint 文件 ETag（用于变更检测与条件请求） |

---

### 表 1.2: sp_folder_index（SharePoint 文件夹索引）

#### 基本信息

- **表名**: `platform_knowledge.sp_folder_index`
- **说明**: 存储 SharePoint 文件夹元数据索引，用于“文件夹名/路径搜索”与搜索结果展示
- **主键**: `id`
- **唯一约束**: `sp_item_id + sp_drive_id`

#### 表结构（节选）

```sql
CREATE TABLE platform_knowledge.sp_folder_index (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  sp_item_id VARCHAR(255) NOT NULL,
  sp_drive_id VARCHAR(255) NOT NULL,
  sp_site_id VARCHAR(255) NOT NULL,
  title VARCHAR(500) NOT NULL,
  folder_path VARCHAR(1000) NOT NULL,
  web_url TEXT NOT NULL,
  created_by VARCHAR(255),
  last_modified_by VARCHAR(255),
  sp_created_at TIMESTAMPTZ,
  sp_modified_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

#### 字段详细说明（新增/关键字段）

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `folder_path` | VARCHAR(1000) | 是 | - | 文件夹完整路径（用于路径检索与展示） |
| `sp_item_id` | VARCHAR(255) | 是 | - | SharePoint 文件夹 Item ID |
| `web_url` | TEXT | 是 | - | SharePoint 文件夹打开链接 |

---

### 表 2: ragflow_documents（RAGFlow 文档映射）

#### 基本信息

- **表名**: `platform_knowledge.ragflow_documents`
- **说明**: 记录 SharePoint 文档/知识库文章与 RAGFlow 文档的映射关系与同步状态
- **主键**: `id`
- **外键**: 无（通过 `sourceType` + `sourceId` 关联业务数据）

#### 表结构

```sql
CREATE TABLE platform_knowledge.ragflow_documents (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  source_type ragflow_source_type NOT NULL,
  source_id VARCHAR(64) NOT NULL,
  ragflow_document_id VARCHAR(64) NOT NULL,
  dataset_id VARCHAR(64) NOT NULL,
  content_hash VARCHAR(128),
  status sync_status NOT NULL DEFAULT 'PENDING',
  sync_error TEXT,
  last_synced_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

#### 字段详细说明

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `source_type` | ENUM | 是 | - | 来源类型（SP_DOCUMENT / ARTICLE） |
| `source_id` | VARCHAR(64) | 是 | - | 来源实体 ID（SharePoint Index ID / Article ID） |
| `ragflow_document_id` | VARCHAR(64) | 是 | - | RAGFlow 文档 ID |
| `dataset_id` | VARCHAR(64) | 是 | - | RAGFlow 数据集 ID |
| `content_hash` | VARCHAR(128) | 否 | NULL | 内容哈希（用于变更检测） |
| `status` | ENUM | 是 | PENDING | 同步状态 |
| `sync_error` | TEXT | 否 | NULL | 同步失败原因 |
| `last_synced_at` | TIMESTAMPTZ | 否 | NULL | 最近同步时间 |

#### 枚举值定义

`ragflow_source_type`:

| 值 | 说明 |
|----|------|
| `SP_DOCUMENT` | SharePoint 文档 |
| `ARTICLE` | 知识库文章 |

`SyncItemStatus`:

| 值 | 说明 |
|----|------|
| `PROCESSING` | 处理中 |
| `COMPLETED` | 已完成 |

---

### 表 3: sync_tasks（同步任务）

#### 基本信息

- **表名**: `platform_knowledge.sync_tasks`
- **说明**: 记录知识库同步任务的执行状态与统计
- **主键**: `id`

#### 表结构

```sql
CREATE TABLE platform_knowledge.sync_tasks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  task_type VARCHAR(50) NOT NULL,
  trigger_source VARCHAR(50),
  status sync_status NOT NULL DEFAULT 'PENDING',
  total_items INT,
  processed_items INT NOT NULL DEFAULT 0,
  processing_items INT NOT NULL DEFAULT 0,
  failed_items INT NOT NULL DEFAULT 0,
  skipped_items INT NOT NULL DEFAULT 0,
  processed_tokens INT NOT NULL DEFAULT 0,
  processed_chunks INT NOT NULL DEFAULT 0,
  error TEXT,
  started_at TIMESTAMPTZ,
  completed_at TIMESTAMPTZ,
  last_progress_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

#### 字段详细说明

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `task_type` | VARCHAR(50) | 是 | - | 任务类型（FULL_SYNC / INCREMENTAL / SINGLE_ITEM） |
| `trigger_source` | VARCHAR(50) | 否 | NULL | 触发方式（MANUAL_FULL / MANUAL_DELTA / DELTA_FALLBACK / SCHEDULED_FULL） |
| `status` | ENUM | 是 | PENDING | 任务状态 |
| `total_items` | INT | 否 | NULL | 同步对象总数 |
| `processed_items` | INT | 是 | 0 | 成功同步数量 |
| `processing_items` | INT | 是 | 0 | 处理中数量（RAGFlow 解析/排队未完成） |
| `failed_items` | INT | 是 | 0 | 失败数量 |
| `skipped_items` | INT | 是 | 0 | 跳过数量（不支持类型等） |
| `processed_tokens` | INT | 是 | 0 | 本次同步产生的 token 数 |
| `processed_chunks` | INT | 是 | 0 | 本次同步产生的 chunk 数 |
| `error` | TEXT | 否 | NULL | 错误信息（任务级） |
| `started_at` | TIMESTAMPTZ | 否 | NULL | 任务开始时间 |
| `completed_at` | TIMESTAMPTZ | 否 | NULL | 任务结束时间 |
| `last_progress_at` | TIMESTAMPTZ | 否 | NULL | 最近进度更新时间（用于停滞检测） |

---

### 表 3.1: sharepoint_sync_cursors（SharePoint 增量游标）

#### 基本信息

- **表名**: `platform_knowledge.sharepoint_sync_cursors`
- **说明**: 存储 SharePoint Delta 增量同步游标（按 drive + 目录范围）
- **主键**: `id`
- **唯一约束**: `drive_id + scope_path`

#### 表结构

```sql
CREATE TABLE platform_knowledge.sharepoint_sync_cursors (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  drive_id VARCHAR(255) NOT NULL,
  scope_path VARCHAR(500) NOT NULL,
  delta_link TEXT NOT NULL,
  last_synced_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

#### 字段详细说明

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `drive_id` | VARCHAR(255) | 是 | - | SharePoint Drive ID |
| `scope_path` | VARCHAR(500) | 是 | - | 增量范围路径（`/` 表示根目录） |
| `delta_link` | TEXT | 是 | - | Graph Delta 游标链接 |
| `last_synced_at` | TIMESTAMPTZ | 否 | NULL | 最近一次增量同步时间 |

---

### 表 4: sync_task_skipped_items（同步跳过明细）

#### 基本信息

- **表名**: `platform_knowledge.sync_task_skipped_items`
- **说明**: 记录同步过程中被跳过的文件明细（不对外暴露，仅用于运维排查）
- **主键**: `id`
- **外键**: `task_id` → `platform_knowledge.sync_tasks.id`

#### 表结构

```sql
CREATE TABLE platform_knowledge.sync_task_skipped_items (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id UUID NOT NULL,
  source_type ragflow_source_type NOT NULL,
  source_id UUID NOT NULL,
  filename VARCHAR(255) NOT NULL,
  file_extension VARCHAR(20),
  mime_type VARCHAR(255),
  reason TEXT NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

#### 字段详细说明

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `task_id` | UUID | 是 | - | 同步任务 ID |
| `source_type` | ENUM | 是 | - | 来源类型（SP_DOCUMENT / ARTICLE） |
| `source_id` | UUID | 是 | - | 来源实体 ID |
| `filename` | VARCHAR(255) | 是 | - | 原始文件名 |
| `file_extension` | VARCHAR(20) | 否 | NULL | 文件扩展名 |
| `mime_type` | VARCHAR(255) | 否 | NULL | MIME 类型 |
| `reason` | TEXT | 是 | - | 跳过原因 |

---

### 表 5: sync_task_processed_items（同步已处理明细）

#### 基本信息

- **表名**: `platform_knowledge.sync_task_processed_items`
- **说明**: 记录同步过程中已处理的文件明细（用于运维排查与验收）
- **主键**: `id`
- **外键**: `task_id` → `platform_knowledge.sync_tasks.id`

#### 表结构

```sql
CREATE TABLE platform_knowledge.sync_task_processed_items (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id UUID NOT NULL,
  source_type ragflow_source_type NOT NULL,
  source_id UUID NOT NULL,
  filename VARCHAR(255) NOT NULL,
  file_extension VARCHAR(20),
  mime_type VARCHAR(255),
  ragflow_document_id VARCHAR(255),
  token_count INT,
  chunk_count INT,
  source_size_bytes BIGINT,
  status SyncItemStatus NOT NULL DEFAULT 'PROCESSING',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

#### 字段详细说明

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `task_id` | UUID | 是 | - | 同步任务 ID |
| `source_type` | ENUM | 是 | - | 来源类型（SP_DOCUMENT / ARTICLE） |
| `source_id` | UUID | 是 | - | 来源实体 ID |
| `filename` | VARCHAR(255) | 是 | - | 原始文件名 |
| `file_extension` | VARCHAR(20) | 否 | NULL | 文件扩展名 |
| `mime_type` | VARCHAR(255) | 否 | NULL | MIME 类型 |
| `ragflow_document_id` | VARCHAR(255) | 否 | NULL | RAGFlow 文档 ID |
| `token_count` | INT | 否 | NULL | 文档 token 数 |
| `chunk_count` | INT | 否 | NULL | 文档 chunk 数 |
| `source_size_bytes` | BIGINT | 否 | NULL | 源文件大小（字节） |
| `status` | ENUM | 是 | PROCESSING | 处理状态（PROCESSING / COMPLETED） |

---

### 表 6: sync_task_failed_items（同步失败明细）

#### 基本信息

- **表名**: `platform_knowledge.sync_task_failed_items`
- **说明**: 记录同步过程中失败的文件明细与错误信息
- **主键**: `id`
- **外键**: `task_id` → `platform_knowledge.sync_tasks.id`

#### 表结构

```sql
CREATE TABLE platform_knowledge.sync_task_failed_items (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id UUID NOT NULL,
  source_type ragflow_source_type NOT NULL,
  source_id UUID NOT NULL,
  filename VARCHAR(255) NOT NULL,
  file_extension VARCHAR(20),
  mime_type VARCHAR(255),
  error TEXT NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

#### 字段详细说明

| 字段名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `task_id` | UUID | 是 | - | 同步任务 ID |
| `source_type` | ENUM | 是 | - | 来源类型（SP_DOCUMENT / ARTICLE） |
| `source_id` | UUID | 是 | - | 来源实体 ID |
| `filename` | VARCHAR(255) | 是 | - | 原始文件名 |
| `file_extension` | VARCHAR(20) | 否 | NULL | 文件扩展名 |
| `mime_type` | VARCHAR(255) | 否 | NULL | MIME 类型 |
| `error` | TEXT | 是 | - | 失败原因 |
