refactor: 大幅清理AIController和GeminiService未使用的API
✅ 保留的核心功能: - analyze-sentence API (前端主要功能) ❌ 刪除的未使用API: - generate API (AI生成詞卡) - generate/{taskId}/save API (保存生成詞卡) - validate-card API (詞卡驗證) - query-word API (單字查詢) - cache-stats API (快取統計) - cache-cleanup API (快取清理) - usage-stats API (使用統計) 🧹 清理GeminiService中對應的未使用方法和DTO類別 📊 代碼量減少約336行,只保留核心句子分析功能 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
be1126e7db
commit
500d70839b
|
|
@ -37,282 +37,13 @@ public class AIController : ControllerBase
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// AI 生成詞卡 (支援 /frontend/app/generate/page.tsx)
|
||||
/// </summary>
|
||||
[HttpPost("generate")]
|
||||
public async Task<ActionResult> GenerateCards([FromBody] GenerateCardsRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = await _authService.GetUserIdFromTokenAsync(Request.Headers.Authorization);
|
||||
if (userId == null)
|
||||
return Unauthorized(new { Success = false, Error = "Invalid token" });
|
||||
|
||||
// 基本驗證
|
||||
if (string.IsNullOrWhiteSpace(request.InputText))
|
||||
{
|
||||
return BadRequest(new { Success = false, Error = "Input text is required" });
|
||||
}
|
||||
|
||||
if (request.InputText.Length > 5000)
|
||||
{
|
||||
return BadRequest(new { Success = false, Error = "Input text must be less than 5000 characters" });
|
||||
}
|
||||
|
||||
if (!new[] { "vocabulary", "smart" }.Contains(request.ExtractionType))
|
||||
{
|
||||
return BadRequest(new { Success = false, Error = "Invalid extraction type" });
|
||||
}
|
||||
|
||||
if (request.CardCount < 5 || request.CardCount > 20)
|
||||
{
|
||||
return BadRequest(new { Success = false, Error = "Card count must be between 5 and 20" });
|
||||
}
|
||||
|
||||
// 檢查每日配額 (簡化版,未來可以基於用戶訂閱狀態)
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
var todayStats = await _context.DailyStats
|
||||
.FirstOrDefaultAsync(ds => ds.UserId == userId && ds.Date == today);
|
||||
|
||||
var todayApiCalls = todayStats?.AiApiCalls ?? 0;
|
||||
var maxApiCalls = 10; // 免費用戶每日限制
|
||||
|
||||
if (todayApiCalls >= maxApiCalls)
|
||||
{
|
||||
return StatusCode(429, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "Daily AI generation limit exceeded"
|
||||
});
|
||||
}
|
||||
|
||||
// 建立生成任務 (簡化版,直接處理而不是非同步)
|
||||
try
|
||||
{
|
||||
var generatedCards = await _geminiService.GenerateCardsAsync(
|
||||
request.InputText,
|
||||
request.ExtractionType,
|
||||
request.CardCount);
|
||||
|
||||
if (generatedCards.Count == 0)
|
||||
{
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "AI generated no valid cards"
|
||||
});
|
||||
}
|
||||
|
||||
// 更新每日統計
|
||||
if (todayStats == null)
|
||||
{
|
||||
todayStats = new DailyStats
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId.Value,
|
||||
Date = today
|
||||
};
|
||||
_context.DailyStats.Add(todayStats);
|
||||
}
|
||||
|
||||
todayStats.AiApiCalls++;
|
||||
todayStats.CardsGenerated += generatedCards.Count;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Success = true,
|
||||
Data = new
|
||||
{
|
||||
TaskId = Guid.NewGuid(), // 模擬任務 ID
|
||||
Status = "completed",
|
||||
GeneratedCards = generatedCards
|
||||
},
|
||||
Message = $"Successfully generated {generatedCards.Count} cards"
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException ex) when (ex.Message.Contains("API key"))
|
||||
{
|
||||
_logger.LogError("Gemini API key not configured");
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "AI service not configured",
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error in AI card generation");
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "Failed to generate cards",
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存生成的詞卡
|
||||
/// </summary>
|
||||
[HttpPost("generate/{taskId}/save")]
|
||||
public async Task<ActionResult> SaveGeneratedCards(
|
||||
Guid taskId,
|
||||
[FromBody] SaveCardsRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = await _authService.GetUserIdFromTokenAsync(Request.Headers.Authorization);
|
||||
if (userId == null)
|
||||
return Unauthorized(new { Success = false, Error = "Invalid token" });
|
||||
|
||||
// 基本驗證
|
||||
if (request.CardSetId == Guid.Empty)
|
||||
{
|
||||
return BadRequest(new { Success = false, Error = "Card set ID is required" });
|
||||
}
|
||||
|
||||
if (request.SelectedCards == null || request.SelectedCards.Count == 0)
|
||||
{
|
||||
return BadRequest(new { Success = false, Error = "Selected cards are required" });
|
||||
}
|
||||
|
||||
// 驗證卡組是否屬於用戶
|
||||
var cardSet = await _context.CardSets
|
||||
.FirstOrDefaultAsync(cs => cs.Id == request.CardSetId && cs.UserId == userId);
|
||||
|
||||
if (cardSet == null)
|
||||
{
|
||||
return NotFound(new { Success = false, Error = "Card set not found" });
|
||||
}
|
||||
|
||||
// 將生成的詞卡轉換為資料庫實體
|
||||
var flashcardsToSave = request.SelectedCards.Select(card => new Flashcard
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId.Value,
|
||||
CardSetId = request.CardSetId,
|
||||
Word = card.Word,
|
||||
Translation = card.Translation,
|
||||
Definition = card.Definition,
|
||||
PartOfSpeech = card.PartOfSpeech,
|
||||
Pronunciation = card.Pronunciation,
|
||||
Example = card.Example,
|
||||
ExampleTranslation = card.ExampleTranslation,
|
||||
DifficultyLevel = card.DifficultyLevel
|
||||
}).ToList();
|
||||
|
||||
_context.Flashcards.AddRange(flashcardsToSave);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Success = true,
|
||||
Data = new
|
||||
{
|
||||
SavedCount = flashcardsToSave.Count,
|
||||
Cards = flashcardsToSave.Select(f => new
|
||||
{
|
||||
f.Id,
|
||||
f.Word,
|
||||
f.Translation,
|
||||
f.Definition
|
||||
})
|
||||
},
|
||||
Message = $"Successfully saved {flashcardsToSave.Count} cards to your deck"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error saving generated cards");
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "Failed to save cards",
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 智能檢測詞卡內容
|
||||
/// </summary>
|
||||
[HttpPost("validate-card")]
|
||||
public async Task<ActionResult> ValidateCard([FromBody] ValidateCardRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = await _authService.GetUserIdFromTokenAsync(Request.Headers.Authorization);
|
||||
if (userId == null)
|
||||
return Unauthorized(new { Success = false, Error = "Invalid token" });
|
||||
|
||||
var flashcard = await _context.Flashcards
|
||||
.FirstOrDefaultAsync(f => f.Id == request.FlashcardId && f.UserId == userId);
|
||||
|
||||
if (flashcard == null)
|
||||
{
|
||||
return NotFound(new { Success = false, Error = "Flashcard not found" });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var validationResult = await _geminiService.ValidateCardAsync(flashcard);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Success = true,
|
||||
Data = new
|
||||
{
|
||||
FlashcardId = request.FlashcardId,
|
||||
ValidationResult = validationResult,
|
||||
CheckedAt = DateTime.UtcNow
|
||||
},
|
||||
Message = "Card validation completed"
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException ex) when (ex.Message.Contains("API key"))
|
||||
{
|
||||
// 模擬檢測結果
|
||||
var mockResult = new ValidationResult
|
||||
{
|
||||
Issues = new List<ValidationIssue>(),
|
||||
Suggestions = new List<string> { "詞卡內容看起來正確", "建議添加更多例句" },
|
||||
OverallScore = 85,
|
||||
Confidence = 0.7
|
||||
};
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Success = true,
|
||||
Data = new
|
||||
{
|
||||
FlashcardId = request.FlashcardId,
|
||||
ValidationResult = mockResult,
|
||||
CheckedAt = DateTime.UtcNow,
|
||||
Note = "Mock validation (Gemini API not configured)"
|
||||
},
|
||||
Message = "Card validation completed (mock mode)"
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error validating card");
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "Failed to validate card",
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 句子分析API - 支援語法修正和高價值標記
|
||||
/// ✅ 句子分析API - 支援語法修正和高價值標記
|
||||
/// 🎯 前端使用:/app/generate/page.tsx (主要功能)
|
||||
/// </summary>
|
||||
[HttpPost("analyze-sentence")]
|
||||
[AllowAnonymous] // 暫時無需認證,開發階段
|
||||
|
|
@ -444,146 +175,9 @@ public class AIController : ControllerBase
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 單字點擊查詢API
|
||||
/// </summary>
|
||||
[HttpPost("query-word")]
|
||||
[AllowAnonymous] // 暫時無需認證,開發階段
|
||||
public async Task<ActionResult> QueryWord([FromBody] QueryWordRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 基本驗證
|
||||
if (string.IsNullOrWhiteSpace(request.Word))
|
||||
{
|
||||
return BadRequest(new { Success = false, Error = "Word is required" });
|
||||
}
|
||||
|
||||
// 簡化邏輯:直接調用 GeminiService 進行詞彙分析
|
||||
var wordAnalysis = await _geminiService.AnalyzeWordAsync(request.Word, request.Sentence);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Success = true,
|
||||
Data = new
|
||||
{
|
||||
Word = request.Word,
|
||||
Analysis = wordAnalysis
|
||||
},
|
||||
Message = "詞彙分析完成"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error analyzing word: {Word}", request.Word);
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "詞彙查詢失敗",
|
||||
Details = ex.Message,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 獲取快取統計資料
|
||||
/// </summary>
|
||||
[HttpGet("cache-stats")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> GetCacheStats()
|
||||
{
|
||||
try
|
||||
{
|
||||
var hitCount = await _cacheService.GetCacheHitCountAsync();
|
||||
var totalCacheItems = await _context.SentenceAnalysisCache
|
||||
.Where(c => c.ExpiresAt > DateTime.UtcNow)
|
||||
.CountAsync();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Success = true,
|
||||
Data = new
|
||||
{
|
||||
TotalCacheItems = totalCacheItems,
|
||||
TotalCacheHits = hitCount,
|
||||
CacheHitRate = totalCacheItems > 0 ? (double)hitCount / totalCacheItems : 0,
|
||||
CacheSize = totalCacheItems
|
||||
},
|
||||
Message = "快取統計資料"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting cache stats");
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "獲取快取統計失敗",
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理過期快取
|
||||
/// </summary>
|
||||
[HttpPost("cache-cleanup")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> CleanupCache()
|
||||
{
|
||||
try
|
||||
{
|
||||
await _cacheService.CleanExpiredCacheAsync();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Success = true,
|
||||
Message = "過期快取清理完成"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error cleaning up cache");
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "快取清理失敗",
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 獲取使用統計
|
||||
/// </summary>
|
||||
[HttpGet("usage-stats")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> GetUsageStats()
|
||||
{
|
||||
try
|
||||
{
|
||||
var mockUserId = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||
var stats = await _usageService.GetUsageStatsAsync(mockUserId);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Success = true,
|
||||
Data = stats,
|
||||
Message = "使用統計資料"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting usage stats");
|
||||
return StatusCode(500, new
|
||||
{
|
||||
Success = false,
|
||||
Error = "獲取使用統計失敗",
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#region 私有輔助方法
|
||||
|
||||
|
|
@ -1251,11 +845,6 @@ public class SaveCardsRequest
|
|||
public List<GeneratedCard> SelectedCards { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ValidateCardRequest
|
||||
{
|
||||
public Guid FlashcardId { get; set; }
|
||||
public Guid? ErrorReportId { get; set; }
|
||||
}
|
||||
|
||||
|
||||
// 新增的API請求/響應 DTOs
|
||||
|
|
@ -1267,12 +856,6 @@ public class AnalyzeSentenceRequest
|
|||
public string AnalysisMode { get; set; } = "full";
|
||||
}
|
||||
|
||||
public class QueryWordRequest
|
||||
{
|
||||
public string Word { get; set; } = string.Empty;
|
||||
public string Sentence { get; set; } = string.Empty;
|
||||
public Guid? AnalysisId { get; set; }
|
||||
}
|
||||
|
||||
public class GrammarCorrectionResult
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,9 +7,7 @@ namespace DramaLing.Api.Services;
|
|||
public interface IGeminiService
|
||||
{
|
||||
Task<List<GeneratedCard>> GenerateCardsAsync(string inputText, string extractionType, int cardCount);
|
||||
Task<ValidationResult> ValidateCardAsync(Flashcard card);
|
||||
Task<SentenceAnalysisResponse> AnalyzeSentenceAsync(string inputText, string userLevel = "A2");
|
||||
Task<WordAnalysisResult> AnalyzeWordAsync(string word, string sentence);
|
||||
}
|
||||
|
||||
public class GeminiService : IGeminiService
|
||||
|
|
@ -50,7 +48,7 @@ public class GeminiService : IGeminiService
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// 真正的句子分析和翻譯 - 調用 Gemini AI
|
||||
/// 句子分析和翻譯 - 調用 Gemini AI
|
||||
/// </summary>
|
||||
public async Task<SentenceAnalysisResponse> AnalyzeSentenceAsync(string inputText, string userLevel = "A2")
|
||||
{
|
||||
|
|
@ -126,81 +124,7 @@ public class GeminiService : IGeminiService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<WordAnalysisResult> AnalyzeWordAsync(string word, string sentence)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(_apiKey) || _apiKey == "your-gemini-api-key-here")
|
||||
{
|
||||
throw new InvalidOperationException("Gemini API key not configured");
|
||||
}
|
||||
|
||||
var prompt = $@"
|
||||
請分析單字 ""{word}"" 在句子 ""{sentence}"" 中的詳細資訊:
|
||||
|
||||
單字: {word}
|
||||
語境: {sentence}
|
||||
|
||||
請以JSON格式回應,不要包含任何其他文字:
|
||||
{{
|
||||
""word"": ""{word}"",
|
||||
""translation"": ""繁體中文翻譯"",
|
||||
""definition"": ""英文定義"",
|
||||
""partOfSpeech"": ""詞性(n./v./adj./adv.等)"",
|
||||
""pronunciation"": ""IPA音標"",
|
||||
""difficultyLevel"": ""CEFR等級(A1/A2/B1/B2/C1/C2)"",
|
||||
""isHighValue"": false,
|
||||
""contextMeaning"": ""在此句子中的具體含義""
|
||||
}}
|
||||
|
||||
要求:
|
||||
1. 翻譯要準確自然
|
||||
2. 定義要簡潔易懂
|
||||
3. 音標使用標準IPA格式
|
||||
4. 根據語境判斷詞性和含義
|
||||
";
|
||||
|
||||
var response = await CallGeminiApiAsync(prompt);
|
||||
return ParseWordAnalysisResponse(response, word);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error analyzing word with Gemini API");
|
||||
|
||||
// 回退到基本資料
|
||||
return new WordAnalysisResult
|
||||
{
|
||||
Word = word,
|
||||
Translation = $"{word} (AI 暫時不可用)",
|
||||
Definition = $"Definition of {word} (service temporarily unavailable)",
|
||||
PartOfSpeech = "unknown",
|
||||
Pronunciation = $"/{word}/",
|
||||
DifficultyLevel = "unknown",
|
||||
IsHighValue = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ValidationResult> ValidateCardAsync(Flashcard card)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(_apiKey) || _apiKey == "your-gemini-api-key-here")
|
||||
{
|
||||
throw new InvalidOperationException("Gemini API key not configured");
|
||||
}
|
||||
|
||||
var prompt = BuildValidationPrompt(card);
|
||||
var response = await CallGeminiApiAsync(prompt);
|
||||
|
||||
return ParseValidationResult(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error validating card with Gemini API");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildPrompt(string inputText, string extractionType, int cardCount)
|
||||
{
|
||||
|
|
@ -211,16 +135,6 @@ public class GeminiService : IGeminiService
|
|||
.Replace("{inputText}", inputText);
|
||||
}
|
||||
|
||||
private string BuildValidationPrompt(Flashcard card)
|
||||
{
|
||||
return CardValidationPrompt
|
||||
.Replace("{word}", card.Word)
|
||||
.Replace("{translation}", card.Translation)
|
||||
.Replace("{definition}", card.Definition)
|
||||
.Replace("{partOfSpeech}", card.PartOfSpeech ?? "")
|
||||
.Replace("{pronunciation}", card.Pronunciation ?? "")
|
||||
.Replace("{example}", card.Example ?? "");
|
||||
}
|
||||
|
||||
private async Task<string> CallGeminiApiAsync(string prompt)
|
||||
{
|
||||
|
|
@ -324,92 +238,7 @@ public class GeminiService : IGeminiService
|
|||
}
|
||||
}
|
||||
|
||||
private ValidationResult ParseValidationResult(string response)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cleanText = response.Trim().Replace("```json", "").Replace("```", "").Trim();
|
||||
var jsonResponse = JsonSerializer.Deserialize<JsonElement>(cleanText);
|
||||
|
||||
var issues = new List<ValidationIssue>();
|
||||
if (jsonResponse.TryGetProperty("issues", out var issuesElement))
|
||||
{
|
||||
foreach (var issueElement in issuesElement.EnumerateArray())
|
||||
{
|
||||
issues.Add(new ValidationIssue
|
||||
{
|
||||
Field = GetStringProperty(issueElement, "field"),
|
||||
Original = GetStringProperty(issueElement, "original"),
|
||||
Corrected = GetStringProperty(issueElement, "corrected"),
|
||||
Reason = GetStringProperty(issueElement, "reason"),
|
||||
Severity = GetStringProperty(issueElement, "severity")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var suggestions = new List<string>();
|
||||
if (jsonResponse.TryGetProperty("suggestions", out var suggestionsElement))
|
||||
{
|
||||
foreach (var suggestion in suggestionsElement.EnumerateArray())
|
||||
{
|
||||
suggestions.Add(suggestion.GetString() ?? "");
|
||||
}
|
||||
}
|
||||
|
||||
return new ValidationResult
|
||||
{
|
||||
Issues = issues,
|
||||
Suggestions = suggestions,
|
||||
OverallScore = jsonResponse.TryGetProperty("overall_score", out var scoreElement)
|
||||
? scoreElement.GetInt32() : 85,
|
||||
Confidence = jsonResponse.TryGetProperty("confidence", out var confidenceElement)
|
||||
? confidenceElement.GetDouble() : 0.9
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error parsing validation result: {Response}", response);
|
||||
throw new InvalidOperationException($"Failed to parse validation response: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析 Gemini AI 詞彙分析響應
|
||||
/// </summary>
|
||||
private WordAnalysisResult ParseWordAnalysisResponse(string response, string word)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cleanText = response.Trim().Replace("```json", "").Replace("```", "").Trim();
|
||||
var jsonResponse = JsonSerializer.Deserialize<JsonElement>(cleanText);
|
||||
|
||||
return new WordAnalysisResult
|
||||
{
|
||||
Word = GetStringProperty(jsonResponse, "word") ?? word,
|
||||
Translation = GetStringProperty(jsonResponse, "translation") ?? $"{word} 的翻譯",
|
||||
Definition = GetStringProperty(jsonResponse, "definition") ?? $"Definition of {word}",
|
||||
PartOfSpeech = GetStringProperty(jsonResponse, "partOfSpeech") ?? "unknown",
|
||||
Pronunciation = GetStringProperty(jsonResponse, "pronunciation") ?? $"/{word}/",
|
||||
DifficultyLevel = GetStringProperty(jsonResponse, "difficultyLevel") ?? "A1",
|
||||
IsHighValue = jsonResponse.TryGetProperty("isHighValue", out var isHighValueElement) && isHighValueElement.GetBoolean()
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to parse word analysis response");
|
||||
|
||||
return new WordAnalysisResult
|
||||
{
|
||||
Word = word,
|
||||
Translation = $"{word} (解析失敗)",
|
||||
Definition = $"Definition of {word} (parsing failed)",
|
||||
PartOfSpeech = "unknown",
|
||||
Pronunciation = $"/{word}/",
|
||||
DifficultyLevel = "unknown",
|
||||
IsHighValue = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析 Gemini AI 句子分析響應
|
||||
|
|
@ -562,23 +391,6 @@ public class GeminiService : IGeminiService
|
|||
|
||||
請按照相同的 JSON 格式回應...";
|
||||
|
||||
private const string CardValidationPrompt = @"
|
||||
請檢查以下詞卡內容的準確性:
|
||||
|
||||
單字: {word}
|
||||
翻譯: {translation}
|
||||
定義: {definition}
|
||||
詞性: {partOfSpeech}
|
||||
發音: {pronunciation}
|
||||
例句: {example}
|
||||
|
||||
請按照以下 JSON 格式回應:
|
||||
{
|
||||
""issues"": [],
|
||||
""suggestions"": [],
|
||||
""overall_score"": 85,
|
||||
""confidence"": 0.9
|
||||
}";
|
||||
}
|
||||
|
||||
// 支援類型
|
||||
|
|
@ -595,22 +407,7 @@ public class GeneratedCard
|
|||
public string DifficultyLevel { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ValidationResult
|
||||
{
|
||||
public List<ValidationIssue> Issues { get; set; } = new();
|
||||
public List<string> Suggestions { get; set; } = new();
|
||||
public int OverallScore { get; set; }
|
||||
public double Confidence { get; set; }
|
||||
}
|
||||
|
||||
public class ValidationIssue
|
||||
{
|
||||
public string Field { get; set; } = string.Empty;
|
||||
public string Original { get; set; } = string.Empty;
|
||||
public string Corrected { get; set; } = string.Empty;
|
||||
public string Reason { get; set; } = string.Empty;
|
||||
public string Severity { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
// 新增句子分析相關類型
|
||||
public class SentenceAnalysisResponse
|
||||
|
|
|
|||
Loading…
Reference in New Issue