Skip to content

二次开发指南

开发指南说明

本指南基于 JitWord 的实际代码结构,提供详细的二次开发指导,包括接口对接、数据流程、扩展开发等核心内容。

⚙️ 开发环境配置

环境要求

  • Node.js:18.0+ 版本
  • 包管理器:pnpm(推荐)、npm 或 yarn
  • 操作系统:Windows、Linux、macOS
  • 浏览器:Chrome 90+、Firefox 88+、Safari 14+

项目启动

1. 后端服务启动

powershell
cd server
pnpm install

# 开发环境
$env:NODE_ENV="development"; npm run start:win

# 生产环境
$env:NODE_ENV="production"; npm run build:js; pm2 start dist/index.js --name jitword-server

关键配置文件

  • src/config/index.js:服务配置(端口、JWT密钥、静态目录等)
  • src/index.js:服务入口(HTTP + WebSocket + 路由注册)

2. 前端应用启动

powershell
cd app
pnpm install
pnpm dev

环境变量配置

  • VITE_BASE_API:API 服务地址
  • BASE_WS_URL:WebSocket 服务地址(见 utils/tool.ts

🔐 认证与权限系统

JWT 认证机制

后端实现src/service/index.js):

javascript
import jwt from 'jsonwebtoken';
import { promisify } from 'util';

const verify = promisify(jwt.verify);

// Token 验证工具
export const useToken = async (ctx) => {
  const authHeader = ctx.header.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    throw new Error('缺少认证令牌');
  }
  
  const token = authHeader.split(' ')[1];
  return await verify(token, config.jwt_secret);
};

// 认证中间件
export const auth = async (ctx, next) => {
  try {
    ctx.state.user = await useToken(ctx);
    await next();
  } catch (error) {
    ctx.status = 401;
    ctx.body = { error: '认证失败' };
  }
};

前端使用

javascript
// 请求拦截器添加 Token
axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

权限控制

白名单配置src/index.js):

javascript
app.use(koajwt({ secret: config.jwt_secret }).unless({
  path: [
    /^\/health/,
    /^\/api\/v1\/auth/,
    /^\/public/,
    /^\/static/
  ]
}));

🗃️ 数据存储设计

存储结构

数据类型存储位置格式说明
版本历史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
      }
    }
  ]
}

📦 版本管理系统

版本管理 API

后端接口router/version.js):

功能方法路径说明
版本列表GET/documents/:docId/versions?page=&limit=分页获取版本列表
创建版本POST/documents/:docId/versions创建新版本
版本详情GET/documents/:docId/versions/:versionId获取版本详情
更新版本PUT/documents/:docId/versions/:versionId更新版本信息
删除版本DELETE/documents/:docId/versions/:versionId删除版本
版本对比GET/documents/:docId/versions/:v1/compare/:v2版本差异对比

前端 API 封装app/src/api/version.ts):

typescript
export const versionApi = {
  // 获取版本列表
  async getVersions(docId: string, page = 1, limit = 20) {
    return request.get(`/documents/${docId}/versions`, {
      params: { page, limit }
    });
  },
  
  // 创建版本
  async createVersion(docId: string, data: CreateVersionData) {
    return request.post(`/documents/${docId}/versions`, data);
  },
  
  // 版本对比
  async compareVersions(docId: string, v1: string, v2: string) {
    return request.get(`/documents/${docId}/versions/${v1}/compare/${v2}`);
  },
  
  // 版本恢复
  async restoreVersion(docId: string, versionId: string) {
    return request.post(`/documents/${docId}/versions/${versionId}/restore`);
  }
};

版本恢复流程

注意事项

  • 版本恢复会覆盖当前协作中的内容,需要用户二次确认
  • 恢复完成后建议立即创建"恢复点"版本
  • 自动保存功能建议使用防抖处理,避免频繁创建版本

📥 文件处理系统

文件上传处理

后端实现router/upload.js):

javascript
import multer from 'koa-multer';
import path from 'path';

// 配置文件上传
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'public/uploads/');
  },
  filename: (req, file, cb) => {
    const uniqueName = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    const ext = path.extname(file.originalname);
    cb(null, `${uniqueName}${ext}`);
  }
});

const upload = multer({
  storage: storage,
  limits: {
    fileSize: 50 * 1024 * 1024 // 50MB
  },
  fileFilter: (req, file, cb) => {
    const allowedTypes = [
      'image/jpeg', 'image/png', 'image/gif', 'image/svg+xml',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'application/pdf', 'audio/mpeg', 'video/mp4'
    ];
    
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('不支持的文件类型'));
    }
  }
});

文档解析系统

DOCX 解析

解析流程router/parse.js):

javascript
import mammoth from 'mammoth';

async function parseDocx(filePath) {
  try {
    // 使用 Mammoth 解析 DOCX
    const result = await mammoth.convertToHtml({ path: filePath });
    
    // 提取图片
    const images = await extractImagesFromDocx(filePath);
    
    // 转换为编辑器格式
    const editorContent = convertHtmlToEditorFormat(result.value);
    
    return {
      success: true,
      content: editorContent,
      images: images,
      metadata: {
        title: extractTitle(result),
        wordCount: countWords(result.value),
        warnings: result.messages
      }
    };
  } catch (error) {
    return {
      success: false,
      error: error.message
    };
  }
}

PDF 解析

javascript
import pdf from 'pdf-parse';

async function parsePdf(filePath) {
  try {
    const dataBuffer = fs.readFileSync(filePath);
    const pdfData = await pdf(dataBuffer);
    
    // 分页处理
    const pages = pdfData.text.split('\f'); // 分页符分割
    
    // 转换为编辑器格式
    const editorContent = convertTextToEditorFormat(pdfData.text);
    
    return {
      success: true,
      content: editorContent,
      metadata: {
        pageCount: pdfData.numpages,
        wordCount: countWords(pdfData.text),
        info: pdfData.info
      }
    };
  } catch (error) {
    return {
      success: false,
      error: error.message
    };
  }
}

导出系统

前端导出器utils/export/):

Word 导出

javascript
// Word 导出实现
export class WordExporter {
  async export(editorContent, options = {}) {
    const { Document, Packer, Paragraph, TextRun } = await import('docx');
    
    const doc = new Document({
      sections: [{
        properties: {},
        children: this.convertContent(editorContent)
      }]
    });
    
    const buffer = await Packer.toBuffer(doc);
    this.downloadFile(buffer, options.fileName || 'document.docx');
  }
  
  convertContent(content) {
    const paragraphs = [];
    
    content.content.forEach(node => {
      switch (node.type) {
        case 'heading':
          paragraphs.push(new Paragraph({
            text: this.extractText(node),
            heading: `Heading${node.attrs.level}`
          }));
          break;
          
        case 'paragraph':
          paragraphs.push(new Paragraph({
            children: this.convertInlineContent(node.content)
          }));
          break;
          
        case 'chart':
          // 图表转换为图片
          paragraphs.push(this.convertChartToImage(node));
          break;
      }
    });
    
    return paragraphs;
  }
}

PDF 导出

javascript
// PDF 导出实现
export class PdfExporter {
  async export(editorContent, options = {}) {
    const { jsPDF } = await import('jspdf');
    
    const pdf = new jsPDF({
      orientation: options.orientation || 'portrait',
      unit: 'mm',
      format: options.format || 'a4'
    });
    
    await this.convertContentToPdf(pdf, editorContent);
    
    pdf.save(options.fileName || 'document.pdf');
  }
  
  async convertContentToPdf(pdf, content) {
    let yPosition = 20;
    
    for (const node of content.content) {
      switch (node.type) {
        case 'heading':
          yPosition = this.addHeading(pdf, node, yPosition);
          break;
          
        case 'paragraph':
          yPosition = this.addParagraph(pdf, node, yPosition);
          break;
          
        case 'chart':
          yPosition = await this.addChart(pdf, node, yPosition);
          break;
      }
      
      // 检查是否需要分页
      if (yPosition > 250) {
        pdf.addPage();
        yPosition = 20;
      }
    }
  }
}

🤝 实时协作系统

协作架构

WebSocket 服务server/src/index.js):

javascript
import { WebSocketServer } from 'ws';
import { setupWSConnection } from 'y-websocket/bin/utils';

// 创建 WebSocket 服务
const wss = new WebSocketServer({ 
  port: config.wsPort || 3002,
  path: '/collaboration'
});

// 处理 WebSocket 连接
wss.on('connection', (ws, req) => {
  const docId = extractDocIdFromUrl(req.url);
  
  // 设置协作连接
  setupWSConnection(ws, req, {
    docName: docId,
    gc: true // 启用垃圾回收
  });
  
  // 用户加入房间
  joinCollaborationRoom(docId, ws);
});

前端协作连接

javascript
// 建立协作连接
class CollaborationManager {
  constructor(docId, editor) {
    this.docId = docId;
    this.editor = editor;
    this.ws = null;
    this.provider = null;
  }
  
  async connect() {
    const wsUrl = `${BASE_WS_URL}/${this.docId}`;
    
    // 创建 Y.js 文档
    this.ydoc = new Y.Doc();
    
    // 创建 WebSocket 提供者
    this.provider = new WebsocketProvider(wsUrl, this.docId, this.ydoc);
    
    // 绑定编辑器
    this.bindEditor();
    
    // 监听连接状态
    this.provider.on('status', ({ status }) => {
      console.log('协作连接状态:', status);
    });
  }
  
  bindEditor() {
    // 将 Y.js 文档与编辑器绑定
    const yXmlFragment = this.ydoc.getXmlFragment('prosemirror');
    
    // 创建协作扩展
    const collaboration = Collaboration.configure({
      document: this.ydoc
    });
    
    // 添加到编辑器
    this.editor.extensionManager.addExtension(collaboration);
  }
}

协作状态管理

javascript
// 协作状态管理
class CollaborationState {
  constructor() {
    this.users = new Map();
    this.cursors = new Map();
  }
  
  // 用户加入
  addUser(userId, userInfo) {
    this.users.set(userId, {
      ...userInfo,
      joinedAt: Date.now(),
      lastActivity: Date.now()
    });
    
    this.broadcastUserList();
  }
  
  // 更新用户光标
  updateCursor(userId, cursor) {
    this.cursors.set(userId, {
      ...cursor,
      timestamp: Date.now()
    });
    
    this.broadcastCursors();
  }
  
  // 广播用户列表
  broadcastUserList() {
    const userList = Array.from(this.users.entries()).map(([id, info]) => ({
      id,
      ...info
    }));
    
    this.broadcast({
      type: 'user_list',
      users: userList
    });
  }
}

🤖 AI 集成系统

AI Provider 管理

Provider 配置utils/ai/providers.ts):

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

export const PROVIDERS: Record<string, ProviderConfig> = {
  deepseek: {
    key: 'deepseek',
    label: 'DeepSeek',
    baseUrl: 'https://api.deepseek.com/v1',
    models: [
      { label: 'DeepSeek Chat', value: 'deepseek-chat' },
      { label: 'DeepSeek Reasoner', value: 'deepseek-reasoner' }
    ]
  },
  kimi: {
    key: 'kimi',
    label: 'Kimi (Moonshot)',
    baseUrl: 'https://api.moonshot.cn/v1',
    models: [
      { label: 'Kimi K2 0905 Preview', value: 'kimi-k2-0905-preview' },
      { label: 'Kimi K2 0711', value: 'kimi-k2-0711' }
    ]
  }
};

AI 流式处理

流式生成实现utils/ai/openaiStream.ts):

typescript
export async function createStreamResponse(
  provider: ProviderConfig,
  payload: any,
  apiKey: string
) {
  const response = await fetch(`${provider.baseUrl}/chat/completions`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      ...provider.buildHeaders?.(apiKey)
    },
    body: JSON.stringify({
      ...payload,
      stream: true
    })
  });
  
  if (!response.body) {
    throw new Error('响应体为空');
  }
  
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  
  return {
    async *[Symbol.asyncIterator]() {
      try {
        while (true) {
          const { done, value } = await reader.read();
          
          if (done) break;
          
          const chunk = decoder.decode(value);
          const lines = chunk.split('\n').filter(line => line.trim());
          
          for (const line of lines) {
            if (line.startsWith('data: ')) {
              const data = line.slice(6);
              
              if (data === '[DONE]') {
                return;
              }
              
              try {
                const parsed = JSON.parse(data);
                const content = parsed.choices?.[0]?.delta?.content;
                
                if (content) {
                  yield { content };
                }
              } catch (e) {
                console.warn('解析流式数据失败:', e);
              }
            }
          }
        }
      } finally {
        reader.releaseLock();
      }
    }
  };
}

AI 功能使用

javascript
// AI 写作功能
export class AIWriter {
  constructor(provider, apiKey) {
    this.provider = provider;
    this.apiKey = apiKey;
  }
  
  // 生成内容
  async generate(prompt, options = {}) {
    const payload = {
      model: options.model || 'deepseek-chat',
      messages: [
        {
          role: 'system',
          content: '你是一个专业的文档写作助手。'
        },
        {
          role: 'user',
          content: prompt
        }
      ],
      max_tokens: options.maxTokens || 2000,
      temperature: options.temperature || 0.7
    };
    
    if (options.stream) {
      return this.generateStream(payload);
    } else {
      return this.generateComplete(payload);
    }
  }
  
  // 流式生成
  async *generateStream(payload) {
    const stream = await createStreamResponse(
      this.provider,
      payload,
      this.apiKey
    );
    
    for await (const chunk of stream) {
      yield chunk.content;
    }
  }
  
  // 改写内容
  async rewrite(content, instruction, options = {}) {
    const prompt = `请根据以下要求改写内容:\n\n要求:${instruction}\n\n原内容:\n${content}`;
    
    return this.generate(prompt, options);
  }
}

📊 扩展组件开发

图表组件开发

组件定义packages/core/extensions/chart/index.js):

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();
        }
      }
    };
  },
  
  addAttributes() {
    return {
      data: {
        default: null,
        parseHTML: element => element.getAttribute('data-chart'),
        renderHTML: attributes => ({
          'data-chart': attributes.data
        })
      }
    };
  },
  
  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();
          }
        }
      };
    };
  }
});

图表数据结构

typescript
// 图表数据类型定义
export 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 Flowchart = Node.create({
  name: 'flowchart',
  
  addNodeView() {
    return ({ node, HTMLAttributes, getPos, editor }) => {
      const container = document.createElement('div');
      container.className = 'flowchart-container';
      
      // 渲染流程图
      this.renderFlowchart(container, node.attrs.data);
      
      return {
        dom: container,
        update: (updatedNode) => {
          if (updatedNode.attrs.data !== node.attrs.data) {
            this.renderFlowchart(container, updatedNode.attrs.data);
          }
          return true;
        }
      };
    };
  },
  
  async renderFlowchart(container, flowchartData) {
    if (!flowchartData) {
      container.innerHTML = '<div class="flowchart-placeholder">点击编辑按钮配置流程图</div>';
      return;
    }
    
    try {
      const data = JSON.parse(flowchartData);
      
      // 使用 Vue Flow 渲染
      const { createApp } = await import('vue');
      const { VueFlow } = await import('@vue-flow/core');
      
      const app = createApp({
        components: { VueFlow },
        template: `
          <VueFlow 
            :nodes="nodes" 
            :edges="edges"
            :fit-view-on-init="true"
          />
        `,
        data() {
          return {
            nodes: data.nodes || [],
            edges: data.edges || []
          };
        }
      });
      
      app.mount(container);
      
    } catch (error) {
      container.innerHTML = `<div class="flowchart-error">流程图渲染错误: ${error.message}</div>`;
    }
  }
});

🔧 API 扩展开发

创建自定义 API

1. 定义路由模块

javascript
// server/src/router/custom-api.js
export default (router, prefix) => {
  // 获取自定义数据
  router.get(`${prefix}/custom/data`, async (ctx) => {
    try {
      const { page = 1, limit = 20 } = ctx.query;
      
      // 业务逻辑
      const data = await getCustomData(page, limit);
      
      ctx.body = {
        code: 200,
        data: data,
        message: 'success'
      };
    } catch (error) {
      ctx.status = 500;
      ctx.body = {
        code: 500,
        message: error.message
      };
    }
  });
  
  // 创建自定义资源
  router.post(`${prefix}/custom/create`, async (ctx) => {
    try {
      const { title, content } = ctx.request.body;
      
      // 参数验证
      if (!title || !content) {
        ctx.status = 400;
        ctx.body = {
          code: 400,
          message: '缺少必要参数'
        };
        return;
      }
      
      // 业务逻辑
      const result = await createCustomResource({ title, content });
      
      ctx.body = {
        code: 200,
        data: result,
        message: 'success'
      };
    } catch (error) {
      ctx.status = 500;
      ctx.body = {
        code: 500,
        message: error.message
      };
    }
  });
};

2. 注册路由

javascript
// server/src/index.js
import customApiRouter from './router/custom-api.js';

// 注册自定义路由
customApiRouter(router, '/api/v1');

3. 前端 API 封装

typescript
// app/src/api/custom.ts
export const customApi = {
  // 获取数据
  async getData(page = 1, limit = 20) {
    return request.get('/custom/data', {
      params: { page, limit }
    });
  },
  
  // 创建资源
  async create(data: CreateCustomData) {
    return request.post('/custom/create', data);
  },
  
  // 更新资源
  async update(id: string, data: UpdateCustomData) {
    return request.put(`/custom/${id}`, data);
  },
  
  // 删除资源
  async delete(id: string) {
    return request.delete(`/custom/${id}`);
  }
};

🔍 调试与测试

开发调试

javascript
// 调试工具配置
if (process.env.NODE_ENV === 'development') {
  // 启用详细日志
  process.env.DEBUG = 'jitword:*';
  
  // 添加性能监控
  app.use(async (ctx, next) => {
    const start = Date.now();
    await next();
    const duration = Date.now() - start;
    console.log(`${ctx.method} ${ctx.url} - ${duration}ms`);
  });
}

单元测试

javascript
// 版本管理测试
describe('版本管理', () => {
  let versionService;
  
  beforeEach(() => {
    versionService = new VersionService();
  });
  
  test('创建版本', async () => {
    const versionData = {
      documentId: 'test_doc',
      title: '测试版本',
      content: { type: 'doc', content: [] },
      author: 'test_user'
    };
    
    const version = await versionService.create(versionData);
    
    expect(version.id).toBeDefined();
    expect(version.title).toBe('测试版本');
    expect(version.author).toBe('test_user');
  });
  
  test('版本对比', async () => {
    const v1 = await versionService.create({
      documentId: 'test_doc',
      content: { type: 'doc', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] }] }
    });
    
    const v2 = await versionService.create({
      documentId: 'test_doc',
      content: { type: 'doc', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello World' }] }] }
    });
    
    const diff = await versionService.compare(v1.id, v2.id);
    
    expect(diff.textChanges).toBeDefined();
    expect(diff.textChanges.length).toBeGreaterThan(0);
  });
});

集成测试

javascript
// 协作功能集成测试
describe('协作功能', () => {
  let server;
  let client1, client2;
  
  beforeAll(async () => {
    server = await startTestServer();
    client1 = await createTestClient('user1');
    client2 = await createTestClient('user2');
  });
  
  afterAll(async () => {
    await server.close();
    client1.close();
    client2.close();
  });
  
  test('多用户协作编辑', async () => {
    const docId = 'test_collaboration';
    
    // 两个用户连接到同一文档
    await client1.connect(docId);
    await client2.connect(docId);
    
    // 用户1编辑
    await client1.edit({ type: 'insert', position: 0, text: 'Hello' });
    
    // 等待同步
    await sleep(100);
    
    // 用户2编辑
    await client2.edit({ type: 'insert', position: 5, text: ' World' });
    
    // 等待同步
    await sleep(100);
    
    // 验证最终内容
    const content1 = await client1.getContent();
    const content2 = await client2.getContent();
    
    expect(content1).toBe('Hello World');
    expect(content2).toBe('Hello World');
  });
});

🚀 部署与运维

生产环境配置

bash
# 环境变量配置
NODE_ENV=production
PORT=3002
JWT_SECRET=your-production-jwt-secret
DB_PATH=/data/jitword/db
UPLOAD_PATH=/data/jitword/uploads
LOG_LEVEL=info
MAX_FILE_SIZE=50MB
REDIS_URL=redis://localhost:6379

PM2 部署配置

javascript
// pm2.config.js
module.exports = {
  apps: [{
    name: 'jitword-server',
    script: 'dist/index.js',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 3002
    },
    error_file: './logs/err.log',
    out_file: './logs/out.log',
    log_file: './logs/combined.log',
    time: true,
    max_memory_restart: '1G',
    node_args: '--max-old-space-size=2048'
  }]
};

监控配置

javascript
// 监控中间件
export function createMonitoringMiddleware() {
  return async (ctx, next) => {
    const start = Date.now();
    
    try {
      await next();
      
      // 记录成功请求
      recordMetric('api_request', {
        method: ctx.method,
        path: ctx.path,
        status: ctx.status,
        duration: Date.now() - start
      });
      
    } catch (error) {
      // 记录错误请求
      recordMetric('api_error', {
        method: ctx.method,
        path: ctx.path,
        error: error.message,
        duration: Date.now() - start
      });
      
      throw error;
    }
  };
}

🛠️ 开发工具与调试

开发工具配置

json
// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "typescript.preferences.importModuleSpecifier": "relative",
  "files.associations": {
    "*.vue": "vue"
  }
}

调试配置

json
// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Server",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/server/src/index.js",
      "env": {
        "NODE_ENV": "development",
        "DEBUG": "jitword:*"
      },
      "console": "integratedTerminal",
      "restart": true,
      "runtimeArgs": ["--experimental-modules"]
    }
  ]
}

📋 最佳实践总结

开发规范

  1. 代码质量

    • 使用 ESLint 和 Prettier 保持代码风格一致
    • 编写单元测试覆盖核心功能
    • 使用 TypeScript 提升代码质量
  2. 性能优化

    • 合理使用缓存减少重复计算
    • 实现防抖节流优化用户体验
    • 使用虚拟滚动处理大量数据
  3. 安全考虑

    • 严格的输入验证和输出过滤
    • 使用 HTTPS 和 WSS 加密传输
    • 实现完善的权限控制机制

企业集成建议

  1. API 设计

    • 遵循 RESTful 设计原则
    • 提供完整的错误码和错误信息
    • 支持批量操作提升效率
  2. 数据管理

    • 实现数据备份和恢复机制
    • 考虑数据迁移和升级策略
    • 建立数据监控和告警
  3. 运维部署

    • 实现健康检查和自动重启
    • 建立完善的监控和日志系统
    • 优化私有化部署方案

📞 技术支持与源码授权

🎯 源码授权说明

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

🛠️ 技术支持服务

  • 技术微信:cxzk_168(工作时间响应)
  • 技术咨询:cxzk_168(微信)
  • 技术文档:完整的开发文档和 API 说明
  • 定制开发:交付源码基础上,提供付费的企业定制化开发服务

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

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