# 智能複習系統 - 技術實作架構規格書 (TAS) **目標讀者**: 全端開發工程師、系統架構師、技術主管 **版本**: 1.0 **日期**: 2025-09-29 **實施狀態**: 🎯 **架構設計階段** --- ## 🏗️ **整體系統架構設計** ### **三層架構模式** ``` ┌─────────────────────────────────────────────────────────────────┐ │ 🌐 前端層 (React) │ ├─────────────────┬─────────────────┬─────────────────┬──────────────┤ │ 複習流程組件 │ 測驗類型組件 │ 狀態管理 │ API整合層 │ │ │ │ │ │ │ SmartReview │ FlipMemoryTest │ ReviewContext │ ReviewAPI │ │ Container │ VocabChoice │ QueueManager │ FlashcardAPI│ │ TestQueue │ SentenceFill │ StateRecovery │ ReviewAPI │ │ Progress │ SentenceReorder│ Navigation │ │ │ Tracker │ ListeningTest │ Controller │ │ │ │ SpeakingTest │ │ │ └─────────────────┴─────────────────┴─────────────────┴──────────────┘ │ ┌─────────────────────────────────────────────────────────────────┐ │ 🔗 API 契約層 │ ├─────────────────────────────────────────────────────────────────┤ │ 統一API規範 + 錯誤處理 + 認證授權 + 快取策略 │ └─────────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────────┐ │ 🏛️ 後端層 (.NET Core) │ ├─────────────────┬─────────────────┬─────────────────┬──────────────┤ │ 控制器層 │ 服務層 │ 資料層 │ 基礎設施 │ │ │ │ │ │ │ ReviewController │ SpacedRepetition│ ReviewRecord │ JWTAuth │ │ FlashcardCtrl │ ReviewSelector │ Flashcard │ Cache │ │ StatsController │ QuestionGen │ DailyStats │ Logging │ │ │ BlankGeneration │ OptionsVocab │ Monitoring │ │ │ CEFRMapping │ │ │ └─────────────────┴─────────────────┴─────────────────┴──────────────┘ │ ┌─────────────────────────────────────────────────────────────────┐ │ 💾 資料庫層 (SQLite/PostgreSQL) │ │ 智能索引 + 關聯關係 + 效能優化 │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 🎯 **前端組件架構設計** ### **核心組件層次結構** ```typescript // 最外層容器 SmartReviewContainer ├── ReviewContextProvider // 全域狀態管理 ├── TestQueueManager // 測驗隊列管理 ├── ProgressTracker // 進度追蹤和可視化 ├── NavigationController // 導航邏輯控制 └── TestRenderer // 動態測驗渲染器 ├── FlipMemoryTest // 翻卡記憶測驗 ├── VocabChoiceTest // 詞彙選擇測驗 ├── SentenceFillTest // 例句填空測驗 ├── SentenceReorderTest // 例句重組測驗 ├── VocabListeningTest // 詞彙聽力測驗 ├── SentenceListeningTest // 例句聽力測驗 └── SentenceSpeakingTest // 例句口說測驗 ``` ### **狀態管理架構** ```typescript // 🧠 全域複習狀態 Context interface ReviewContextState { // 基礎數據 dueCards: Flashcard[] completedTests: CompletedTest[] // 隊列管理 testQueue: TestItem[] currentTestIndex: number skippedTests: TestItem[] // 用戶狀態 userCEFRLevel: string learningSession: LearningSession // UI 狀態 isAnswered: boolean showResult: boolean navigationState: 'skip' | 'continue' } // 🎯 隊列管理邏輯 class TestQueueManager { // 智能排序邏輯 prioritizeTests(tests: TestItem[]): TestItem[] { return tests.sort((a, b) => { // 1. 新測驗 (最高優先級) if (a.status === 'new' && b.status !== 'new') return -1 if (b.status === 'new' && a.status !== 'new') return 1 // 2. 答錯測驗 (中等優先級) if (a.status === 'incorrect' && b.status === 'skipped') return -1 if (b.status === 'incorrect' && a.status === 'skipped') return 1 // 3. 跳過測驗 (最低優先級) return 0 }) } // 處理測驗結果 handleTestResult(testId: string, result: 'correct' | 'incorrect' | 'skipped') { switch (result) { case 'correct': // 從隊列完全移除 this.removeFromQueue(testId) break case 'incorrect': // 移到隊列最後 this.moveToEnd(testId, 'incorrect') break case 'skipped': // 移到隊列最後 this.moveToEnd(testId, 'skipped') break } } } ``` ### **測驗組件標準化介面** ```typescript // 🧩 測驗組件統一介面 interface TestComponentProps { flashcard: Flashcard testType: TestType onSubmit: (result: TestResult) => void onSkip: () => void isDisabled: boolean } // 📝 測驗結果標準格式 interface TestResult { flashcardId: string testType: TestType isCorrect: boolean userAnswer: string confidenceLevel?: number responseTimeMs: number } // 🎮 導航狀態管理 type NavigationState = 'pre-answer' | 'post-answer' type ButtonAction = 'skip' | 'continue' ``` --- ## 🏛️ **後端服務架構設計** ### **控制器職責重新劃分** ```csharp // 📚 FlashcardsController - 純粹詞卡資料管理 [Route("api/flashcards")] public class FlashcardsController { // 純粹的 CRUD 操作 GET / // 詞卡列表查詢 POST / // 創建新詞卡 GET /{id} // 詞卡詳情 PUT /{id} // 更新詞卡 DELETE /{id} // 刪除詞卡 POST /{id}/favorite // 收藏切換 } // 🎯 ReviewController - 完整複習管理 [Route("api/review")] public class ReviewController { // 複習狀態管理 GET /today // 今日複習總覽 GET /next // 下一個測驗 GET /progress // 複習進度 // 測驗執行 POST /{id}/question // 生成題目選項 POST /{id}/submit // 提交測驗結果 POST /{id}/skip // 跳過測驗 // 智能適配 POST /{id}/optimal-mode // 智能模式選擇 // 狀態持久化 GET /completed-tests // 已完成測驗 POST /record-test // 記錄測驗 GET /stats // 複習統計 } ``` ### **智能複習服務群架構** ```csharp // 🧠 核心算法服務 public interface ISpacedRepetitionService { Task ProcessReviewAsync(Guid flashcardId, ReviewRequest request); Task> GetTodaysDueCardsAsync(Guid userId); Task GetNextTestAsync(Guid userId); int CalculateCurrentMasteryLevel(Flashcard flashcard); } // 🎯 智能適配服務 public interface IReviewAdaptationService { Task SelectOptimalModeAsync(Flashcard flashcard, User user); string[] GetAvailableTestTypes(string userCEFR, string wordCEFR); AdaptationContext GetAdaptationContext(string userCEFR, string wordCEFR); } // 🔄 隊列管理服務 public interface ITestQueueService { Task BuildTodaysQueueAsync(Guid userId); Task GetNextTestItemAsync(Guid userId); Task HandleTestResultAsync(Guid userId, TestResult result); Task GetQueueStatusAsync(Guid userId); } // 📝 題目生成服務 public interface IQuestionGeneratorService { Task GenerateQuestionAsync(Guid flashcardId, TestType testType); } // 💾 狀態持久化服務 public interface IReviewStateService { Task GetCompletedTestsAsync(Guid userId, DateTime? date = null); Task RecordTestCompletionAsync(Guid userId, TestResult result); Task GetProgressAsync(Guid userId); } ``` --- ## 💾 **資料模型設計規範** ### **核心實體關係** ```csharp // 👤 用戶實體 (擴展 CEFR 支援) public class User { public Guid Id { get; set; } public string Username { get; set; } public string Email { get; set; } // 🆕 CEFR 智能適配 public string EnglishLevel { get; set; } = "A2"; // A1-C2 public DateTime LevelUpdatedAt { get; set; } public bool IsLevelVerified { get; set; } // Navigation Properties public ICollection Flashcards { get; set; } public ICollection ReviewRecords { get; set; } public ICollection DailyStats { get; set; } } // 📚 詞卡實體 (智能複習增強) public class Flashcard { public Guid Id { get; set; } public Guid UserId { get; set; } // 詞卡內容 public string Word { get; set; } public string Translation { get; set; } public string Definition { get; set; } public string? PartOfSpeech { get; set; } public string? Pronunciation { get; set; } public string? Example { get; set; } public string? ExampleTranslation { get; set; } // 🆕 智能複習參數 public string? DifficultyLevel { get; set; } // A1-C2 public float EasinessFactor { get; set; } = 2.5f; // SM-2 算法 public int Repetitions { get; set; } = 0; public int IntervalDays { get; set; } = 1; public DateTime NextReviewDate { get; set; } public int MasteryLevel { get; set; } = 0; // 0-100 public int TimesReviewed { get; set; } = 0; public int TimesCorrect { get; set; } = 0; public DateTime? LastReviewedAt { get; set; } // 🆕 測驗歷史追蹤 public string? ReviewHistory { get; set; } // JSON public string? LastQuestionType { get; set; } // Navigation Properties public User User { get; set; } public ICollection ReviewRecords { get; set; } } // 📊 複習記錄實體 (簡化無 Session) public class ReviewRecord { public Guid Id { get; set; } public Guid UserId { get; set; } public Guid FlashcardId { get; set; } // 測驗資訊 public string ReviewMode { get; set; } // 測驗類型 public int QualityRating { get; set; } // 1-5 (SM-2) public bool IsCorrect { get; set; } public string? UserAnswer { get; set; } public int? ResponseTimeMs { get; set; } public DateTime StudiedAt { get; set; } // SM-2 追蹤參數 public double? PreviousEasinessFactor { get; set; } public double? NewEasinessFactor { get; set; } public int? PreviousIntervalDays { get; set; } public int? NewIntervalDays { get; set; } public DateTime? NextReviewDate { get; set; } // Navigation Properties public User User { get; set; } public Flashcard Flashcard { get; set; } } // 📈 每日統計實體 public class DailyStats { public Guid Id { get; set; } public Guid UserId { get; set; } public DateOnly Date { get; set; } // 複習統計 public int WordsStudied { get; set; } = 0; public int WordsCorrect { get; set; } = 0; public int ReviewTimeSeconds { get; set; } = 0; public int SessionCount { get; set; } = 0; // Navigation Properties public User User { get; set; } } ``` ### **資料庫索引策略** ```sql -- 🎯 智能複習核心索引 CREATE INDEX IX_Flashcards_UserDue ON flashcards(user_id, next_review_date) WHERE is_archived = 0; -- 📊 複習記錄查詢優化 CREATE UNIQUE INDEX IX_ReviewRecord_UserCardTest ON review_records(user_id, flashcard_id, review_mode); -- 📈 統計查詢優化 CREATE UNIQUE INDEX IX_DailyStats_UserDate ON daily_stats(user_id, date); -- 🔍 詞卡搜尋優化 CREATE INDEX IX_Flashcards_Search ON flashcards(user_id, word, translation) WHERE is_archived = 0; -- ⚡ CEFR 等級查詢優化 CREATE INDEX IX_Flashcards_CEFR ON flashcards(user_id, difficulty_level, mastery_level); ``` --- ## 🔗 **API 設計規範** ### **統一 API 契約** ```typescript // 🌐 統一響應格式 interface ApiResponse { success: boolean data?: T error?: string message?: string timestamp: string requestId?: string } // ⚠️ 統一錯誤格式 interface ApiError { code: string message: string details?: any suggestions?: string[] } ``` ### **智能複習 API 設計** ```typescript // 📋 今日複習總覽 API GET /api/review/today Response: { success: true, data: { dueCards: Flashcard[], totalTests: number, completedTests: number, estimatedTimeMinutes: number, adaptationSummary: { a1Protection: boolean, primaryAdaptation: "簡單詞彙" | "適中詞彙" | "困難詞彙", recommendedTestTypes: string[] } } } // 🎯 下一個測驗 API GET /api/review/next Response: { success: true, data: { testItem: { flashcardId: string, testType: string, flashcard: Flashcard, questionData: QuestionData }, queueStatus: { remaining: number, completed: number, skipped: number }, navigationState: "skip" | "continue" } } // 📝 提交測驗 API POST /api/review/{flashcardId}/submit Request: { testType: string, isCorrect: boolean, userAnswer: string, confidenceLevel?: number, responseTimeMs: number } Response: { success: true, data: { reviewResult: ReviewResult, nextAction: "continue" | "session_complete", masteryUpdate: { previousLevel: number, newLevel: number, nextReviewDate: string } } } // ⏭️ 跳過測驗 API POST /api/review/{flashcardId}/skip Request: { testType: string, reason?: string } Response: { success: true, data: { skippedTestId: string, nextAction: "continue", queueStatus: QueueStatus } } ``` --- ## 🧠 **智能適配算法設計** ### **CEFR 四情境適配邏輯** ```csharp // 🎯 CEFRMappingService - 等級轉換服務 public static class CEFRMappingService { private static readonly Dictionary CEFRToLevel = new() { { "A1", 20 }, { "A2", 35 }, { "B1", 50 }, { "B2", 65 }, { "C1", 80 }, { "C2", 95 } }; public static int GetNumericLevel(string cefrLevel) => CEFRToLevel.GetValueOrDefault(cefrLevel, 50); public static string GetCEFRLevel(int numericLevel) => CEFRToLevel.FirstOrDefault(kvp => kvp.Value == numericLevel).Key ?? "B1"; } // 🛡️ 智能適配服務實現 public class ReviewAdaptationService : IReviewAdaptationService { public async Task SelectOptimalModeAsync(Flashcard flashcard, User user) { var userLevel = CEFRMappingService.GetNumericLevel(user.EnglishLevel); var wordLevel = CEFRMappingService.GetNumericLevel(flashcard.DifficultyLevel ?? "B1"); // 四情境判斷邏輯 var adaptationContext = GetAdaptationContext(userLevel, wordLevel); var availableTestTypes = GetAvailableTestTypes(adaptationContext); var selectedMode = await SelectModeWithHistory(flashcard.Id, availableTestTypes); return new ReviewModeResult { SelectedMode = selectedMode, AdaptationContext = adaptationContext, AvailableTestTypes = availableTestTypes, Reason = GetSelectionReason(adaptationContext, selectedMode) }; } private AdaptationContext GetAdaptationContext(int userLevel, int wordLevel) { var difficulty = wordLevel - userLevel; if (userLevel <= 20) // A1 保護 return AdaptationContext.A1Protection; if (difficulty < -10) // 簡單詞彙 return AdaptationContext.EasyVocabulary; if (difficulty >= -10 && difficulty <= 10) // 適中詞彙 return AdaptationContext.ModerateVocabulary; return AdaptationContext.DifficultVocabulary; // 困難詞彙 } private string[] GetAvailableTestTypes(AdaptationContext context) { return context switch { AdaptationContext.A1Protection => new[] { "flip-memory", "vocab-choice", "vocab-listening" }, AdaptationContext.EasyVocabulary => new[] { "sentence-reorder", "sentence-fill" }, AdaptationContext.ModerateVocabulary => new[] { "sentence-fill", "sentence-reorder", "sentence-speaking" }, AdaptationContext.DifficultVocabulary => new[] { "flip-memory", "vocab-choice" }, _ => new[] { "flip-memory", "vocab-choice" } }; } } ``` ### **隊列管理算法** ```csharp // 🔄 測驗隊列服務 public class TestQueueService : ITestQueueService { public async Task BuildTodaysQueueAsync(Guid userId) { // 1. 獲取今日到期詞卡 var dueCards = await GetTodaysDueCardsAsync(userId); // 2. 獲取已完成測驗 var completedTests = await GetCompletedTestsAsync(userId, DateTime.Today); // 3. 計算剩餘測驗 var allTests = GenerateAllPossibleTests(dueCards); var remainingTests = FilterCompletedTests(allTests, completedTests); // 4. 智能排序 var prioritizedTests = PrioritizeTests(remainingTests); return new TestQueue { Tests = prioritizedTests, TotalCount = allTests.Count, CompletedCount = completedTests.Count, RemainingCount = remainingTests.Count }; } private List PrioritizeTests(List tests) { return tests .OrderBy(t => t.Status switch { TestStatus.New => 0, // 優先處理新測驗 TestStatus.Incorrect => 1, // 然後是答錯的 TestStatus.Skipped => 2, // 最後是跳過的 _ => 3 }) .ThenBy(t => t.LastAttemptAt ?? DateTime.MinValue) // 按最後嘗試時間排序 .ToList(); } } ``` --- ## 🎮 **前端狀態管理架構** ### **Context + Hooks 架構** ```typescript // 🧠 Review Context Provider export const ReviewContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [state, dispatch] = useReducer(reviewReducer, initialState) // 核心方法 const contextValue = { // 狀態 ...state, // 操作方法 loadTodaysReview: () => dispatch({ type: 'LOAD_TODAYS_REVIEW' }), submitTest: (result: TestResult) => dispatch({ type: 'SUBMIT_TEST', payload: result }), skipTest: (testId: string) => dispatch({ type: 'SKIP_TEST', payload: testId }), proceedToNext: () => dispatch({ type: 'PROCEED_TO_NEXT' }), // 狀態查詢 getCurrentTest: () => state.testQueue[state.currentTestIndex], getNavigationState: () => state.isAnswered ? 'continue' : 'skip', isSessionComplete: () => state.testQueue.every(t => t.status === 'completed') } return ( {children} ) } // 🎯 自定義 Hooks export const useReviewFlow = () => { const context = useContext(ReviewContext) return { // 當前測驗 currentTest: context.getCurrentTest(), // 導航控制 navigationState: context.getNavigationState(), canSkip: !context.isAnswered, canContinue: context.isAnswered, // 操作方法 submitAnswer: context.submitTest, skipCurrent: context.skipTest, proceedNext: context.proceedToNext, // 進度資訊 progress: { completed: context.testQueue.filter(t => t.status === 'completed').length, total: context.testQueue.length, isComplete: context.isSessionComplete() } } } ``` ### **組件化架構實現** ```typescript // 🎮 智能複習主容器 export const SmartReviewContainer: React.FC = () => { return (
) } // 📊 進度追蹤組件 export const ProgressTracker: React.FC = () => { const { progress } = useReviewFlow() return (
{/* 雙層進度條實現 */}
{progress.completed}/{progress.total} 已完成
) } // 🎯 動態測驗渲染器 export const TestRenderer: React.FC = () => { const { currentTest } = useReviewFlow() if (!currentTest) { return } // 動態載入對應的測驗組件 const TestComponent = getTestComponent(currentTest.testType) return (
) } // 🧭 導航控制器 export const NavigationController: React.FC = () => { const { navigationState, canSkip, canContinue, skipCurrent, proceedNext } = useReviewFlow() return (
{navigationState === 'skip' && canSkip && ( )} {navigationState === 'continue' && canContinue && ( )}
) } ``` --- ## 🔧 **服務層實現規範** ### **依賴注入配置** ```csharp // 📦 服務註冊 (Program.cs) public static class ServiceCollectionExtensions { public static IServiceCollection AddSmartReviewServices(this IServiceCollection services) { // 核心智能複習服務 services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); // 輔助服務 services.AddSingleton(); services.AddScoped(); services.AddScoped(); // 配置選項 services.Configure(config.GetSection("SpacedRepetition")); return services; } } ``` ### **錯誤處理統一標準** ```csharp // 🛡️ 全域錯誤處理中間件 public class SmartReviewErrorHandlingMiddleware { public async Task InvokeAsync(HttpContext context, RequestDelegate next) { try { await next(context); } catch (Exception ex) { await HandleExceptionAsync(context, ex); } } private async Task HandleExceptionAsync(HttpContext context, Exception ex) { var response = ex switch { ArgumentException => CreateErrorResponse("INVALID_INPUT", ex.Message, 400), UnauthorizedAccessException => CreateErrorResponse("UNAUTHORIZED", "認證失敗", 401), InvalidOperationException => CreateErrorResponse("INVALID_OPERATION", ex.Message, 400), _ => CreateErrorResponse("INTERNAL_ERROR", "系統內部錯誤", 500) }; context.Response.StatusCode = response.StatusCode; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonSerializer.Serialize(response.Content)); } } ``` --- ## 📊 **效能優化架構** ### **快取策略設計** ```csharp // 🚀 多層快取架構 public class SmartReviewCacheService { // L1: Memory Cache (最快) private readonly IMemoryCache _memoryCache; // L2: Distributed Cache (Redis/SQL) private readonly IDistributedCache _distributedCache; // L3: Database (最持久) private readonly DramaLingDbContext _context; public async Task GetAsync(string key) where T : class { // 1. 嘗試記憶體快取 if (_memoryCache.TryGetValue(key, out T? cachedValue)) return cachedValue; // 2. 嘗試分散式快取 var distributedValue = await _distributedCache.GetStringAsync(key); if (distributedValue != null) { var deserializedValue = JsonSerializer.Deserialize(distributedValue); _memoryCache.Set(key, deserializedValue, TimeSpan.FromMinutes(5)); return deserializedValue; } // 3. 資料庫查詢 (最後選擇) return null; } } // 📈 關鍵快取策略 var cacheStrategies = new Dictionary { ["today_due_cards"] = new(TimeSpan.FromMinutes(15), CacheLevel.Memory), ["completed_tests"] = new(TimeSpan.FromMinutes(30), CacheLevel.Distributed), ["user_cefr_mapping"] = new(TimeSpan.FromHours(1), CacheLevel.Memory), ["question_options"] = new(TimeSpan.FromMinutes(10), CacheLevel.Memory) }; ``` ### **API 效能優化** ```csharp // ⚡ 查詢優化策略 public class OptimizedReviewService { // 批量預載入相關資料 public async Task> GetDueCardsWithOptimizationAsync(Guid userId) { return await _context.Flashcards .Where(f => f.UserId == userId && f.NextReviewDate <= DateTime.Today) .Include(f => f.ReviewRecords.Where(sr => sr.StudiedAt.Date == DateTime.Today)) .AsNoTracking() // 只讀查詢優化 .AsSplitQuery() // 分割查詢避免笛卡爾積 .ToListAsync(); } // 智能預測下一個測驗 public async Task PredictNextTestAsync(Guid userId) { // 使用演算法預測,減少即時計算 var cachedPrediction = await _cache.GetAsync($"next_test:{userId}"); if (cachedPrediction != null) return cachedPrediction; // 重新計算並快取 var nextTest = await ComputeNextTestAsync(userId); await _cache.SetAsync($"next_test:{userId}", nextTest, TimeSpan.FromMinutes(5)); return nextTest; } } ``` --- ## 🔐 **安全與認證架構** ### **JWT 認證策略** ```csharp // 🔑 JWT 配置 (Program.cs) builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = supabaseUrl, ValidAudience = "authenticated", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret)) }; // 智能複習特殊處理:允許過期 token 緩衝期 options.Events = new JwtBearerEvents { OnTokenValidated = context => { // 檢查複習狀態,允許正在複習的用戶有 5 分鐘緩衝 return Task.CompletedTask; } }; }); ``` ### **權限控制策略** ```csharp // 🛡️ 智能複習權限檢查 [AttributeUsage(AttributeTargets.Method)] public class RequireFlashcardOwnershipAttribute : Attribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { // 檢查詞卡所有權 var flashcardId = GetFlashcardIdFromRoute(context); var userId = GetUserIdFromToken(context); if (!ValidateOwnership(flashcardId, userId)) { context.Result = new UnauthorizedResult(); } } } // 使用範例 [HttpPost("{id}/submit")] [RequireFlashcardOwnership] public async Task SubmitTest(Guid id, [FromBody] TestResult result) { // 已通過權限檢查,直接處理業務邏輯 } ``` --- ## 📈 **監控與可觀測性** ### **關鍵指標監控** ```csharp // 📊 智能複習指標收集 public class SmartReviewMetrics { // 業務指標 [Counter("smart_review_tests_completed_total")] public static readonly Counter TestsCompleted; [Counter("smart_review_tests_skipped_total")] public static readonly Counter TestsSkipped; [Histogram("smart_review_response_time_ms")] public static readonly Histogram ResponseTime; [Gauge("smart_review_active_sessions")] public static readonly Gauge ActiveSessions; // CEFR 適配指標 [Counter("cefr_adaptation_selections_total")] public static readonly Counter CEFRAdaptations; [Histogram("cefr_adaptation_accuracy")] public static readonly Histogram AdaptationAccuracy; } ``` ### **健康檢查端點** ```csharp // 🩺 智能複習系統健康檢查 [HttpGet("/health/smart-review")] public async Task GetSmartReviewHealth() { var healthChecks = new Dictionary { ["database"] = await CheckDatabaseConnectionAsync(), ["cefr_mapping"] = CheckCEFRMappingService(), ["spaced_repetition"] = CheckSpacedRepetitionAlgorithm(), ["test_queue"] = await CheckTestQueueServiceAsync(), ["api_response_time"] = await MeasureAPIResponseTimeAsync() }; var isHealthy = healthChecks.Values.All(v => v.ToString() == "Healthy"); return Ok(new { Status = isHealthy ? "Healthy" : "Degraded", Checks = healthChecks, Timestamp = DateTime.UtcNow, SystemInfo = new { ActiveUsers = await GetActiveUserCountAsync(), TestsCompletedToday = await GetTodaysTestCountAsync(), AverageResponseTimeMs = GetAverageResponseTime() } }); } ``` --- ## 🚀 **部署架構規範** ### **環境配置標準** ```json // 🔧 appsettings.json (生產環境) { "SmartReview": { "EnableAdaptiveAlgorithm": true, "A1ProtectionLevel": 20, "CEFRMappingCache": { "ExpirationMinutes": 60, "MaxEntries": 1000 }, "TestQueue": { "MaxQueueSize": 100, "PriorityRecalculationMinutes": 15, "SkipLimitPerSession": 50 }, "Performance": { "MaxConcurrentSessions": 1000, "ResponseTimeThresholdMs": 100, "CacheHitRateThreshold": 0.8 } }, "SpacedRepetition": { "Algorithm": "SM2Enhanced", "GrowthFactors": { "ShortTerm": 1.8, "MediumTerm": 1.4, "LongTerm": 1.2, "VeryLongTerm": 1.1 }, "OverduePenalties": { "Light": 0.9, "Medium": 0.75, "Heavy": 0.5, "Extreme": 0.3 } } } ``` ### **容器化部署配置** ```dockerfile # 🐳 Dockerfile (生產環境) FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 8080 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["DramaLing.Api.csproj", "."] RUN dotnet restore COPY . . RUN dotnet build -c Release -o /app/build FROM build AS publish RUN dotnet publish -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . # 智能複習系統特殊配置 ENV ASPNETCORE_ENVIRONMENT=Production ENV SMART_REVIEW_ENABLED=true ENV CEFR_CACHE_SIZE=1000 ENTRYPOINT ["dotnet", "DramaLing.Api.dll"] ``` --- ## 🧪 **測試架構設計** ### **測試策略規範** ```csharp // 🧪 智能複習服務測試 [TestFixture] public class SmartReviewServiceTests { private ITestQueueService _queueService; private ISpacedRepetitionService _spacedRepService; private Mock _mockContext; [Test] public async Task BuildTodaysQueue_ShouldPrioritizeNewTests() { // Arrange var userId = Guid.NewGuid(); var dueCards = CreateTestDueCards(); var completedTests = CreateTestCompletedTests(); // Act var queue = await _queueService.BuildTodaysQueueAsync(userId); // Assert Assert.That(queue.Tests.First().Status, Is.EqualTo(TestStatus.New)); Assert.That(queue.Tests.Where(t => t.Status == TestStatus.New).Count(), Is.GreaterThan(0)); } [Test] public async Task CEFRAdaptation_A1User_ShouldLimitToBasicTests() { // Arrange var userLevel = 20; // A1 var wordLevel = 50; // B1 // Act var availableTests = _adaptationService.GetAvailableTestTypes(userLevel, wordLevel); // Assert Assert.That(availableTests, Is.EquivalentTo(new[] { "flip-memory", "vocab-choice", "vocab-listening" })); } } ``` ### **前端組件測試** ```typescript // 🧪 React 組件測試 describe('SmartReviewContainer', () => { test('should load todays review on mount', async () => { render( ) await waitFor(() => { expect(screen.getByText(/今日複習/)).toBeInTheDocument() }) }) test('should show skip button before answering', () => { const { getByText } = renderWithContext(, { isAnswered: false, navigationState: 'skip' }) expect(getByText('跳過這題')).toBeInTheDocument() }) test('should show continue button after answering', () => { const { getByText } = renderWithContext(, { isAnswered: true, navigationState: 'continue' }) expect(getByText('繼續下一題')).toBeInTheDocument() }) }) ``` --- ## 🎯 **實施路線圖** ### **階段一:核心架構 (第1-2週)** 1. **後端服務層重構** - 實現 TestQueueService - 重構 ReviewController API - 移除 Session 複雜性 2. **前端組件基礎** - 建立 ReviewContext - 實現核心 Hooks - 基礎組件框架 ### **階段二:智能適配 (第3-4週)** 1. **CEFR 適配系統** - ReviewAdaptationService 實現 - 四情境邏輯完整實現 - A1 保護機制 2. **測驗類型組件** - 7 種測驗組件實現 - 統一介面標準化 - 答案驗證邏輯 ### **階段三:隊列管理 (第5-6週)** 1. **智能隊列系統** - 優先級排序算法 - 跳過邏輯實現 - 狀態持久化 2. **導航控制系統** - 狀態驅動導航 - 流暢的用戶體驗 - 錯誤恢復機制 ### **階段四:優化與監控 (第7-8週)** 1. **效能優化** - 快取策略實施 - 查詢優化 - 響應時間優化 2. **監控與測試** - 監控指標實施 - 自動化測試 - 效能基準測試 --- ## 📋 **開發檢查清單** ### **前端開發檢查項目** - [ ] ReviewContext 實現完成 - [ ] 7 個測驗組件實現完成 - [ ] 導航控制邏輯實現 - [ ] 隊列管理邏輯實現 - [ ] 狀態持久化機制 - [ ] 錯誤處理和重試機制 - [ ] 響應式設計適配 - [ ] 無障礙設計支援 ### **後端開發檢查項目** - [ ] ReviewController 重構完成 - [ ] 智能複習服務實現 - [ ] CEFR 適配邏輯實現 - [ ] 隊列管理服務實現 - [ ] API 錯誤處理統一 - [ ] 認證授權機制 - [ ] 效能優化實施 - [ ] 監控指標收集 ### **整合測試檢查項目** - [ ] 前後端 API 契約測試 - [ ] 四情境適配邏輯測試 - [ ] 隊列管理端到端測試 - [ ] 狀態持久化測試 - [ ] 效能基準測試 - [ ] 用戶體驗流程測試 --- **文檔版本**: 1.0 **創建日期**: 2025-09-29 **技術負責**: 開發團隊 **審核狀態**: 待審核 **實施優先級**: P0 (最高優先級)