diff --git a/frontend/bindings/voidraft/internal/services/backupservice.ts b/frontend/bindings/voidraft/internal/services/backupservice.ts index 582eda0..960822e 100644 --- a/frontend/bindings/voidraft/internal/services/backupservice.ts +++ b/frontend/bindings/voidraft/internal/services/backupservice.ts @@ -10,6 +10,9 @@ // @ts-ignore: Unused imports import {Call as $Call, Create as $Create} from "@wailsio/runtime"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Unused imports import * as models$0 from "../models/models.js"; @@ -54,6 +57,11 @@ export function ServiceShutdown(): Promise & { cancel(): void } { return $resultPromise; } +export function ServiceStartup(options: application$0.ServiceOptions): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(2900331732, options) as any; + return $resultPromise; +} + /** * StartAutoBackup 启动自动备份定时器 */ diff --git a/frontend/bindings/voidraft/internal/services/documentservice.ts b/frontend/bindings/voidraft/internal/services/documentservice.ts index c630d60..9758245 100644 --- a/frontend/bindings/voidraft/internal/services/documentservice.ts +++ b/frontend/bindings/voidraft/internal/services/documentservice.ts @@ -49,14 +49,6 @@ export function GetDocumentByID(id: number): Promise & return $typingPromise; } -/** - * GetFirstDocumentID gets the first active document's ID for frontend initialization - */ -export function GetFirstDocumentID(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(2970773833) as any; - return $resultPromise; -} - /** * ListAllDocumentsMeta lists all active (non-deleted) document metadata */ diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 89c1b82..7a73c2e 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -5,7 +5,6 @@ import {useSystemStore} from '@/stores/systemStore'; import {useKeybindingStore} from '@/stores/keybindingStore'; import {useThemeStore} from '@/stores/themeStore'; import {useUpdateStore} from '@/stores/updateStore'; -import {useBackupStore} from '@/stores/backupStore'; import WindowTitleBar from '@/components/titlebar/WindowTitleBar.vue'; const configStore = useConfigStore(); @@ -13,7 +12,6 @@ const systemStore = useSystemStore(); const keybindingStore = useKeybindingStore(); const themeStore = useThemeStore(); const updateStore = useUpdateStore(); -const backupStore = useBackupStore(); // 应用启动时加载配置和初始化系统信息 onMounted(async () => { @@ -27,10 +25,7 @@ onMounted(async () => { // 初始化语言和主题 await configStore.initializeLanguage(); themeStore.initializeTheme(); - - // 初始化备份服务 - await backupStore.initialize(); - + // 启动时检查更新 await updateStore.checkOnStartup(); }); diff --git a/frontend/src/common/async/README.md b/frontend/src/common/async/README.md deleted file mode 100644 index 1937e45..0000000 --- a/frontend/src/common/async/README.md +++ /dev/null @@ -1,285 +0,0 @@ -# 异步操作管理器 (AsyncOperationManager) - -一个用于控制异步操作竞态条件的 TypeScript 模块,确保操作的正确性和一致性。 - -## 功能特性 - -- 🚀 **竞态条件控制**: 自动取消同一资源的过时操作 -- 🔄 **操作生命周期管理**: 完整的状态跟踪和回调支持 -- 🎯 **资源隔离**: 基于资源ID的操作隔离机制 -- ⚡ **并发控制**: 支持最大并发数限制 -- ⏰ **超时处理**: 可配置的操作超时机制 -- 🧹 **内存管理**: 自动清理已完成的操作 -- 🐛 **调试友好**: 内置日志系统 - -## 安装和导入 - -```typescript -import { AsyncOperationManager } from '@/common/async'; -// 或者 -import AsyncOperationManager from '@/common/async'; -``` - -## 基本用法 - -### 创建管理器实例 - -```typescript -const operationManager = new AsyncOperationManager({ - timeout: 5000, // 5秒超时 - autoCleanup: true, // 自动清理已完成操作 - maxConcurrent: 3, // 最大并发数 - debug: true // 启用调试日志 -}); -``` - -### 执行异步操作 - -```typescript -const result = await operationManager.executeOperation( - 'document-123', // 资源ID - async (signal, operationId) => { - // 检查操作是否被取消 - if (signal.aborted) { - throw new Error('Operation cancelled'); - } - - // 执行异步操作 - const data = await fetchData(); - - // 再次检查取消状态 - if (signal.aborted) { - throw new Error('Operation cancelled'); - } - - return data; - }, - 'fetch-data' // 操作类型(可选,用于调试) -); - -if (result.success) { - console.log('操作成功:', result.data); -} else { - console.error('操作失败:', result.error); -} -``` - -## API 文档 - -### 构造函数 - -```typescript -new AsyncOperationManager(config?, callbacks?) -``` - -#### 配置选项 (AsyncOperationManagerConfig) - -| 参数 | 类型 | 默认值 | 描述 | -|------|------|--------|------| -| `timeout` | `number` | `0` | 操作超时时间(毫秒),0表示不超时 | -| `autoCleanup` | `boolean` | `true` | 是否自动清理已完成的操作 | -| `maxConcurrent` | `number` | `0` | 最大并发操作数,0表示无限制 | -| `debug` | `boolean` | `false` | 是否启用调试模式 | - -#### 回调函数 (OperationCallbacks) - -```typescript -{ - onStart?: (operation: OperationInfo) => void; // 操作开始 - onComplete?: (operation: OperationInfo) => void; // 操作完成 - onCancel?: (operation: OperationInfo) => void; // 操作取消 - onError?: (operation: OperationInfo, error: Error) => void; // 操作失败 -} -``` - -### 主要方法 - -#### executeOperation(resourceId, executor, operationType?) - -执行异步操作的核心方法。 - -**参数:** -- `resourceId: string | number` - 资源标识符 -- `executor: OperationExecutor` - 操作执行函数 -- `operationType?: string` - 操作类型(可选) - -**返回:** `Promise>` - -#### cancelResourceOperations(resourceId, excludeOperationId?) - -取消指定资源的所有操作。 - -#### cancelOperation(operationId) - -取消指定的操作。 - -#### cancelAllOperations() - -取消所有正在进行的操作。 - -#### isOperationValid(operationId, resourceId?) - -检查操作是否仍然有效。 - -### 查询方法 - -- `getOperation(operationId)` - 获取操作信息 -- `getCurrentOperationId(resourceId)` - 获取资源的当前操作ID -- `getPendingOperations()` - 获取所有待处理操作 -- `getRunningOperationsCount()` - 获取运行中的操作数量 - -## 使用场景 - -### 1. 编辑器文档切换 - -```typescript -// 防止快速切换文档时的内容混乱 -const loadDocument = async (documentId: number) => { - const result = await operationManager.executeOperation( - documentId, - async (signal) => { - // 保存当前文档 - await saveCurrentDocument(); - - if (signal.aborted) return; - - // 加载新文档 - const content = await loadDocumentContent(documentId); - - if (signal.aborted) return; - - return content; - }, - 'load-document' - ); - - if (result.success) { - updateEditor(result.data); - } -}; -``` - -### 2. 搜索功能 - -```typescript -// 取消过时的搜索请求 -const search = async (query: string) => { - const result = await operationManager.executeOperation( - 'search', - async (signal) => { - const results = await searchAPI(query, { signal }); - return results; - }, - 'search-query' - ); - - if (result.success) { - displaySearchResults(result.data); - } -}; -``` - -### 3. 数据加载 - -```typescript -// 避免页面切换时的数据竞态 -const loadPageData = async (pageId: string) => { - const result = await operationManager.executeOperation( - `page-${pageId}`, - async (signal) => { - const [userData, pageContent, settings] = await Promise.all([ - fetchUserData(signal), - fetchPageContent(pageId, signal), - fetchSettings(signal) - ]); - - return { userData, pageContent, settings }; - }, - 'load-page' - ); - - if (result.success) { - renderPage(result.data); - } -}; -``` - -## 操作状态 - -操作在生命周期中会经历以下状态: - -- `PENDING` - 待处理 -- `RUNNING` - 运行中 -- `COMPLETED` - 已完成 -- `CANCELLED` - 已取消 -- `FAILED` - 失败 - -## 最佳实践 - -### 1. 合理使用资源ID - -```typescript -// ✅ 好的做法:使用具体的资源标识 -operationManager.executeOperation('document-123', executor); -operationManager.executeOperation('user-456', executor); - -// ❌ 避免:使用过于宽泛的标识 -operationManager.executeOperation('global', executor); -``` - -### 2. 及时检查取消状态 - -```typescript -// ✅ 在关键点检查取消状态 -const executor = async (signal) => { - const step1 = await longRunningTask1(); - if (signal.aborted) throw new Error('Cancelled'); - - const step2 = await longRunningTask2(); - if (signal.aborted) throw new Error('Cancelled'); - - return processResults(step1, step2); -}; -``` - -### 3. 使用有意义的操作类型 - -```typescript -// ✅ 使用描述性的操作类型 -operationManager.executeOperation(docId, executor, 'auto-save'); -operationManager.executeOperation(docId, executor, 'manual-save'); -operationManager.executeOperation(docId, executor, 'export-pdf'); -``` - -### 4. 适当的错误处理 - -```typescript -const result = await operationManager.executeOperation(resourceId, executor); - -if (!result.success) { - if (result.operation.status === OperationStatus.CANCELLED) { - // 操作被取消,通常不需要显示错误 - console.log('Operation was cancelled'); - } else { - // 真正的错误,需要处理 - console.error('Operation failed:', result.error); - showErrorMessage(result.error.message); - } -} -``` - -## 注意事项 - -1. **内存管理**: 启用 `autoCleanup` 以防止内存泄漏 -2. **并发控制**: 根据应用需求设置合适的 `maxConcurrent` 值 -3. **超时设置**: 为长时间运行的操作设置合理的超时时间 -4. **错误处理**: 区分取消和真正的错误,提供适当的用户反馈 -5. **调试模式**: 在开发环境启用 `debug` 模式以便排查问题 - -## 类型定义 - -完整的类型定义请参考 `types.ts` 文件。 - -## 许可证 - -本模块遵循项目的整体许可证。 \ No newline at end of file diff --git a/frontend/src/common/async/index.ts b/frontend/src/common/async/index.ts deleted file mode 100644 index 544a80e..0000000 --- a/frontend/src/common/async/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * 异步操作管理模块 - * - * 该模块提供了用于管理异步操作竞态条件的完整解决方案。 - * 主要用于防止同一资源上的并发操作冲突,确保操作的正确性和一致性。 - * - * @fileoverview 异步操作管理模块入口文件 - * @author VoidRaft Team - * @since 1.0.0 - * - * @example - * ```typescript - * import { AsyncOperationManager, OperationStatus } from '@/common/async'; - * - * // 创建管理器实例 - * const manager = new AsyncOperationManager({ - * timeout: 30000, - * maxConcurrent: 5, - * debug: true - * }); - * - * // 执行异步操作 - * const result = await manager.executeOperation( - * 'document-123', - * async (signal, operationId) => { - * if (signal.aborted) throw new Error('Cancelled'); - * return await saveDocument(data); - * } - * ); - * - * if (result.success) { - * console.log('Operation completed:', result.data); - * } - * ``` - */ - -/** - * 导出异步操作管理器类 - * - * AsyncOperationManager 是该模块的核心类,提供了完整的异步操作管理功能。 - * - * @see {@link AsyncOperationManager} 异步操作管理器类的详细文档 - */ -export { AsyncOperationManager } from './manager'; - -/** - * 导出所有类型定义 - * - * 包括操作状态枚举、接口定义、配置选项等所有相关类型。 - * 这些类型为使用异步操作管理器提供了完整的 TypeScript 类型支持。 - * - * 导出的类型包括: - * - OperationStatus: 操作状态枚举 - * - OperationInfo: 操作信息接口 - * - AsyncOperationManagerConfig: 管理器配置接口 - * - OperationCallbacks: 操作回调函数接口 - * - OperationExecutor: 操作执行器函数类型 - * - OperationResult: 操作结果接口 - * - * @see {@link ./types} 类型定义文件 - */ -export * from './types'; - -/** - * 默认导出异步操作管理器类 - * - * 提供默认导出,方便使用 `import AsyncOperationManager from '@/common/async'` 的方式导入。 - * - * @default AsyncOperationManager - * - * @example - * ```typescript - * import AsyncOperationManager from '@/common/async'; - * - * const manager = new AsyncOperationManager(); - * ``` - */ -export { AsyncOperationManager as default } from './manager'; \ No newline at end of file diff --git a/frontend/src/common/async/manager.ts b/frontend/src/common/async/manager.ts deleted file mode 100644 index 8bbb3a6..0000000 --- a/frontend/src/common/async/manager.ts +++ /dev/null @@ -1,574 +0,0 @@ -import { - OperationStatus, - OperationInfo, - AsyncOperationManagerConfig, - OperationCallbacks, - OperationExecutor, - OperationResult -} from './types'; - -/** - * 异步操作管理器 - * - * 用于控制异步操作的竞态条件,确保操作的正确性和一致性。 - * 该管理器提供了操作的生命周期管理、并发控制、超时处理等功能。 - * - * @class AsyncOperationManager - * @author VoidRaft Team - * @since 1.0.0 - * - * @example - * ```typescript - * import { AsyncOperationManager } from './manager'; - * - * const manager = new AsyncOperationManager({ - * timeout: 30000, - * maxConcurrent: 5, - * debug: true - * }); - * - * // 执行异步操作 - * const result = await manager.executeOperation( - * 'document-123', - * async (signal, operationId) => { - * // 执行实际的异步操作 - * return await saveDocument(documentData); - * }, - * 'save-document' - * ); - * ``` - */ -export class AsyncOperationManager { - /** - * 操作序列号生成器 - * @private - * @type {number} - */ - private operationSequence = 0; - - /** - * 待处理操作映射表 - * @private - * @type {Map} - */ - private pendingOperations = new Map(); - - /** - * 当前资源操作映射表 - * 记录每个资源当前正在执行的操作ID - * @private - * @type {Map} - */ - private currentResourceOperation = new Map(); - - /** - * 管理器配置 - * @private - * @type {Required} - */ - private config: Required; - - /** - * 操作回调函数集合 - * @private - * @type {OperationCallbacks} - */ - private callbacks: OperationCallbacks; - - /** - * 创建异步操作管理器实例 - * - * @param {AsyncOperationManagerConfig} config - 管理器配置选项 - * @param {OperationCallbacks} callbacks - 操作生命周期回调函数 - * - * @example - * ```typescript - * const manager = new AsyncOperationManager( - * { - * timeout: 30000, - * autoCleanup: true, - * maxConcurrent: 10, - * debug: false - * }, - * { - * onStart: (op) => console.log(`Operation ${op.id} started`), - * onComplete: (op) => console.log(`Operation ${op.id} completed`), - * onError: (op, err) => console.error(`Operation ${op.id} failed:`, err) - * } - * ); - * ``` - */ - constructor( - config: AsyncOperationManagerConfig = {}, - callbacks: OperationCallbacks = {} - ) { - this.config = { - timeout: 0, - autoCleanup: true, - maxConcurrent: 0, - debug: false, - ...config - }; - this.callbacks = callbacks; - } - - /** - * 生成新的操作ID - * - * @private - * @returns {number} 新的操作ID - */ - private getNextOperationId(): number { - return ++this.operationSequence; - } - - /** - * 记录调试日志 - * - * 仅在调试模式下输出日志信息。 - * - * @private - * @param {string} message - 日志消息 - * @param {...any} args - 额外的日志参数 - */ - private log(message: string, ...args: any[]): void { - if (this.config.debug) { - console.log(`[AsyncOperationManager] ${message}`, ...args); - } - } - - /** - * 创建操作信息对象 - * - * @private - * @param {string | number} resourceId - 资源ID - * @param {string} [type] - 操作类型标识 - * @returns {OperationInfo} 新创建的操作信息 - */ - private createOperation( - resourceId: string | number, - type?: string - ): OperationInfo { - const operation: OperationInfo = { - id: this.getNextOperationId(), - resourceId, - status: OperationStatus.PENDING, - abortController: new AbortController(), - createdAt: Date.now(), - type - }; - - this.log(`Created operation ${operation.id} for resource ${resourceId}`, operation); - return operation; - } - - /** - * 清理已完成的操作 - * - * 自动从内存中移除已完成、已取消或失败的操作,释放内存资源。 - * 仅在启用自动清理配置时执行。 - * - * @private - */ - private cleanupCompletedOperations(): void { - if (!this.config.autoCleanup) return; - - const completedStatuses = [ - OperationStatus.COMPLETED, - OperationStatus.CANCELLED, - OperationStatus.FAILED - ]; - - for (const [id, operation] of this.pendingOperations.entries()) { - if (completedStatuses.includes(operation.status)) { - this.pendingOperations.delete(id); - this.log(`Cleaned up operation ${id}`); - } - } - } - - /** - * 取消指定资源的所有操作 - * - * 取消指定资源上正在运行的所有操作,可以排除指定的操作ID。 - * 这对于防止竞态条件非常有用,确保同一资源上只有最新的操作在执行。 - * - * @public - * @param {string | number} resourceId - 要取消操作的资源ID - * @param {number} [excludeOperationId] - 要排除的操作ID(不会被取消) - * - * @example - * ```typescript - * // 取消文档 'doc-123' 上的所有操作,除了操作 456 - * manager.cancelResourceOperations('doc-123', 456); - * ``` - */ - public cancelResourceOperations( - resourceId: string | number, - excludeOperationId?: number - ): void { - this.log(`Cancelling operations for resource ${resourceId}, exclude: ${excludeOperationId}`); - - for (const [id, operation] of this.pendingOperations.entries()) { - if ( - operation.resourceId === resourceId && - id !== excludeOperationId && - operation.status === OperationStatus.RUNNING - ) { - this.cancelOperation(id); - } - } - } - - /** - * 取消指定的操作 - * - * 通过操作ID取消正在运行的操作。只有状态为 RUNNING 的操作才能被取消。 - * - * @public - * @param {number} operationId - 要取消的操作ID - * @returns {boolean} 是否成功取消操作 - * - * @example - * ```typescript - * const cancelled = manager.cancelOperation(123); - * if (cancelled) { - * console.log('Operation 123 was cancelled'); - * } else { - * console.log('Operation 123 could not be cancelled (not found or not running)'); - * } - * ``` - */ - public cancelOperation(operationId: number): boolean { - const operation = this.pendingOperations.get(operationId); - if (!operation) return false; - - if (operation.status === OperationStatus.RUNNING) { - operation.abortController.abort(); - operation.status = OperationStatus.CANCELLED; - this.log(`Cancelled operation ${operationId}`); - - this.callbacks.onCancel?.(operation); - this.cleanupCompletedOperations(); - return true; - } - - return false; - } - - /** - * 取消所有操作 - * - * 取消管理器中所有正在运行的操作,并清空资源操作映射表。 - * - * @public - * - * @example - * ```typescript - * // 在应用关闭或重置时取消所有操作 - * manager.cancelAllOperations(); - * ``` - */ - public cancelAllOperations(): void { - this.log('Cancelling all operations'); - - for (const [id] of this.pendingOperations.entries()) { - this.cancelOperation(id); - } - - this.currentResourceOperation.clear(); - } - - /** - * 检查操作是否仍然有效 - * - * 验证操作是否存在、未被取消、状态为运行中,以及(如果指定了资源ID) - * 是否为该资源的当前活跃操作。 - * - * @public - * @param {number} operationId - 要检查的操作ID - * @param {string | number} [resourceId] - 可选的资源ID,用于验证是否为当前活跃操作 - * @returns {boolean} 操作是否有效 - * - * @example - * ```typescript - * // 在长时间运行的操作中定期检查有效性 - * const saveDocument = async (signal: AbortSignal, operationId: number) => { - * for (let i = 0; i < 100; i++) { - * if (!manager.isOperationValid(operationId, 'doc-123')) { - * throw new Error('Operation is no longer valid'); - * } - * await processChunk(i); - * } - * }; - * ``` - */ - public isOperationValid( - operationId: number, - resourceId?: string | number - ): boolean { - const operation = this.pendingOperations.get(operationId); - - if (!operation) return false; - if (operation.abortController.signal.aborted) return false; - if (operation.status !== OperationStatus.RUNNING) return false; - - // 如果指定了资源ID,检查是否为当前资源的活跃操作 - if (resourceId !== undefined) { - const currentOperationId = this.currentResourceOperation.get(resourceId); - if (currentOperationId !== operationId) return false; - } - - return true; - } - - /** - * 执行异步操作 - * - * 这是管理器的核心方法,用于执行异步操作并管理其生命周期。 - * 该方法会自动处理并发控制、竞态条件、超时管理等复杂逻辑。 - * - * @public - * @template T - 操作返回值的类型 - * @param {string | number} resourceId - 操作关联的资源ID - * @param {OperationExecutor} executor - 实际执行操作的函数 - * @param {string} [operationType] - 操作类型标识,用于调试和日志 - * @returns {Promise>} 操作执行结果 - * - * @throws {Error} 当达到最大并发限制时抛出错误 - * - * @example - * ```typescript - * // 执行文档保存操作 - * const result = await manager.executeOperation( - * 'document-123', - * async (signal, operationId) => { - * // 检查操作是否被取消 - * if (signal.aborted) { - * throw new Error('Operation was cancelled'); - * } - * - * // 执行实际的保存逻辑 - * const saved = await api.saveDocument(documentData); - * return saved; - * }, - * 'save-document' - * ); - * - * if (result.success) { - * console.log('Document saved successfully:', result.data); - * } else { - * console.error('Failed to save document:', result.error); - * } - * ``` - */ - public async executeOperation( - resourceId: string | number, - executor: OperationExecutor, - operationType?: string - ): Promise> { - // 检查并发限制 - if (this.config.maxConcurrent > 0) { - const runningCount = Array.from(this.pendingOperations.values()) - .filter(op => op.status === OperationStatus.RUNNING).length; - - if (runningCount >= this.config.maxConcurrent) { - throw new Error(`Maximum concurrent operations limit reached: ${this.config.maxConcurrent}`); - } - } - - const operation = this.createOperation(resourceId, operationType); - - try { - // 取消同一资源的其他操作 - this.cancelResourceOperations(resourceId, operation.id); - - // 设置当前资源的活跃操作 - this.currentResourceOperation.set(resourceId, operation.id); - - // 添加到待处理操作列表 - this.pendingOperations.set(operation.id, operation); - - // 设置超时 - if (this.config.timeout > 0) { - setTimeout(() => { - if (this.isOperationValid(operation.id)) { - this.cancelOperation(operation.id); - } - }, this.config.timeout); - } - - // 更新状态为运行中 - operation.status = OperationStatus.RUNNING; - this.callbacks.onStart?.(operation); - this.log(`Started operation ${operation.id} for resource ${resourceId}`); - - // 执行操作 - const result = await executor(operation.abortController.signal, operation.id); - - // 检查操作是否仍然有效 - if (!this.isOperationValid(operation.id, resourceId)) { - throw new Error('Operation was cancelled'); - } - - // 操作成功完成 - operation.status = OperationStatus.COMPLETED; - this.callbacks.onComplete?.(operation); - this.log(`Completed operation ${operation.id}`); - - this.cleanupCompletedOperations(); - - return { - success: true, - data: result, - operation - }; - - } catch (error) { - const err = error instanceof Error ? error : new Error(String(error)); - - if (operation.abortController.signal.aborted) { - operation.status = OperationStatus.CANCELLED; - this.log(`Operation ${operation.id} was cancelled`); - } else { - operation.status = OperationStatus.FAILED; - this.callbacks.onError?.(operation, err); - this.log(`Operation ${operation.id} failed:`, err.message); - } - - this.cleanupCompletedOperations(); - - return { - success: false, - error: err, - operation - }; - } finally { - // 清理当前资源操作记录 - if (this.currentResourceOperation.get(resourceId) === operation.id) { - this.currentResourceOperation.delete(resourceId); - } - } - } - - /** - * 获取操作信息 - * - * 根据操作ID获取操作的详细信息。 - * - * @public - * @param {number} operationId - 操作ID - * @returns {OperationInfo | undefined} 操作信息,如果操作不存在则返回 undefined - * - * @example - * ```typescript - * const operation = manager.getOperation(123); - * if (operation) { - * console.log(`Operation ${operation.id} status: ${operation.status}`); - * } else { - * console.log('Operation not found'); - * } - * ``` - */ - public getOperation(operationId: number): OperationInfo | undefined { - return this.pendingOperations.get(operationId); - } - - /** - * 获取资源的当前操作ID - * - * 获取指定资源当前正在执行的操作ID。 - * - * @public - * @param {string | number} resourceId - 资源ID - * @returns {number | undefined} 当前操作ID,如果没有正在执行的操作则返回 undefined - * - * @example - * ```typescript - * const currentOpId = manager.getCurrentOperationId('document-123'); - * if (currentOpId) { - * console.log(`Document 123 is currently being processed by operation ${currentOpId}`); - * } else { - * console.log('No active operation for document 123'); - * } - * ``` - */ - public getCurrentOperationId(resourceId: string | number): number | undefined { - return this.currentResourceOperation.get(resourceId); - } - - /** - * 获取所有待处理操作 - * - * 返回管理器中所有待处理操作的列表,包括待执行、正在执行和已完成的操作。 - * - * @public - * @returns {OperationInfo[]} 所有待处理操作的数组 - * - * @example - * ```typescript - * const allOperations = manager.getPendingOperations(); - * console.log(`Total operations: ${allOperations.length}`); - * - * allOperations.forEach(op => { - * console.log(`Operation ${op.id}: ${op.status} (${op.type || 'unknown'})`); - * }); - * ``` - */ - public getPendingOperations(): OperationInfo[] { - return Array.from(this.pendingOperations.values()); - } - - /** - * 获取运行中的操作数量 - * - * 返回当前正在执行的操作数量,用于监控并发情况。 - * - * @public - * @returns {number} 正在运行的操作数量 - * - * @example - * ```typescript - * const runningCount = manager.getRunningOperationsCount(); - * console.log(`Currently running operations: ${runningCount}`); - * - * if (runningCount >= maxConcurrent) { - * console.warn('Approaching maximum concurrent operations limit'); - * } - * ``` - */ - public getRunningOperationsCount(): number { - return Array.from(this.pendingOperations.values()) - .filter(op => op.status === OperationStatus.RUNNING).length; - } - - /** - * 销毁管理器 - * - * 清理管理器的所有资源,取消所有正在运行的操作,清空所有映射表。 - * 通常在应用关闭或组件卸载时调用。 - * - * @public - * - * @example - * ```typescript - * // 在组件卸载时清理资源 - * useEffect(() => { - * return () => { - * manager.destroy(); - * }; - * }, []); - * - * // 或在应用关闭时 - * window.addEventListener('beforeunload', () => { - * manager.destroy(); - * }); - * ``` - */ - public destroy(): void { - this.log('Destroying AsyncOperationManager'); - this.cancelAllOperations(); - this.pendingOperations.clear(); - this.currentResourceOperation.clear(); - } -} \ No newline at end of file diff --git a/frontend/src/common/async/types.ts b/frontend/src/common/async/types.ts deleted file mode 100644 index 6f50135..0000000 --- a/frontend/src/common/async/types.ts +++ /dev/null @@ -1,289 +0,0 @@ -/** - * 异步操作竞态条件控制相关类型定义 - * - * 该模块提供了用于管理异步操作竞态条件的类型定义, - * 包括操作状态、配置选项、回调函数等核心类型。 - * - * @fileoverview 异步操作管理器类型定义 - * @author VoidRaft Team - * @since 1.0.0 - */ - -/** - * 操作状态枚举 - * - * 定义异步操作在其生命周期中可能的状态。 - * - * @enum {string} - * @readonly - * - * @example - * ```typescript - * import { OperationStatus } from './types'; - * - * const status = OperationStatus.RUNNING; - * console.log(status); // 'running' - * ``` - */ -export enum OperationStatus { - /** 操作已创建但尚未开始执行 */ - PENDING = 'pending', - /** 操作正在执行中 */ - RUNNING = 'running', - /** 操作已成功完成 */ - COMPLETED = 'completed', - /** 操作已被取消 */ - CANCELLED = 'cancelled', - /** 操作执行失败 */ - FAILED = 'failed' -} - -/** - * 操作信息接口 - * - * 描述单个异步操作的完整信息,包括标识符、状态、控制器等。 - * - * @interface OperationInfo - * - * @example - * ```typescript - * const operation: OperationInfo = { - * id: 1, - * resourceId: 'document-123', - * status: OperationStatus.RUNNING, - * abortController: new AbortController(), - * createdAt: Date.now(), - * type: 'save-document' - * }; - * ``` - */ -export interface OperationInfo { - /** - * 操作的唯一标识符 - * @type {number} - */ - id: number; - - /** - * 关联的资源ID(如文档ID、用户ID等) - * 用于标识操作所作用的资源,支持字符串或数字类型 - * @type {string | number} - */ - resourceId: string | number; - - /** - * 当前操作状态 - * @type {OperationStatus} - */ - status: OperationStatus; - - /** - * 用于取消操作的控制器 - * 通过调用 abortController.abort() 可以取消正在执行的操作 - * @type {AbortController} - */ - abortController: AbortController; - - /** - * 操作创建时间戳(毫秒) - * @type {number} - */ - createdAt: number; - - /** - * 操作类型标识(可选) - * 用于调试和日志记录,帮助识别不同类型的操作 - * @type {string} - * @optional - */ - type?: string; -} - -/** - * 异步操作管理器配置接口 - * - * 定义异步操作管理器的配置选项,用于控制操作的行为和限制。 - * - * @interface AsyncOperationManagerConfig - * - * @example - * ```typescript - * const config: AsyncOperationManagerConfig = { - * timeout: 30000, // 30秒超时 - * autoCleanup: true, // 自动清理已完成操作 - * maxConcurrent: 5, // 最多5个并发操作 - * debug: true // 启用调试模式 - * }; - * ``` - */ -export interface AsyncOperationManagerConfig { - /** - * 操作超时时间(毫秒) - * 设置为 0 表示不设置超时限制 - * @type {number} - * @default 0 - * @optional - */ - timeout?: number; - - /** - * 是否自动清理已完成的操作 - * 启用后会自动从内存中移除已完成、已取消或失败的操作 - * @type {boolean} - * @default true - * @optional - */ - autoCleanup?: boolean; - - /** - * 最大并发操作数 - * 设置为 0 表示无并发限制 - * @type {number} - * @default 0 - * @optional - */ - maxConcurrent?: number; - - /** - * 调试模式开关 - * 启用后会在控制台输出详细的操作日志 - * @type {boolean} - * @default false - * @optional - */ - debug?: boolean; -} - -/** - * 操作回调函数集合接口 - * - * 定义在操作生命周期的不同阶段可以执行的回调函数。 - * - * @interface OperationCallbacks - * - * @example - * ```typescript - * const callbacks: OperationCallbacks = { - * onStart: (operation) => console.log(`Operation ${operation.id} started`), - * onComplete: (operation) => console.log(`Operation ${operation.id} completed`), - * onCancel: (operation) => console.log(`Operation ${operation.id} cancelled`), - * onError: (operation, error) => console.error(`Operation ${operation.id} failed:`, error) - * }; - * ``` - */ -export interface OperationCallbacks { - /** - * 操作开始时的回调函数 - * @param {OperationInfo} operation - 操作信息 - * @optional - */ - onStart?: (operation: OperationInfo) => void; - - /** - * 操作成功完成时的回调函数 - * @param {OperationInfo} operation - 操作信息 - * @optional - */ - onComplete?: (operation: OperationInfo) => void; - - /** - * 操作被取消时的回调函数 - * @param {OperationInfo} operation - 操作信息 - * @optional - */ - onCancel?: (operation: OperationInfo) => void; - - /** - * 操作执行失败时的回调函数 - * @param {OperationInfo} operation - 操作信息 - * @param {Error} error - 错误对象 - * @optional - */ - onError?: (operation: OperationInfo, error: Error) => void; -} - -/** - * 操作执行器函数类型 - * - * 定义实际执行异步操作的函数签名。 - * - * @template T - 操作返回值的类型 - * @param {AbortSignal} signal - 用于检测操作是否被取消的信号 - * @param {number} operationId - 操作的唯一标识符 - * @returns {Promise} 操作执行结果的 Promise - * - * @example - * ```typescript - * const saveDocument: OperationExecutor = async (signal, operationId) => { - * // 检查操作是否被取消 - * if (signal.aborted) { - * throw new Error('Operation was cancelled'); - * } - * - * // 执行实际的保存操作 - * const result = await api.saveDocument(documentData); - * return result.success; - * }; - * ``` - */ -export type OperationExecutor = ( - signal: AbortSignal, - operationId: number -) => Promise; - -/** - * 操作执行结果接口 - * - * 封装异步操作的执行结果,包括成功状态、数据和错误信息。 - * - * @template T - 操作结果数据的类型 - * @interface OperationResult - * - * @example - * ```typescript - * // 成功的操作结果 - * const successResult: OperationResult = { - * success: true, - * data: 'Operation completed successfully', - * operation: operationInfo - * }; - * - * // 失败的操作结果 - * const failureResult: OperationResult = { - * success: false, - * error: new Error('Operation failed'), - * operation: operationInfo - * }; - * ``` - */ -export interface OperationResult { - /** - * 操作是否成功执行 - * true 表示操作成功完成,false 表示操作失败或被取消 - * @type {boolean} - */ - success: boolean; - - /** - * 操作成功时的结果数据 - * 仅在 success 为 true 时有值 - * @type {T} - * @optional - */ - data?: T; - - /** - * 操作失败时的错误信息 - * 仅在 success 为 false 时有值 - * @type {Error} - * @optional - */ - error?: Error; - - /** - * 关联的操作信息 - * 包含操作的完整元数据 - * @type {OperationInfo} - */ - operation: OperationInfo; -} \ No newline at end of file diff --git a/frontend/src/common/cache/README.md b/frontend/src/common/cache/README.md deleted file mode 100644 index 2fa1f5f..0000000 --- a/frontend/src/common/cache/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# 缓存系统 - -简洁高效的 LRU 缓存实现,支持泛型和自动清理。 - -## 特性 - -- 🚀 高性能 LRU 缓存算法 -- 🔧 TypeScript 泛型支持 -- 🧹 自动资源清理 -- 📊 缓存统计信息 -- ⏰ TTL 过期支持 -- 🎯 简洁易用的 API -- 🔐 多种哈希算法支持 -- 🏗️ 模块化设计,易于扩展 - -## 基础用法 - -### 创建缓存 - -```typescript -import { LruCache, CacheManager, createCacheItem } from '@/common/cache'; - -// 直接创建缓存 -const cache = new LruCache({ - maxSize: 100, - ttl: 5 * 60 * 1000, // 5 分钟 - onEvict: (item) => console.log('Evicted:', item) -}); - -// 使用缓存管理器 -const manager = new CacheManager(); -const myCache = manager.createCache('myCache', { maxSize: 50 }); -``` - -### 缓存操作 - -```typescript -// 创建缓存项 -const item = createCacheItem('key1', { - name: 'example', - data: { foo: 'bar' } -}); - -// 设置缓存 -cache.set('key1', item); - -// 获取缓存 -const cached = cache.get('key1'); - -// 检查存在 -if (cache.has('key1')) { - // 处理逻辑 -} - -// 移除缓存 -cache.remove('key1'); -``` - -### 自动清理资源 - -```typescript -interface MyItem extends CacheItem, DisposableCacheItem { - resource: SomeResource; - dispose(): void; -} - -const item: MyItem = { - id: 'resource1', - lastAccessed: new Date(), - createdAt: new Date(), - resource: new SomeResource(), - dispose() { - this.resource.cleanup(); - } -}; - -// 当项被驱逐或移除时,dispose 方法会自动调用 -cache.set('resource1', item); -``` - -### 哈希工具使用 - -```typescript -import { createHash, generateCacheKey } from '@/common/cache'; - -// 生成简单哈希 -const hash = createHash('some content'); - -// 生成缓存键 -const key = generateCacheKey('user', userId, 'profile'); -``` - -## API 参考 - -### LruCache - -- `get(id)` - 获取缓存项(O(1)) -- `set(id, item)` - 设置缓存项(O(1)) -- `remove(id)` - 移除缓存项(O(1)) -- `has(id)` - 检查是否存在(O(1)) -- `clear()` - 清空缓存 -- `size()` - 获取缓存大小 -- `getStats()` - 获取统计信息 -- `cleanup()` - 清理过期项 - -### CacheManager - -- `getCache(name, config?)` - 获取或创建缓存 -- `createCache(name, config)` - 创建新缓存 -- `removeCache(name)` - 删除缓存 -- `clearAll()` - 清空所有缓存 -- `getAllStats()` - 获取所有统计信息 -- `cleanupAll()` - 清理所有缓存的过期项 - -## 工具函数 - -- `generateCacheKey(...parts)` - 生成缓存键 -- `createHash(content)` - 创建内容哈希 -- `createCacheItem(id, data)` - 创建缓存项 \ No newline at end of file diff --git a/frontend/src/common/cache/doublyLinkedList.ts b/frontend/src/common/cache/doublyLinkedList.ts deleted file mode 100644 index 3d2478b..0000000 --- a/frontend/src/common/cache/doublyLinkedList.ts +++ /dev/null @@ -1,166 +0,0 @@ -import type { DoublyLinkedNode } from './interfaces'; - -/** - * 双向链表实现 - * 用于高效管理LRU缓存的访问顺序,所有操作都是O(1)时间复杂度 - * - * @template T 节点值的类型 - */ -export class DoublyLinkedList { - /** 头节点(虚拟节点) */ - private head: DoublyLinkedNode; - /** 尾节点(虚拟节点) */ - private tail: DoublyLinkedNode; - /** 当前节点数量 */ - private count: number = 0; - - /** - * 构造函数 - * 创建头尾虚拟节点,简化边界处理 - */ - constructor() { - // 创建虚拟头节点 - this.head = { - value: null as any, - key: 'head', - prev: null, - next: null - }; - - // 创建虚拟尾节点 - this.tail = { - value: null as any, - key: 'tail', - prev: null, - next: null - }; - - // 连接头尾节点 - this.head.next = this.tail; - this.tail.prev = this.head; - } - - /** - * 在头部添加节点 - * 时间复杂度: O(1) - * - * @param node 要添加的节点 - */ - addToHead(node: DoublyLinkedNode): void { - node.prev = this.head; - node.next = this.head.next; - - if (this.head.next) { - this.head.next.prev = node; - } - this.head.next = node; - - this.count++; - } - - /** - * 移除指定节点 - * 时间复杂度: O(1) - * - * @param node 要移除的节点 - */ - removeNode(node: DoublyLinkedNode): void { - if (node.prev) { - node.prev.next = node.next; - } - if (node.next) { - node.next.prev = node.prev; - } - - this.count--; - } - - /** - * 移除尾部节点 - * 时间复杂度: O(1) - * - * @returns 被移除的节点,如果链表为空则返回null - */ - removeTail(): DoublyLinkedNode | null { - const lastNode = this.tail.prev; - - if (lastNode === this.head) { - return null; // 链表为空 - } - - this.removeNode(lastNode!); - return lastNode!; - } - - /** - * 将节点移动到头部 - * 时间复杂度: O(1) - * - * @param node 要移动的节点 - */ - moveToHead(node: DoublyLinkedNode): void { - this.removeNode(node); - this.addToHead(node); - } - - /** - * 创建新节点 - * - * @param key 节点键 - * @param value 节点值 - * @returns 新创建的节点 - */ - createNode(key: string | number, value: T): DoublyLinkedNode { - return { - key, - value, - prev: null, - next: null - }; - } - - /** - * 获取链表大小 - * - * @returns 当前节点数量 - */ - size(): number { - return this.count; - } - - /** - * 检查链表是否为空 - * - * @returns 是否为空 - */ - isEmpty(): boolean { - return this.count === 0; - } - - /** - * 清空链表 - */ - clear(): void { - this.head.next = this.tail; - this.tail.prev = this.head; - this.count = 0; - } - - /** - * 获取所有节点的值(从头到尾) - * 主要用于调试和测试 - * - * @returns 所有节点值的数组 - */ - toArray(): T[] { - const result: T[] = []; - let current = this.head.next; - - while (current && current !== this.tail) { - result.push(current.value); - current = current.next; - } - - return result; - } -} \ No newline at end of file diff --git a/frontend/src/common/cache/index.ts b/frontend/src/common/cache/index.ts deleted file mode 100644 index f80d951..0000000 --- a/frontend/src/common/cache/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -export type { - CacheItem, - DisposableCacheItem, - CacheConfig, - CacheStats, - CacheStrategy, - DoublyLinkedNode -} from './interfaces'; -export { LruCache } from './lruCache'; -export { CacheManager, globalCacheManager } from './manager'; -export { DoublyLinkedList } from './doublyLinkedList'; -export { - createHash, - generateCacheKey, - createCacheItem, - calculateHitRate, - formatCacheSize, - isExpired, - debounce, - throttle -} from './utils'; \ No newline at end of file diff --git a/frontend/src/common/cache/interfaces.ts b/frontend/src/common/cache/interfaces.ts deleted file mode 100644 index 4a3c66f..0000000 --- a/frontend/src/common/cache/interfaces.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * 缓存项基础接口 - */ -export interface CacheItem { - /** 缓存项的唯一标识 */ - id: string | number; - /** 最后访问时间 */ - lastAccessed: Date; - /** 创建时间 */ - createdAt: Date; -} - -/** - * 可清理的缓存项接口 - */ -export interface DisposableCacheItem extends CacheItem { - /** 清理资源的方法 */ - dispose(): void | Promise; -} - -/** - * 缓存配置接口 - */ -export interface CacheConfig { - /** 最大缓存数量 */ - maxSize: number; - /** 生存时间(毫秒),可选 */ - ttl?: number; - /** 驱逐回调函数,可选 */ - onEvict?: (item: any) => void | Promise; -} - -/** - * 缓存统计信息接口 - */ -export interface CacheStats { - /** 当前缓存项数量 */ - size: number; - /** 最大容量 */ - maxSize: number; - /** 命中次数 */ - hits: number; - /** 未命中次数 */ - misses: number; - /** 命中率 */ - hitRate: number; -} - -/** - * 通用缓存策略接口 - * 所有缓存实现都应该实现这个接口 - */ -export interface CacheStrategy { - /** - * 获取缓存项 - * @param id 缓存项ID - * @returns 缓存项或null - */ - get(id: string | number): T | null; - - /** - * 设置缓存项 - * @param id 缓存项ID - * @param item 缓存项 - */ - set(id: string | number, item: T): void; - - /** - * 移除缓存项 - * @param id 缓存项ID - * @returns 是否成功移除 - */ - remove(id: string | number): boolean; - - /** - * 检查是否存在 - * @param id 缓存项ID - * @returns 是否存在 - */ - has(id: string | number): boolean; - - /** - * 清空缓存 - */ - clear(): void; - - /** - * 获取所有项 - * @returns 所有缓存项 - */ - getAll(): T[]; - - /** - * 获取缓存大小 - * @returns 当前缓存项数量 - */ - size(): number; - - /** - * 获取统计信息 - * @returns 缓存统计信息 - */ - getStats(): CacheStats; - - /** - * 清理过期项 - * @returns 清理的项数量 - */ - cleanup(): number; -} - -/** - * 双向链表节点接口 - */ -export interface DoublyLinkedNode { - /** 节点值 */ - value: T; - /** 节点键 */ - key: string | number; - /** 前一个节点 */ - prev: DoublyLinkedNode | null; - /** 下一个节点 */ - next: DoublyLinkedNode | null; -} \ No newline at end of file diff --git a/frontend/src/common/cache/lruCache.ts b/frontend/src/common/cache/lruCache.ts deleted file mode 100644 index 6d2e4f6..0000000 --- a/frontend/src/common/cache/lruCache.ts +++ /dev/null @@ -1,276 +0,0 @@ -import type { CacheItem, CacheConfig, CacheStats, DisposableCacheItem, CacheStrategy, DoublyLinkedNode } from './interfaces'; -import { DoublyLinkedList } from './doublyLinkedList'; - -/** - * 高性能LRU缓存实现 - * 使用双向链表 + Map 的组合,所有核心操作都是O(1)时间复杂度 - * - * @template T 缓存项类型,必须继承自CacheItem - */ -export class LruCache implements CacheStrategy { - /** 存储缓存项的Map,提供O(1)的查找性能 */ - private items = new Map>(); - - /** 双向链表,管理访问顺序,提供O(1)的插入/删除性能 */ - private accessList = new DoublyLinkedList(); - - /** 缓存配置 */ - private config: CacheConfig; - - /** 统计信息 */ - private stats = { - hits: 0, - misses: 0 - }; - - /** - * 构造函数 - * - * @param config 缓存配置 - */ - constructor(config: CacheConfig) { - this.config = config; - } - - /** - * 获取缓存项 - * 时间复杂度: O(1) - * - * @param id 缓存项ID - * @returns 缓存项或null - */ - get(id: string | number): T | null { - const node = this.items.get(id); - - if (!node) { - this.stats.misses++; - return null; - } - - // 检查是否过期 - if (this.isExpired(node.value)) { - this.remove(id); - this.stats.misses++; - return null; - } - - // 更新访问时间 - node.value.lastAccessed = new Date(); - - // 将节点移动到链表头部(最近访问) - this.accessList.moveToHead(node); - - this.stats.hits++; - return node.value; - } - - /** - * 设置缓存项 - * 时间复杂度: O(1) - * - * @param id 缓存项ID - * @param item 缓存项 - */ - set(id: string | number, item: T): void { - const existingNode = this.items.get(id); - - // 如果已存在,更新值并移动到头部 - if (existingNode) { - existingNode.value = item; - this.accessList.moveToHead(existingNode); - return; - } - - // 检查容量,必要时驱逐最旧的项 - while (this.items.size >= this.config.maxSize) { - this.evictLeastRecentlyUsed(); - } - - // 创建新节点并添加到头部 - const newNode = this.accessList.createNode(id, item); - this.accessList.addToHead(newNode); - this.items.set(id, newNode); - } - - /** - * 移除缓存项 - * 时间复杂度: O(1) - * - * @param id 缓存项ID - * @returns 是否成功移除 - */ - remove(id: string | number): boolean { - const node = this.items.get(id); - if (!node) return false; - - // 从链表中移除 - this.accessList.removeNode(node); - - // 从Map中移除 - this.items.delete(id); - - // 调用清理逻辑 - this.disposeItem(node.value); - - return true; - } - - /** - * 检查是否存在 - * 时间复杂度: O(1) - * - * @param id 缓存项ID - * @returns 是否存在 - */ - has(id: string | number): boolean { - const node = this.items.get(id); - if (!node) return false; - - if (this.isExpired(node.value)) { - this.remove(id); - return false; - } - - return true; - } - - /** - * 清空缓存 - */ - clear(): void { - // 清理所有项 - for (const node of this.items.values()) { - this.disposeItem(node.value); - } - - this.items.clear(); - this.accessList.clear(); - this.stats.hits = 0; - this.stats.misses = 0; - } - - /** - * 获取所有项 - * 按访问顺序返回(最近访问的在前) - * - * @returns 所有缓存项 - */ - getAll(): T[] { - return this.accessList.toArray(); - } - - /** - * 获取缓存大小 - * - * @returns 当前缓存项数量 - */ - size(): number { - return this.items.size; - } - - /** - * 获取统计信息 - * - * @returns 缓存统计信息 - */ - getStats(): CacheStats { - const total = this.stats.hits + this.stats.misses; - return { - size: this.items.size, - maxSize: this.config.maxSize, - hits: this.stats.hits, - misses: this.stats.misses, - hitRate: total > 0 ? this.stats.hits / total : 0 - }; - } - - /** - * 清理过期项 - * - * @returns 清理的项数量 - */ - cleanup(): number { - let cleanedCount = 0; - - if (!this.config.ttl) return cleanedCount; - - // 收集过期的键 - const expiredKeys: (string | number)[] = []; - for (const [id, node] of this.items.entries()) { - if (this.isExpired(node.value)) { - expiredKeys.push(id); - } - } - - // 移除过期项 - for (const key of expiredKeys) { - if (this.remove(key)) { - cleanedCount++; - } - } - - return cleanedCount; - } - - // 私有方法 - - /** - * 检查项是否过期 - * - * @param item 缓存项 - * @returns 是否过期 - */ - private isExpired(item: T): boolean { - if (!this.config.ttl) return false; - return Date.now() - item.lastAccessed.getTime() > this.config.ttl; - } - - /** - * 驱逐最近最少使用的项 - */ - private evictLeastRecentlyUsed(): void { - const tailNode = this.accessList.removeTail(); - if (tailNode) { - // 调用驱逐回调 - if (this.config.onEvict) { - this.config.onEvict(tailNode.value); - } - - // 从Map中移除 - this.items.delete(tailNode.key); - - // 清理资源 - this.disposeItem(tailNode.value); - } - } - - /** - * 清理缓存项资源 - * - * @param item 要清理的缓存项 - */ - private disposeItem(item: T): void { - if (this.isDisposable(item)) { - try { - const result = item.dispose(); - if (result instanceof Promise) { - result.catch(error => { - console.warn('Failed to dispose cache item:', error); - }); - } - } catch (error) { - console.warn('Failed to dispose cache item:', error); - } - } - } - - /** - * 检查项是否可清理 - * - * @param item 缓存项 - * @returns 是否可清理 - */ - private isDisposable(item: T): item is T & DisposableCacheItem { - return 'dispose' in item && typeof (item as any).dispose === 'function'; - } -} \ No newline at end of file diff --git a/frontend/src/common/cache/manager.ts b/frontend/src/common/cache/manager.ts deleted file mode 100644 index 61487ee..0000000 --- a/frontend/src/common/cache/manager.ts +++ /dev/null @@ -1,263 +0,0 @@ -import type { CacheItem, CacheConfig, CacheStats, CacheStrategy } from './interfaces'; -import { LruCache } from './lruCache'; - -/** - * 缓存管理器 - * 统一管理多个缓存实例,提供全局缓存操作和自动清理功能 - * 支持不同的缓存策略,默认使用LRU缓存 - */ -export class CacheManager { - /** 存储所有缓存实例的Map */ - private caches = new Map>(); - - /** 自动清理定时器ID */ - private cleanupInterval?: number; - - /** - * 构造函数 - * - * @param options 配置选项 - * @param options.cleanupInterval 自动清理间隔(毫秒),默认不启用 - */ - constructor(options?: { - /** 自动清理间隔(毫秒),默认 5 分钟 */ - cleanupInterval?: number; - }) { - if (options?.cleanupInterval) { - this.startAutoCleanup(options.cleanupInterval); - } - } - - /** - * 创建或获取缓存实例 - * 如果缓存不存在且提供了配置,则创建新的缓存实例 - * - * @template T 缓存项类型 - * @param name 缓存名称 - * @param config 缓存配置(仅在创建新缓存时需要) - * @param strategy 缓存策略构造函数,默认使用LruCache - * @returns 缓存实例 - * @throws 如果缓存不存在且未提供配置 - * @example - * ```typescript - * const userCache = manager.getCache('users', { - * maxSize: 100, - * ttl: 5 * 60 * 1000 // 5分钟 - * }); - * ``` - */ - getCache( - name: string, - config?: CacheConfig, - strategy: new (config: CacheConfig) => CacheStrategy = LruCache - ): CacheStrategy { - if (!this.caches.has(name)) { - if (!config) { - throw new Error(`Cache "${name}" does not exist and no config provided`); - } - this.caches.set(name, new strategy(config)); - } - return this.caches.get(name)!; - } - - /** - * 创建新的缓存实例 - * 如果同名缓存已存在,则抛出错误 - * - * @template T 缓存项类型 - * @param name 缓存名称 - * @param config 缓存配置 - * @param strategy 缓存策略构造函数,默认使用LruCache - * @returns 新创建的缓存实例 - * @throws 如果同名缓存已存在 - * @example - * ```typescript - * const productCache = manager.createCache('products', { - * maxSize: 200, - * ttl: 10 * 60 * 1000 // 10分钟 - * }); - * ``` - */ - createCache( - name: string, - config: CacheConfig, - strategy: new (config: CacheConfig) => CacheStrategy = LruCache - ): CacheStrategy { - if (this.caches.has(name)) { - throw new Error(`Cache "${name}" already exists`); - } - const cache = new strategy(config); - this.caches.set(name, cache); - return cache; - } - - /** - * 删除缓存实例 - * 会先清空缓存内容,然后从管理器中移除 - * - * @param name 缓存名称 - * @returns 是否成功删除 - * @example - * ```typescript - * const removed = manager.removeCache('temp-cache'); - * console.log(removed); // true 或 false - * ``` - */ - removeCache(name: string): boolean { - const cache = this.caches.get(name); - if (cache) { - cache.clear(); - this.caches.delete(name); - return true; - } - return false; - } - - /** - * 检查缓存是否存在 - * - * @param name 缓存名称 - * @returns 是否存在 - * @example - * ```typescript - * if (manager.hasCache('users')) { - * const userCache = manager.getCache('users'); - * } - * ``` - */ - hasCache(name: string): boolean { - return this.caches.has(name); - } - - /** - * 获取所有缓存名称 - * - * @returns 缓存名称数组 - * @example - * ```typescript - * const cacheNames = manager.getCacheNames(); - * console.log('Active caches:', cacheNames); - * ``` - */ - getCacheNames(): string[] { - return Array.from(this.caches.keys()); - } - - /** - * 清空所有缓存 - * 清空所有缓存实例的内容,但不删除缓存实例本身 - * - * @example - * ```typescript - * manager.clearAll(); // 清空所有缓存内容 - * ``` - */ - clearAll(): void { - for (const cache of this.caches.values()) { - cache.clear(); - } - } - - /** - * 获取所有缓存的统计信息 - * - * @returns 包含所有缓存统计信息的对象 - * @example - * ```typescript - * const stats = manager.getAllStats(); - * console.log('Cache stats:', stats); - * // 输出: { users: { size: 50, hits: 100, ... }, products: { ... } } - * ``` - */ - getAllStats(): Record { - const stats: Record = {}; - for (const [name, cache] of this.caches.entries()) { - stats[name] = cache.getStats(); - } - return stats; - } - - /** - * 清理所有缓存中的过期项 - * - * @returns 包含每个缓存清理项数量的对象 - * @example - * ```typescript - * const results = manager.cleanupAll(); - * console.log('Cleanup results:', results); - * // 输出: { users: 5, products: 2 } // 表示清理的项数量 - * ``` - */ - cleanupAll(): Record { - const results: Record = {}; - for (const [name, cache] of this.caches.entries()) { - results[name] = cache.cleanup(); - } - return results; - } - - /** - * 启动自动清理 - * 定期清理所有缓存中的过期项 - * - * @param interval 清理间隔(毫秒) - * @example - * ```typescript - * manager.startAutoCleanup(5 * 60 * 1000); // 每5分钟清理一次 - * ``` - */ - startAutoCleanup(interval: number): void { - this.stopAutoCleanup(); - this.cleanupInterval = window.setInterval(() => { - this.cleanupAll(); - }, interval); - } - - /** - * 停止自动清理 - * - * @example - * ```typescript - * manager.stopAutoCleanup(); - * ``` - */ - stopAutoCleanup(): void { - if (this.cleanupInterval) { - clearInterval(this.cleanupInterval); - this.cleanupInterval = undefined; - } - } - - /** - * 销毁管理器 - * 停止自动清理,清空所有缓存,并移除所有缓存实例 - * - * @example - * ```typescript - * manager.destroy(); // 完全清理管理器 - * ``` - */ - destroy(): void { - this.stopAutoCleanup(); - this.clearAll(); - this.caches.clear(); - } -} - -/** - * 全局缓存管理器实例 - * 提供开箱即用的缓存管理功能,默认启用5分钟自动清理 - * - * @example - * ```typescript - * import { globalCacheManager } from './cache'; - * - * const userCache = globalCacheManager.getCache('users', { - * maxSize: 100, - * ttl: 5 * 60 * 1000 - * }); - * ``` - */ -export const globalCacheManager = new CacheManager({ - cleanupInterval: 5 * 60 * 1000 // 5 分钟 -}); \ No newline at end of file diff --git a/frontend/src/common/cache/utils.ts b/frontend/src/common/cache/utils.ts deleted file mode 100644 index 97e89dc..0000000 --- a/frontend/src/common/cache/utils.ts +++ /dev/null @@ -1,180 +0,0 @@ -import type { CacheItem } from './interfaces'; - -/** - * 简单哈希函数 - * 使用FNV-1a算法生成哈希值,提供良好的分布性 - * - * @param content 要哈希的内容 - * @returns 哈希值字符串 - * @example - * ```typescript - * const hash = createHash('some content'); - * // 结果: 类似 '1a2b3c4d' - * ``` - */ -export function createHash(content: string): string { - const FNV_OFFSET_BASIS = 2166136261; - const FNV_PRIME = 16777619; - - let hash = FNV_OFFSET_BASIS; - - for (let i = 0; i < content.length; i++) { - hash ^= content.charCodeAt(i); - hash = (hash * FNV_PRIME) >>> 0; // 无符号32位整数 - } - - return hash.toString(36); -} - -/** - * 生成缓存键 - * 将多个部分组合成一个缓存键 - * - * @param parts 键的各个部分 - * @returns 组合后的缓存键 - * @example - * ```typescript - * const key = generateCacheKey('user', 123, 'profile'); - * // 结果: 'user:123:profile' - * ``` - */ -export function generateCacheKey(...parts: (string | number)[]): string { - return parts.join(':'); -} - -/** - * 创建基础缓存项 - * 为任意数据创建符合CacheItem接口的缓存项 - * - * @template T 数据类型 - * @param id 缓存项的唯一标识 - * @param data 要缓存的数据 - * @returns 包含缓存元数据的缓存项 - * @example - * ```typescript - * const cacheItem = createCacheItem('user:123', { name: 'John', age: 30 }); - * // 结果包含 id, lastAccessed, createdAt 以及原始数据 - * ``` - */ -export function createCacheItem>( - id: string | number, - data: T -): CacheItem & T { - const now = new Date(); - return { - id, - lastAccessed: now, - createdAt: now, - ...data - }; -} - -/** - * 计算缓存命中率 - * 根据命中次数和未命中次数计算命中率 - * - * @param hits 命中次数 - * @param misses 未命中次数 - * @returns 命中率(0-1之间的数值) - * @example - * ```typescript - * const hitRate = calculateHitRate(80, 20); - * // 结果: 0.8 (80% 命中率) - * ``` - */ -export function calculateHitRate(hits: number, misses: number): number { - const total = hits + misses; - return total > 0 ? hits / total : 0; -} - -/** - * 格式化缓存大小 - * 将字节数格式化为人类可读的大小字符串 - * - * @param size 大小(字节) - * @returns 格式化后的大小字符串 - * @example - * ```typescript - * formatCacheSize(1024); // '1.0 KB' - * formatCacheSize(1048576); // '1.0 MB' - * formatCacheSize(500); // '500 B' - * ``` - */ -export function formatCacheSize(size: number): string { - if (size < 1024) return `${size} B`; - if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`; - if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)} MB`; - return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GB`; -} - -/** - * 检查项是否过期 - * 根据最后访问时间和TTL判断缓存项是否过期 - * - * @param item 缓存项 - * @param ttl 生存时间(毫秒) - * @returns 是否过期 - * @example - * ```typescript - * const item = createCacheItem('test', { data: 'value' }); - * const expired = isExpired(item, 5000); // 5秒TTL - * ``` - */ -export function isExpired(item: CacheItem, ttl: number): boolean { - return Date.now() - item.lastAccessed.getTime() > ttl; -} - -/** - * 防抖函数,用于缓存操作 - * 在指定时间内多次调用只执行最后一次 - * - * @template T 函数类型 - * @param func 要防抖的函数 - * @param wait 等待时间(毫秒) - * @returns 防抖后的函数 - * @example - * ```typescript - * const debouncedSave = debounce(saveToCache, 300); - * debouncedSave(data); // 只有在300ms内没有新调用时才执行 - * ``` - */ -export function debounce any>( - func: T, - wait: number -): (...args: Parameters) => void { - let timeout: number | undefined; - - return (...args: Parameters) => { - clearTimeout(timeout); - timeout = window.setTimeout(() => func(...args), wait); - }; -} - -/** - * 节流函数,用于缓存清理 - * 在指定时间内最多执行一次 - * - * @template T 函数类型 - * @param func 要节流的函数 - * @param limit 限制时间间隔(毫秒) - * @returns 节流后的函数 - * @example - * ```typescript - * const throttledCleanup = throttle(cleanupCache, 1000); - * throttledCleanup(); // 1秒内最多执行一次 - * ``` - */ -export function throttle any>( - func: T, - limit: number -): (...args: Parameters) => void { - let inThrottle: boolean; - - return (...args: Parameters) => { - if (!inThrottle) { - func(...args); - inThrottle = true; - setTimeout(() => inThrottle = false, limit); - } - }; -} \ No newline at end of file diff --git a/frontend/src/common/constant/locales.ts b/frontend/src/common/constant/locales.ts index 70b6273..c74c3a4 100644 --- a/frontend/src/common/constant/locales.ts +++ b/frontend/src/common/constant/locales.ts @@ -2,12 +2,12 @@ export type SupportedLocaleType = 'zh-CN' | 'en-US'; // 支持的语言列表 export const SUPPORTED_LOCALES = [ - { - code: 'zh-CN' as SupportedLocaleType, - name: '简体中文' - }, { code: 'en-US' as SupportedLocaleType, name: 'English' + }, + { + code: 'zh-CN' as SupportedLocaleType, + name: '简体中文' } ] as const; \ No newline at end of file diff --git a/frontend/src/common/utils/debounce.ts b/frontend/src/common/utils/debounce.ts new file mode 100644 index 0000000..80c88cc --- /dev/null +++ b/frontend/src/common/utils/debounce.ts @@ -0,0 +1,111 @@ +/** + * 防抖函数工具类 + * 用于限制函数的执行频率,在指定时间内只执行最后一次调用 + */ + +export interface DebounceOptions { + /** 延迟时间(毫秒),默认 300ms */ + delay?: number; + /** 是否立即执行第一次调用,默认 false */ + immediate?: boolean; +} + +/** + * 创建防抖函数 + * @param fn 要防抖的函数 + * @param options 防抖选项 + * @returns 返回防抖后的函数和清理函数 + */ +export function createDebounce any>( + fn: T, + options: DebounceOptions = {} +): { + debouncedFn: T; + cancel: () => void; + flush: () => void; +} { + const { delay = 300, immediate = false } = options; + let timeoutId: number | null = null; + let lastArgs: Parameters | null = null; + let lastThis: any = null; + + const debouncedFn = function (this: any, ...args: Parameters) { + lastArgs = args; + lastThis = this; + + const callNow = immediate && !timeoutId; + + if (timeoutId) { + clearTimeout(timeoutId); + } + + timeoutId = window.setTimeout(() => { + timeoutId = null; + if (!immediate && lastArgs) { + fn.apply(lastThis, lastArgs); + } + }, delay); + + if (callNow) { + return fn.apply(this, args); + } + } as T; + + const cancel = () => { + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + lastArgs = null; + lastThis = null; + }; + + const flush = () => { + if (timeoutId && lastArgs) { + clearTimeout(timeoutId); + fn.apply(lastThis, lastArgs); + timeoutId = null; + lastArgs = null; + lastThis = null; + } + }; + + return { + debouncedFn, + cancel, + flush + }; +} + + +/** + * 节流函数 + * 在指定时间内最多执行一次函数 + * @param fn 要节流的函数 + * @param delay 节流时间间隔(毫秒) + * @returns 节流后的函数 + */ +export function throttle any>( + fn: T, + delay: number = 300 +): T { + let lastExecTime = 0; + let timeoutId: number | null = null; + + return ((...args: Parameters) => { + const now = Date.now(); + + if (now - lastExecTime >= delay) { + lastExecTime = now; + fn(...args); + } else { + if (timeoutId) { + clearTimeout(timeoutId); + } + timeoutId = window.setTimeout(() => { + lastExecTime = Date.now(); + fn(...args); + }, delay - (now - lastExecTime)); + } + }) as T; +} \ No newline at end of file diff --git a/frontend/src/common/utils/timerUtils.ts b/frontend/src/common/utils/timerUtils.ts new file mode 100644 index 0000000..91f8a5f --- /dev/null +++ b/frontend/src/common/utils/timerUtils.ts @@ -0,0 +1,162 @@ +/** + * 定时器管理工具类 + * 提供安全的定时器创建、清理和管理功能 + */ + +/** + * 定时器管理器接口 + */ +export interface TimerManager { + /** 当前定时器 ID */ + readonly timerId: number | null; + /** 清除定时器 */ + clear(): void; + /** 设置定时器 */ + set(callback: () => void, delay: number): void; +} + +/** + * 创建定时器管理器工厂函数 + * 提供安全的定时器管理,自动处理清理和重置 + * + * @returns 定时器管理器实例 + * + * @example + * ```typescript + * const timer = createTimerManager(); + * + * // 设置定时器 + * timer.set(() => { + * console.log('Timer executed'); + * }, 1000); + * + * // 清除定时器 + * timer.clear(); + * + * // 检查定时器状态 + * if (timer.timerId !== null) { + * console.log('Timer is running'); + * } + * ``` + */ +export const createTimerManager = (): TimerManager => { + let timerId: number | null = null; + + return { + get timerId() { + return timerId; + }, + + clear() { + if (timerId !== null) { + window.clearTimeout(timerId); + timerId = null; + } + }, + + set(callback: () => void, delay: number) { + // 先清除现有定时器 + this.clear(); + + // 设置新定时器 + timerId = window.setTimeout(() => { + callback(); + timerId = null; // 执行完成后自动重置 + }, delay); + } + }; +}; + +/** + * 创建带有自动清理功能的定时器 + * 适用于需要在组件卸载时自动清理的场景 + * + * @param onCleanup 清理回调函数,通常在 onScopeDispose 或 onUnmounted 中调用 + * @returns 定时器管理器实例 + * + * @example + * ```typescript + * import { onScopeDispose } from 'vue'; + * + * const timer = createAutoCleanupTimer(() => { + * // 组件卸载时自动清理 + * }); + * + * onScopeDispose(() => { + * timer.clear(); + * }); + * ``` + */ +export const createAutoCleanupTimer = (onCleanup?: () => void): TimerManager => { + const timer = createTimerManager(); + + // 如果提供了清理回调,则包装 clear 方法 + if (onCleanup) { + const originalClear = timer.clear.bind(timer); + timer.clear = () => { + originalClear(); + onCleanup(); + }; + } + + return timer; +}; + +/** + * 延迟执行工具函数 + * 简化的 Promise 版本延迟执行 + * + * @param delay 延迟时间(毫秒) + * @returns Promise + * + * @example + * ```typescript + * await delay(1000); // 延迟 1 秒 + * console.log('1 second later'); + * ``` + */ +export const delay = (delay: number): Promise => { + return new Promise(resolve => { + setTimeout(resolve, delay); + }); +}; + +/** + * 创建可取消的延迟 Promise + * + * @param delay 延迟时间(毫秒) + * @returns 包含 promise 和 cancel 方法的对象 + * + * @example + * ```typescript + * const { promise, cancel } = createCancelableDelay(1000); + * + * promise + * .then(() => console.log('Executed')) + * .catch(() => console.log('Cancelled')); + * + * // 取消延迟 + * cancel(); + * ``` + */ +export const createCancelableDelay = (delay: number) => { + let timeoutId: number; + let cancelled = false; + + const promise = new Promise((resolve, reject) => { + timeoutId = window.setTimeout(() => { + if (!cancelled) { + resolve(); + } + }, delay); + }); + + const cancel = () => { + cancelled = true; + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + + return { promise, cancel }; +}; \ No newline at end of file diff --git a/frontend/src/components/monitor/MemoryMonitor.vue b/frontend/src/components/monitor/MemoryMonitor.vue index 3ee6b01..32b17a5 100644 --- a/frontend/src/components/monitor/MemoryMonitor.vue +++ b/frontend/src/components/monitor/MemoryMonitor.vue @@ -1,18 +1,19 @@