dramaling-vocab-learning/backend/DramaLing.Api/Controllers/OptimizedAIController.cs.bak

240 lines
8.7 KiB
C#

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;
/// <summary>
/// 優化後的 AI 控制器,使用新的架構和快取策略
/// </summary>
[ApiController]
[Route("api/v2/ai")]
public class OptimizedAIController : ControllerBase
{
private readonly IAIProviderManager _aiProviderManager;
private readonly ICacheService _cacheService;
private readonly ILogger<OptimizedAIController> _logger;
public OptimizedAIController(
IAIProviderManager aiProviderManager,
ICacheService cacheService,
ILogger<OptimizedAIController> logger)
{
_aiProviderManager = aiProviderManager ?? throw new ArgumentNullException(nameof(aiProviderManager));
_cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 智能分析英文句子 (優化版本)
/// </summary>
/// <param name="request">分析請求</param>
/// <returns>分析結果</returns>
[HttpPost("analyze-sentence")]
[AllowAnonymous]
public async Task<ActionResult<SentenceAnalysisResponse>> 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<SentenceAnalysisData>(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));
}
}
/// <summary>
/// 取得 AI 服務健康狀態
/// </summary>
[HttpGet("health")]
[AllowAnonymous]
public async Task<ActionResult> 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服務狀態" });
}
}
/// <summary>
/// 取得快取統計資訊
/// </summary>
[HttpGet("cache-stats")]
[AllowAnonymous]
public async Task<ActionResult> 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<string> GetSuggestionsForError(string errorCode)
{
return errorCode switch
{
"INVALID_INPUT" => new List<string> { "請檢查輸入格式", "確保文本長度在限制內" },
"AI_SERVICE_ERROR" => new List<string> { "請稍後重試", "如果問題持續,請聯繫客服" },
"RATE_LIMIT_EXCEEDED" => new List<string> { "請降低請求頻率", "稍後再試" },
_ => new List<string> { "請稍後重試" }
};
}
#endregion
}