using DramaLing.Api.Models.DTOs; using DramaLing.Api.Services.Caching; using System.Security.Cryptography; using System.Text; using System.Diagnostics; namespace DramaLing.Api.Services; /// /// 分析服務實作,整合快取和 AI 服務 /// public class AnalysisService : IAnalysisService { private readonly IGeminiService _geminiService; private readonly ICacheService _cacheService; private readonly ILogger _logger; private static readonly AnalysisStats _stats = new(); public AnalysisService( IGeminiService geminiService, ICacheService cacheService, ILogger logger) { _geminiService = geminiService ?? throw new ArgumentNullException(nameof(geminiService)); _cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task AnalyzeSentenceAsync(string inputText, AnalysisOptions options) { var stopwatch = Stopwatch.StartNew(); var cacheKey = GenerateCacheKey(inputText, options); try { _logger.LogInformation("Starting analysis for text: {Text} (cache key: {CacheKey})", inputText.Substring(0, Math.Min(50, inputText.Length)), cacheKey); // 1. 快取檢查 var cachedResult = await _cacheService.GetAsync(cacheKey); if (cachedResult != null) { stopwatch.Stop(); _stats.CachedAnalyses++; _stats.TotalAnalyses++; _logger.LogInformation("Cache hit for analysis in {ElapsedMs}ms", stopwatch.ElapsedMilliseconds); return cachedResult; } // 2. 快取未命中,執行 AI 分析 _logger.LogInformation("Cache miss, calling AI service"); var analysisResult = await _geminiService.AnalyzeSentenceAsync(inputText, options); // 3. 存入快取 (三層快取會自動同步到資料庫) await _cacheService.SetAsync(cacheKey, analysisResult, TimeSpan.FromHours(2)); // 4. 更新統計 stopwatch.Stop(); _stats.TotalAnalyses++; _stats.LastAnalysisAt = DateTime.UtcNow; UpdateAverageResponseTime((int)stopwatch.ElapsedMilliseconds); _logger.LogInformation("AI analysis completed and cached in {ElapsedMs}ms", stopwatch.ElapsedMilliseconds); return analysisResult; } catch (Exception ex) { stopwatch.Stop(); _logger.LogError(ex, "Error in analysis service for text: {Text}", inputText); throw; } } public async Task HasCachedAnalysisAsync(string inputText, AnalysisOptions options) { try { var cacheKey = GenerateCacheKey(inputText, options); return await _cacheService.ExistsAsync(cacheKey); } catch (Exception ex) { _logger.LogError(ex, "Error checking cache existence"); return false; } } public async Task ClearAnalysisCacheAsync(string inputText, AnalysisOptions options) { try { var cacheKey = GenerateCacheKey(inputText, options); return await _cacheService.RemoveAsync(cacheKey); } catch (Exception ex) { _logger.LogError(ex, "Error clearing analysis cache"); return false; } } public Task GetAnalysisStatsAsync() { _stats.ProviderUsageStats["Gemini"] = _stats.TotalAnalyses - _stats.CachedAnalyses; return Task.FromResult(_stats); } #region 私有方法 private string GenerateCacheKey(string inputText, AnalysisOptions options) { // 根據輸入和選項生成穩定的快取鍵 var optionsString = $"{options.IncludeGrammarCheck}_{options.IncludeVocabularyAnalysis}_{options.IncludeIdiomDetection}"; var combinedInput = $"{inputText}_{optionsString}"; using var sha256 = SHA256.Create(); var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(combinedInput)); var hash = Convert.ToHexString(hashBytes)[..16]; return $"analysis:{hash}"; } private void UpdateAverageResponseTime(int responseTimeMs) { if (_stats.AverageResponseTimeMs == 0) { _stats.AverageResponseTimeMs = responseTimeMs; } else { _stats.AverageResponseTimeMs = (_stats.AverageResponseTimeMs + responseTimeMs) / 2; } } #endregion }