feat(ocr): 添加OCR API客户端工厂及示例实现

实现OCR API客户端工厂模式,支持根据不同提供商创建对应的客户端
新增OcrBaseApiClient作为基础类,提供通用功能
添加OcrExampleApiClient作为示例实现
修改OcrService以使用新的客户端工厂
This commit is contained in:
icarus 2025-08-23 15:50:00 +08:00
parent 4b031597eb
commit 31cf452974
4 changed files with 95 additions and 2 deletions

View File

@ -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<OcrResult> => {
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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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}` }
}
}