# 知识库模块 - 架构设计

## 📋 文档信息

| 项目 | 内容 |
|------|------|
| 模块名称 | 知识库管理系统 - 技术架构 |
| 版本 | v1.1.0 |
| 创建日期 | 2025-12-22 |
| 最后更新 | 2026-02-28 |
| 负责人 | Tech Team |
| 状态 | 🟡 设计中 |

---

## 📖 目录

- [1. 系统架构](#1-系统架构)
- [2. 技术选型](#2-技术选型)
- [3. SharePoint 集成架构](#3-sharepoint-集成架构)
- [4. 数据模型](#4-数据模型)
- [5. 核心模块](#5-核心模块)
- [6. 接口设计](#6-接口设计)
- [7. 部署架构](#7-部署架构)

---

## 1. 系统架构

### 1.1 整体架构

```
┌─────────────────────────────────────────────────────────┐
│                    前端层 (Next.js)                      │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────┐ │
│  │ 文档管理 │  │ AI 问答  │  │ 搜索     │  │ 协作    │ │
│  └──────────┘  └──────────┘  └──────────┘  └─────────┘ │
│         │                                                │
│         └─ 调用文档编辑引擎 (KB 文章编辑) ⭐              │
└─────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────┐
│                   API 网关层 (NestJS)                    │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐              │
│  │ 认证鉴权 │  │ 限流控制 │  │ 审计日志 │              │
│  └──────────┘  └──────────┘  └──────────┘              │
└─────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────┐
│                     业务服务层                           │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │ 知识库服务   │  │  AI 服务     │  │ 搜索服务     │  │
│  │ - 文档管理   │  │ - RAG 引擎   │  │ - 语义搜索   │  │
│  │ - 元数据管理 │  │ - RAGFlow   │  │ - 全文搜索   │  │
│  │ - 权限管理   │  │ - LLM 调用   │  │ - 过滤排序   │  │
│  └──────┬───────┘  └──────────────┘  └──────────────┘  │
│         │                                                │
│         ↓ 依赖                                          │
│  ┌─────────────────┐                                    │
│  │ 文档编辑引擎 ⭐  │  (独立模块)                         │
│  │ - 富文本编辑    │                                    │
│  │ - 协同编辑      │                                    │
│  │ - 版本管理      │                                    │
│  └─────────────────┘                                    │
└─────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────┐
│                      数据层                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────┐│
│  │PostgreSQL│  │SharePoint│  │ RAGFlow  │  │ Redis   ││
│  │(元数据)  │  │(文件存储)│  │(检索引擎)│  │(缓存)   ││
│  └──────────┘  └──────────┘  └──────────┘  └─────────┘│
└─────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────┐
│                    AI 基础设施                           │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐              │
│  │ OpenAI   │  │ RAGFlow  │  │ OCR/ASR  │              │
│  │ API      │  │ 检索/索引│  │ Service  │              │
│  └──────────┘  └──────────┘  └──────────┘              │
└─────────────────────────────────────────────────────────┘
```

### 1.2 SharePoint 混合架构

**架构选择**：混合模式（SharePoint + 自建增强层）

```
┌─────────────────────────────────────────────────┐
│       前端层 (Next.js)                           │
│  统一的用户界面和体验                            │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│    业务逻辑层 (NestJS)                           │
│  • AI 增强功能                                   │
│  • 术语表管理                                    │
│  • 智能推荐                                      │
│  • 去重检测                                      │
│  • 审批流程                                      │
└─────────────────────────────────────────────────┘
                    ↓
        ┌───────────┴───────────┐
        ↓                       ↓
┌──────────────────┐   ┌──────────────────┐
│  SharePoint      │   │  自建服务         │
│  - 文档存储      │   │  - AI 服务       │
│  - 文件管理      │   │  - RAGFlow 检索  │
│  - 版本控制      │   │  - 术语库        │
│  - 权限管理      │   │  - 统计分析      │
│  - 基础搜索      │   │  - 反馈系统      │
│  - Office 集成   │   │  - FAQ 管理      │
└──────────────────┘   └──────────────────┘
```

**架构优势**：
- ✅ 利用 SharePoint 成熟的文档管理能力
- ✅ 通过 AI 层提供智能化增强
- ✅ 降低开发成本和运维复杂度
- ✅ 保持技术灵活性和扩展性

---

## 2. 技术选型

### 2.1 核心技术栈

| 层次 | 技术 | 版本 | 说明 |
|------|------|------|------|
| **前端** | Next.js + React | 14 + 18 | 服务端渲染 + 客户端交互 |
| **UI 组件** | Radix UI + Tailwind CSS | - | 无障碍 + 响应式 |
| **编辑器** | ProseMirror（参考 Outline） | - | 富文本编辑器 |
| **后端** | NestJS + TypeScript | - | 模块化架构 |
| **数据库** | PostgreSQL | 15+ | 关系型数据 |
| **文件存储** | SharePoint | - | ⭐️ 推荐（公司已有）|
| **文件存储（备选）** | MinIO / S3 | - | 对象存储 |
| **检索引擎** | RAGFlow (Elasticsearch) | - | 混合检索 / 语义检索 |
| **搜索引擎** | SharePoint Search + RAGFlow | - | 混合搜索 |
| **缓存** | Redis | 7+ | 缓存 + Session |
| **消息队列** | Bull (Redis) | - | 异步任务 |
| **AI 模型** | OpenAI GPT-4 / Claude | - | LLM |
| **Embedding** | RAGFlow 托管 | - | 文本向量化 |
| **OCR** | Tesseract / 百度 OCR | - | 图片识别 |
| **实时通信** | Socket.io | - | WebSocket |

### 2.2 SharePoint 集成技术栈

| 技术 | 版本 | 用途 | 说明 |
|-----|------|------|------|
| **@pnp/sp** | ^3.x | SharePoint 客户端库 | TypeScript SDK |
| **@microsoft/microsoft-graph-client** | ^3.x | Graph API 客户端 | 统一 API 访问 |
| **@azure/msal-node** | ^2.x | Azure AD 认证 | OAuth 2.0 |
| **SharePoint REST API** | v1/v2 | 直接 REST 调用 | 低级 API 访问 |
| **Microsoft Graph API** | v1.0 | 统一 Microsoft API | 文件、用户、活动 |
| **Power Automate** | - | 工作流自动化 | 审批流程、通知 |

### 2.3 SharePoint 功能矩阵

| 功能模块 | SharePoint | 自建服务 | 集成方式 | 优先级 |
|---------|-----------|---------|---------|--------|
| **📁 文档存储** | ✅ **主要** | 🔄 备份 | SharePoint 文档库 | P0 |
| **📂 文件管理** | ✅ **主要** | 📊 元数据 | REST API / Graph API | P0 |
| **🔄 版本控制** | ✅ **主要**（外部文件） | 🔗 [文档编辑引擎](../document-editor/)（KB 原生文章） | 版本历史 API / 编辑引擎 API | P0 |
| **🔐 权限管理** | ✅ **主要** | 🔗 同步 | 权限 API | P0 |
| **📝 Office 编辑** | ✅ **独有** | - | Office Online | P0 |
| **📝 KB 文章编辑** | ❌ | ✅ [文档编辑引擎](../document-editor/) | 编辑引擎 API + 插件 | P0 |
| **🔍 基础搜索** | ✅ **主要** | ➕ 增强 | Search API | P1 |
| **⚙️ 工作流** | ✅ **主要** | ➕ 自定义 | Power Automate | P1 |
| **💬 协作评论** | ✅ **主要** | ➕ 增强 | Comments API + [文档编辑引擎](../document-editor/) | P1 |
| **📋 术语库(基础)** | ✅ **基础** | ➕ AI增强 | Term Store API | P1 |
| **🤖 AI 问答** | ❌ | ✅ **必需** | 自建 RAG | P0 |
| **🔎 语义搜索** | ❌ | ✅ **必需** | RAGFlow 检索 | P0 |
| **🏷️ 术语表(AI)** | 🔄 | ✅ **增强** | 混合模式 | P1 |
| **♻️ 去重检测** | ❌ | ✅ **必需** | AI 分析 | P1 |
| **💡 智能推荐** | ❌ | ✅ **必需** | 推荐引擎 | P1 |
| **👥 贡献者追踪** | 🔄 基础 | ✅ **增强** | Analytics | P2 |
| **✅ 审批流程** | 🔄 | ✅ [审批引擎](../approval/) | 审批引擎 API | P0 |

### 2.4 SharePoint API 集成示例

```typescript
// SharePoint REST API 集成服务
@Injectable()
export class SharePointService {
  constructor(
    private readonly httpService: HttpService,
    private readonly authService: MsalAuthService,
  ) {}
  
  // 文档操作
  async listDocuments(libraryId: string): Promise<Document[]> {
    const token = await this.authService.getAccessToken();
    return this.httpService.get(
      `${SHAREPOINT_SITE}/_api/web/lists('${libraryId}')/items`,
      { headers: { Authorization: `Bearer ${token}` }}
    );
  }
  
  async uploadDocument(file: File, libraryId: string): Promise<string> {
    const token = await this.authService.getAccessToken();
    const formData = new FormData();
    formData.append('file', file);
    
    return this.httpService.post(
      `${SHAREPOINT_SITE}/_api/web/lists('${libraryId}')/RootFolder/Files/add`,
      formData,
      { headers: { Authorization: `Bearer ${token}` }}
    );
  }
  
  // 版本管理
  async getVersionHistory(itemId: string): Promise<Version[]> {
    const token = await this.authService.getAccessToken();
    return this.httpService.get(
      `${SHAREPOINT_SITE}/_api/web/lists/items(${itemId})/versions`,
      { headers: { Authorization: `Bearer ${token}` }}
    );
  }
  
  // 搜索
  async search(query: string, filters?: SearchFilters): Promise<SearchResult[]> {
    const token = await this.authService.getAccessToken();
    return this.httpService.post(
      `${SHAREPOINT_SITE}/_api/search/query`,
      {
        Querytext: query,
        RowLimit: filters?.limit || 50,
        SelectProperties: filters?.properties,
      },
      { headers: { Authorization: `Bearer ${token}` }}
    );
  }
  
  // 术语库
  async getTerms(termSetId: string): Promise<Term[]> {
    const token = await this.authService.getAccessToken();
    return this.httpService.get(
      `${SHAREPOINT_SITE}/_api/v2.1/termStore/sets/${termSetId}/terms`,
      { headers: { Authorization: `Bearer ${token}` }}
    );
  }
}
```

### 2.5 同步策略（全量）

```typescript
// 全量同步服务（SharePoint + KB 文章 → RAGFlow）
@Injectable()
export class RagflowSyncService {
  constructor(
    private readonly sharePointSyncService: SharePointSyncService,
    private readonly ragflowService: RagflowService,
    private readonly prisma: PrismaService,
  ) {}

  async syncAll(): Promise<void> {
    const datasetId = await this.ragflowService.resolveDatasetId();
    const { driveId, documents } = await this.sharePointSyncService.syncAllMetadata();
    const articles = await this.prisma.knowledgeArticle.findMany({ where: { deletedAt: null } });

    // 1) SharePoint 文档上传到 RAGFlow
    for (const doc of documents) {
      await this.syncSharePointDocument(datasetId, driveId, doc);
    }

    // 2) KB 文章上传到 RAGFlow
    for (const article of articles) {
      await this.syncKnowledgeArticle(datasetId, article);
    }

    // 3) 清理已删除或失效映射
    await this.cleanupStaleMappings(datasetId, documents, articles);
  }
}
```

### 2.6 同步策略（增量）

**目标**：在不重复全量扫描的情况下，仅同步 SharePoint 发生变更的文件（新增/修改/删除）。

**关键机制**：
- 使用 Microsoft Graph Drive Delta 接口获取增量变更
- 按 `KB_SP_INCLUDED_PATHS` 范围拆分增量游标（无配置则使用根目录）
- Delta 游标持久化在 `sharepoint_sync_cursors`，支持断点续跑
- 删除项：清理本地索引 + RAGFlow 映射，避免“幽灵文档”

**使用场景**：
- **增量同步**：日常更新（新增/修改/删除），默认触发
- **全量同步**：首次接入、白名单路径变更、游标失效、RAGFlow 数据集重建/Embedding 变更
- **自动切换**：增量同步检测游标缺失/失效时，自动回退为全量同步并提示

#### 2.6.1 同步节省策略（避免重复下载与嵌入）

**核心原则**：先比对 `etag/lastModified/size`，无变化跳过下载；使用条件请求；仅 hash 变化再上传/解析。

执行顺序：
1) **元数据快速比对**：对比 `etag`、`lastModified`、`size`，无变化直接跳过下载与嵌入
2) **条件请求**：携带 `If-None-Match` / `If-Modified-Since`，返回 304 直接跳过
3) **内容哈希兜底**：仅在需要下载时计算 `content_hash`，hash 未变则不上传 RAGFlow
4) **额度保护**：Embedding 调用失败（授权/额度不足）时终止任务并记录原因，避免继续消耗

#### 2.6.2 Embedding 模型切换与额度提示

- **模型切换（如 v3 → v4）必须重建索引**：向量空间不可兼容，需触发全量同步或索引重建。
- **额度提示**：若 DashScope 开启“仅免费额度”且额度耗尽，将导致 embedding 失败；需关闭免费额度限制或使用付费 Key。

#### 2.6.3 远端映射健康检查与自愈（未变更文档）

**问题背景**：仅依赖本地 `etag/lastModified/size` 判定“未变更即跳过”会导致一种隐患：本地映射仍是 `COMPLETED`，但远端 RAGFlow 文档已失效（删除、owner 变化、数据集重建后残留旧 ID）。

**策略目标**：在不破坏同步效率的前提下，自动发现并修复“本地成功、远端失效”的映射。

执行规则：
1) **未变更文档仍可跳过**：默认保持“跳过下载与嵌入”以保障效率。  
2) **按时间窗口抽检远端状态**：仅当超过健康检查间隔（默认 24h，可配置）时才调用 RAGFlow 文档状态接口。  
3) **远端健康**：继续跳过并刷新本地 `lastSyncedAt`。  
4) **远端失效且可恢复**（`not found` / `don't own the document` 等）：标记为需重传，重新下载并上传，重建映射。  
5) **远端失效且不可恢复**：记录失败明细并终止当前条目，避免静默吞错。  

**收益**：
- 保留“未变更跳过”节省策略；
- 避免历史失效映射长期滞留，保障检索完整性；
- 与增量/全量同步任务兼容，无需新增外部 API。

```typescript
// 增量同步服务（SharePoint Delta → RAGFlow）
async syncDelta(): Promise<void> {
  const datasetId = await this.ragflowService.resolveDatasetId();
  const { driveId, documents, removedItemIds, cursors } =
    await this.sharePointSyncService.syncDeltaMetadata();

  for (const doc of documents) {
    await this.syncSharePointDocument(datasetId, driveId, doc);
  }

  await this.cleanupRemovedSharePointItems(datasetId, driveId, removedItemIds);
  await this.sharePointSyncService.saveDeltaCursors(driveId, cursors);
}
```

---

## 3. SharePoint 集成架构

### 3.1 集成策略

**混合模式架构**：利用 SharePoint 作为文档存储和管理基础，在其之上构建 AI 增强层。

```
┌─────────────────────────────────────────────────────────┐
│                  用户界面层                              │
│  统一的现代化界面（Next.js + React）                     │
│  - 简洁易用的 UI/UX                                      │
│  - AI 智能助手                                           │
│  - 个性化推荐                                            │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│              AI 增强层（自建服务）                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ AI 问答引擎 │  │ 语义搜索    │  │ 智能推荐    │     │
│  │  (RAG)     │  │ (RAGFlow)  │  │ (ML 模型)   │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ 术语表管理  │  │ 去重检测    │  │ 反馈系统    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│            SharePoint 基础层（现有基础设施）              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ 文档存储    │  │ 版本控制    │  │ 权限管理    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ Office 集成 │  │ 基础搜索    │  │ 工作流      │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘
```

### 3.2 功能映射

| 功能 | SharePoint 能力 | 自建增强 | 集成方式 | 实现难度 |
|------|----------------|---------|---------|---------|
| **文档上传** | ✅ 完整 | - | REST API | 🟢 低 |
| **文件预览** | ✅ Office Online | 🖼️ 图片/PDF | Office Online | 🟢 低 |
| **版本控制** | ✅ 完整 | 📊 可视化 | Version API | 🟢 低 |
| **权限管理** | ✅ 完整 | 🔗 映射 | Permission API | 🟡 中 |
| **基础搜索** | ✅ 关键词 | 🤖 语义 | Search API + RAGFlow | 🟡 中 |
| **评论** | ✅ 基础 | 💬 增强 | Comments API | 🟢 低 |
| **标签** | 🔄 托管元数据 | ✅ AI 标签 | Term Store + 自定义 | 🟡 中 |
| **工作流** | ✅ Power Automate | ➕ 自定义 | Flow 触发 | 🟡 中 |
| **AI 问答** | ❌ | ✅ RAG | SharePoint 内容源 | 🔴 高 |
| **去重检测** | ❌ | ✅ AI 分析 | RAGFlow 相似度 | 🟡 中 |
| **术语表** | 🔄 Term Store | ✅ AI 增强 | 混合 | 🟡 中 |
| **智能推荐** | ❌ | ✅ ML 模型 | 使用数据分析 | 🔴 高 |
| **统计分析** | 🔄 基础 | ✅ 高级 | Analytics API + 自建 | 🟡 中 |

### 3.3 数据流设计

#### 3.3.1 文档上传流程

```mermaid
sequenceDiagram
    participant U as 用户
    participant F as 前端
    participant API as API 网关
    participant SP as SharePoint 服务
    participant SPO as SharePoint Online
    participant RF as RAGFlow
    participant DB as PostgreSQL

    U->>F: 上传文档
    F->>API: POST /documents/upload
    API->>SP: 上传到 SharePoint
    SP->>SPO: REST API 上传
    SPO-->>SP: 返回文档 ID
    SP->>DB: 保存元数据
    SP->>RF: 上传文档到数据集
    RF->>RF: 触发解析 /datasets/{id}/chunks
    RF->>DB: 更新 ragflow_documents 映射
    DB-->>API: 上传完成
    API-->>F: 返回成功
    F-->>U: 显示成功消息
```

#### 3.3.2 AI 问答流程

```mermaid
sequenceDiagram
    participant U as 用户
    participant F as 前端
    participant API as API 网关
    participant RF as RAGFlow

    U->>F: 提出问题
    F->>API: POST /ai/query
    API->>RF: 调用 /retrieval 与 /chats_openai
    RF-->>API: 返回答案 + 引用片段
    API-->>F: 响应
    F-->>U: 显示答案
```

#### 3.3.3 双向同步流程

```mermaid
graph TB
    A[全量同步触发] --> B[同步服务]
    B --> C[拉取 SharePoint 元数据]
    B --> D[拉取 KB 文章]
    C --> E[下载文件并上传 RAGFlow]
    D --> F[文章转文本并上传 RAGFlow]
    E --> E2[触发解析 /datasets/{id}/chunks]
    F --> F2[触发解析 /datasets/{id}/chunks]
    E2 --> G[写入 ragflow_documents]
    F2 --> G
    G --> H[清理过期映射/文档]
```

**同步范围说明**：
- 默认同步 SharePoint 文档库的全部目录
- 可通过 `KB_SP_INCLUDED_PATHS` 配置路径白名单，仅同步指定目录及其子目录

### 3.4 认证与授权

#### 3.4.1 Azure AD 应用注册

```bash
# 1. 在 Azure Portal 注册应用
# https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps

# 2. 配置 API 权限
API Permissions:
  - Microsoft Graph:
    • Sites.Read.All
    • Files.ReadWrite.All
    • User.Read.All
  
  - SharePoint:
    • AllSites.FullControl
    • TermStore.ReadWrite.All

# 3. 获取凭证
Tenant ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Client ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Client Secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

#### 3.4.2 MSAL 配置

```typescript
// src/config/msal.config.ts
import { Configuration } from '@azure/msal-node';

export const msalConfig: Configuration = {
  auth: {
    clientId: process.env.AZURE_CLIENT_ID,
    authority: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}`,
    clientSecret: process.env.AZURE_CLIENT_SECRET,
  },
  system: {
    loggerOptions: {
      logLevel: LogLevel.Info,
      loggerCallback: (level, message, containsPii) => {
        if (!containsPii) {
          console.log(message);
        }
      }
    }
  }
};

// 认证服务
@Injectable()
export class MsalAuthService {
  private cca: ConfidentialClientApplication;
  
  constructor() {
    this.cca = new ConfidentialClientApplication(msalConfig);
  }
  
  async getAccessToken(scopes: string[] = ['https://graph.microsoft.com/.default']): Promise<string> {
    const result = await this.cca.acquireTokenByClientCredential({
      scopes,
    });
    return result.accessToken;
  }
}
```

### 3.5 SharePoint 服务实现

```typescript
// src/modules/knowledge-base/services/sharepoint.service.ts
import { sp } from '@pnp/sp/presets/all';
import '@pnp/sp/webs';
import '@pnp/sp/lists';
import '@pnp/sp/items';
import '@pnp/sp/files';
import '@pnp/sp/folders';

@Injectable()
export class SharePointService {
  constructor(
    private readonly authService: MsalAuthService,
    private readonly configService: ConfigService,
  ) {
    this.initializeSP();
  }
  
  private async initializeSP() {
    const token = await this.authService.getAccessToken();
    
    sp.setup({
      sp: {
        baseUrl: this.configService.get('SHAREPOINT_SITE_URL'),
        fetchClientFactory: () => {
          return {
            fetch: async (url, options) => {
              return fetch(url, {
                ...options,
                headers: {
                  ...options.headers,
                  'Authorization': `Bearer ${token}`,
                }
              });
            }
          };
        }
      }
    });
  }
  
  // 列出文档
  async listDocuments(libraryName: string): Promise<SharePointDocument[]> {
    const items = await sp.web.lists
      .getByTitle(libraryName)
      .items
      .select('Id', 'Title', 'FileRef', 'Modified', 'Editor/Title', 'File/Length')
      .expand('Editor', 'File')
      .get();
    
    return items.map(item => ({
      id: item.Id,
      title: item.Title,
      url: item.FileRef,
      modified: new Date(item.Modified),
      modifiedBy: item.Editor.Title,
      size: item.File.Length,
    }));
  }
  
  // 上传文档
  async uploadDocument(
    libraryName: string,
    fileName: string,
    content: Buffer,
  ): Promise<string> {
    const result = await sp.web.lists
      .getByTitle(libraryName)
      .rootFolder
      .files
      .addUsingPath(fileName, content, { Overwrite: true });
    
    return result.data.UniqueId;
  }
  
  // 获取文档内容
  async getDocumentContent(fileUrl: string): Promise<Buffer> {
    const file = sp.web.getFileByServerRelativePath(fileUrl);
    const blob = await file.getBlob();
    return Buffer.from(await blob.arrayBuffer());
  }
  
  // 获取版本历史
  async getVersionHistory(fileUrl: string): Promise<DocumentVersion[]> {
    const file = sp.web.getFileByServerRelativePath(fileUrl);
    const versions = await file.versions.get();
    
    return versions.map(v => ({
      versionId: v.ID,
      versionLabel: v.VersionLabel,
      created: new Date(v.Created),
      createdBy: v.CreatedBy.Name,
      size: v.Size,
      url: v.Url,
    }));
  }
  
  // 设置权限
  async setPermissions(
    itemId: number,
    principalId: number,
    roleType: 'read' | 'contribute' | 'edit' | 'fullControl',
  ): Promise<void> {
    const item = await sp.web.lists
      .getByTitle('Documents')
      .items
      .getById(itemId);
    
    // 停止继承
    await item.breakRoleInheritance(false, false);
    
    // 添加权限
    const roleDefId = await this.getRoleDefinitionId(roleType);
    await item.roleAssignments.add(principalId, roleDefId);
  }
  
  // 搜索文档
  async search(query: string, filters?: SearchFilters): Promise<SearchResult[]> {
    const results = await sp.search({
      Querytext: query,
      RowLimit: filters?.limit || 50,
      SelectProperties: [
        'Title',
        'Path',
        'Author',
        'Size',
        'Write',
        'Description',
      ],
      SortList: [{
        Property: 'Rank',
        Direction: SortDirection.Descending
      }],
      ...filters,
    });
    
    return results.PrimarySearchResults.map(r => ({
      title: r.Title,
      url: r.Path,
      author: r.Author,
      modified: new Date(r.Write),
      snippet: r.HitHighlightedSummary,
      rank: r.Rank,
    }));
  }
  
  // Term Store 操作
  async getTerms(termSetId: string): Promise<Term[]> {
    const terms = await sp.termStore
      .sets
      .getById(termSetId)
      .terms
      .get();
    
    return terms.map(t => ({
      id: t.id,
      name: t.labels[0].name,
      description: t.descriptions[0]?.description,
    }));
  }
  
  async createTerm(termSetId: string, termName: string, description: string): Promise<string> {
    const term = await sp.termStore
      .sets
      .getById(termSetId)
      .terms
      .add({
        labels: [{ name: termName, isDefault: true, languageTag: 'zh-CN' }],
        descriptions: [{ description, languageTag: 'zh-CN' }],
      });
    
    return term.data.id;
  }
}
```

### 3.6 同步服务实现

```typescript
// src/modules/knowledge-base/services/ragflow-sync.service.ts
@Injectable()
export class RagflowSyncService {
  constructor(
    private readonly sharePointSyncService: SharePointSyncService,
    private readonly ragflowService: RagflowService,
    private readonly prosemirrorTextService: ProseMirrorTextService,
    private readonly prisma: PrismaService,
  ) {}

  // 全量同步（SharePoint + KB 文章）
  @Cron('0 2 * * *') // 每天凌晨 2 点
  async fullSync() {
    const datasetId = await this.ragflowService.resolveDatasetId();
    const { driveId, documents } = await this.sharePointSyncService.syncAllMetadata();
    const articles = await this.prisma.knowledgeArticle.findMany({ where: { deletedAt: null } });

    for (const doc of documents) {
      await this.syncSharePointDocument(datasetId, driveId, doc);
    }

    for (const article of articles) {
      const text = this.prosemirrorTextService.toText(article.content);
      await this.syncKnowledgeArticle(datasetId, article, text);
    }

    await this.cleanupStaleMappings(datasetId, documents, articles);
  }
}
```

### 3.7 Term Store 集成

**SharePoint 原生术语库 + AI 增强**：

```typescript
// SharePoint Term Store 集成
interface TermStoreIntegration {
  // 从 SharePoint Term Store 读取
  syncFromTermStore: async () => {
    const termSets = await sp.termStore.groups.getByName('Knowledge Base').termSets;
    
    for (const termSet of termSets) {
      const terms = await termSet.terms.get();
      
      // 同步到本地数据库
      for (const term of terms) {
        await prisma.glossaryTerm.upsert({
          where: { sharePointTermId: term.id },
          create: {
            sharePointTermId: term.id,
            term: term.name,
            definition: term.description,
            source: 'sharepoint',
          },
          update: {
            term: term.name,
            definition: term.description,
          }
        });
      }
    }
  },
  
  // AI 增强的术语推送到 SharePoint
  pushToTermStore: async (termId: string) => {
    const term = await prisma.glossaryTerm.findUnique({ where: { id: termId } });
    
    // 创建或更新 SharePoint 术语
    await sp.termStore.groups.getByName('Knowledge Base')
      .termSets.getByName('AI Generated')
      .terms.add(term.term, term.id, {
        Description: term.definition,
        CustomProperties: {
          aiGenerated: 'true',
          confidence: term.confidence.toString(),
        }
      });
  }
}
```

### 3.8 性能优化

```typescript
// 1. 批量操作
async batchUpload(files: File[]) {
  const batch = sp.web.createBatch();
  
  files.forEach(file => {
    sp.web.lists
      .getByTitle('Documents')
      .rootFolder
      .files
      .addUsingPath(file.name, file, { Overwrite: true })
      .inBatch(batch);
  });
  
  await batch.execute();
}

// 2. 请求缓存
@Cacheable({
  ttl: 300, // 5 minutes
  key: (libraryName) => `sp:list:${libraryName}`
})
async listDocuments(libraryName: string) {
  // SharePoint API 调用
}

// 3. 并发控制
import pLimit from 'p-limit';

const limit = pLimit(5); // 最多5个并发请求

async syncAllDocuments(docs: Document[]) {
  await Promise.all(
    docs.map(doc => limit(() => this.syncDocument(doc)))
  );
}
```

### 3.9 错误处理

```typescript
// 统一错误处理
async safeSharePointCall<T>(
  operation: () => Promise<T>,
  fallback?: T
): Promise<T> {
  try {
    return await operation();
  } catch (error) {
    if (error.status === 429) {
      // 限流，等待后重试
      await this.sleep(error.retryAfter * 1000);
      return this.safeSharePointCall(operation, fallback);
    }
    
    if (error.status === 401) {
      // Token 过期，刷新后重试
      await this.authService.refreshToken();
      return operation();
    }
    
    // 记录错误
    this.logger.error('SharePoint API error', error);
    
    if (fallback !== undefined) {
      return fallback;
    }
    
    throw error;
  }
}
```

### 3.10 监控指标

```typescript
// 关键监控指标
interface SharePointMetrics {
  // API 调用
  apiCalls: {
    total: number;
    success: number;
    failed: number;
    avgResponseTime: number;
    throttled: number;
  };
  
  // 同步状态
  sync: {
    lastSyncTime: Date;
    documentsProcessed: number;
    syncDuration: number;
    failedDocuments: number;
  };
  
  // RAGFlow 索引
  ragflowIndexing: {
    pending: number;
    processing: number;
    completed: number;
    avgProcessingTime: number;
  };
}
```

### 3.11 技术风险与应对

| 风险 | 影响 | 概率 | 应对措施 |
|------|------|------|---------|
| **API 限流** | 中 | 高 | • 实施请求缓存<br>• 批量操作<br>• 降级方案 |
| **同步延迟** | 中 | 中 | • 定时全量同步<br>• 监控与重试机制 |
| **大文件处理** | 中 | 中 | • 分块处理<br>• 异步任务队列 |
| **权限复杂性** | 中 | 中 | • 简化权限映射<br>• 文档化规则 |
| **SharePoint 故障** | 高 | 低 | • 本地缓存<br>• 降级模式<br>• 监控告警 |

---

## 4. 数据模型

> 以 `docs/modules/knowledge-base/06-data-model.md` 与 Prisma schema 为准，本节仅保留核心关系与 RAGFlow 映射。

### 4.1 核心实体

```
SPDocumentIndex (SharePoint 文档元数据)
KnowledgeArticle (KB 原生文章)
RagflowDocument (RAGFlow 文档映射)
SyncTask (同步任务)
AIQALog / SearchLog (问答与检索日志)
```

### 4.2 RAGFlow 映射关系

- **source_type + source_id** 唯一定位业务实体（SP 文档 / 文章）
- **ragflow_document_id** 关联 RAGFlow 文档
- **dataset_id** 记录数据集归属
- **content_hash** 用于判断是否需要重新上传

### 4.3 业务接口定义

本节包含核心业务功能的接口定义，用于指导前后端开发。

#### 3.4.1 标签系统接口

```typescript
// 标签实体
interface Tag {
  id: string;
  name: string;              // 标签名称
  displayName: Json;         // 显示名称（支持多语言）
  slug: string;              // URL 友好标识符
  color: string;             // 标签颜色 (#hex)
  icon?: string;             // 标签图标或 emoji
  category: TagCategory;     // 标签类别
  parentId?: string;         // 父标签 ID（支持层级）
  
  // 统计信息
  usage: number;             // 使用次数
  documentCount: number;     // 关联文档数
  viewCount: number;         // 浏览次数
  
  // 智能增强
  aliases: string[];         // 同义词/别名
  relatedTags: string[];     // 相关标签 ID
  aiGenerated: boolean;      // 是否 AI 生成
  
  // 权限控制
  visibility: 'public' | 'internal' | 'private';
  restrictedTo?: string[];   // 限制使用的部门/角色
  
  // 元数据
  description?: string;      // 标签描述
  createdBy: string;
  createdAt: Date;
  updatedAt: Date;
}

// 标签类别
enum TagCategory {
  DEPARTMENT = 'department',    // 部门标签
  TOPIC = 'topic',             // 主题标签
  TECHNOLOGY = 'technology',   // 技术标签
  STATUS = 'status',           // 状态标签
  LEVEL = 'level',             // 级别标签
  PRIORITY = 'priority',       // 优先级标签
  TYPE = 'type',               // 类型标签
  CUSTOM = 'custom',           // 自定义标签
}

// 文档标签关联
interface DocumentTag {
  id: string;
  documentId: string;
  tagId: string;
  addedBy: string;             // 谁添加的
  addedAt: Date;
  aiSuggested: boolean;        // 是否 AI 建议
  confidence?: number;         // AI 建议的置信度
}

// AI 自动标注
interface AutoTagging {
  documentId: string;
  suggestedTags: Array<{
    tagId: string;
    confidence: number;        // 置信度 0-1
    reason: string;            // 推荐原因
    source: 'content' | 'title' | 'metadata';
  }>;
  
  // AI 分析维度
  analysis: {
    keywords: string[];         // 关键词提取
    entities: string[];         // 实体识别
    topics: string[];           // 主题模型
    sentiment: 'positive' | 'neutral' | 'negative';
  };
}

// 标签搜索
interface TagSearchFeatures {
  // 基础搜索
  byName?: string;
  byCategory?: TagCategory[];
  
  // 组合搜索
  andTags?: string[];          // 同时包含（AND）
  orTags?: string[];           // 任一包含（OR）
  notTags?: string[];          // 排除（NOT）
  
  // 高级功能
  fuzzySearch?: boolean;       // 模糊搜索
  aliasSearch?: boolean;       // 包含别名搜索
  hierarchySearch?: boolean;   // 包含子标签
}

// 标签统计
interface TagStatistics {
  tagId: string;
  stats: {
    totalDocuments: number;
    totalViews: number;
    avgRating: number;
    topContributors: User[];
    usageTrend: TimeSeries[];
    relatedTags: Array<{
      tagId: string;
      coOccurrence: number;    // 共现次数
    }>;
  };
}
```

#### 3.4.2 搜索与去重接口

```typescript
// 去重检测
interface DuplicationDetection {
  // 检测维度
  titleSimilarity: number;        // 标题相似度 (0-1)
  contentSimilarity: number;      // 内容相似度 (0-1)
  semanticSimilarity: number;     // 语义相似度 (0-1)
  
  // 重复类型
  duplicationType: 'exact' | 'near-duplicate' | 'related';
  
  // 建议操作
  suggestion: {
    action: 'merge' | 'link' | 'update-existing' | 'create-new';
    targetDocuments: Document[];
    reason: string;
  };
}

// 搜索增强
interface SearchWithTags {
  query: string;
  tags?: {
    include: string[];         // 必须包含
    exclude: string[];         // 必须排除
    any: string[];            // 任一即可
  };
  filters?: {
    departments?: string[];
    dateRange?: [Date, Date];
    status?: string[];
  };
}
```

#### 3.4.3 贡献者与协作接口

```typescript
// 贡献者指标
interface ContributorMetrics {
  userId: string;
  
  // 贡献统计
  contributions: {
    documentsCreated: number;
    documentsUpdated: number;
    commentsPosted: number;
    helpfulVotes: number;
    expertAnswers: number;
  };
  
  // 影响力指标
  impact: {
    totalViews: number;
    avgRating: number;
    citedCount: number;
    followerCount: number;
  };
  
  // 活跃度
  activity: {
    lastActiveDate: Date;
    activeDays: number;
    streak: number;
    weeklyContributions: number;
  };
  
  // 徽章和成就
  badges: Badge[];
  level: 'Novice' | 'Contributor' | 'Expert' | 'Master';
}

// 评论
interface Comment {
  id: string;
  documentId: string;
  userId: string;
  parentId?: string;
  content: string;
  mentions: string[];          // @提及的用户
  replies?: Comment[];         // 嵌套回复
  likeCount: number;
  createdAt: Date;
  updatedAt: Date;
}

// 评分
interface Rating {
  id: string;
  documentId: string;
  userId: string;
  score: 1 | 2 | 3 | 4 | 5;
  dimensions?: Json;           // 多维度评分
  helpful?: boolean;
  tags?: string[];             // 评价标签
  comment?: string;
  createdAt: Date;
}
```

#### 3.4.4 反馈与支持接口

```typescript
// 反馈系统
interface FeedbackSystem {
  // 文档级反馈
  documentFeedback: {
    helpful: boolean;
    reason?: string;
    suggestions?: string;
    type: 'content' | 'format' | 'accuracy' | 'outdated';
  };
  
  // 系统级反馈
  systemFeedback: {
    feature: string;
    issueType: 'bug' | 'feature-request' | 'improvement';
    priority: 'low' | 'medium' | 'high' | 'urgent';
    description: string;
    screenshots?: string[];
  };
  
  // AI 问答反馈
  aiFeedback: {
    queryId: string;
    satisfaction: 'satisfied' | 'neutral' | 'dissatisfied';
    accuracy: boolean;
    completeness: boolean;
    issues?: string[];
  };
}

// FAQ 系统
interface FAQSystem {
  faqEntry: {
    id: string;
    question: string;
    answer: string;
    category: string;
    relatedDocs: string[];
    viewCount: number;
    helpfulCount: number;
    lastUpdated: Date;
  };
  
  intelligence: {
    autoGenerated: boolean;
    suggestedQuestions: string[];
    popularQuestions: FAQEntry[];
    personalizedFAQs: FAQEntry[];
  };
}

// 帮助聊天
interface HelpChat {
  // AI 自动回复
  aiAssistant: {
    enabled: boolean;
    autoReply: boolean;
    escalateToHuman: boolean;
    confidenceThreshold: number;
  };
  
  // 人工支持
  humanSupport: {
    available: boolean;
    averageWaitTime: number;
    queuePosition?: number;
  };
  
  // 聊天功能
  features: {
    fileSharing: boolean;
    screenSharing: boolean;
    coBrowsing: boolean;
    chatHistory: boolean;
  };
}
```

#### 3.4.5 权限与审批接口

```typescript
// 权限枚举
enum Permission {
  READ = 'kb:read',
  SEARCH = 'kb:search',
  CREATE = 'kb:create',
  EDIT = 'kb:edit',
  DELETE = 'kb:delete',
  PUBLISH = 'kb:publish',
  REVIEW = 'kb:review',
  ADMIN = 'kb:admin',
}

// 敏感内容配置
interface SensitiveConfig {
  level: 'public' | 'internal' | 'confidential' | 'secret';
  watermark: boolean;
  copyProtection: boolean;
  downloadDisabled: boolean;
  expiresAt?: Date;
  approvalRequired: boolean;
}

// 审批 SLA
interface ApprovalSLA {
  // 审批配置
  config: {
    defaultTimeout: number;      // 默认 48 小时
    warningTime: number;         // 36 小时预警
    escalationTime: number;      // 48 小时升级
  };
  
  // 提醒机制
  notifications: {
    '24h': string[];             // 邮件
    '36h': string[];             // 邮件+微信
    '48h': string[];             // 全通道+页面
  };
  
  // 升级规则
  escalation: {
    firstLevel: {
      after: number;
      action: 'notify-manager' | 'auto-approve';
      method: 'page' | 'email' | 'sms';
    };
    secondLevel: {
      after: number;
      action: 'auto-approve' | 'escalate';
      notify: 'all-stakeholders';
    };
  };
}

// 智能审批路由
interface SmartApprovalRouting {
  autoRouting: {
    byDepartment: boolean;
    byContent: boolean;
    byTags: boolean;
    bySensitivity: boolean;
  };
  
  approverPool: {
    primary: User[];
    backup: User[];
    delegates: User[];
  };
  
  parallelApproval: {
    enabled: boolean;
    requireAll: boolean;
    threshold?: number;          // 通过阈值
  };
}

// 审批历史
interface ApprovalHistory {
  documentId: string;
  timeline: Array<{
    timestamp: Date;
    actor: string;
    action: 'submitted' | 'approved' | 'rejected' | 'escalated' | 'auto-approved';
    level: number;
    comment?: string;
    duration: number;            // 审批耗时（分钟）
  }>;
  
  stats: {
    totalTime: number;
    averageTime: number;
    escalationCount: number;
  };
}
```

#### 3.4.6 AI 功能接口

```typescript
// AI 查询
interface AIQuery {
  question: string;
  context?: string;
  filters?: {
    departments?: string[];
    tags?: string[];
    dateRange?: [Date, Date];
  };
}

// AI 答案
interface AIAnswer {
  answer: string;
  confidence: number;           // 置信度 (0-1)
  sources: Array<{
    documentId: string;
    title: string;
    excerpt: string;
    relevance: number;
  }>;
  suggestions: string[];        // 相关问题建议
  metadata: {
    processingTime: number;
    model: string;
    tokensUsed: number;
  };
}

// 文档摘要
interface DocumentSummary {
  brief: string;               // 一句话总结（50 字内）
  abstract: string;            // 摘要（200-300 字）
  keyPoints: string[];         // 关键要点（3-5 个）
  toc?: TableOfContent[];      // 自动生成目录
  keywords: string[];          // 关键词提取
}

// 推荐策略
interface RecommendationStrategy {
  // 基于内容
  contentBased: {
    ragflowRelevance: number;
    keywordMatch: number;
    categoryMatch: boolean;
  };
  
  // 基于协同
  collaborative: {
    userBehavior: string[];
    coView: number;
    coEdit: number;
  };
  
  // 基于规则
  ruleBased: {
    mustHaveTags: string[];
    departmentMatch: boolean;
    roleMatch: boolean;
  };
}

// 智能标注
interface AutoTagging {
  contentAnalysis: string[];
  entityExtraction: {
    persons: string[];
    organizations: string[];
    locations: string[];
    technologies: string[];
  };
  topicModeling: string[];
  sentiment: 'positive' | 'neutral' | 'negative';
}

// AI 聊天机器人
interface ChatBot {
  // 基础能力
  qa: (question: string) => Promise<Answer>;
  
  // 多轮对话
  conversation: {
    context: Message[];
    clarify: (question: string) => Promise<string>;
    follow: (question: string) => Promise<Answer>;
  };
  
  // 任务执行
  tasks: {
    search: (query: string) => Promise<Document[]>;
    summarize: (documentId: string) => Promise<Summary>;
    compare: (docIds: string[]) => Promise<Comparison>;
  };
}
```

#### 3.4.7 术语表接口

```typescript
// 术语实体
interface GlossaryTerm {
  id: string;
  term: string;
  slug: string;
  aliases: string[];
  definition: string;
  category: string;
  
  // 关联信息
  relatedTerms: string[];
  relatedDocuments: string[];
  examples: string[];
  
  // 多语言支持
  translations: {
    [lang: string]: {
      term: string;
      definition: string;
    };
  };
  
  // 使用统计
  usageCount: number;
  documentCount: number;
  viewCount: number;
  trending: boolean;
  
  // 来源
  source: 'manual' | 'ai-suggested' | 'usage-detected';
  
  // 状态
  status: 'draft' | 'approved' | 'deprecated';
  owner: string;
  reviewers: string[];
  lastReviewDate?: Date;
  
  createdBy: string;
  createdAt: Date;
  updatedAt: Date;
}

// 术语检测
interface TermDetection {
  sources: {
    frequencyAnalysis: {
      threshold: number;
      contexts: string[];
      confidence: number;
    };
    inlineMarking: {
      userHighlighted: boolean;
      aiSuggested: boolean;
      requiresReview: boolean;
    };
    contextAnalysis: {
      technicalTerms: string[];
      businessTerms: string[];
      acronyms: string[];
      properNouns: string[];
    };
  };
  
  suggestions: Array<{
    term: string;
    frequency: number;
    documents: string[];
    suggestedDefinition?: string;
    priority: 'high' | 'medium' | 'low';
  }>;
}

// 术语链接
interface TermLinking {
  autoLink: {
    enabled: boolean;
    firstOccurrenceOnly: boolean;
    respectCaseSensitivity: boolean;
    excludeCodeBlocks: boolean;
  };
  
  linkStyle: {
    underline: 'dotted' | 'solid' | 'none';
    color: string;
    icon: boolean;
    tooltip: boolean;
  };
  
  interaction: {
    clickAction: 'tooltip' | 'modal' | 'sidebar' | 'new-tab';
    showDefinition: boolean;
    showRelatedTerms: boolean;
    showRelatedDocs: boolean;
  };
}

// 批量术语标记
interface BulkTermMarking {
  batchProcess: {
    documentIds: string[];
    terms: string[];
    mode: 'auto' | 'review';
  };
  
  result: {
    processed: number;
    linked: number;
    skipped: number;
    conflicts: Array<{
      document: string;
      term: string;
      reason: string;
    }>;
  };
}

// 术语分析
interface TermAnalytics {
  termId: string;
  
  usage: {
    totalOccurrences: number;
    uniqueDocuments: number;
    uniqueAuthors: number;
    trendingScore: number;
    growthRate: number;
  };
  
  distribution: {
    byDepartment: Map<string, number>;
    byDocumentType: Map<string, number>;
    byTimePeriod: TimeSeries[];
  };
  
  correlations: {
    coOccurringTerms: Array<{
      term: string;
      frequency: number;
    }>;
    relatedTopics: string[];
    commonContexts: string[];
  };
}

// 术语版本
interface TermVersion {
  termId: string;
  version: number;
  
  changes: {
    definition: {
      old: string;
      new: string;
    };
    status: {
      old: string;
      new: string;
    };
  };
  
  changeReason: string;
  reviewedBy: string[];
  effectiveDate?: Date;
  impactedDocuments: string[];
  notificationSent: boolean;
}
```

#### 3.4.8 度量与分析接口

```typescript
// 时间节省指标
interface TimeSavedMetrics {
  searchTime: {
    before: number;
    after: number;
    saved: number;
    savingsRate: number;
  };
  
  resolutionTime: {
    avgTimeToAnswer: number;
    traditionalMethod: number;
    timeSaved: number;
  };
  
  trainingTime: {
    onboardingBefore: number;
    onboardingAfter: number;
    reductionRate: number;
  };
  
  documentCreation: {
    withTemplate: number;
    withAI: number;
    traditional: number;
    efficiencyGain: number;
  };
  
  totalSaved: {
    hoursPerMonth: number;
    costSavings: number;
    productivity: number;
  };
}

// 使用率指标
interface UsageMetrics {
  overall: {
    dailyActiveUsers: number;
    monthlyActiveUsers: number;
    sessionsPerUser: number;
    avgSessionDuration: number;
  };
  
  featureAdoption: {
    search: number;
    aiQA: number;
    collaboration: number;
    glossary: number;
    feedback: number;
  };
  
  contentEngagement: {
    viewsPerDocument: number;
    commentsPerDocument: number;
    ratingsPerDocument: number;
    sharesPerDocument: number;
  };
  
  contributionRate: {
    activeContributors: number;
    contributorRatio: number;
    documentsPerContributor: number;
    qualityScore: number;
  };
}

// 采用率指标
interface AdoptionMetrics {
  byDepartment: Array<{
    department: string;
    adoptionRate: number;
    activeUsers: number;
    documentsCreated: number;
    engagementScore: number;
  }>;
  
  adoptionTrend: {
    week1: number;
    month1: number;
    month3: number;
    month6: number;
    targetAdoption: number;
    currentAdoption: number;
  };
  
  userSegments: {
    powerUsers: number;
    regularUsers: number;
    occasionalUsers: number;
    inactiveUsers: number;
  };
}

// 质量指标
interface QualityMetrics {
  approvalCycle: {
    avgApprovalTime: number;
    before48h: number;
    escalationRate: number;
    rejectionRate: number;
    target: number;
  };
  
  errorReduction: {
    productErrors: {
      before: number;
      after: number;
      reduction: number;
    };
    operationalErrors: {
      before: number;
      after: number;
      reduction: number;
    };
    attributionToKB: number;
  };
  
  consistency: {
    duplicateReduction: number;
    versionConflicts: number;
    outdatedDocs: number;
    updateFrequency: number;
  };
  
  contentQuality: {
    avgRating: number;
    completenesScore: number;
    accuracyRate: number;
    freshness: number;
  };
}

// 组织记忆指标
interface OrganizationalMemoryMetrics {
  knowledgeRetention: {
    totalDocuments: number;
    coverageScore: number;
    criticalKnowledge: {
      documented: number;
      missing: number;
      coverage: number;
    };
  };
  
  knowledgeTransfer: {
    employeeTurnover: number;
    knowledgeLossRate: number;
    knowledgeRetainedRate: number;
    successorReadiness: number;
  };
  
  traceability: {
    versionedDocuments: number;
    avgVersionsPerDoc: number;
    oldestDocument: Date;
    archiveSize: number;
  };
  
  reuseMetrics: {
    referencesCount: number;
    mostReferencedDocs: Array<{
      title: string;
      refCount: number;
    }>;
    crossDeptReuse: number;
  };
}
```

---

## 5. 核心模块

### 5.1 模块结构

```
backend/src/modules/knowledge-base/
├── dto/
├── knowledge-base.controller.ts
├── knowledge-base.module.ts
├── knowledge-base.service.ts
├── knowledge-article.service.ts
├── services/
│   ├── activity.service.ts
│   ├── metrics.service.ts
│   ├── sharepoint-sync.service.ts
│   ├── ragflow.service.ts
│   ├── ragflow-search.service.ts
│   ├── ragflow-sync.service.ts
│   ├── knowledge-qa.service.ts
│   └── prosemirror-text.service.ts
├── tasks/
│   └── sync-scheduler.task.ts
└── knowledge-base.module.ts
```

### 5.2 服务层设计

#### 5.2.1 检索服务（RAGFlow）

```typescript
@Injectable()
export class RagflowSearchService {
  constructor(
    private readonly ragflowService: RagflowService,
    private readonly prisma: PrismaService,
  ) {}

  async search(query: string, limit: number) {
    const datasetId = await this.ragflowService.resolveDatasetId();
    const data = await this.ragflowService.retrieve(datasetId, query, limit);
    return this.mergeWithMetadata(data);
  }
}
```

#### 5.2.2 问答服务（RAGFlow）

```typescript
@Injectable()
export class KnowledgeQaService {
  constructor(
    private readonly ragflowService: RagflowService,
    private readonly prisma: PrismaService,
  ) {}

  async ask(question: string) {
    const datasetId = await this.ragflowService.resolveDatasetId();
    const chatId = await this.ragflowService.resolveChatId(datasetId);
    return this.ragflowService.createChatCompletion(chatId, question);
  }
}
```

---

## 6. 接口设计

### 5.1 RESTful API

详见：[API 文档](./07-api.md)

**核心端点**：

```typescript
// 搜索
GET    /api/knowledge-base/semantic-search        // 语义/关键词/混合搜索

// AI 问答
POST   /api/knowledge-base/ask                    // AI 问答
POST   /api/knowledge-base/ask/:id/feedback       // 问答反馈
GET    /api/knowledge-base/ask/history            // 问答历史

// 原生文章
POST   /api/knowledge-base/articles               // 创建文章
GET    /api/knowledge-base/articles/:id           // 获取文章
PUT    /api/knowledge-base/articles/:id           // 更新文章

// 上传
POST   /api/knowledge-base/upload                 // 上传文件

// 同步
POST   /api/knowledge-base/sync/full              // 全量同步
GET    /api/knowledge-base/sync/tasks/:taskId     // 同步任务状态

// 统计与活动
GET    /api/knowledge-base/metrics                // 指标概览
GET    /api/knowledge-base/metrics/trend          // 每日趋势
GET    /api/knowledge-base/metrics/top-queries    // 搜索热词
GET    /api/knowledge-base/recently-visited       // 最近访问
POST   /api/knowledge-base/view/:type/:id         // 记录浏览
GET    /api/knowledge-base/activity               // 活动动态
GET    /api/knowledge-base/user-analytics         // 用户统计
GET    /api/knowledge-base/unread-mentions        // 未读提及
POST   /api/knowledge-base/mentions/:id/read      // 标记提及已读
```

### 5.2 WebSocket 事件

```typescript
// 实时协作
io.on('connection', (socket) => {
  // 文档编辑
  socket.on('document:edit', (data) => {
    // 广播给其他用户
    socket.to(data.documentId).emit('document:updated', data);
  });
  
  // AI 问答流式响应
  socket.on('ai:query:stream', async (data) => {
    const stream = await aiService.queryStream(data.question);
    
    for await (const chunk of stream) {
      socket.emit('ai:answer:chunk', chunk);
    }
    
    socket.emit('ai:answer:complete');
  });
});
```

---

## 7. 部署架构

### 6.1 开发环境

开发环境统一使用 `docker/docker-compose.yml`，通过 `knowledge` profile 启动 RAGFlow 及依赖（MySQL / MinIO / Elasticsearch / Redis）。

关键配置：
- RAGFlow API 地址：`RAGFLOW_BASE_URL`
- 后端与 RAGFlow 的鉴权：`RAGFLOW_API_KEY`
- 数据集/助手名称：`RAGFLOW_DATASET_NAME`、`RAGFLOW_CHAT_NAME`

### 6.2 生产环境

生产环境建议将 RAGFlow 及其依赖组件独立部署：
- RAGFlow 服务（API + Web）
- Elasticsearch（索引）
- MySQL（元数据）
- MinIO（对象存储）
- Redis（缓存/队列）

后端仅依赖 RAGFlow API（`RAGFLOW_BASE_URL` + `RAGFLOW_API_KEY`），不再直接访问底层索引存储。

### 6.3 监控配置

```yaml
# Prometheus 监控
scrape_configs:
  - job_name: 'knowledge-base-api'
    static_configs:
      - targets: ['knowledge-base-api:3001']
    metrics_path: '/metrics'

# Grafana 仪表板
# - API 请求量和响应时间
# - SharePoint API 调用统计
# - RAGFlow 检索性能
# - AI 问答成功率
# - 缓存命中率
```

---

## 8. 性能优化

### 7.1 缓存策略

```typescript
// 多级缓存
class CacheStrategy {
  // L1: 内存缓存（最热数据）
  private memoryCache = new LRUCache<string, any>({ max: 1000 });
  
  // L2: Redis 缓存（常用数据）
  private redisCache: Redis;
  
  // L3: SharePoint + 数据库（持久化数据）
  
  async get(key: string): Promise<any> {
    // 1. 检查内存缓存
    let value = this.memoryCache.get(key);
    if (value) return value;
    
    // 2. 检查 Redis
    value = await this.redisCache.get(key);
    if (value) {
      this.memoryCache.set(key, value);
      return value;
    }
    
    // 3. 从数据库/SharePoint 获取
    value = await this.fetchFromSource(key);
    
    // 回填缓存
    await this.redisCache.setex(key, 3600, value);
    this.memoryCache.set(key, value);
    
    return value;
  }
}
```

### 7.2 批量操作优化

```typescript
// SharePoint 批量请求
class BatchOperationService {
  async batchGetDocuments(ids: string[]): Promise<Document[]> {
    const batch = this.sp.web.createBatch();
    
    const promises = ids.map(id =>
      this.sp.web.lists
        .getByTitle('Documents')
        .items
        .getById(id)
        .inBatch(batch)
        .get()
    );
    
    await batch.execute();
    
    return Promise.all(promises);
  }
}
```

### 7.3 检索优化（RAGFlow）

```typescript
// RAGFlow 检索策略（示意）
const retrievalConfig = {
  pageSize: 10,
  useHybrid: true,
  rerank: true,
  filters: {
    authorityLevel: ['OFFICIAL', 'EXPERT', 'PUBLISHED'],
    docType: ['POLICY', 'MANUAL', 'FAQ'],
  },
};
```

---

## 9. 安全架构

### 8.1 认证与授权

```typescript
// Azure AD + JWT
@Injectable()
export class AuthGuard implements CanActivate {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    
    // 1. 验证 Azure AD Token
    const token = this.extractToken(request);
    const user = await this.authService.validateAzureToken(token);
    
    // 2. 检查用户权限
    const hasPermission = await this.permissionService.check(
      user.id,
      request.path,
      request.method
    );
    
    return hasPermission;
  }
}
```

### 8.2 数据加密

- **传输加密**：TLS 1.3
- **存储加密**：
  - PostgreSQL: 数据库级加密
  - SharePoint: Microsoft 托管加密
  - 检索索引数据: 加密存储

### 8.3 审计日志

```typescript
@Injectable()
export class AuditService {
  async log(event: AuditEvent) {
    await this.prisma.auditLog.create({
      data: {
        userId: event.userId,
        action: event.action,
        resource: event.resource,
        resourceId: event.resourceId,
        changes: event.changes,
        ip: event.ip,
        userAgent: event.userAgent,
        timestamp: new Date(),
      }
    });
  }
}
```

---

## 10. 技术风险与应对

| 风险 | 影响 | 概率 | 应对措施 |
|------|------|------|---------|
| **SharePoint API 限流** | 中 | 高 | 请求缓存、批量操作、降级方案 |
| **RAGFlow 检索性能** | 中 | 中 | 检索参数调优、缓存 |
| **AI 成本超标** | 高 | 中 | Token 优化、缓存、模型选型 |
| **数据同步延迟** | 中 | 中 | 全量同步 + 监控告警 |
| **大文件处理** | 中 | 中 | 分块上传、异步处理、队列 |

---

## 10. 相关文档

- [PRD.md](./01-prd.md) - 产品需求文档
- [07-api.md](./07-api.md) - API 接口文档
- [DATA_MODEL.md](./05-data-model.md) - 数据模型设计
- [CHANGELOG.md](./99-changelog.md) - 变更日志
- [README.md](./README.md) - 模块说明

### 依赖模块文档

- [文档编辑引擎](../document-editor/) - KB 原生文章编辑、版本控制
- [审批引擎](../approval/) - 文档审批流程
- [组织架构](../organization/) - 用户和权限管理
- [通知引擎](../notification/) - 消息推送和通知
- [反馈系统](../feedback/) - 用户反馈收集

---

## 📝 变更历史

| 版本 | 日期 | 变更说明 |
|------|------|----------|
| v1.2.0 | 2026-02-03 | 统一切换为 RAGFlow 架构与同步方案 |
| v1.1.0 | 2026-01-05 | **模块依赖明确化**：<br>• 更新 SharePoint 功能矩阵，标注版本控制、编辑、审批的模块依赖<br>• 添加依赖模块文档链接<br>• 同步 PRD v1.2.2 的模块职责优化 |
| v1.0.0 | 2025-12-22 | 初始版本 - 从 PRD.md 拆分技术架构内容 |

---

**维护人**：Tech Team  
**最后更新**：2025-12-22
