8.4 KiB
8.4 KiB
智能複習系統 - 後端功能規格書 (BFS)
目標讀者: 後端開發工程師、系統架構師 版本: 1.0 日期: 2025-09-25
🏗️ 系統架構
核心服務
┌─────────────────────┐
│ 復習記錄 API │
└─────────┬───────────┘
│
┌─────▼─────┐
│ 輸入驗證層 │
└─────┬─────┘
│
┌───────▼────────┐
│ SpacedRepetition │
│ Service │
│ ┌─────────────┐ │
│ │ 逾期檢測 │ │
│ │ 間隔計算 │ │
│ │ 熟悉度更新 │ │
│ └─────────────┘ │
└───────┬────────┘
│
┌─────▼─────┐
│ 數據持久化 │
└───────────┘
關鍵類別設計
SpacedRepetitionService
public class SpacedRepetitionService
{
public ReviewResult ProcessReview(ReviewRequest request)
{
// 1. 計算逾期天數 (明確時間基準)
var actualReviewDate = DateTime.Now.Date; // 復習行為當日
var overdueDays = (actualReviewDate - request.NextReviewDate.Date).Days;
// 2. 應用記憶衰減
var adjustedMastery = ApplyMemoryDecay(request.CurrentMastery, overdueDays);
// 3. 計算新間隔
var newInterval = CalculateNewInterval(
request.CurrentInterval,
request.IsCorrect,
request.ConfidenceLevel,
overdueDays
);
// 4. 更新基礎熟悉程度 (存入資料庫)
var newBaseMastery = CalculateMasteryLevel(
request.TimesCorrect + (request.IsCorrect ? 1 : 0),
request.TotalReviews + 1,
newInterval
);
return new ReviewResult
{
NewInterval = newInterval,
NextReviewDate = actualReviewDate.AddDays(newInterval), // 以復習當日為基準
BaseMasteryLevel = newBaseMastery, // 基礎熟悉度
CurrentMasteryLevel = newBaseMastery, // 剛復習完,兩者相等
IsOverdue = overdueDays > 0,
OverdueDays = Math.Max(0, overdueDays)
};
}
/// <summary>
/// 計算當前熟悉度 (實時計算,不存資料庫)
/// </summary>
public int CalculateCurrentMasteryLevel(Flashcard flashcard)
{
var daysSinceLastReview = (DateTime.Now.Date - flashcard.LastReviewDate.Date).Days;
// 如果沒有時間經過,返回基礎熟悉度
if (daysSinceLastReview <= 0)
return flashcard.BaseMasteryLevel;
// 應用記憶衰減
return ApplyMemoryDecay(flashcard.BaseMasteryLevel, daysSinceLastReview);
}
}
🔌 API 設計
POST /api/flashcards/{id}/review
請求格式
{
"isCorrect": boolean,
"confidenceLevel": number, // 1-5, 翻卡題必須
"questionType": "flipcard" | "multiple_choice" | "fill_blank"
}
響應格式
{
"success": true,
"data": {
"newInterval": 15,
"nextReviewDate": "2025-10-10",
"baseMasteryLevel": 65, // 基礎熟悉度 (存資料庫)
"currentMasteryLevel": 65, // 當前熟悉度 (實時計算)
"isOverdue": false,
"overdueDays": 0
}
}
GET /api/flashcards/{id}
響應格式
{
"success": true,
"data": {
"id": 123,
"word": "apple",
"definition": "蘋果",
"baseMasteryLevel": 75, // 基礎熟悉度 (資料庫值)
"currentMasteryLevel": 68, // 當前熟悉度 (考慮衰減)
"lastReviewDate": "2025-09-20",
"nextReviewDate": "2025-10-04",
"currentInterval": 14,
"timesCorrect": 8,
"totalReviews": 10,
"isOverdue": true,
"overdueDays": 1
}
}
批次查詢 API
GET /api/flashcards/batch?ids=1,2,3,4,5
{
"success": true,
"data": [
{
"id": 1,
"baseMasteryLevel": 75,
"currentMasteryLevel": 68,
"isOverdue": true,
"overdueDays": 1
},
// ... 更多詞卡
]
}
錯誤響應
{
"success": false,
"error": {
"code": "VALUE_OUT_OF_RANGE",
"message": "信心程度必須在 1-5 範圍內",
"field": "confidenceLevel"
}
}
🛡️ 安全與驗證
輸入驗證規則
public class ReviewRequestValidator : AbstractValidator<ReviewRequest>
{
public ReviewRequestValidator()
{
RuleFor(x => x.IsCorrect).NotNull();
RuleFor(x => x.ConfidenceLevel)
.InclusiveBetween(1, 5)
.When(x => x.QuestionType == "flipcard");
RuleFor(x => x.QuestionType)
.Must(BeValidQuestionType)
.WithMessage("questionType 必須是 flipcard, multiple_choice 或 fill_blank");
}
}
錯誤處理策略
- 4xx 錯誤: 客戶端輸入錯誤,返回詳細錯誤訊息
- 5xx 錯誤: 服務器錯誤,記錄日誌並返回通用錯誤訊息
- 資料庫錯誤: 重試機制,最多3次重試
💾 資料庫設計
資料表更新
-- 現有 Flashcards 表需要的欄位
ALTER TABLE Flashcards ADD COLUMN
LastReviewDate DATETIME, -- 上次實際復習日期
BaseMasteryLevel INT DEFAULT 0, -- 基礎熟悉度 (上次復習時的值)
OverdueCount INT DEFAULT 0, -- 逾期次數統計
ConsecutiveOverdue INT DEFAULT 0; -- 連續逾期次數
-- 注意: CurrentMasteryLevel 不存資料庫,透過 API 實時計算
索引優化
-- 提升查詢到期詞卡的性能
CREATE INDEX IX_Flashcards_NextReviewDate
ON Flashcards(NextReviewDate, UserId);
-- 提升逾期統計查詢性能
CREATE INDEX IX_Flashcards_OverdueStats
ON Flashcards(LastReviewDate, NextReviewDate);
⚙️ 配置管理
appsettings.json 配置
{
"SpacedRepetition": {
"GrowthFactors": {
"ShortTerm": 1.8,
"MediumTerm": 1.4,
"LongTerm": 1.2,
"VeryLongTerm": 1.1
},
"OverduePenalties": {
"Light": 0.9, // 1-3天
"Medium": 0.75, // 4-7天
"Heavy": 0.5, // 8-30天
"Extreme": 0.3 // >30天
},
"MemoryDecayRate": 0.05, // 每天5%衰減
"MaxInterval": 365
}
}
🔍 監控與日誌
關鍵指標監控
public class ReviewMetrics
{
[Counter("reviews_processed_total")]
public static readonly Counter ReviewsProcessed;
[Histogram("review_calculation_duration_ms")]
public static readonly Histogram CalculationDuration;
[Histogram("mastery_calculation_duration_ms")]
public static readonly Histogram MasteryCalculationDuration;
[Gauge("overdue_reviews_current")]
public static readonly Gauge OverdueReviews;
[Counter("mastery_calculations_total")]
public static readonly Counter MasteryCalculations;
}
日誌記錄
- INFO: 正常復習記錄
- WARN: 逾期復習、異常參數
- ERROR: 計算失敗、資料庫錯誤
🚀 部署需求
性能要求
- API 響應時間: P95 < 100ms
- 並發處理: 支援 1000+ 同時用戶
- 資料庫連線: 連線池最大 50 連線
環境配置
- .NET 8+ 運行環境
- SQLite/PostgreSQL 資料庫
- Memory/Redis 緩存 (可選)
部署檢查清單
- 資料庫遷移腳本執行
- 配置文件更新
- 監控指標接入
- 日誌收集配置
- 性能測試通過
🧪 測試策略
單元測試
[Test]
public void CalculateCurrentMasteryLevel_ShouldApplyDecay_WhenOverdue()
{
// Arrange
var flashcard = new Flashcard
{
BaseMasteryLevel = 80,
LastReviewDate = DateTime.Now.AddDays(-7)
};
// Act
var result = _service.CalculateCurrentMasteryLevel(flashcard);
// Assert
Assert.That(result, Is.LessThan(80)); // 應該有衰減
Assert.That(result, Is.GreaterThan(50)); // 不應該衰減太多
}
整合測試
- API 端點測試
- 資料庫整合測試
- 錯誤處理測試
性能測試
- 1000+ 並發用戶測試
- 大量詞卡批次處理測試
- 記憶體使用量監控
實施時間: 2-3個工作日 測試時間: 1個工作日 上線影響: 零停機時間部署