From 31cf452974713b7d7eb05c64428bf025ec35ff69 Mon Sep 17 00:00:00 2001 From: icarus Date: Sat, 23 Aug 2025 15:50:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(ocr):=20=E6=B7=BB=E5=8A=A0OCR=20API?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=B7=A5=E5=8E=82=E5=8F=8A=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现OCR API客户端工厂模式,支持根据不同提供商创建对应的客户端 新增OcrBaseApiClient作为基础类,提供通用功能 添加OcrExampleApiClient作为示例实现 修改OcrService以使用新的客户端工厂 --- src/renderer/src/services/ocr/OcrService.ts | 11 ++++- .../ocr/clients/OcrApiClientFactory.ts | 28 ++++++++++++ .../services/ocr/clients/OcrBaseApiClient.ts | 43 +++++++++++++++++++ .../ocr/clients/OcrExampleApiClient.ts | 15 +++++++ 4 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/renderer/src/services/ocr/clients/OcrApiClientFactory.ts create mode 100644 src/renderer/src/services/ocr/clients/OcrBaseApiClient.ts create mode 100644 src/renderer/src/services/ocr/clients/OcrExampleApiClient.ts diff --git a/src/renderer/src/services/ocr/OcrService.ts b/src/renderer/src/services/ocr/OcrService.ts index cf06b4d79e..10ad4d5736 100644 --- a/src/renderer/src/services/ocr/OcrService.ts +++ b/src/renderer/src/services/ocr/OcrService.ts @@ -1,4 +1,6 @@ -import { OcrProvider, OcrResult, SupportedOcrFile } from '@renderer/types' +import { isOcrApiProvider, OcrProvider, OcrResult, SupportedOcrFile } from '@renderer/types' + +import { OcrApiClientFactory } from './clients/OcrApiClientFactory' // const logger = loggerService.withContext('renderer:OcrService') @@ -10,5 +12,10 @@ import { OcrProvider, OcrResult, SupportedOcrFile } from '@renderer/types' * @throws {Error} */ export const ocr = async (file: SupportedOcrFile, provider: OcrProvider): Promise => { - return window.api.ocr.ocr(file, provider) + if (isOcrApiProvider(provider)) { + const client = OcrApiClientFactory.create(provider) + return client.ocr(file) + } else { + return window.api.ocr.ocr(file, provider) + } } diff --git a/src/renderer/src/services/ocr/clients/OcrApiClientFactory.ts b/src/renderer/src/services/ocr/clients/OcrApiClientFactory.ts new file mode 100644 index 0000000000..e685c0e3f9 --- /dev/null +++ b/src/renderer/src/services/ocr/clients/OcrApiClientFactory.ts @@ -0,0 +1,28 @@ +import { loggerService } from '@logger' +import { OcrApiProvider } from '@renderer/types' + +import { OcrBaseApiClient } from './OcrBaseApiClient' +import { OcrExampleApiClient } from './OcrExampleApiClient' + +const logger = loggerService.withContext('OcrApiClientFactory') + +export class OcrApiClientFactory { + /** + * Create an ApiClient instance for the given provider + * 为给定的提供者创建ApiClient实例 + */ + static create(provider: OcrApiProvider): OcrBaseApiClient { + logger.debug(`Creating ApiClient for provider:`, { + id: provider.id, + config: provider.config + }) + + let instance: OcrBaseApiClient + + // Extend other clients here + // eslint-disable-next-line prefer-const + instance = new OcrExampleApiClient(provider) + + return instance + } +} diff --git a/src/renderer/src/services/ocr/clients/OcrBaseApiClient.ts b/src/renderer/src/services/ocr/clients/OcrBaseApiClient.ts new file mode 100644 index 0000000000..c9605671ae --- /dev/null +++ b/src/renderer/src/services/ocr/clients/OcrBaseApiClient.ts @@ -0,0 +1,43 @@ +import { OcrApiProvider, OcrHandler } from '@renderer/types' + +export abstract class OcrBaseApiClient { + public provider: OcrApiProvider + protected host: string + protected apiKey: string + + constructor(provider: OcrApiProvider) { + this.provider = provider + this.host = this.getHost() + this.apiKey = this.getApiKey() + } + + abstract ocr: OcrHandler + + // copy from BaseApiClient + public getHost(): string { + return this.provider.config.api.apiHost + } + + // copy from BaseApiClient + public getApiKey() { + const keys = this.provider.config.api.apiKey.split(',').map((key) => key.trim()) + const keyName = `ocr_provider:${this.provider.id}:last_used_key` + + if (keys.length === 1) { + return keys[0] + } + + const lastUsedKey = window.keyv.get(keyName) + if (!lastUsedKey) { + window.keyv.set(keyName, keys[0]) + return keys[0] + } + + const currentIndex = keys.indexOf(lastUsedKey) + const nextIndex = (currentIndex + 1) % keys.length + const nextKey = keys[nextIndex] + window.keyv.set(keyName, nextKey) + + return nextKey + } +} diff --git a/src/renderer/src/services/ocr/clients/OcrExampleApiClient.ts b/src/renderer/src/services/ocr/clients/OcrExampleApiClient.ts new file mode 100644 index 0000000000..34d28173bb --- /dev/null +++ b/src/renderer/src/services/ocr/clients/OcrExampleApiClient.ts @@ -0,0 +1,15 @@ +import { OcrApiProvider, SupportedOcrFile } from '@renderer/types' + +import { OcrBaseApiClient } from './OcrBaseApiClient' + +export type OcrExampleProvider = OcrApiProvider + +export class OcrExampleApiClient extends OcrBaseApiClient { + constructor(provider: OcrApiProvider) { + super(provider) + } + + public ocr = async (file: SupportedOcrFile) => { + return { text: `Example output: ${file.path}` } + } +}