智能图书馆管理系统开发实战系列(五):前后端集成 - koffi调用与接口设计

前言

在前面的文章中,我们分别完成了前端React应用和后端C++ DLL的开发。本文将详细介绍如何通过koffi库实现JavaScript与C++的无缝集成,这是整个系统最关键的技术环节。我们将深入探讨数据类型转换、错误处理、性能优化等核心实践。

koffi技术方案概述

为什么选择koffi?

koffi是一个现代化的Node.js原生库调用方案,相比传统的node-ffi具有以下优势:

  1. 性能更优: 基于最新的V8 API,调用开销更小
  2. 类型安全: 更好的TypeScript支持和类型检查
  3. 内存管理: 自动的内存管理,减少内存泄漏风险
  4. 现代API: 支持Promise、async/await等现代异步模式
  5. 跨平台: 支持Windows、Linux、macOS等多平台

技术架构设计

我们的前后端集成采用三层架构:

复制代码
Frontend (React/TypeScript)
    ↓ (IPC)
Electron Main Process (koffi)
    ↓ (FFI)
C++ DLL (Business Logic)

数据流向:

  1. React组件调用useBackendAPI Hook
  2. 通过Electron IPC发送请求到主进程
  3. 主进程使用koffi调用C++ DLL函数
  4. C++ DLL处理业务逻辑并返回JSON结果
  5. 结果通过相同路径返回到React组件

koffi集成实现

1. 主进程koffi配置

code/frontend/electron/main.ts 中配置koffi:

typescript 复制代码
import { koffi } from 'koffi';
import path from 'path';

interface BackendLibrary {
  InitLibrary: (configJson: string) => boolean;
  ShutdownLibrary: () => void;
  AddBook: (bookJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
  DeleteBook: (bookId: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
  EditBook: (bookJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
  GetBookList: (queryJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
  BorrowBook: (requestJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
  ReturnBook: (requestJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
  GetDashboardStats: (resultJson: koffi.Buffer, resultSize: number) => boolean;
}

class BackendManager {
  private static instance: BackendManager;
  private library: BackendLibrary | null = null;
  private initialized = false;

  public static getInstance(): BackendManager {
    if (!BackendManager.instance) {
      BackendManager.instance = new BackendManager();
    }
    return BackendManager.instance;
  }

  public initialize(): boolean {
    try {
      // 确定DLL路径
      const dllPath = this.getDllPath();
      
      // 加载DLL
      const lib = koffi.load(dllPath);

      // 定义函数接口
      this.library = {
        InitLibrary: lib.func('InitLibrary', 'bool', ['string']),
        ShutdownLibrary: lib.func('ShutdownLibrary', 'void', []),
        
        // 图书管理接口
        AddBook: lib.func('AddBook', 'bool', ['string', 'char *', 'int']),
        DeleteBook: lib.func('DeleteBook', 'bool', ['string', 'char *', 'int']),
        EditBook: lib.func('EditBook', 'bool', ['string', 'char *', 'int']),
        GetBookList: lib.func('GetBookList', 'bool', ['string', 'char *', 'int']),
        
        // 借阅管理接口
        BorrowBook: lib.func('BorrowBook', 'bool', ['string', 'char *', 'int']),
        ReturnBook: lib.func('ReturnBook', 'bool', ['string', 'char *', 'int']),
        
        // 仪表板接口
        GetDashboardStats: lib.func('GetDashboardStats', 'bool', ['char *', 'int'])
      };

      // 初始化C++库
      const config = {
        dbPath: path.join(app.getPath('userData'), 'library.db'),
        logPath: path.join(app.getPath('userData'), 'logs'),
        maxCacheSize: 1000,
        enableDebugLog: process.env.NODE_ENV === 'development'
      };

      const success = this.library.InitLibrary(JSON.stringify(config));
      if (success) {
        this.initialized = true;
        console.log('Backend library initialized successfully');
      }

      return success;
    } catch (error) {
      console.error('Failed to initialize backend library:', error);
      return false;
    }
  }

  private getDllPath(): string {
    const isDev = process.env.NODE_ENV === 'development';
    const basePath = isDev 
      ? path.join(__dirname, '../../dll')
      : path.join(process.resourcesPath, 'dll');
    
    const dllName = process.platform === 'win32' 
      ? 'libBackend.dll'
      : process.platform === 'darwin'
      ? 'libBackend.dylib'
      : 'libBackend.so';
      
    return path.join(basePath, dllName);
  }

  public async callFunction<T = any>(
    functionName: keyof BackendLibrary,
    params?: any
  ): Promise<{ success: boolean; data?: T; error?: string }> {
    if (!this.initialized || !this.library) {
      throw new Error('Backend library not initialized');
    }

    return new Promise((resolve) => {
      try {
        const resultBuffer = Buffer.alloc(8192); // 8KB缓冲区
        let success = false;

        // 根据函数类型调用不同的接口
        switch (functionName) {
          case 'GetDashboardStats':
            success = this.library.GetDashboardStats(resultBuffer, resultBuffer.length);
            break;
            
          case 'AddBook':
          case 'EditBook':
            success = this.library[functionName](
              JSON.stringify(params),
              resultBuffer,
              resultBuffer.length
            );
            break;
            
          case 'DeleteBook':
            success = this.library.DeleteBook(
              params.id,
              resultBuffer,
              resultBuffer.length
            );
            break;
            
          case 'GetBookList':
            success = this.library.GetBookList(
              JSON.stringify(params || {}),
              resultBuffer,
              resultBuffer.length
            );
            break;
            
          case 'BorrowBook':
          case 'ReturnBook':
            success = this.library[functionName](
              JSON.stringify(params),
              resultBuffer,
              resultBuffer.length
            );
            break;
            
          default:
            resolve({ success: false, error: 'Unknown function' });
            return;
        }

        if (success) {
          // 解析返回的JSON数据
          const resultStr = resultBuffer.toString('utf8').replace(/\0.*$/g, '');
          try {
            const result = JSON.parse(resultStr);
            resolve({ success: true, data: result });
          } catch (parseError) {
            resolve({ success: false, error: 'Failed to parse result JSON' });
          }
        } else {
          resolve({ success: false, error: 'Backend function call failed' });
        }
      } catch (error) {
        resolve({ 
          success: false, 
          error: error instanceof Error ? error.message : 'Unknown error' 
        });
      }
    });
  }

  public shutdown(): void {
    if (this.initialized && this.library) {
      this.library.ShutdownLibrary();
      this.initialized = false;
      console.log('Backend library shutdown');
    }
  }
}

export { BackendManager };

2. IPC通信层

实际项目中的IPC处理器通过 DllBridge 类实现:

typescript 复制代码
// electron/main.ts 中的主进程配置
import { app, BrowserWindow, ipcMain } from 'electron'
import path from 'node:path'
import { DllBridge } from './dllBridge'

app.whenReady().then(() => {
  // 初始化 DLL 桥接器
  DllBridge.getInstance();
  
  createWindow()
})

// electron/dllBridge.ts 中的IPC处理
import { ipcMain } from 'electron';
import libGlobalDll from '../src/utils/libGlobalDll';
import dllManager from '../src/utils/dllManager';

export class DllBridge {
  private static instance: DllBridge;

  private constructor() {
    this.setupIpcHandlers();
  }

  private setupIpcHandlers() {
    const bookManager = dllManager.getBookManager();
    const readerManager = dllManager.getReaderManager();

    // 图书管理相关IPC处理器
    ipcMain.handle('dll:getBookList', async (event, jsonInput: string) => {
      return await bookManager.getBookList(jsonInput);
    });

    ipcMain.handle('dll:addBook', async (event, jsonInput: string) => {
      return bookManager.addBook(jsonInput);
    });

    ipcMain.handle('dll:editBook', async (event, jsonInput: string) => {
      return bookManager.editBook(jsonInput);
    });

    ipcMain.handle('dll:deleteBook', async (event, jsonInput: string) => {
      return bookManager.deleteBook(jsonInput);
    });

    // 读者管理相关IPC处理器
    ipcMain.handle('dll:getReaderList', async (event, jsonInput: string) => {
      return await readerManager.getReaderList(jsonInput);
    });

    ipcMain.handle('dll:registerReader', async (event, jsonInput: string) => {
      return readerManager.registerReader(jsonInput);
    });
  }
}

3. 前端API封装

code/frontend/src/hooks/useBackendAPI.ts 中封装API调用:

typescript 复制代码
import { useCallback, useRef } from 'react';

interface BackendResponse<T = any> {
  success: boolean;
  data?: T;
  error?: string;
}

// 类型定义
interface Book {
  id: string;
  title: string;
  author: string;
  isbn: string;
  category: string;
  status: 'available' | 'borrowed' | 'reserved' | 'maintenance';
  publishDate: string;
  addedDate: string;
}

interface BorrowRequest {
  bookId: string;
  userId: string;
}

interface DashboardStats {
  totalBooks: number;
  availableBooks: number;
  borrowedBooks: number;
  totalUsers: number;
  activeUsers: number;
  activeBorrows: number;
  overdueBooks: number;
  todayBorrows: number;
  todayReturns: number;
  borrowTrend: Array<{ date: string; count: number }>;
  categoryDistribution: Array<{ category: string; count: number }>;
}

export const useBackendAPI = () => {
  const requestIdRef = useRef(0);

  const callBackend = useCallback(async <T = any>(
    channel: string,
    params?: any
  ): Promise<T> => {
    const requestId = ++requestIdRef.current;
    const jsonInput = JSON.stringify(params || {});
    console.log(`[API Request ${requestId}] ${channel}:`, jsonInput);

    try {
      const startTime = performance.now();
      const result = await window.electronAPI.invoke(channel, jsonInput);
      const endTime = performance.now();
      
      console.log(`[API Response ${requestId}] ${channel} (${(endTime - startTime).toFixed(2)}ms):`, result);
      return result;
    } catch (error) {
      console.error(`[API Error ${requestId}] ${channel}:`, error);
      throw error;
    }
  }, []);

  // 图书管理API
  const bookAPI = {
    addBook: useCallback(async (book: Omit<Book, 'id' | 'addedDate'>): Promise<any> => {
      return await callBackend('dll:addBook', book);
    }, [callBackend]),

    getBookList: useCallback(async (query?: {
      keyword?: string;
      category?: string;
      status?: string;
      page?: number;
      pageSize?: number;
    }): Promise<any> => {
      return await callBackend('dll:getBookList', query);
    }, [callBackend]),

    editBook: useCallback(async (book: Partial<Book>): Promise<any> => {
      return await callBackend('dll:editBook', book);
    }, [callBackend]),

    deleteBook: useCallback(async (bookId: string): Promise<any> => {
      return await callBackend('dll:deleteBook', { bookId });
    }, [callBackend])
  };

  // 读者管理API
  const readerAPI = {
    getReaderList: useCallback(async (query?: any): Promise<any> => {
      return await callBackend('dll:getReaderList', query);
    }, [callBackend]),

    registerReader: useCallback(async (reader: any): Promise<any> => {
      return await callBackend('dll:registerReader', reader);
    }, [callBackend]),

    editReader: useCallback(async (reader: any): Promise<any> => {
      return await callBackend('dll:editReader', reader);
    }, [callBackend]),

    deleteReader: useCallback(async (readerId: string): Promise<any> => {
      return await callBackend('dll:deleteReader', { readerId });
    }, [callBackend])
  };

  // 状态检查API
  const statusAPI = {
    checkStatus: useCallback(async (): Promise<any> => {
      return await callBackend('dll:checkStatus');
    }, [callBackend]),

    initAllModules: useCallback(async (): Promise<any> => {
      return await callBackend('dll:initAllModules');
    }, [callBackend]),

    testAllModules: useCallback(async (): Promise<any> => {
      return await callBackend('dll:testAllModules');
    }, [callBackend])
  };

  return {
    book: bookAPI,
    reader: readerAPI,
    status: statusAPI
  };
};

// Hook使用示例
export const useBooks = () => {
  const { book } = useBackendAPI();
  
  return {
    addBook: book.addBook,
    getBookList: book.getBookList,
    editBook: book.editBook,
    deleteBook: book.deleteBook
  };
};

export const useReaders = () => {
  const { reader } = useBackendAPI();
  
  return {
    getReaderList: reader.getReaderList,
    registerReader: reader.registerReader,
    editReader: reader.editReader,
    deleteReader: reader.deleteReader
  };
};

export const useStatus = () => {
  const { status } = useBackendAPI();
  
  return {
    checkStatus: status.checkStatus,
    initAllModules: status.initAllModules,
    testAllModules: status.testAllModules
  };
};

数据类型转换与验证

1. TypeScript类型定义

typescript 复制代码
// src/types/api.ts
export interface APIResponse<T = any> {
  success: boolean;
  data?: T;
  error?: string;
}

export interface PaginatedResponse<T> {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
  totalPages: number;
}

export interface BookQueryParams {
  keyword?: string;
  category?: string;
  status?: BookStatus;
  author?: string;
  publishYear?: number;
  page?: number;
  pageSize?: number;
  sortBy?: 'title' | 'author' | 'publishDate' | 'addedDate';
  sortOrder?: 'asc' | 'desc';
}

export interface BookCreateRequest {
  title: string;
  author: string;
  isbn: string;
  category: string;
  publisher: string;
  publishDate: string;
  description?: string;
  coverUrl?: string;
}

export interface BorrowBookRequest {
  bookId: string;
  userId: string;
  duration?: number; // 借阅天数,默认30天
}

2. 数据验证工具

typescript 复制代码
// src/utils/validation.ts
import { z } from 'zod';

// 图书数据验证模式
export const BookSchema = z.object({
  title: z.string().min(1, '书名不能为空').max(200, '书名过长'),
  author: z.string().min(1, '作者不能为空').max(100, '作者名过长'),
  isbn: z.string().regex(/^(?:\d{9}[\dX]|\d{13})$/, 'ISBN格式不正确'),
  category: z.string().min(1, '分类不能为空'),
  publisher: z.string().min(1, '出版社不能为空'),
  publishDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, '日期格式不正确'),
  description: z.string().optional(),
  coverUrl: z.string().url('封面URL格式不正确').optional()
});

// 借阅请求验证
export const BorrowRequestSchema = z.object({
  bookId: z.string().min(1, '图书ID不能为空'),
  userId: z.string().min(1, '用户ID不能为空'),
  duration: z.number().min(1).max(90).optional()
});

// 验证函数
export const validateBookData = (data: any): { valid: boolean; errors?: string[] } => {
  try {
    BookSchema.parse(data);
    return { valid: true };
  } catch (error) {
    if (error instanceof z.ZodError) {
      return {
        valid: false,
        errors: error.errors.map(err => err.message)
      };
    }
    return { valid: false, errors: ['验证失败'] };
  }
};

export const validateBorrowRequest = (data: any): { valid: boolean; errors?: string[] } => {
  try {
    BorrowRequestSchema.parse(data);
    return { valid: true };
  } catch (error) {
    if (error instanceof z.ZodError) {
      return {
        valid: false,
        errors: error.errors.map(err => err.message)
      };
    }
    return { valid: false, errors: ['验证失败'] };
  }
};

错误处理机制

1. 统一错误处理

typescript 复制代码
// src/utils/errorHandling.ts
export enum APIErrorCode {
  NETWORK_ERROR = 'NETWORK_ERROR',
  VALIDATION_ERROR = 'VALIDATION_ERROR',
  BACKEND_ERROR = 'BACKEND_ERROR',
  PERMISSION_ERROR = 'PERMISSION_ERROR',
  NOT_FOUND = 'NOT_FOUND',
  CONFLICT = 'CONFLICT'
}

export interface APIError {
  code: APIErrorCode;
  message: string;
  details?: any;
}

export class APIErrorHandler {
  static handleError(error: any): APIError {
    console.error('API Error:', error);

    // 网络错误
    if (error.name === 'TypeError' && error.message.includes('fetch')) {
      return {
        code: APIErrorCode.NETWORK_ERROR,
        message: '网络连接失败,请检查网络状态'
      };
    }

    // 后端返回的错误
    if (error.response) {
      const { status, data } = error.response;
      
      switch (status) {
        case 400:
          return {
            code: APIErrorCode.VALIDATION_ERROR,
            message: data.message || '请求参数错误'
          };
        case 403:
          return {
            code: APIErrorCode.PERMISSION_ERROR,
            message: '权限不足'
          };
        case 404:
          return {
            code: APIErrorCode.NOT_FOUND,
            message: '资源不存在'
          };
        case 409:
          return {
            code: APIErrorCode.CONFLICT,
            message: '数据冲突,请刷新后重试'
          };
        default:
          return {
            code: APIErrorCode.BACKEND_ERROR,
            message: data.message || '服务器内部错误'
          };
      }
    }

    // 默认错误
    return {
      code: APIErrorCode.BACKEND_ERROR,
      message: error.message || '未知错误'
    };
  }

  static createErrorMessage(error: APIError): string {
    switch (error.code) {
      case APIErrorCode.NETWORK_ERROR:
        return '网络连接失败,请检查网络状态后重试';
      case APIErrorCode.VALIDATION_ERROR:
        return `数据验证失败:${error.message}`;
      case APIErrorCode.PERMISSION_ERROR:
        return '您没有执行此操作的权限';
      case APIErrorCode.NOT_FOUND:
        return '请求的资源不存在';
      case APIErrorCode.CONFLICT:
        return '操作冲突,请刷新页面后重试';
      default:
        return error.message || '操作失败,请稍后重试';
    }
  }
}

2. React错误边界

typescript 复制代码
// src/components/ErrorBoundary.tsx
import React, { Component, ReactNode } from 'react';
import { APIErrorHandler, APIError } from '../utils/errorHandling';

interface ErrorBoundaryState {
  hasError: boolean;
  error?: APIError;
}

interface ErrorBoundaryProps {
  children: ReactNode;
  fallback?: (error: APIError) => ReactNode;
}

export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
  constructor(props: ErrorBoundaryProps) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    const apiError = APIErrorHandler.handleError(error);
    return {
      hasError: true,
      error: apiError
    };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Error Boundary caught an error:', error, errorInfo);
  }

  render() {
    if (this.state.hasError && this.state.error) {
      if (this.props.fallback) {
        return this.props.fallback(this.state.error);
      }

      return (
        <div className="flex flex-col items-center justify-center h-64 p-8">
          <div className="text-red-500 text-6xl mb-4">
            <i className="fas fa-exclamation-triangle"></i>
          </div>
          <h2 className="text-xl font-semibold text-gray-800 mb-2">
            出现错误
          </h2>
          <p className="text-gray-600 text-center mb-4">
            {APIErrorHandler.createErrorMessage(this.state.error)}
          </p>
          <button
            onClick={() => this.setState({ hasError: false, error: undefined })}
            className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
          >
            重试
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

性能优化策略

1. 缓存机制

typescript 复制代码
// src/hooks/useCache.ts
import { useRef, useCallback } from 'react';

interface CacheEntry<T> {
  data: T;
  timestamp: number;
  expiry: number;
}

export const useCache = <T = any>(defaultExpiry: number = 5 * 60 * 1000) => {
  const cache = useRef<Map<string, CacheEntry<T>>>(new Map());

  const get = useCallback((key: string): T | null => {
    const entry = cache.current.get(key);
    if (!entry) return null;

    const now = Date.now();
    if (now > entry.timestamp + entry.expiry) {
      cache.current.delete(key);
      return null;
    }

    return entry.data;
  }, []);

  const set = useCallback((key: string, data: T, expiry?: number): void => {
    cache.current.set(key, {
      data,
      timestamp: Date.now(),
      expiry: expiry || defaultExpiry
    });
  }, [defaultExpiry]);

  const clear = useCallback((pattern?: string): void => {
    if (pattern) {
      const regex = new RegExp(pattern);
      for (const key of cache.current.keys()) {
        if (regex.test(key)) {
          cache.current.delete(key);
        }
      }
    } else {
      cache.current.clear();
    }
  }, []);

  const has = useCallback((key: string): boolean => {
    return get(key) !== null;
  }, [get]);

  return { get, set, clear, has };
};

// 使用缓存的API Hook
export const useCachedBookList = () => {
  const { book } = useBackendAPI();
  const cache = useCache<{ books: Book[]; total: number }>();

  const getBookList = useCallback(async (query?: BookQueryParams) => {
    const cacheKey = `books:${JSON.stringify(query || {})}`;
    
    // 尝试从缓存获取
    const cached = cache.get(cacheKey);
    if (cached) {
      return cached;
    }

    // 从后端获取
    const result = await book.getBookList(query);
    
    // 缓存结果
    cache.set(cacheKey, result, 2 * 60 * 1000); // 2分钟缓存
    
    return result;
  }, [book, cache]);

  const invalidateCache = useCallback((pattern?: string) => {
    cache.clear(pattern || 'books:');
  }, [cache]);

  return { getBookList, invalidateCache };
};

2. 请求队列管理

typescript 复制代码
// src/utils/requestQueue.ts
interface QueuedRequest {
  id: string;
  promise: Promise<any>;
  timestamp: number;
}

class RequestQueue {
  private queue: Map<string, QueuedRequest> = new Map();
  private maxConcurrent: number = 5;
  private currentCount: number = 0;

  async enqueue<T>(key: string, requestFn: () => Promise<T>): Promise<T> {
    // 如果已经有相同的请求在进行,返回现有的Promise
    const existing = this.queue.get(key);
    if (existing) {
      return existing.promise;
    }

    // 等待队列有空位
    while (this.currentCount >= this.maxConcurrent) {
      await new Promise(resolve => setTimeout(resolve, 10));
    }

    this.currentCount++;

    const promise = requestFn()
      .finally(() => {
        this.queue.delete(key);
        this.currentCount--;
      });

    const request: QueuedRequest = {
      id: key,
      promise,
      timestamp: Date.now()
    };

    this.queue.set(key, request);
    return promise;
  }

  clear(): void {
    this.queue.clear();
    this.currentCount = 0;
  }

  getQueueSize(): number {
    return this.queue.size;
  }
}

export const requestQueue = new RequestQueue();

// 使用请求队列的Hook
export const useQueuedRequest = () => {
  const { book } = useBackendAPI();

  const queuedGetBookList = useCallback(async (query?: BookQueryParams) => {
    const key = `getBookList:${JSON.stringify(query || {})}`;
    return requestQueue.enqueue(key, () => book.getBookList(query));
  }, [book]);

  return { queuedGetBookList };
};

3. 内存泄漏防护

typescript 复制代码
// src/hooks/useAbortableRequest.ts
import { useRef, useEffect, useCallback } from 'react';

export const useAbortableRequest = () => {
  const abortControllersRef = useRef<Set<AbortController>>(new Set());

  const createRequest = useCallback(<T>(
    requestFn: (signal: AbortSignal) => Promise<T>
  ): Promise<T> => {
    const controller = new AbortController();
    abortControllersRef.current.add(controller);

    return requestFn(controller.signal)
      .finally(() => {
        abortControllersRef.current.delete(controller);
      });
  }, []);

  const abortAll = useCallback(() => {
    abortControllersRef.current.forEach(controller => {
      controller.abort();
    });
    abortControllersRef.current.clear();
  }, []);

  // 组件卸载时取消所有请求
  useEffect(() => {
    return () => {
      abortAll();
    };
  }, [abortAll]);

  return { createRequest, abortAll };
};

// 带取消功能的API Hook
export const useAbortableBookAPI = () => {
  const { book } = useBackendAPI();
  const { createRequest } = useAbortableRequest();

  const getBookList = useCallback(async (query?: BookQueryParams) => {
    return createRequest(async (signal) => {
      // 这里需要在底层API中支持AbortSignal
      return book.getBookList(query);
    });
  }, [book, createRequest]);

  return { getBookList };
};

调试与监控

1. 性能监控

typescript 复制代码
// src/utils/performance.ts
class PerformanceMonitor {
  private metrics: Map<string, number[]> = new Map();

  startTiming(operation: string): () => void {
    const startTime = performance.now();
    
    return () => {
      const endTime = performance.now();
      const duration = endTime - startTime;
      
      if (!this.metrics.has(operation)) {
        this.metrics.set(operation, []);
      }
      
      this.metrics.get(operation)!.push(duration);
      
      // 只保留最近100次记录
      const records = this.metrics.get(operation)!;
      if (records.length > 100) {
        records.shift();
      }
      
      console.log(`[Performance] ${operation}: ${duration.toFixed(2)}ms`);

    };
  }

  getAverageTime(operation: string): number {
    const records = this.metrics.get(operation);
    if (!records || records.length === 0) return 0;
    
    const sum = records.reduce((a, b) => a + b, 0);
    return sum / records.length;
  }

  getMetrics(): Record<string, { average: number; count: number }> {
    const result: Record<string, { average: number; count: number }> = {};
    
    for (const [operation, records] of this.metrics) {
      result[operation] = {
        average: this.getAverageTime(operation),
        count: records.length
      };
    }
    
    return result;
  }
}

export const performanceMonitor = new PerformanceMonitor();

// 使用性能监控的Hook
export const useMonitoredBackendAPI = () => {
  const { book, loan, dashboard } = useBackendAPI();

  const monitoredBook = {
    getBookList: useCallback(async (query?: BookQueryParams) => {
      const endTiming = performanceMonitor.startTiming('getBookList');
      try {
        return await book.getBookList(query);
      } finally {
        endTiming();
      }
    }, [book]),

    addBook: useCallback(async (bookData: any) => {
      const endTiming = performanceMonitor.startTiming('addBook');
      try {
        return await book.addBook(bookData);
      } finally {
        endTiming();
      }
    }, [book])
  };

  return { book: monitoredBook, loan, dashboard };
};

2. 错误上报

typescript 复制代码
// src/utils/errorReporting.ts
interface ErrorReport {
  timestamp: string;
  error: string;
  stack?: string;
  userAgent: string;
  url: string;
  userId?: string;
  additionalInfo?: any;
}

class ErrorReporter {
  private reports: ErrorReport[] = [];
  private maxReports = 50;

  reportError(error: Error, additionalInfo?: any): void {
    const report: ErrorReport = {
      timestamp: new Date().toISOString(),
      error: error.message,
      stack: error.stack,
      userAgent: navigator.userAgent,
      url: window.location.href,
      additionalInfo
    };

    this.reports.push(report);
    
    // 限制报告数量
    if (this.reports.length > this.maxReports) {
      this.reports.shift();
    }

    // 在开发环境下输出详细错误信息
    if (process.env.NODE_ENV === 'development') {
      console.error('Error Report:', report);
    }
  }

  getReports(): ErrorReport[] {
    return [...this.reports];
  }

  clearReports(): void {
    this.reports = [];
  }

  exportReports(): string {
    return JSON.stringify(this.reports, null, 2);
  }
}

export const errorReporter = new ErrorReporter();

// 全局错误处理
window.addEventListener('error', (event) => {
  errorReporter.reportError(event.error, {
    filename: event.filename,
    lineno: event.lineno,
    colno: event.colno
  });
});

window.addEventListener('unhandledrejection', (event) => {
  const error = event.reason instanceof Error 
    ? event.reason 
    : new Error(String(event.reason));
  
  errorReporter.reportError(error, {
    type: 'unhandledRejection'
  });
});

下期预告

在下一篇文章中,我们将深入介绍Google Test单元测试实践,包括如何为C++后端代码编写全面的测试用例,以及如何在CI/CD流程中集成自动化测试。

总结

本文详细介绍了前后端集成的核心技术实践,包括:

  1. koffi集成方案: 高性能的JavaScript与C++互操作
  2. IPC通信架构: 安全可靠的进程间通信
  3. 类型安全设计: 完整的TypeScript类型系统
  4. 错误处理机制: 统一的错误处理和用户反馈
  5. 性能优化策略: 缓存、队列管理、内存保护
  6. 调试监控工具: 性能监控和错误上报

通过这些实践,我们成功实现了前后端的无缝集成,为用户提供了流畅的桌面应用体验,同时保证了系统的稳定性和可维护性。

系列文章目录

  1. 项目架构设计与技术选型
  2. 高保真原型设计与用户体验测试
  3. 前端工程化实践:Electron + React + TypeScript
  4. 后端C++ DLL开发与模块化设计
  5. 前后端集成:koffi调用与接口设计
  6. Google Test单元测试实践
  7. CMake构建系统与持续集成
  8. 性能优化与部署发布

通过这个系列文章,您将学习到现代桌面应用开发的完整流程和最佳实践。

程序及源码附件下载

程序及源码

相关推荐
源代码•宸2 小时前
深入浅出设计模式——创建型模式之建造者模式 Builder
c++·经验分享·设计模式·建造者模式
爱吃KFC的大肥羊4 小时前
C/C++常用字符串函数
c语言·数据结构·c++·算法
岁忧6 小时前
(nice!!!)(LeetCode 每日一题) 2561. 重排水果 (哈希表 + 贪心)
java·c++·算法·leetcode·go·散列表
我要成为c嘎嘎大王6 小时前
【C++】类和对象(2)
开发语言·c++
C++ 老炮儿的技术栈7 小时前
在vscode 如何运行a.nut 程序(Squirrel语言)
c语言·开发语言·c++·ide·vscode·算法·编辑器
HKUST_ZJH7 小时前
交互 Codeforces Round 1040 Interactive RBS
c++·算法·交互
weixin_307779138 小时前
设计Mock华为昇腾GPU的MindSpore和CANN的库的流程与实现
c++·算法·华为·系统架构·gpu算力
kk”10 小时前
C++ 模板初阶
开发语言·c++
仟濹12 小时前
【C/C++】整数超过多少位时用「高精度」
c语言·c++·算法