using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using DramaLing.Api.Models.DTOs; using DramaLing.Api.Services.AI; using DramaLing.Api.Services.Caching; using System.Diagnostics; using System.Security.Cryptography; using System.Text; namespace DramaLing.Api.Controllers; /// /// 優化後的 AI 控制器,使用新的架構和快取策略 /// [ApiController] [Route("api/v2/ai")] public class OptimizedAIController : ControllerBase { private readonly IAIProviderManager _aiProviderManager; private readonly ICacheService _cacheService; private readonly ILogger _logger; public OptimizedAIController( IAIProviderManager aiProviderManager, ICacheService cacheService, ILogger logger) { _aiProviderManager = aiProviderManager ?? throw new ArgumentNullException(nameof(aiProviderManager)); _cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// /// 智能分析英文句子 (優化版本) /// /// 分析請求 /// 分析結果 [HttpPost("analyze-sentence")] [AllowAnonymous] public async Task> AnalyzeSentenceOptimized( [FromBody] SentenceAnalysisRequest request) { var requestId = Guid.NewGuid().ToString(); var stopwatch = Stopwatch.StartNew(); try { _logger.LogInformation("Processing optimized sentence analysis request {RequestId}", requestId); // 輸入驗證 if (!ModelState.IsValid) { return BadRequest(CreateErrorResponse("INVALID_INPUT", "輸入格式錯誤", requestId)); } // 生成快取鍵 var cacheKey = GenerateCacheKey(request.InputText, request.Options); // 嘗試從快取取得結果 var cachedResult = await _cacheService.GetAsync(cacheKey); if (cachedResult != null) { stopwatch.Stop(); _logger.LogInformation("Cache hit for request {RequestId} in {ElapsedMs}ms", requestId, stopwatch.ElapsedMilliseconds); return Ok(new SentenceAnalysisResponse { Success = true, ProcessingTime = stopwatch.Elapsed.TotalSeconds, Data = cachedResult, FromCache = true }); } // 快取未命中,執行 AI 分析 _logger.LogInformation("Cache miss, calling AI service for request {RequestId}", requestId); var options = request.Options ?? new AnalysisOptions(); var analysisData = await _aiProviderManager.AnalyzeSentenceAsync( request.InputText, options, ProviderSelectionStrategy.Performance); // 更新 metadata analysisData.Metadata.ProcessingDate = DateTime.UtcNow; // 將結果存入快取 await _cacheService.SetAsync(cacheKey, analysisData, TimeSpan.FromHours(2)); stopwatch.Stop(); _logger.LogInformation("Sentence analysis completed for request {RequestId} in {ElapsedMs}ms", requestId, stopwatch.ElapsedMilliseconds); return Ok(new SentenceAnalysisResponse { Success = true, ProcessingTime = stopwatch.Elapsed.TotalSeconds, Data = analysisData, FromCache = false }); } catch (ArgumentException ex) { _logger.LogWarning(ex, "Invalid input for request {RequestId}", requestId); return BadRequest(CreateErrorResponse("INVALID_INPUT", ex.Message, requestId)); } catch (InvalidOperationException ex) when (ex.Message.Contains("AI")) { _logger.LogError(ex, "AI service error for request {RequestId}", requestId); return StatusCode(502, CreateErrorResponse("AI_SERVICE_ERROR", "AI服務暫時不可用", requestId)); } catch (Exception ex) { _logger.LogError(ex, "Unexpected error processing request {RequestId}", requestId); return StatusCode(500, CreateErrorResponse("INTERNAL_ERROR", "伺服器內部錯誤", requestId)); } } /// /// 取得 AI 服務健康狀態 /// [HttpGet("health")] [AllowAnonymous] public async Task GetAIHealth() { try { var healthReport = await _aiProviderManager.CheckAllProvidersHealthAsync(); var response = new { Status = healthReport.HealthyProviders > 0 ? "Healthy" : "Unhealthy", TotalProviders = healthReport.TotalProviders, HealthyProviders = healthReport.HealthyProviders, CheckedAt = healthReport.CheckedAt, Providers = healthReport.ProviderHealthInfos.Select(p => new { Name = p.ProviderName, IsHealthy = p.IsHealthy, ResponseTimeMs = p.ResponseTimeMs, ErrorMessage = p.ErrorMessage, Stats = new { TotalRequests = p.Stats.TotalRequests, SuccessRate = p.Stats.SuccessRate, AverageResponseTimeMs = p.Stats.AverageResponseTimeMs } }) }; return Ok(response); } catch (Exception ex) { _logger.LogError(ex, "Error checking AI service health"); return StatusCode(500, new { Status = "Error", Message = "無法檢查AI服務狀態" }); } } /// /// 取得快取統計資訊 /// [HttpGet("cache-stats")] [AllowAnonymous] public async Task GetCacheStats() { try { var stats = await _cacheService.GetStatsAsync(); return Ok(new { Success = true, Data = new { TotalKeys = stats.TotalKeys, HitRate = stats.HitRate, TotalRequests = stats.TotalRequests, HitCount = stats.HitCount, MissCount = stats.MissCount, LastUpdated = stats.LastUpdated } }); } catch (Exception ex) { _logger.LogError(ex, "Error getting cache stats"); return StatusCode(500, new { Success = false, Error = "無法取得快取統計資訊" }); } } #region 私有方法 private string GenerateCacheKey(string inputText, AnalysisOptions? options) { // 使用輸入文本和選項組合生成唯一快取鍵 var optionsString = options != null ? $"{options.IncludeGrammarCheck}_{options.IncludeVocabularyAnalysis}_{options.IncludeIdiomDetection}" : "default"; var combinedInput = $"{inputText}_{optionsString}"; // 使用 SHA256 生成穩定的快取鍵 using var sha256 = SHA256.Create(); var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(combinedInput)); var hash = Convert.ToHexString(hashBytes)[..16]; // 取前16個字符 return $"analysis:{hash}"; } private object CreateErrorResponse(string code, string message, string requestId) { return new { Success = false, Error = new { Code = code, Message = message, Suggestions = GetSuggestionsForError(code) }, RequestId = requestId, Timestamp = DateTime.UtcNow }; } private List GetSuggestionsForError(string errorCode) { return errorCode switch { "INVALID_INPUT" => new List { "請檢查輸入格式", "確保文本長度在限制內" }, "AI_SERVICE_ERROR" => new List { "請稍後重試", "如果問題持續,請聯繫客服" }, "RATE_LIMIT_EXCEEDED" => new List { "請降低請求頻率", "稍後再試" }, _ => new List { "請稍後重試" } }; } #endregion }