# DramaLing 後端 API 開發計劃 ## 1. 概述 ### 1.1 計劃目的 本開發計劃旨在基於現有的詞卡管理 API 規格,完善和優化後端 API 實現,確保 API 功能完整性、效能和穩定性。 ### 1.2 依賴文檔 > 📋 **參考文檔引用** > > 本開發計劃基於以下文檔制定: > > **🔧 API 規格文檔 (主要參考)** > - [詞卡管理 API 規格](./api/flashcard-management-api.md) - API 介面定義和實現邏輯的完整規格 > > **🏗️ 技術架構文檔** > - [後端架構詳細說明](../04_technical/backend-architecture.md) - 了解 ASP.NET Core 架構約束和設計模式 > - [系統架構總覽](../04_technical/system-architecture.md) - 了解整體系統設計和技術棧 > > **📋 需求規格文檔** > - [詞卡管理功能產品需求規格](../01_requirement/詞卡管理功能產品需求規格.md) - 了解業務需求和用戶故事 ### 1.3 當前狀態評估 根據 [詞卡管理 API 規格](./api/flashcard-management-api.md) 分析,目前後端 API 狀態: #### ✅ **已完成的 API 端點** - ✅ `GET /api/flashcards` - 取得詞卡列表 (含搜尋和收藏篩選) - ✅ `GET /api/flashcards/{id}` - 取得單一詞卡 - ✅ `POST /api/flashcards` - 創建新詞卡 (含重複檢測) - ✅ `PUT /api/flashcards/{id}` - 更新詞卡 - ✅ `DELETE /api/flashcards/{id}` - 刪除詞卡 (軟刪除) - ✅ `POST /api/flashcards/{id}/favorite` - 切換收藏狀態 #### 🎯 **需要改進的項目** - 🔄 搜尋功能擴展 (目前不支援例句搜尋) - 🔄 進階篩選 API (CEFR 等級、詞性、掌握度) - 🔄 批量操作 API (未來功能) - 🔄 效能優化 (查詢索引、快取機制) - 🔄 API 文檔生成 (Swagger 增強) ## 2. 開發任務清單 ### 2.1 搜尋功能增強 (優先級:🔴 高) #### 任務 1: 擴展搜尋範圍支援例句 **影響檔案**: - `backend/DramaLing.Api/Controllers/FlashcardsController.cs` - 修改 GetFlashcards 方法 **當前實現**: ```csharp // 目前搜尋邏輯 (第 53-59 行) if (!string.IsNullOrEmpty(search)) { query = query.Where(f => f.Word.Contains(search) || f.Translation.Contains(search) || (f.Definition != null && f.Definition.Contains(search))); } ``` **改進實現**: ```csharp // 擴展搜尋範圍,新增例句搜尋 if (!string.IsNullOrEmpty(search)) { query = query.Where(f => f.Word.Contains(search) || f.Translation.Contains(search) || (f.Definition != null && f.Definition.Contains(search)) || (f.Example != null && f.Example.Contains(search)) || (f.ExampleTranslation != null && f.ExampleTranslation.Contains(search))); } ``` **驗收標準**: - [ ] 搜尋範圍包含例句 (Example) 和例句翻譯 (ExampleTranslation) - [ ] 搜尋效能無明顯下降 - [ ] 搜尋結果準確性維持 100% #### 任務 2: 新增進階篩選查詢參數 **影響檔案**: - `backend/DramaLing.Api/Controllers/FlashcardsController.cs` - 修改 GetFlashcards 方法參數 **新增查詢參數**: ```csharp [HttpGet] public async Task GetFlashcards( [FromQuery] string? search = null, [FromQuery] bool favoritesOnly = false, [FromQuery] string? cefrLevel = null, // 新增: A1, A2, B1, B2, C1, C2 [FromQuery] string? partOfSpeech = null, // 新增: noun, verb, adjective, etc. [FromQuery] string? masteryLevel = null // 新增: high, medium, low ) ``` **篩選邏輯實現**: ```csharp // CEFR 等級篩選 if (!string.IsNullOrEmpty(cefrLevel)) { query = query.Where(f => f.DifficultyLevel == cefrLevel); } // 詞性篩選 if (!string.IsNullOrEmpty(partOfSpeech)) { query = query.Where(f => f.PartOfSpeech == partOfSpeech); } // 掌握度篩選 if (!string.IsNullOrEmpty(masteryLevel)) { switch (masteryLevel.ToLower()) { case "high": query = query.Where(f => f.MasteryLevel >= 80); break; case "medium": query = query.Where(f => f.MasteryLevel >= 60 && f.MasteryLevel < 80); break; case "low": query = query.Where(f => f.MasteryLevel < 60); break; } } ``` **驗收標準**: - [ ] 支援 CEFR 等級篩選 (A1-C2) - [ ] 支援詞性篩選 (noun, verb, adjective 等) - [ ] 支援掌握度篩選 (high, medium, low) - [ ] 多重篩選條件正確組合 (AND 邏輯) ### 2.2 效能優化 (優先級:🟡 中) #### 任務 3: 資料庫查詢優化 **影響檔案**: - `backend/DramaLing.Api/Data/DramaLingDbContext.cs` - 新增索引配置 **索引優化**: ```csharp // 在 OnModelCreating 方法中新增索引 protected override void OnModelCreating(ModelBuilder modelBuilder) { // 現有配置... // 搜尋優化索引 modelBuilder.Entity() .HasIndex(f => f.Word) .HasDatabaseName("IX_Flashcards_Word"); modelBuilder.Entity() .HasIndex(f => f.Translation) .HasDatabaseName("IX_Flashcards_Translation"); // 複合查詢索引 modelBuilder.Entity() .HasIndex(f => new { f.UserId, f.IsArchived, f.IsFavorite }) .HasDatabaseName("IX_Flashcards_UserId_IsArchived_IsFavorite"); // CEFR 等級索引 modelBuilder.Entity() .HasIndex(f => f.DifficultyLevel) .HasDatabaseName("IX_Flashcards_DifficultyLevel"); } ``` **查詢邏輯優化**: ```csharp // 使用 AsNoTracking 提升查詢效能 var flashcards = await query .AsNoTracking() .OrderByDescending(f => f.CreatedAt) .ToListAsync(); ``` **驗收標準**: - [ ] 新增適當的資料庫索引 - [ ] 查詢時間 < 200ms (1000+ 詞卡) - [ ] 搜尋響應時間 < 100ms #### 任務 4: 快取機制實現 **影響檔案**: - `backend/DramaLing.Api/Controllers/FlashcardsController.cs` - 整合快取服務 - `backend/DramaLing.Api/Extensions/ServiceCollectionExtensions.cs` - 已有快取配置 **快取策略實現**: ```csharp // 在 FlashcardsController 中使用快取 private readonly ICacheService _cacheService; [HttpGet] public async Task GetFlashcards(...) { var cacheKey = $"flashcards:user:{userId}:search:{search}:favorites:{favoritesOnly}"; var cachedResult = await _cacheService.GetAsync(cacheKey); if (cachedResult != null) { return Ok(cachedResult); } // 執行資料庫查詢... var result = new { Success = true, Data = ... }; // 快取結果 (30分鐘) await _cacheService.SetAsync(cacheKey, result, TimeSpan.FromMinutes(30)); return Ok(result); } ``` **驗收標準**: - [ ] 詞卡列表查詢快取 30 分鐘 - [ ] 快取命中率 > 70% - [ ] 快取失效機制正確 (CRUD 操作後清除) ### 2.3 API 增強功能 (優先級:🟡 中) #### 任務 5: 新增批量操作 API **影響檔案**: - `backend/DramaLing.Api/Controllers/FlashcardsController.cs` - 新增批量操作端點 **新增 API 端點**: ```csharp [HttpPost("batch/favorite")] public async Task BatchToggleFavorite([FromBody] BatchFavoriteRequest request) { try { var userId = GetUserId(); var flashcards = await _context.Flashcards .Where(f => request.FlashcardIds.Contains(f.Id) && f.UserId == userId && !f.IsArchived) .ToListAsync(); foreach (var flashcard in flashcards) { flashcard.IsFavorite = request.IsFavorite; flashcard.UpdatedAt = DateTime.UtcNow; } await _context.SaveChangesAsync(); return Ok(new { Success = true, UpdatedCount = flashcards.Count }); } catch (Exception ex) { _logger.LogError(ex, "Error in batch favorite operation"); return StatusCode(500, new { Success = false, Error = "批量操作失敗" }); } } [HttpDelete("batch")] public async Task BatchDelete([FromBody] BatchDeleteRequest request) { // 批量軟刪除實現 } ``` **新增 DTO 類別**: ```csharp public class BatchFavoriteRequest { public List FlashcardIds { get; set; } = new(); public bool IsFavorite { get; set; } } public class BatchDeleteRequest { public List FlashcardIds { get; set; } = new(); } ``` **驗收標準**: - [ ] 支援批量收藏/取消收藏 - [ ] 支援批量刪除 (軟刪除) - [ ] 批量操作事務性 (全部成功或全部失敗) - [ ] 操作日誌記錄完整 #### 任務 6: 統計資料 API **影響檔案**: - `backend/DramaLing.Api/Controllers/FlashcardsController.cs` - 新增統計端點 **新增統計 API**: ```csharp [HttpGet("statistics")] public async Task GetStatistics() { try { var userId = GetUserId(); var stats = await _context.Flashcards .Where(f => f.UserId == userId && !f.IsArchived) .GroupBy(f => 1) // 單一群組用於統計 .Select(g => new { TotalCount = g.Count(), FavoriteCount = g.Count(f => f.IsFavorite), MasteredCount = g.Count(f => f.MasteryLevel >= 80), LearningCount = g.Count(f => f.MasteryLevel >= 60 && f.MasteryLevel < 80), NewCount = g.Count(f => f.MasteryLevel < 60), CefrDistribution = g.GroupBy(f => f.DifficultyLevel) .ToDictionary(cg => cg.Key, cg => cg.Count()) }) .FirstOrDefaultAsync(); return Ok(new { Success = true, Data = stats }); } catch (Exception ex) { _logger.LogError(ex, "Error getting flashcard statistics"); return StatusCode(500, new { Success = false, Error = "統計資料載入失敗" }); } } ``` **驗收標準**: - [ ] 提供詞卡總數、收藏數、掌握度分布統計 - [ ] 提供 CEFR 等級分布統計 - [ ] 統計資料準確性 100% - [ ] 響應時間 < 200ms ### 2.4 錯誤處理增強 (優先級:🟡 中) #### 任務 7: 標準化錯誤回應格式 **影響檔案**: - `backend/DramaLing.Api/Models/DTOs/` - 新增錯誤回應 DTO - `backend/DramaLing.Api/Controllers/FlashcardsController.cs` - 統一錯誤處理 **錯誤回應 DTO**: ```csharp public class ApiErrorResponse { public bool Success { get; set; } = false; public string Error { get; set; } = string.Empty; public string? Details { get; set; } public string? ErrorCode { get; set; } public DateTime Timestamp { get; set; } = DateTime.UtcNow; } public class ApiSuccessResponse { public bool Success { get; set; } = true; public T? Data { get; set; } public string? Message { get; set; } } ``` **統一錯誤處理**: ```csharp // 基礎控制器類別 public abstract class BaseApiController : ControllerBase { protected ActionResult ApiError(string message, string? details = null, string? errorCode = null) { return BadRequest(new ApiErrorResponse { Error = message, Details = details, ErrorCode = errorCode }); } protected ActionResult ApiSuccess(T data, string? message = null) { return Ok(new ApiSuccessResponse { Data = data, Message = message }); } } ``` **驗收標準**: - [ ] 所有 API 端點使用統一錯誤格式 - [ ] 錯誤代碼標準化 (如 FLASHCARD_NOT_FOUND) - [ ] 錯誤訊息本地化 (中文) - [ ] 詳細錯誤信息僅在開發環境顯示 ### 2.5 認證與授權準備 (優先級:🟢 低) #### 任務 8: 準備生產環境認證 **影響檔案**: - `backend/DramaLing.Api/Controllers/FlashcardsController.cs` - 準備認證代碼 **實現內容**: ```csharp // 保留現有測試模式,準備生產環境切換 private Guid GetUserId() { // 開發環境:使用固定測試用戶 if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") { return Guid.Parse("00000000-0000-0000-0000-000000000001"); } // 生產環境:解析 JWT Token var userIdString = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? User.FindFirst("sub")?.Value; if (Guid.TryParse(userIdString, out var userId)) return userId; throw new UnauthorizedAccessException("Invalid user ID in token"); } // 準備生產環境控制器標註 // [Authorize] // 生產環境時啟用 [AllowAnonymous] // 開發環境暫時保持 public class FlashcardsController : BaseApiController ``` **驗收標準**: - [ ] 開發環境認證邏輯保持不變 - [ ] 生產環境認證代碼已準備 - [ ] 環境切換機制正確 - [ ] JWT Token 解析邏輯完整 ## 3. 資料庫改進 ### 3.1 Entity Framework 優化 #### 任務 9: 新增資料庫索引遷移 **影響檔案**: - `backend/DramaLing.Api/Data/DramaLingDbContext.cs` - 索引配置 - 新增 EF 遷移檔案 **遷移步驟**: ```bash # 生成新的遷移 dotnet ef migrations add AddFlashcardSearchIndexes # 更新資料庫 dotnet ef database update ``` **索引策略**: - 單欄索引:Word, Translation, DifficultyLevel, PartOfSpeech - 複合索引:(UserId, IsArchived, IsFavorite) - 搜尋優化:全文搜尋索引 (如果 SQLite 支援) **驗收標準**: - [ ] 索引正確創建 - [ ] 查詢計劃顯示索引使用 - [ ] 搜尋效能明顯提升 #### 任務 10: 資料驗證增強 **影響檔案**: - `backend/DramaLing.Api/Models/DTOs/CreateFlashcardRequest.cs` - 新增驗證特性 **驗證規則**: ```csharp public class CreateFlashcardRequest { [Required(ErrorMessage = "詞彙為必填項目")] [StringLength(255, ErrorMessage = "詞彙長度不得超過 255 字元")] public string Word { get; set; } = string.Empty; [Required(ErrorMessage = "翻譯為必填項目")] public string Translation { get; set; } = string.Empty; [Required(ErrorMessage = "定義為必填項目")] public string Definition { get; set; } = string.Empty; [StringLength(255, ErrorMessage = "發音長度不得超過 255 字元")] public string Pronunciation { get; set; } = string.Empty; [RegularExpression("^(noun|verb|adjective|adverb|preposition|interjection)$", ErrorMessage = "詞性必須為有效值")] public string PartOfSpeech { get; set; } = "noun"; [Required(ErrorMessage = "例句為必填項目")] public string Example { get; set; } = string.Empty; public string? ExampleTranslation { get; set; } [RegularExpression("^(A1|A2|B1|B2|C1|C2)$", ErrorMessage = "CEFR 等級必須為有效值")] public string? DifficultyLevel { get; set; } = "A2"; } ``` **驗收標準**: - [ ] 所有輸入資料驗證完整 - [ ] 錯誤訊息本地化和友善 - [ ] 驗證失敗時返回具體錯誤信息 - [ ] 防止無效資料進入資料庫 ## 4. API 文檔與測試 ### 4.1 Swagger 文檔增強 #### 任務 11: 完善 API 文檔 **影響檔案**: - `backend/DramaLing.Api/Extensions/ServiceCollectionExtensions.cs` - Swagger 配置 **Swagger 增強**: ```csharp services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new() { Title = "DramaLing API - 詞卡管理", Version = "v1", Description = "DramaLing 詞卡管理功能的完整 API 文檔" }); // XML 註解檔案 var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); // API 範例 c.SchemaFilter(); }); ``` **XML 註解增強**: ```csharp /// /// 取得用戶的詞卡列表,支援搜尋和篩選 /// /// 搜尋關鍵字,搜尋範圍:詞彙、翻譯、定義、例句 /// 僅顯示收藏詞卡 /// CEFR 難度等級篩選 (A1-C2) /// 詞性篩選 /// 掌握度篩選 (high/medium/low) /// 詞卡列表和數量 [HttpGet] public async Task GetFlashcards(...) ``` **驗收標準**: - [ ] Swagger UI 顯示完整 API 文檔 - [ ] 所有參數和回應格式有詳細說明 - [ ] 提供 API 使用範例 - [ ] 錯誤代碼和狀態碼說明完整 ### 4.2 API 測試套件 #### 任務 12: 整合測試實現 **影響檔案**: - 新增 `backend/DramaLing.Api.Tests/Controllers/FlashcardsControllerTests.cs` **測試涵蓋範圍**: ```csharp [TestClass] public class FlashcardsControllerTests { [TestMethod] public async Task GetFlashcards_WithSearch_ReturnsFilteredResults() { // 測試搜尋功能 } [TestMethod] public async Task CreateFlashcard_WithValidData_CreatesSuccessfully() { // 測試詞卡創建 } [TestMethod] public async Task CreateFlashcard_WithDuplicateWord_ReturnsDuplicateError() { // 測試重複詞卡檢測 } [TestMethod] public async Task GetFlashcards_WithCefrFilter_ReturnsCorrectLevel() { // 測試 CEFR 等級篩選 } } ``` **驗收標準**: - [ ] 所有 API 端點有對應測試 - [ ] 測試覆蓋率 > 80% - [ ] 包含邊界條件和錯誤情況測試 - [ ] 測試可在 CI/CD 中自動執行 ## 5. 實施時程 ### 5.1 開發階段規劃 #### 第一階段:核心功能增強 (預估:2-3小時) 1. **擴展搜尋功能** (45分鐘) 2. **新增進階篩選參數** (60分鐘) 3. **資料驗證增強** (45分鐘) 4. **測試和驗證** (30分鐘) #### 第二階段:效能優化 (預估:2-3小時) 5. **資料庫索引優化** (60分鐘) 6. **快取機制實現** (90分鐘) 7. **效能測試和調整** (30分鐘) #### 第三階段:API 增強 (預估:3-4小時) 8. **批量操作 API** (120分鐘) 9. **統計資料 API** (90分鐘) 10. **Swagger 文檔完善** (30分鐘) #### 第四階段:測試和部署準備 (預估:2-3小時) 11. **整合測試實現** (120分鐘) 12. **生產環境認證準備** (60分鐘) ### 5.2 里程碑檢查點 #### 里程碑 1: 基礎功能完善 ✅ - 搜尋和篩選功能完整 - 資料驗證機制健全 - 基本測試通過 #### 里程碑 2: 效能達標 ✅ - 查詢響應時間 < 200ms - 快取命中率 > 70% - 無明顯效能瓶頸 #### 里程碑 3: API 完整性 ✅ - 所有計劃 API 端點實現 - Swagger 文檔完整 - 錯誤處理標準化 #### 里程碑 4: 生產準備 ✅ - 整合測試覆蓋率 > 80% - 生產環境配置準備 - 部署文檔更新 ## 6. 品質保證 ### 6.1 程式碼審查檢查清單 #### API 設計檢查 - [ ] RESTful 設計原則遵循 - [ ] HTTP 狀態碼正確使用 - [ ] 回應格式標準化 - [ ] 查詢參數命名一致 #### 安全性檢查 - [ ] 輸入驗證完整 - [ ] SQL 注入防護 - [ ] 用戶資料隔離 - [ ] 敏感資訊保護 #### 效能檢查 - [ ] 查詢優化 - [ ] 索引使用合理 - [ ] 記憶體使用最佳化 - [ ] 併發處理安全 ### 6.2 測試策略 > 📋 **測試策略參考** > - [測試策略文檔](../04_testing/test-strategy.md) - 了解完整的測試方法和標準 #### 單元測試 - Controller 方法邏輯測試 - 資料驗證規則測試 - 錯誤處理機制測試 #### 整合測試 - API 端點完整流程測試 - 資料庫操作測試 - 快取機制測試 #### 效能測試 - 大量資料載入測試 - 並發請求壓力測試 - 記憶體洩漏檢測 ## 7. 部署與監控 ### 7.1 部署準備 #### 環境配置 ```bash # 生產環境變數 export ASPNETCORE_ENVIRONMENT=Production export DRAMALING_DB_CONNECTION="Data Source=production.db" export USE_INMEMORY_DB=false ``` #### 健康檢查 ```csharp // 增強健康檢查 services.AddHealthChecks() .AddDbContextCheck() .AddCheck("cache") .AddCheck("api"); ``` ### 7.2 監控指標 #### 關鍵效能指標 (KPI) - API 響應時間平均值 < 200ms - API 成功率 > 99.5% - 資料庫連接健康度 > 99% - 快取命中率 > 70% #### 業務指標 - 每日 API 呼叫次數 - 詞卡創建成功率 - 搜尋查詢頻率 - 使用者活躍度 --- **計劃版本**: v1.0 **制定日期**: 2025-09-24 **預估完成時間**: 9-13小時 (分 4 個階段) **負責開發**: 後端開發團隊 **審核負責**: 技術主管 > 📋 **開發前必讀文檔** > > **🔧 主要規格參考** > - [詞卡管理 API 規格](./api/flashcard-management-api.md) - 開發的主要依據,包含所有 API 介面定義 > > **🏗️ 架構約束** > - [後端架構詳細說明](../04_technical/backend-architecture.md) - 必須遵循的技術架構約束 > - [系統架構總覽](../04_technical/system-architecture.md) - 了解整體系統設計脈絡 > > **📋 業務需求** > - [詞卡管理功能產品需求規格](../01_requirement/詞卡管理功能產品需求規格.md) - 理解業務需求和用戶期望