dramaling-vocab-learning/複習系統重構計劃.md

9.6 KiB
Raw Blame History

複習系統後端驅動架構重構計劃

專案概述

問題現狀

  1. 邏輯錯誤:完成一個測驗就標記詞卡完成,但實際還有其他測驗未完成
  2. 架構問題:前端管理複習會話狀態,容易出現數據不一致
  3. UI問題:雙進度條視覺效果不佳,用戶希望整合為分段式進度條
  4. 維護困難:複習邏輯散落在前端各處,調試和維護困難

解決方案

將複習會話狀態管理移至後端實現真正的後端驅動架構同時優化進度條UI設計。

技術架構設計

後端架構

1. 數據模型設計

// 學習會話實體
public class StudySession
{
    public Guid Id { get; set; }
    public Guid UserId { get; set; }
    public DateTime StartedAt { get; set; }
    public DateTime? CompletedAt { get; set; }
    public SessionStatus Status { get; set; }
    public List<StudyCard> Cards { get; set; } = new();
    public int CurrentCardIndex { get; set; }
    public string? CurrentTestType { get; set; }
    public int TotalTests { get; set; }
    public int CompletedTests { get; set; }
}

// 詞卡學習進度
public class StudyCard
{
    public Guid Id { get; set; }
    public Guid StudySessionId { get; set; }
    public Guid FlashcardId { get; set; }
    public string Word { get; set; } = string.Empty;
    public List<string> PlannedTests { get; set; } = new();
    public List<TestResult> CompletedTests { get; set; } = new();
    public bool IsCompleted => CompletedTests.Count >= PlannedTests.Count;
    public int Order { get; set; }

    // 導航屬性
    public StudySession StudySession { get; set; }
    public Flashcard Flashcard { get; set; }
}

// 測驗結果實體
public class TestResult
{
    public Guid Id { get; set; }
    public Guid StudyCardId { get; set; }
    public string TestType { get; set; } = string.Empty;
    public bool IsCorrect { get; set; }
    public string? UserAnswer { get; set; }
    public int? ConfidenceLevel { get; set; } // 1-5, 用於翻卡記憶
    public int ResponseTimeMs { get; set; }
    public DateTime CompletedAt { get; set; }

    // 導航屬性
    public StudyCard StudyCard { get; set; }
}

// 會話狀態枚舉
public enum SessionStatus
{
    Active,      // 進行中
    Completed,   // 已完成
    Paused,      // 暫停
    Abandoned    // 放棄
}

2. 服務層設計

// 學習會話服務介面
public interface IStudySessionService
{
    Task<StudySessionDto> StartSessionAsync(Guid userId);
    Task<CurrentTestDto> GetCurrentTestAsync(Guid sessionId);
    Task<SubmitTestResponseDto> SubmitTestAsync(Guid sessionId, SubmitTestRequestDto request);
    Task<NextTestDto> GetNextTestAsync(Guid sessionId);
    Task<ProgressDto> GetProgressAsync(Guid sessionId);
    Task<CompleteSessionResponseDto> CompleteSessionAsync(Guid sessionId);
}

// 測驗模式選擇服務
public interface IReviewModeSelector
{
    List<string> GetPlannedTests(string userCEFRLevel, string wordCEFRLevel);
    string GetNextTestType(StudyCard card);
}

3. API端點設計

[Route("api/study/sessions")]
public class StudySessionController : ControllerBase
{
    // 開始學習會話
    [HttpPost("start")]
    public async Task<ActionResult<StudySessionDto>> StartSession()

    // 獲取當前測驗
    [HttpGet("{sessionId}/current-test")]
    public async Task<ActionResult<CurrentTestDto>> GetCurrentTest(Guid sessionId)

    // 提交測驗結果
    [HttpPost("{sessionId}/submit-test")]
    public async Task<ActionResult<SubmitTestResponseDto>> SubmitTest(Guid sessionId, SubmitTestRequestDto request)

    // 獲取下一個測驗
    [HttpGet("{sessionId}/next-test")]
    public async Task<ActionResult<NextTestDto>> GetNextTest(Guid sessionId)

    // 獲取詳細進度
    [HttpGet("{sessionId}/progress")]
    public async Task<ActionResult<ProgressDto>> GetProgress(Guid sessionId)

    // 結束會話
    [HttpPut("{sessionId}/complete")]
    public async Task<ActionResult<CompleteSessionResponseDto>> CompleteSession(Guid sessionId)
}

前端架構

1. 服務層重構

// 學習會話服務
class StudySessionService {
    async startSession(): Promise<StudySessionResponse> {
        return await this.post('/api/study/sessions/start');
    }

    async getCurrentTest(sessionId: string): Promise<CurrentTestResponse> {
        return await this.get(`/api/study/sessions/${sessionId}/current-test`);
    }

    async submitTest(sessionId: string, result: TestResult): Promise<SubmitTestResponse> {
        return await this.post(`/api/study/sessions/${sessionId}/submit-test`, result);
    }

    async getNextTest(sessionId: string): Promise<NextTestResponse> {
        return await this.get(`/api/study/sessions/${sessionId}/next-test`);
    }

    async getProgress(sessionId: string): Promise<ProgressResponse> {
        return await this.get(`/api/study/sessions/${sessionId}/progress`);
    }

    async completeSession(sessionId: string): Promise<CompleteSessionResponse> {
        return await this.put(`/api/study/sessions/${sessionId}/complete`);
    }
}

2. React組件簡化

// 簡化的 LearnPage 組件
function LearnPage() {
    const [session, setSession] = useState<StudySession | null>(null);
    const [currentTest, setCurrentTest] = useState<CurrentTest | null>(null);
    const [progress, setProgress] = useState<Progress | null>(null);

    // 簡化的狀態管理 - 只保留UI相關狀態
    const [selectedAnswer, setSelectedAnswer] = useState<string | null>(null);
    const [showResult, setShowResult] = useState(false);
    const [isLoading, setIsLoading] = useState(false);

    // 簡化的邏輯
    const handleStartSession = async () => {
        const newSession = await studyService.startSession();
        setSession(newSession);
        loadCurrentTest(newSession.id);
    };

    const handleSubmitAnswer = async (result: TestResult) => {
        await studyService.submitTest(session.id, result);
        loadNextTest();
    };
}

實施計劃

階段一:後端擴展 (預計2-3天)

Day 1: 數據模型和遷移

  • 創建 StudySession, StudyCard, TestResult 實體
  • 創建資料庫遷移
  • 更新 DbContext

Day 2: 服務層實現

  • 實現 StudySessionService
  • 實現 ReviewModeSelector
  • 單元測試

Day 3: API控制器

  • 實現 StudySessionController
  • API集成測試
  • Swagger文檔更新

階段二:前端重構 (預計2天)

Day 4: 服務層和狀態管理

  • 創建 StudySessionService
  • 重構 LearnPage 組件
  • 移除複雜的本地狀態

Day 5: UI組件優化

  • 簡化測驗組件
  • 更新導航邏輯
  • 錯誤處理優化

階段三:進度條美化 (預計1天)

Day 6: 分段式進度條

  • 設計 SegmentedProgressBar 組件
  • 實現詞卡分段顯示
  • 添加hover tooltip功能
  • 響應式布局優化

階段四:測試與部署 (預計1天)

Day 7: 完整測試

  • 端到端學習流程測試
  • 進度追蹤準確性驗證
  • 性能測試
  • 用戶體驗測試

數據流程圖

graph TD
    A[用戶開始學習] --> B[POST /sessions/start]
    B --> C[後端創建StudySession]
    C --> D[後端規劃詞卡測驗]
    D --> E[返回會話信息]
    E --> F[GET /sessions/{id}/current-test]
    F --> G[後端返回當前測驗]
    G --> H[前端顯示測驗UI]
    H --> I[用戶完成測驗]
    I --> J[POST /sessions/{id}/submit-test]
    J --> K[後端記錄結果]
    K --> L{該詞卡測驗完成?}
    L -->|否| M[GET /sessions/{id}/next-test]
    L -->|是| N[後端計算SM2並更新]
    N --> O{所有詞卡完成?}
    O -->|否| M
    O -->|是| P[PUT /sessions/{id}/complete]
    M --> G
    P --> Q[學習完成]

關鍵優勢

可靠性提升

  • 數據一致性:狀態存在資料庫,不怕頁面刷新
  • 錯誤恢復:會話可暫停和恢復
  • 邏輯正確:只有完成所有測驗才標記詞卡完成

維護性改善

  • 業務邏輯集中:複習邏輯在後端統一管理
  • 前端簡化專注UI渲染和用戶互動
  • 測試友好API可獨立測試

用戶體驗優化

  • 進度條美化:分段式設計更直觀
  • 響應速度:減少前端複雜計算
  • 數據准確:實時同步學習進度

風險評估與應對

風險點

  1. 數據遷移風險:現有學習記錄可能需要轉換
  2. API性能風險頻繁API調用可能影響響應速度
  3. 向下兼容風險:可能影響現有功能

應對措施

  1. 分階段部署:先在測試環境驗證,再逐步上線
  2. 數據備份:重構前完整備份現有數據
  3. 性能監控實施API性能監控和優化
  4. 回滾方案:保留舊版本代碼,必要時快速回滾

成功標準

功能標準

  • 詞卡必須完成所有預定測驗才能標記為完成
  • 學習進度準確追蹤和顯示
  • 支持會話暫停和恢復
  • 分段式進度條正確顯示詞卡分佈

性能標準

  • API響應時間 < 500ms
  • 頁面載入時間 < 2s
  • 學習流程無明顯卡頓

用戶體驗標準

  • 學習流程直觀流暢
  • 進度顯示清晰準確
  • 錯誤處理友好

創建時間: 2025-09-26 負責人: Claude Code 預計完成: 2025-10-03 (7個工作天)