Skip to content

技术架构设计

企业级架构说明

本文档基于 JitWord 的实际代码实现,详细介绍系统架构、核心算法、扩展机制与数据流设计,为企业二次开发提供技术指导。

🎯 产品技术概览

JitWord 是一款企业级智能协同文档编辑器,具备以下核心技术特色:

  • 超低延迟协作:基于 CRDT 算法实现 10-30ms 协作延迟
  • 智能版本管理:自动版本保存与可视化版本对比
  • 多格式文档处理:支持 DOCX、PDF、Markdown 等格式的智能解析与导出
  • AI 深度集成:集成多种主流大模型,提供智能写作和改写能力
  • 企业级安全:JWT 认证、权限控制、操作审计等安全机制
  • 高度可扩展:模块化架构,支持自定义组件和功能扩展

🏗️ 系统架构设计

整体架构图

项目结构详解

app/                          # 前端应用
  src/
    api/                      # API 接口封装
      document.ts             # 文档相关接口
      version.ts              # 版本管理接口
      file.ts                 # 文件处理接口
    components/               # 业务组件
      VersionManager.vue      # 版本管理组件
      VersionCompare.vue      # 版本对比组件
      AIWriterModal.vue       # AI 写作弹窗
      AIRewriteModal.vue      # AI 改写弹窗
    packages/                 # 编辑器核心包
      core/                   # 核心功能模块
        extensions/           # 扩展组件
          chart/              # 图表组件
          flowchart/          # 流程图组件
          table/              # 表格组件
          audio.js            # 音频组件
          video.js            # 视频组件
      vue3/                   # Vue 3 适配层
        components/           # UI 组件
        kit/                  # 功能套件
    utils/                    # 工具函数
      ai/                     # AI 相关工具
        providers.ts          # AI 服务提供商
        config.ts             # AI 配置管理
        openaiStream.ts       # 流式处理
      export/                 # 导出工具
        wordExporter.js       # Word 导出
        pdfExporter.js        # PDF 导出
        markdownExporter.js   # Markdown 导出
      import/                 # 导入工具
        mammothImporter.js    # DOCX 导入
        markdownImporter.js   # Markdown 导入
    views/
      notion-page/            # 主编辑页面
        notion-page.vue       # 编辑器主界面

server/                       # 后端服务
  src/
    index.js                  # 服务入口,Koa + WebSocket
    config/
      index.js                # 服务配置
    router/                   # 路由模块
      version.js              # 版本管理路由
      upload.js               # 文件上传路由
      parse.js                # 文档解析路由
      message.js              # SSE 事件推送
    lib/                      # 工具库
      file.js                 # 文件处理工具
      ydoc.js                 # 协作文档工具
    service/                  # 业务服务
      index.js                # 通用服务方法
  db/
    version/                  # 版本数据存储
      {docId}.json            # 每个文档的版本历史
  public/
    uploads/                  # 上传文件存储
    y-static/                 # 静态资源

🔄 核心算法设计

实时协作算法

JitWord 采用 CRDT(Conflict-free Replicated Data Type) 算法实现无冲突的多人实时协作:

算法特点

  1. 操作可交换性:不同用户的操作可以以任意顺序执行,最终结果一致
  2. 操作可合并性:多个操作可以智能合并,减少网络传输
  3. 最终一致性:所有客户端最终会收敛到相同的文档状态
  4. 无需中央协调:不依赖中央服务器进行冲突解决

协作流程

冲突解决策略

冲突类型解决策略说明
同位置插入时间戳优先较早的操作优先执行
同内容删除幂等处理重复删除操作被忽略
格式冲突最后写入胜出最后的格式设置生效
结构冲突智能合并基于语义的智能合并

版本管理算法

版本存储结构

json
{
  "documentId": "doc_123456",
  "versions": [
    {
      "id": "v_001",
      "title": "初始版本",
      "description": "文档创建",
      "content": {...},
      "author": "user_001",
      "isAutoSave": false,
      "createdAt": "2024-01-01T10:00:00Z",
      "metadata": {
        "wordCount": 1500,
        "characterCount": 8000,
        "changesSinceLastVersion": 150
      }
    }
  ]
}

版本对比算法

采用 Myers 差分算法 进行版本内容对比:

  1. 文本级对比:基于字符和单词的精确差异识别
  2. 结构级对比:识别段落、标题、列表等结构变化
  3. 样式级对比:检测格式、颜色、字体等样式变化
  4. 组件级对比:图表、表格等复杂组件的变化识别

🧩 扩展组件架构

组件扩展机制

JitWord 提供了强大的组件扩展系统,支持开发者创建自定义组件:

扩展组件结构

javascript
// 组件定义示例
export const CustomComponent = Node.create({
  name: 'customComponent',
  
  // 组件配置
  addOptions() {
    return {
      name: 'customComponent',
      desc: '自定义组件',
      slash: true, // 支持斜杠命令
      command: ({ editor, range }) => {
        // 插入逻辑
      }
    };
  },
  
  // 属性定义
  addAttributes() {
    return {
      data: {
        default: null,
        parseHTML: element => element.getAttribute('data-config'),
        renderHTML: attributes => ({
          'data-config': attributes.data
        })
      }
    };
  },
  
  // 渲染逻辑
  addNodeView() {
    return ({ node, HTMLAttributes, getPos, editor }) => {
      // 创建 DOM 元素
      // 绑定事件处理
      // 返回更新和销毁方法
    };
  }
});

内置扩展组件

图表组件系统

支持的图表类型

  • 柱状图(Bar Chart)
  • 折线图(Line Chart)
  • 面积图(Area Chart)
  • 饼图(Pie Chart)
  • 环形图(Doughnut Chart)
  • 散点图(Scatter Chart)
  • 雷达图(Radar Chart)

图表配置能力

javascript
const chartConfig = {
  type: 'bar',
  title: '销售数据统计',
  data: {
    categories: ['Q1', 'Q2', 'Q3', 'Q4'],
    series: [
      { name: '产品A', data: [120, 200, 150, 80] },
      { name: '产品B', data: [80, 160, 120, 100] }
    ]
  },
  config: {
    theme: 'default',
    showLegend: true,
    legendPosition: 'bottom',
    showGrid: true,
    showTooltip: true,
    colors: ['#5470c6', '#91cc75', '#fac858']
  }
};

流程图组件系统

节点类型

  • 开始/结束节点
  • 处理节点
  • 判断节点
  • 数据节点
  • 连接器节点

连接线类型

  • 直线连接
  • 曲线连接
  • 折线连接
  • 带箭头连接

表格组件系统

表格功能

  • 动态行列增删
  • 单元格合并拆分
  • 表格样式设置
  • 数据排序和筛选
  • 表格导入导出

📊 数据流设计

文档数据流

AI 处理数据流

🔧 核心模块详解

协作同步模块

文件位置server/src/index.jsapp/src/utils/collaboration/

核心功能

  • WebSocket 连接管理
  • 房间(文档)级别的用户管理
  • 操作广播和同步
  • 断线重连处理
  • 协作状态推送

关键实现逻辑

javascript
// 协作房间管理
const rooms = new Map();

// 用户加入房间
function joinRoom(docId, userId, ws) {
  if (!rooms.has(docId)) {
    rooms.set(docId, new Set());
  }
  rooms.get(docId).add({ userId, ws });
}

// 广播操作到房间内其他用户
function broadcastToRoom(docId, operation, excludeUserId) {
  const room = rooms.get(docId);
  if (room) {
    room.forEach(({ userId, ws }) => {
      if (userId !== excludeUserId) {
        ws.send(JSON.stringify(operation));
      }
    });
  }
}

版本管理模块

文件位置server/src/router/version.jsapp/src/api/version.ts

存储设计

  • 每个文档对应一个 JSON 文件:server/db/version/{docId}.json
  • 版本数据包含完整的文档内容快照
  • 支持版本元数据(标题、描述、作者、时间戳)

版本对比算法

javascript
// 版本差异计算
function calculateDiff(version1, version2) {
  const diff = {
    added: [],
    removed: [],
    modified: []
  };
  
  // 使用 Myers 算法计算文本差异
  const textDiff = diffText(version1.content, version2.content);
  
  // 结构化差异分析
  const structureDiff = diffStructure(version1.structure, version2.structure);
  
  return { ...diff, textDiff, structureDiff };
}

文件处理模块

文件位置server/src/router/upload.jsserver/src/router/parse.js

处理流程

  1. 文件上传:使用 Multer 中间件处理文件上传
  2. 格式识别:基于文件扩展名和 MIME 类型识别格式
  3. 内容解析:调用相应的解析器处理文件内容
  4. 结果缓存:使用 LRU 缓存提升解析性能

DOCX 解析流程

javascript
// DOCX 解析核心逻辑
async function parseDocx(filePath) {
  const result = await mammoth.convertToHtml({ path: filePath });
  
  // 提取图片
  const images = await extractImages(filePath);
  
  // 转换为编辑器格式
  const editorContent = convertHtmlToEditorFormat(result.value);
  
  return {
    content: editorContent,
    images: images,
    metadata: extractMetadata(result)
  };
}

AI 集成模块

文件位置app/src/utils/ai/

架构设计

  • Provider 适配器:统一不同 AI 服务商的接口
  • 流式处理:支持实时流式内容生成
  • 配置管理:灵活的模型和参数配置
  • 错误处理:完善的错误处理和降级机制

Provider 接口设计

typescript
interface AIProvider {
  key: string;
  label: string;
  baseUrl: string;
  models: ModelConfig[];
  buildHeaders?: (apiKey: string) => Record<string, string>;
}

interface ModelConfig {
  label: string;
  value: string;
  maxTokens?: number;
  supportStream?: boolean;
}

🔌 扩展开发指南

自定义组件开发

1. 创建组件定义

javascript
// 在 app/src/packages/core/extensions/ 下创建组件
export const MyCustomNode = Node.create({
  name: 'myCustomNode',
  
  addOptions() {
    return {
      name: 'myCustomNode',
      desc: '我的自定义组件',
      slash: true,
      command: ({ editor, range }) => {
        // 插入组件的逻辑
        editor.chain().focus().insertContent({
          type: 'myCustomNode',
          attrs: { data: '{}' }
        }).run();
      }
    };
  },
  
  addAttributes() {
    return {
      data: {
        default: '{}',
        parseHTML: element => element.getAttribute('data-config'),
        renderHTML: attributes => ({
          'data-config': attributes.data
        })
      }
    };
  },
  
  addNodeView() {
    return ({ node, HTMLAttributes, getPos, editor }) => {
      // 创建 DOM 元素
      const dom = document.createElement('div');
      dom.className = 'my-custom-component';
      
      // 渲染组件内容
      this.renderComponent(dom, node.attrs.data);
      
      return {
        dom,
        update: (updatedNode) => {
          if (updatedNode.attrs.data !== node.attrs.data) {
            this.renderComponent(dom, updatedNode.attrs.data);
          }
          return true;
        },
        destroy: () => {
          // 清理资源
        }
      };
    };
  }
});

2. 注册组件

javascript
// 在 app/src/packages/vue3/kit/notion-kit.js 中注册
import { MyCustomNode } from '../extensions/my-custom-node';

export default BasicKit.extend({
  name: 'notionKit',
  
  addExtensions() {
    return [
      ...this.parent?.(),
      MyCustomNode,
      // 其他扩展
    ];
  }
});

API 扩展开发

1. 创建路由模块

javascript
// 在 server/src/router/ 下创建新的路由文件
export default (router, prefix) => {
  // GET 接口
  router.get(`${prefix}/my-api/list`, async (ctx) => {
    try {
      // 业务逻辑
      ctx.body = {
        code: 200,
        data: result,
        message: 'success'
      };
    } catch (error) {
      ctx.body = {
        code: 500,
        message: error.message
      };
    }
  });
  
  // POST 接口
  router.post(`${prefix}/my-api/create`, async (ctx) => {
    // 实现创建逻辑
  });
};

2. 注册路由

javascript
// 在 server/src/index.js 中注册路由
import myApiRouter from './router/my-api.js';

// 注册路由
myApiRouter(router, '/api/v1');

📈 性能优化策略

前端性能优化

  1. 组件懒加载:大型组件按需加载
  2. 虚拟滚动:长文档的虚拟滚动优化
  3. 防抖节流:用户输入的防抖处理
  4. 缓存策略:合理的数据缓存和更新策略

后端性能优化

  1. 连接池管理:WebSocket 连接的高效管理
  2. 内存缓存:LRU 缓存热点数据
  3. 异步处理:文件解析等耗时操作异步化
  4. 负载均衡:支持多实例部署和负载均衡

数据库优化

  1. 索引设计:为查询频繁的字段建立索引
  2. 分页查询:大数据量的分页处理
  3. 数据归档:历史数据的定期归档
  4. 读写分离:支持读写分离架构

🔒 安全架构设计

认证安全

  1. JWT Token:无状态认证,支持分布式部署
  2. Token 刷新:自动 Token 刷新机制
  3. 权限验证:基于角色的权限控制
  4. 会话管理:安全的会话生命周期管理

数据安全

  1. 传输加密:HTTPS/WSS 加密传输
  2. 存储加密:敏感数据加密存储
  3. 访问控制:细粒度的数据访问控制
  4. 审计日志:完整的操作审计记录

接口安全

  1. 参数验证:严格的输入参数验证
  2. SQL 注入防护:参数化查询防止注入攻击
  3. XSS 防护:输出内容的 XSS 过滤
  4. CSRF 防护:跨站请求伪造防护

🚀 部署架构设计

单机部署

扩展架构设计

JitWord 采用模块化架构设计,支持良好的前端扩展能力:

架构特点

  • 前端扩展友好:支持各种组件、样式扩展,开发门槛低
  • 数据对接容易:标准化的数据接口,便于企业系统集成
  • 模块化设计:清晰的模块划分,便于维护和扩展
  • 轻量级部署:单体应用架构,部署简单,也支持横向拓展,适应企业内部业务场景

🔍 监控与运维

关键指标监控

指标类别具体指标监控阈值处理建议
性能指标API 响应时间< 200ms优化查询逻辑
WebSocket 延迟< 50ms检查网络和服务器性能
内存使用率< 80%增加内存或优化内存使用
CPU 使用率< 70%扩容或优化算法
业务指标在线用户数实时监控容量规划
文档创建数日/周/月统计业务分析
版本创建频率实时监控存储容量规划
AI 调用次数实时监控成本控制
错误指标错误率< 1%排查和修复
协作连接失败率< 0.1%网络和服务优化
文件解析失败率< 5%解析器优化

日志系统设计

javascript
// 统一日志格式
const logEntry = {
  timestamp: new Date().toISOString(),
  level: 'info', // debug, info, warn, error
  module: 'collaboration', // 模块名
  action: 'user_join', // 操作类型
  docId: 'doc_123456',
  userId: 'user_001',
  metadata: {
    userAgent: req.headers['user-agent'],
    ip: req.ip,
    duration: 150, // 操作耗时(ms)
    success: true
  }
};

🛠️ 开发最佳实践

代码规范

  1. 命名规范:使用语义化的变量和函数命名
  2. 注释规范:关键逻辑添加详细注释
  3. 错误处理:完善的错误处理和用户提示
  4. 类型安全:使用 TypeScript 确保类型安全

测试策略

  1. 单元测试:核心算法和工具函数的单元测试
  2. 集成测试:API 接口的集成测试
  3. 协作测试:多用户协作场景的压力测试
  4. 性能测试:系统性能和并发能力测试

部署策略

  1. 灰度发布:新功能的灰度发布和回滚机制
  2. 监控告警:完善的监控和告警体系
  3. 备份恢复:数据备份和灾难恢复方案
  4. 容量规划:基于业务增长的容量规划

📋 数据存储设计

存储结构

数据类型存储位置格式说明
版本历史server/db/version/{docId}.jsonJSON每文档一个文件,包含完整版本历史
上传文件server/public/uploads/原始文件用户上传的图片、文档等文件
静态资源server/public/y-static/静态文件系统静态资源和生成的文件
解析缓存内存 LRU 缓存JSON文档解析结果的临时缓存

版本数据结构

json
{
  "documentId": "doc_123456",
  "versions": [
    {
      "id": "v_20240101_001",
      "title": "项目需求文档 v1.0",
      "description": "初始版本,包含基础需求",
      "content": {
        "type": "doc",
        "content": [...]
      },
      "author": "user_001",
      "isAutoSave": false,
      "createdAt": "2024-01-01T10:00:00Z",
      "updatedAt": "2024-01-01T10:00:00Z",
      "metadata": {
        "wordCount": 1500,
        "characterCount": 8000,
        "paragraphCount": 25,
        "imageCount": 3,
        "chartCount": 2,
        "tableCount": 1
      }
    }
  ]
}

🔧 运行时配置

环境变量配置

前端配置

bash
# API 服务地址
VITE_BASE_API=http://localhost:3002/api/v1

# WebSocket 服务地址
BASE_WS_URL=ws://localhost:3002

# 图片服务配置
IMAGE_SERVICE_HOST=localhost
IMAGE_SERVICE_PORT=3002
IMAGE_PROXY_ENABLED=auto

# 构建配置
NODE_ENV=production

后端配置

bash
# 运行环境
NODE_ENV=production

# 服务端口
PORT=3002

# JWT 密钥
JWT_SECRET=your-jwt-secret-key

# 数据存储路径
DB_PATH=./db
UPLOAD_PATH=./public/uploads
STATIC_PATH=./public/y-static

# 文件上传限制
MAX_FILE_SIZE=50MB
ALLOWED_FILE_TYPES=jpg,png,gif,svg,docx,pdf,mp3,mp4

# 缓存配置
CACHE_MAX_SIZE=1000
CACHE_TTL=3600

📊 扩展组件示例:图表组件

组件实现流程

图表数据结构

typescript
interface ChartData {
  type: 'bar' | 'line' | 'pie' | 'area' | 'scatter' | 'radar' | 'doughnut';
  title: string;
  data: {
    categories: string[];
    series: Array<{
      name: string;
      data: number[];
      color?: string;
    }>;
  };
  config: {
    theme: 'default' | 'dark' | 'light';
    showLegend: boolean;
    legendPosition: 'top' | 'bottom' | 'left' | 'right';
    showGrid: boolean;
    showTooltip: boolean;
    colors?: string[];
    animation?: boolean;
  };
}

图表组件核心代码

javascript
// 图表组件的核心实现
export const Chart = Node.create({
  name: 'chart',
  
  addOptions() {
    return {
      name: 'chart',
      desc: '图表',
      slash: true,
      command: ({ editor, range }) => {
        // 删除斜杠命令文本
        if (range) {
          editor.chain().focus().deleteRange(range).run();
        }
        
        // 打开图表编辑器
        if (window.openChartEditor) {
          window.isSlashCommand = true;
          window.slashEditor = editor;
          window.openChartEditor();
        }
      }
    };
  },
  
  addNodeView() {
    return ({ node, HTMLAttributes, getPos, editor }) => {
      const dom = document.createElement('div');
      dom.className = 'chart-container';
      
      // 创建图表容器
      const chartContainer = document.createElement('div');
      chartContainer.style.cssText = `
        width: 100%;
        height: 400px;
        border: 1px solid #e0e0e0;
        border-radius: 4px;
        background: #fff;
      `;
      
      // 创建编辑按钮
      const editButton = document.createElement('button');
      editButton.innerHTML = '编辑图表';
      editButton.onclick = () => {
        const chartData = node.attrs.data ? JSON.parse(node.attrs.data) : null;
        window.currentChartPosition = getPos();
        if (window.openChartEditor) {
          window.openChartEditor(chartData);
        }
      };
      
      dom.appendChild(chartContainer);
      dom.appendChild(editButton);
      
      // 渲染图表
      this.renderChart(chartContainer, node.attrs.data);
      
      return {
        dom,
        update: (updatedNode) => {
          if (updatedNode.attrs.data !== node.attrs.data) {
            this.renderChart(chartContainer, updatedNode.attrs.data);
          }
          return true;
        },
        destroy: () => {
          // 清理图表实例
          if (chartContainer._chartInstance) {
            chartContainer._chartInstance.dispose();
          }
        }
      };
    };
  }
});

📞 技术支持与源码授权

🎯 源码授权服务

  • ✅ 源码永久授权:一次购买,永久使用,支持商业化部署
  • ✅ 可二次开发:完全开放源码,支持企业定制化开发
  • ❌ 源码不可二次分发:不得将源码再次销售或分发给第三方

🛠️ 技术支持

  • 技术咨询微信:cxzk_168(工作时间响应)
  • 企业咨询:cxzk_168(微信)
  • 技术文档:完整的架构设计和开发指导

我们专注于为企业提供可靠的私有化文档协作解决方案和专业的技术支持服务。

让技术更平权,致力于高性价办公协同解决方案