240 lines
8.7 KiB
C#
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
|
|
} |