feat: 添加本地缓存功能,减少API调用

- 实现SQLite缓存模块,支持高效查询和存储
- 添加缓存键生成策略(基于原文+语言对的SHA256哈希)
- 集成缓存到Translator类,先查缓存再调用API
- 添加缓存管理命令:cache clear, cache stats, cache cleanup
- 实现组合缓存清理策略(数量限制+时间过期)
- 添加完整的单元测试
- 更新配置文件模板,添加缓存配置
- 更新文档和版本记录

版本: v0.5.1
This commit is contained in:
2026-03-29 21:10:28 +08:00
parent ceed482444
commit b71f76c8b3
15 changed files with 1545 additions and 7 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"time"
"github.com/titor/fanyi/internal/cache"
"github.com/titor/fanyi/internal/config"
"github.com/titor/fanyi/internal/content"
"github.com/titor/fanyi/internal/provider"
@@ -16,16 +17,32 @@ type Translator struct {
provider provider.Provider
prompt *PromptManager
contentParser *content.Parser
cache cache.Cache
}
// NewTranslator 创建翻译器实例
func NewTranslator(config *config.Config, provider provider.Provider) *Translator {
return &Translator{
translator := &Translator{
config: config,
provider: provider,
prompt: NewPromptManager(config.Prompts),
contentParser: content.NewParser(config.SkipKeywords),
}
// 初始化缓存(如果启用)
if config.Cache.Enabled {
cacheConfig := &cache.CacheConfig{
Enabled: config.Cache.Enabled,
MaxRecords: config.Cache.MaxRecords,
ExpireDays: config.Cache.ExpireDays,
DBPath: config.Cache.DBPath,
}
if cacheInstance, err := cache.NewSQLiteCache(cacheConfig); err == nil {
translator.cache = cacheInstance
}
}
return translator
}
// Translate 执行翻译
@@ -68,6 +85,26 @@ func (t *Translator) Translate(ctx context.Context, text string, options *Transl
Options: options.ExtraOptions,
}
// 检查缓存
if t.cache != nil {
cacheKey := cache.GenerateCacheKey(filteredText, options.FromLang, options.ToLang)
if cachedEntry, err := t.cache.Get(ctx, cacheKey); err == nil && cachedEntry != nil {
// 缓存命中
return &TranslateResult{
Original: text,
Translated: cachedEntry.TranslatedText,
FromLang: cachedEntry.FromLang,
ToLang: cachedEntry.ToLang,
Model: cachedEntry.Model,
Usage: &provider.Usage{
PromptTokens: cachedEntry.PromptTokens,
CompletionTokens: cachedEntry.CompletionTokens,
TotalTokens: cachedEntry.TotalTokens,
},
}, nil
}
}
// 调用厂商API
resp, err := t.provider.Translate(timeoutCtx, req)
if err != nil {
@@ -81,6 +118,26 @@ func (t *Translator) Translate(ctx context.Context, text string, options *Transl
translatedText = t.contentParser.Reconstruct(parseResult, resp.Text)
}
// 保存到缓存
if t.cache != nil {
cacheKey := cache.GenerateCacheKey(filteredText, options.FromLang, options.ToLang)
cacheEntry := &cache.CacheEntry{
CacheKey: cacheKey,
OriginalText: filteredText,
TranslatedText: translatedText,
FromLang: resp.FromLang,
ToLang: resp.ToLang,
Model: resp.Model,
PromptName: options.PromptName,
PromptContent: prompt,
PromptTokens: resp.Usage.PromptTokens,
CompletionTokens: resp.Usage.CompletionTokens,
TotalTokens: resp.Usage.TotalTokens,
}
// 异步保存缓存,不阻塞翻译结果返回
go t.cache.Set(context.Background(), cacheEntry)
}
// 构建结果
return &TranslateResult{
Original: text,