1184 lines
27 KiB
Markdown
1184 lines
27 KiB
Markdown
# DramaLing 後端開發計劃
|
||
|
||
## 1. 總體架構概述
|
||
|
||
### 1.1 技術棧確認 (已更新為 .NET Core)
|
||
- **API 框架**: ASP.NET Core Web API 8.0 ⚡ (已從 Next.js 14 API Routes 重寫)
|
||
- **資料庫**: PostgreSQL via Supabase (Entity Framework Core)
|
||
- **認證系統**: JWT + ASP.NET Core Identity (兼容 Supabase)
|
||
- **AI 服務**: Google Gemini API (.NET SDK)
|
||
- **檔案存儲**: Supabase Storage
|
||
- **部署平台**: Azure App Service / Railway (從 Vercel 遷移)
|
||
|
||
### 1.2 前端依賴對應
|
||
基於已實作的前端頁面,後端需要支援以下功能:
|
||
- `/app/dashboard/page.tsx` → 儀表板統計 API
|
||
- `/app/flashcards/page.tsx` → 詞卡管理 API + 智能檢測 API
|
||
- `/app/learn/page.tsx` → 學習系統 API + 錯誤回報 API
|
||
- `/app/generate/page.tsx` → AI 生成詞卡 API
|
||
|
||
## 2. 資料庫架構設計
|
||
|
||
### 2.1 完整 Schema 設計
|
||
|
||
```sql
|
||
-- =========================================
|
||
-- 用戶相關表
|
||
-- =========================================
|
||
|
||
-- 用戶基本信息表
|
||
CREATE TABLE users (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
email VARCHAR(255) UNIQUE NOT NULL,
|
||
display_name VARCHAR(100),
|
||
avatar_url TEXT,
|
||
subscription_type VARCHAR(20) DEFAULT 'free', -- 'free', 'premium'
|
||
subscription_expires_at TIMESTAMP,
|
||
preferences JSONB DEFAULT '{}', -- 用戶偏好設定
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 用戶學習設定表
|
||
CREATE TABLE user_settings (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
daily_goal INTEGER DEFAULT 20, -- 每日目標詞數
|
||
reminder_time TIME DEFAULT '09:00:00', -- 提醒時間
|
||
reminder_enabled BOOLEAN DEFAULT true,
|
||
difficulty_preference VARCHAR(20) DEFAULT 'balanced', -- 'conservative', 'balanced', 'aggressive'
|
||
auto_play_audio BOOLEAN DEFAULT true,
|
||
show_pronunciation BOOLEAN DEFAULT true,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- =========================================
|
||
-- 詞卡相關表
|
||
-- =========================================
|
||
|
||
-- 卡組表
|
||
CREATE TABLE card_sets (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
name VARCHAR(255) NOT NULL,
|
||
description TEXT,
|
||
color VARCHAR(50) DEFAULT 'bg-blue-500',
|
||
is_public BOOLEAN DEFAULT false,
|
||
card_count INTEGER DEFAULT 0,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 詞卡主表
|
||
CREATE TABLE flashcards (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
card_set_id UUID REFERENCES card_sets(id) ON DELETE CASCADE,
|
||
|
||
-- 基本詞卡信息
|
||
word VARCHAR(255) NOT NULL,
|
||
part_of_speech VARCHAR(50), -- 詞性
|
||
pronunciation_us VARCHAR(255), -- 美式發音
|
||
pronunciation_uk VARCHAR(255), -- 英式發音
|
||
translation TEXT NOT NULL, -- 中文翻譯
|
||
definition TEXT NOT NULL, -- 英文定義
|
||
difficulty_level VARCHAR(10), -- CEFR等級: A1, A2, B1, B2, C1, C2
|
||
|
||
-- 例句信息
|
||
original_example TEXT, -- 原始例句
|
||
original_example_translation TEXT, -- 原始例句翻譯
|
||
generated_example TEXT, -- AI生成例句
|
||
generated_example_translation TEXT, -- AI生成例句翻譯
|
||
example_image_url TEXT, -- 例句圖片URL
|
||
|
||
-- 學習相關
|
||
synonyms TEXT[], -- 同義詞陣列
|
||
antonyms TEXT[], -- 反義詞陣列
|
||
|
||
-- SM-2算法參數
|
||
easiness_factor FLOAT DEFAULT 2.5, -- 難度係數
|
||
repetitions INTEGER DEFAULT 0, -- 重複次數
|
||
interval_days INTEGER DEFAULT 1, -- 間隔天數
|
||
next_review_date DATE DEFAULT CURRENT_DATE,
|
||
|
||
-- 統計信息
|
||
times_reviewed INTEGER DEFAULT 0,
|
||
times_correct INTEGER DEFAULT 0,
|
||
last_reviewed_at TIMESTAMP,
|
||
|
||
-- 狀態
|
||
is_favorite BOOLEAN DEFAULT false,
|
||
is_archived BOOLEAN DEFAULT false,
|
||
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 標籤表
|
||
CREATE TABLE tags (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
name VARCHAR(100) NOT NULL,
|
||
color VARCHAR(50) DEFAULT '#3B82F6',
|
||
usage_count INTEGER DEFAULT 0,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
|
||
UNIQUE(user_id, name)
|
||
);
|
||
|
||
-- 詞卡標籤關聯表
|
||
CREATE TABLE flashcard_tags (
|
||
flashcard_id UUID REFERENCES flashcards(id) ON DELETE CASCADE,
|
||
tag_id UUID REFERENCES tags(id) ON DELETE CASCADE,
|
||
PRIMARY KEY (flashcard_id, tag_id)
|
||
);
|
||
|
||
-- =========================================
|
||
-- 學習系統表
|
||
-- =========================================
|
||
|
||
-- 學習會話表
|
||
CREATE TABLE study_sessions (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
session_type VARCHAR(50), -- 'flip', 'quiz', 'fill', 'listening', 'speaking'
|
||
started_at TIMESTAMP DEFAULT NOW(),
|
||
ended_at TIMESTAMP,
|
||
total_cards INTEGER DEFAULT 0,
|
||
correct_count INTEGER DEFAULT 0,
|
||
duration_seconds INTEGER DEFAULT 0
|
||
);
|
||
|
||
-- 學習記錄表
|
||
CREATE TABLE study_records (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
flashcard_id UUID REFERENCES flashcards(id) ON DELETE CASCADE,
|
||
session_id UUID REFERENCES study_sessions(id) ON DELETE CASCADE,
|
||
|
||
study_mode VARCHAR(50) NOT NULL, -- 'flip', 'quiz', 'fill', 'listening', 'speaking'
|
||
rating INTEGER NOT NULL, -- 1-5評分
|
||
response_time_ms INTEGER, -- 回應時間(毫秒)
|
||
user_answer TEXT, -- 用戶答案
|
||
is_correct BOOLEAN,
|
||
|
||
-- SM-2算法更新後的值
|
||
new_easiness_factor FLOAT,
|
||
new_interval_days INTEGER,
|
||
new_repetitions INTEGER,
|
||
|
||
studied_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- =========================================
|
||
-- AI生成相關表
|
||
-- =========================================
|
||
|
||
-- AI生成任務表
|
||
CREATE TABLE ai_generation_tasks (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
|
||
-- 輸入參數
|
||
input_text TEXT NOT NULL,
|
||
extraction_type VARCHAR(50) NOT NULL, -- 'vocabulary', 'smart'
|
||
card_count INTEGER DEFAULT 10,
|
||
|
||
-- 任務狀態
|
||
status VARCHAR(50) DEFAULT 'pending', -- 'pending', 'processing', 'completed', 'failed'
|
||
progress_percentage INTEGER DEFAULT 0,
|
||
|
||
-- 結果
|
||
generated_cards JSONB, -- 生成的詞卡JSON數據
|
||
error_message TEXT,
|
||
|
||
-- API使用統計
|
||
api_calls_used INTEGER DEFAULT 0,
|
||
cost_estimated DECIMAL(10,4) DEFAULT 0,
|
||
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
completed_at TIMESTAMP
|
||
);
|
||
|
||
-- =========================================
|
||
-- 錯誤回報系統表
|
||
-- =========================================
|
||
|
||
-- 錯誤回報表
|
||
CREATE TABLE error_reports (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
flashcard_id UUID REFERENCES flashcards(id) ON DELETE CASCADE,
|
||
|
||
-- 回報信息
|
||
report_type VARCHAR(100) NOT NULL, -- '發音錯誤', '翻譯不準確', '例句錯誤' 等
|
||
description TEXT,
|
||
study_mode VARCHAR(50), -- 回報來源的學習模式
|
||
|
||
-- 狀態追蹤
|
||
status VARCHAR(50) DEFAULT 'pending', -- 'pending', 'resolved', 'dismissed'
|
||
admin_notes TEXT,
|
||
resolved_at TIMESTAMP,
|
||
resolved_by UUID REFERENCES users(id),
|
||
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- =========================================
|
||
-- 統計相關表
|
||
-- =========================================
|
||
|
||
-- 每日學習統計表
|
||
CREATE TABLE daily_stats (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||
date DATE NOT NULL,
|
||
|
||
-- 學習統計
|
||
words_studied INTEGER DEFAULT 0,
|
||
words_correct INTEGER DEFAULT 0,
|
||
study_time_seconds INTEGER DEFAULT 0,
|
||
session_count INTEGER DEFAULT 0,
|
||
|
||
-- 生成統計
|
||
cards_generated INTEGER DEFAULT 0,
|
||
ai_api_calls INTEGER DEFAULT 0,
|
||
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
|
||
UNIQUE(user_id, date)
|
||
);
|
||
|
||
-- =========================================
|
||
-- 系統配置表
|
||
-- =========================================
|
||
|
||
-- 系統設定表
|
||
CREATE TABLE system_settings (
|
||
key VARCHAR(255) PRIMARY KEY,
|
||
value TEXT NOT NULL,
|
||
description TEXT,
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 插入預設系統設定
|
||
INSERT INTO system_settings (key, value, description) VALUES
|
||
('max_daily_ai_generations_free', '5', '免費用戶每日AI生成次數限制'),
|
||
('max_daily_ai_generations_premium', '50', '付費用戶每日AI生成次數限制'),
|
||
('max_cards_per_generation', '20', '單次生成最大詞卡數量'),
|
||
('sm2_initial_interval', '1', 'SM-2算法初始間隔天數'),
|
||
('sm2_max_interval', '365', 'SM-2算法最大間隔天數');
|
||
```
|
||
|
||
### 2.2 索引優化
|
||
|
||
```sql
|
||
-- 性能關鍵索引
|
||
CREATE INDEX idx_flashcards_user_next_review ON flashcards(user_id, next_review_date);
|
||
CREATE INDEX idx_flashcards_user_set ON flashcards(user_id, card_set_id);
|
||
CREATE INDEX idx_flashcards_word_search ON flashcards USING gin(to_tsvector('english', word || ' ' || translation));
|
||
CREATE INDEX idx_study_records_user_date ON study_records(user_id, studied_at);
|
||
CREATE INDEX idx_error_reports_status ON error_reports(status, created_at);
|
||
CREATE INDEX idx_daily_stats_user_date ON daily_stats(user_id, date);
|
||
```
|
||
|
||
### 2.3 Row Level Security (RLS) 設定
|
||
|
||
```sql
|
||
-- 啟用RLS
|
||
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE card_sets ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE flashcards ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE tags ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE study_sessions ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE study_records ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE error_reports ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE daily_stats ENABLE ROW LEVEL SECURITY;
|
||
|
||
-- 用戶只能存取自己的數據
|
||
CREATE POLICY "Users can only access their own data" ON users
|
||
FOR ALL USING (auth.uid() = id);
|
||
|
||
CREATE POLICY "Users can only access their own card sets" ON card_sets
|
||
FOR ALL USING (auth.uid() = user_id);
|
||
|
||
CREATE POLICY "Users can only access their own flashcards" ON flashcards
|
||
FOR ALL USING (auth.uid() = user_id);
|
||
|
||
CREATE POLICY "Users can only access their own tags" ON tags
|
||
FOR ALL USING (auth.uid() = user_id);
|
||
|
||
CREATE POLICY "Users can only access their own study data" ON study_sessions
|
||
FOR ALL USING (auth.uid() = user_id);
|
||
|
||
CREATE POLICY "Users can only access their own study records" ON study_records
|
||
FOR ALL USING (auth.uid() = user_id);
|
||
|
||
CREATE POLICY "Users can only access their own error reports" ON error_reports
|
||
FOR ALL USING (auth.uid() = user_id);
|
||
|
||
CREATE POLICY "Users can only access their own stats" ON daily_stats
|
||
FOR ALL USING (auth.uid() = user_id);
|
||
```
|
||
|
||
## 3. API 設計規格
|
||
|
||
### 3.1 認證系統 API
|
||
|
||
#### 3.1.1 基於 Supabase Auth
|
||
```typescript
|
||
// /app/api/auth/* 使用 Supabase Auth SDK
|
||
// 無需自建API,前端直接使用 Supabase 客戶端
|
||
|
||
// 需要的API: 用戶資料更新
|
||
PUT /api/users/profile
|
||
{
|
||
display_name?: string;
|
||
preferences?: Record<string, any>;
|
||
}
|
||
```
|
||
|
||
### 3.2 詞卡管理 API
|
||
|
||
#### 3.2.1 卡組管理
|
||
```typescript
|
||
// 獲取用戶所有卡組 (支援 /app/dashboard/page.tsx)
|
||
GET /api/card-sets
|
||
Response: {
|
||
sets: Array<{
|
||
id: string;
|
||
name: string;
|
||
description: string;
|
||
color: string;
|
||
card_count: number;
|
||
progress: number; // 計算得出
|
||
last_studied: string;
|
||
tags: string[];
|
||
}>
|
||
}
|
||
|
||
// 創建卡組
|
||
POST /api/card-sets
|
||
Body: {
|
||
name: string;
|
||
description?: string;
|
||
color?: string;
|
||
}
|
||
|
||
// 更新卡組
|
||
PUT /api/card-sets/:id
|
||
Body: {
|
||
name?: string;
|
||
description?: string;
|
||
color?: string;
|
||
}
|
||
|
||
// 刪除卡組
|
||
DELETE /api/card-sets/:id
|
||
```
|
||
|
||
#### 3.2.2 詞卡 CRUD (支援 /app/flashcards/page.tsx)
|
||
```typescript
|
||
// 獲取詞卡列表
|
||
GET /api/flashcards
|
||
Query: {
|
||
set_id?: string;
|
||
search?: string;
|
||
tags?: string[];
|
||
difficulty?: string;
|
||
favorites_only?: boolean;
|
||
limit?: number;
|
||
offset?: number;
|
||
}
|
||
Response: {
|
||
flashcards: Array<FlashcardType>;
|
||
total: number;
|
||
has_more: boolean;
|
||
}
|
||
|
||
// 創建詞卡
|
||
POST /api/flashcards
|
||
Body: {
|
||
card_set_id: string;
|
||
word: string;
|
||
translation: string;
|
||
definition: string;
|
||
// ... 其他欄位
|
||
}
|
||
|
||
// 更新詞卡
|
||
PUT /api/flashcards/:id
|
||
Body: Partial<FlashcardType>
|
||
|
||
// 刪除詞卡
|
||
DELETE /api/flashcards/:id
|
||
|
||
// 批量操作
|
||
POST /api/flashcards/batch
|
||
Body: {
|
||
action: 'delete' | 'move' | 'tag' | 'reset_progress';
|
||
flashcard_ids: string[];
|
||
target_set_id?: string; // for move
|
||
tag_ids?: string[]; // for tag
|
||
}
|
||
```
|
||
|
||
#### 3.2.3 標籤管理
|
||
```typescript
|
||
// 獲取標籤
|
||
GET /api/tags
|
||
Response: {
|
||
tags: Array<{
|
||
id: string;
|
||
name: string;
|
||
color: string;
|
||
usage_count: number;
|
||
}>
|
||
}
|
||
|
||
// 創建標籤
|
||
POST /api/tags
|
||
Body: {
|
||
name: string;
|
||
color?: string;
|
||
}
|
||
|
||
// 詞卡標籤關聯
|
||
POST /api/flashcards/:id/tags
|
||
Body: {
|
||
tag_ids: string[];
|
||
}
|
||
```
|
||
|
||
### 3.3 AI 生成 API (支援 /app/generate/page.tsx)
|
||
|
||
#### 3.3.1 生成任務管理
|
||
```typescript
|
||
// 創建生成任務
|
||
POST /api/ai/generate
|
||
Body: {
|
||
input_text: string;
|
||
extraction_type: 'vocabulary' | 'smart';
|
||
card_count: number; // 5-20
|
||
}
|
||
Response: {
|
||
task_id: string;
|
||
status: 'pending';
|
||
}
|
||
|
||
// 查詢生成進度
|
||
GET /api/ai/generate/:task_id
|
||
Response: {
|
||
task_id: string;
|
||
status: 'pending' | 'processing' | 'completed' | 'failed';
|
||
progress_percentage: number;
|
||
generated_cards?: Array<GeneratedCardType>;
|
||
error_message?: string;
|
||
}
|
||
|
||
// 保存生成的詞卡
|
||
POST /api/ai/generate/:task_id/save
|
||
Body: {
|
||
card_set_id: string;
|
||
selected_cards: string[]; // 生成卡片的ID
|
||
}
|
||
```
|
||
|
||
#### 3.3.2 智能檢測 API (支援詞卡錯誤檢測)
|
||
```typescript
|
||
// 檢測單一詞卡
|
||
POST /api/ai/validate-card
|
||
Body: {
|
||
flashcard_id: string;
|
||
error_report_id?: string; // 如果來自錯誤回報
|
||
}
|
||
Response: {
|
||
issues: Array<{
|
||
field: string;
|
||
original: string;
|
||
corrected: string;
|
||
reason: string;
|
||
}>;
|
||
suggestions: string[];
|
||
}
|
||
|
||
// 批量檢測
|
||
POST /api/ai/validate-cards
|
||
Body: {
|
||
flashcard_ids: string[];
|
||
}
|
||
Response: {
|
||
results: Array<{
|
||
flashcard_id: string;
|
||
issues: Array<ValidationIssue>;
|
||
}>;
|
||
}
|
||
|
||
// 應用修正
|
||
POST /api/ai/apply-corrections
|
||
Body: {
|
||
flashcard_id: string;
|
||
corrections: Array<{
|
||
field: string;
|
||
new_value: string;
|
||
}>;
|
||
}
|
||
```
|
||
|
||
### 3.4 學習系統 API (支援 /app/learn/page.tsx)
|
||
|
||
#### 3.4.1 學習會話管理
|
||
```typescript
|
||
// 獲取待複習詞卡
|
||
GET /api/study/due-cards
|
||
Query: {
|
||
limit?: number; // 預設50
|
||
mode?: 'flip' | 'quiz' | 'fill' | 'listening' | 'speaking';
|
||
}
|
||
Response: {
|
||
cards: Array<FlashcardType>;
|
||
total_due: number;
|
||
}
|
||
|
||
// 開始學習會話
|
||
POST /api/study/sessions
|
||
Body: {
|
||
mode: string;
|
||
card_ids: string[];
|
||
}
|
||
Response: {
|
||
session_id: string;
|
||
cards: Array<FlashcardType>;
|
||
}
|
||
|
||
// 記錄學習結果
|
||
POST /api/study/sessions/:session_id/record
|
||
Body: {
|
||
flashcard_id: string;
|
||
rating: number; // 1-5
|
||
response_time_ms: number;
|
||
user_answer?: string;
|
||
is_correct: boolean;
|
||
}
|
||
Response: {
|
||
next_review_date: string;
|
||
new_interval_days: number;
|
||
}
|
||
|
||
// 結束學習會話
|
||
POST /api/study/sessions/:session_id/complete
|
||
Body: {
|
||
duration_seconds: number;
|
||
}
|
||
```
|
||
|
||
#### 3.4.2 錯誤回報 API
|
||
```typescript
|
||
// 提交錯誤回報
|
||
POST /api/error-reports
|
||
Body: {
|
||
flashcard_id: string;
|
||
report_type: string;
|
||
description?: string;
|
||
study_mode: string;
|
||
}
|
||
|
||
// 獲取錯誤回報列表
|
||
GET /api/error-reports
|
||
Query: {
|
||
status?: 'pending' | 'resolved' | 'dismissed';
|
||
limit?: number;
|
||
offset?: number;
|
||
}
|
||
Response: {
|
||
reports: Array<{
|
||
id: string;
|
||
flashcard: FlashcardType;
|
||
report_type: string;
|
||
description: string;
|
||
status: string;
|
||
created_at: string;
|
||
}>;
|
||
total: number;
|
||
}
|
||
|
||
// 處理錯誤回報
|
||
PUT /api/error-reports/:id
|
||
Body: {
|
||
status: 'resolved' | 'dismissed';
|
||
admin_notes?: string;
|
||
}
|
||
```
|
||
|
||
### 3.5 統計分析 API (支援 /app/dashboard/page.tsx)
|
||
|
||
#### 3.5.1 儀表板統計
|
||
```typescript
|
||
// 獲取儀表板概覽
|
||
GET /api/stats/dashboard
|
||
Response: {
|
||
total_words: number;
|
||
words_today: number;
|
||
streak_days: number;
|
||
accuracy_percentage: number;
|
||
today_review_count: number;
|
||
completed_today: number;
|
||
recent_words: Array<{
|
||
word: string;
|
||
translation: string;
|
||
status: 'learned' | 'learning' | 'new';
|
||
}>;
|
||
}
|
||
|
||
// 獲取學習趨勢
|
||
GET /api/stats/trends
|
||
Query: {
|
||
period: 'week' | 'month' | 'year';
|
||
}
|
||
Response: {
|
||
daily_counts: Array<{
|
||
date: string;
|
||
words_studied: number;
|
||
words_correct: number;
|
||
study_time_seconds: number;
|
||
}>;
|
||
weekly_stats: {
|
||
current_week: number;
|
||
previous_week: number;
|
||
growth_percentage: number;
|
||
};
|
||
}
|
||
|
||
// 獲取詳細統計
|
||
GET /api/stats/detailed
|
||
Response: {
|
||
by_difficulty: Record<string, number>;
|
||
by_category: Record<string, number>;
|
||
learning_curve: Array<{
|
||
date: string;
|
||
accuracy: number;
|
||
}>;
|
||
top_mistakes: Array<{
|
||
word: string;
|
||
error_count: number;
|
||
}>;
|
||
}
|
||
```
|
||
|
||
### 3.6 用戶設定 API
|
||
```typescript
|
||
// 獲取用戶設定
|
||
GET /api/users/settings
|
||
Response: UserSettingsType
|
||
|
||
// 更新用戶設定
|
||
PUT /api/users/settings
|
||
Body: Partial<UserSettingsType>
|
||
|
||
// 獲取使用配額
|
||
GET /api/users/usage
|
||
Response: {
|
||
current_month: {
|
||
ai_generations: number;
|
||
ai_generations_limit: number;
|
||
cards_generated: number;
|
||
};
|
||
subscription: {
|
||
type: 'free' | 'premium';
|
||
expires_at?: string;
|
||
features: string[];
|
||
};
|
||
}
|
||
```
|
||
|
||
## 4. AI 服務整合
|
||
|
||
### 4.1 Google Gemini API 整合
|
||
|
||
#### 4.1.1 詞卡生成服務
|
||
```typescript
|
||
// /lib/ai/gemini-generator.ts
|
||
export class GeminiCardGenerator {
|
||
async generateCards(params: {
|
||
inputText: string;
|
||
extractionType: 'vocabulary' | 'smart';
|
||
cardCount: number;
|
||
userLevel?: string;
|
||
}): Promise<GeneratedCard[]> {
|
||
// 實作 Gemini API 調用
|
||
// 使用不同的 prompt 模板
|
||
// 處理 API 回應和錯誤
|
||
}
|
||
|
||
async validateCard(card: FlashcardType): Promise<ValidationResult> {
|
||
// 智能檢測詞卡內容
|
||
// 返回修正建議
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4.1.2 Prompt 模板設計
|
||
```typescript
|
||
// /lib/ai/prompts.ts
|
||
export const CARD_GENERATION_PROMPTS = {
|
||
vocabulary: `
|
||
從以下英文文本中萃取重要詞彙,為每個詞彙生成詞卡:
|
||
|
||
輸入文本:{input_text}
|
||
目標數量:{card_count}
|
||
|
||
請按照以下JSON格式回應:
|
||
{
|
||
"cards": [
|
||
{
|
||
"word": "string",
|
||
"part_of_speech": "string",
|
||
"pronunciation_us": "string",
|
||
"pronunciation_uk": "string",
|
||
"translation": "string",
|
||
"definition": "string (A1-A2 level)",
|
||
"synonyms": ["string"],
|
||
"antonyms": ["string"],
|
||
"difficulty_level": "A1|A2|B1|B2|C1|C2",
|
||
"generated_example": "string",
|
||
"generated_example_translation": "string"
|
||
}
|
||
]
|
||
}
|
||
`,
|
||
|
||
smart: `
|
||
分析以下英文文本,識別片語、俚語和常用表達:
|
||
|
||
輸入文本:{input_text}
|
||
目標數量:{card_count}
|
||
|
||
重點關注:
|
||
1. 片語和俚語
|
||
2. 文化相關表達
|
||
3. 語境特定用法
|
||
|
||
請按照相同JSON格式回應...
|
||
`,
|
||
|
||
validation: `
|
||
檢查以下詞卡內容的準確性:
|
||
|
||
詞卡信息:{card_data}
|
||
錯誤回報:{error_report}
|
||
|
||
請指出任何錯誤並提供修正建議:
|
||
{
|
||
"issues": [
|
||
{
|
||
"field": "string",
|
||
"original": "string",
|
||
"corrected": "string",
|
||
"reason": "string"
|
||
}
|
||
],
|
||
"suggestions": ["string"]
|
||
}
|
||
`
|
||
};
|
||
```
|
||
|
||
### 4.2 API 配額管理
|
||
```typescript
|
||
// /lib/ai/quota-manager.ts
|
||
export class APIQuotaManager {
|
||
async checkQuota(userId: string): Promise<{
|
||
canGenerate: boolean;
|
||
remaining: number;
|
||
resetTime: Date;
|
||
}> {
|
||
// 檢查用戶配額
|
||
}
|
||
|
||
async consumeQuota(userId: string, cost: number): Promise<void> {
|
||
// 消耗配額
|
||
}
|
||
}
|
||
```
|
||
|
||
## 5. SM-2 間隔重複算法實現
|
||
|
||
### 5.1 算法實現
|
||
```typescript
|
||
// /lib/learning/sm2-algorithm.ts
|
||
export interface SM2Result {
|
||
easiness_factor: number;
|
||
repetitions: number;
|
||
interval_days: number;
|
||
next_review_date: Date;
|
||
}
|
||
|
||
export class SM2Algorithm {
|
||
calculate(params: {
|
||
quality: number; // 1-5 評分
|
||
easiness_factor: number;
|
||
repetitions: number;
|
||
interval_days: number;
|
||
}): SM2Result {
|
||
let { quality, easiness_factor, repetitions, interval_days } = params;
|
||
|
||
// SM-2 算法實現
|
||
if (quality >= 3) {
|
||
if (repetitions === 0) {
|
||
interval_days = 1;
|
||
} else if (repetitions === 1) {
|
||
interval_days = 6;
|
||
} else {
|
||
interval_days = Math.round(interval_days * easiness_factor);
|
||
}
|
||
repetitions += 1;
|
||
} else {
|
||
repetitions = 0;
|
||
interval_days = 1;
|
||
}
|
||
|
||
// 更新難度係數
|
||
easiness_factor = Math.max(1.3,
|
||
easiness_factor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02))
|
||
);
|
||
|
||
// 計算下次複習日期
|
||
const next_review_date = new Date();
|
||
next_review_date.setDate(next_review_date.getDate() + interval_days);
|
||
|
||
return {
|
||
easiness_factor,
|
||
repetitions,
|
||
interval_days,
|
||
next_review_date
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.2 複習排程服務
|
||
```typescript
|
||
// /lib/learning/review-scheduler.ts
|
||
export class ReviewScheduler {
|
||
async getDueCards(userId: string, limit: number = 50): Promise<FlashcardType[]> {
|
||
// 獲取到期詞卡
|
||
// 按優先級排序
|
||
}
|
||
|
||
async scheduleReview(flashcardId: string, result: SM2Result): Promise<void> {
|
||
// 更新詞卡複習排程
|
||
}
|
||
}
|
||
```
|
||
|
||
## 6. 錯誤處理與日誌
|
||
|
||
### 6.1 統一錯誤處理
|
||
```typescript
|
||
// /lib/api/error-handler.ts
|
||
export interface APIError {
|
||
code: string;
|
||
message: string;
|
||
details?: any;
|
||
timestamp: Date;
|
||
}
|
||
|
||
export const errorCodes = {
|
||
// 認證錯誤
|
||
UNAUTHORIZED: 'AUTH_001',
|
||
INVALID_TOKEN: 'AUTH_002',
|
||
|
||
// 資源錯誤
|
||
NOT_FOUND: 'RESOURCE_001',
|
||
ALREADY_EXISTS: 'RESOURCE_002',
|
||
|
||
// AI服務錯誤
|
||
AI_QUOTA_EXCEEDED: 'AI_001',
|
||
AI_SERVICE_ERROR: 'AI_002',
|
||
|
||
// 驗證錯誤
|
||
VALIDATION_ERROR: 'VALIDATION_001',
|
||
|
||
// 系統錯誤
|
||
INTERNAL_ERROR: 'SYSTEM_001',
|
||
DATABASE_ERROR: 'SYSTEM_002'
|
||
};
|
||
|
||
export function handleAPIError(error: unknown): Response {
|
||
// 統一錯誤處理邏輯
|
||
}
|
||
```
|
||
|
||
### 6.2 結構化日誌
|
||
```typescript
|
||
// /lib/logging/logger.ts
|
||
export class Logger {
|
||
static info(message: string, metadata?: Record<string, any>) {
|
||
console.log(JSON.stringify({
|
||
level: 'info',
|
||
message,
|
||
metadata,
|
||
timestamp: new Date().toISOString()
|
||
}));
|
||
}
|
||
|
||
static error(message: string, error?: Error, metadata?: Record<string, any>) {
|
||
console.error(JSON.stringify({
|
||
level: 'error',
|
||
message,
|
||
error: error?.stack,
|
||
metadata,
|
||
timestamp: new Date().toISOString()
|
||
}));
|
||
}
|
||
}
|
||
```
|
||
|
||
## 7. 快取策略
|
||
|
||
### 7.1 前端快取 (TanStack Query)
|
||
```typescript
|
||
// 前端查詢配置
|
||
export const queryKeys = {
|
||
flashcards: (userId: string, filters?: any) => ['flashcards', userId, filters],
|
||
cardSets: (userId: string) => ['card-sets', userId],
|
||
dueCards: (userId: string) => ['due-cards', userId],
|
||
stats: (userId: string, period?: string) => ['stats', userId, period]
|
||
};
|
||
|
||
// 快取時間配置
|
||
export const cacheConfig = {
|
||
flashcards: 5 * 60 * 1000, // 5分鐘
|
||
cardSets: 10 * 60 * 1000, // 10分鐘
|
||
dueCards: 1 * 60 * 1000, // 1分鐘
|
||
stats: 15 * 60 * 1000 // 15分鐘
|
||
};
|
||
```
|
||
|
||
### 7.2 API層快取
|
||
```typescript
|
||
// /lib/cache/redis-cache.ts (未來考慮)
|
||
export class CacheManager {
|
||
async get<T>(key: string): Promise<T | null> {
|
||
// Redis 快取獲取
|
||
}
|
||
|
||
async set<T>(key: string, value: T, ttl: number): Promise<void> {
|
||
// Redis 快取設置
|
||
}
|
||
|
||
async invalidate(pattern: string): Promise<void> {
|
||
// 快取失效
|
||
}
|
||
}
|
||
```
|
||
|
||
## 8. 安全措施
|
||
|
||
### 8.1 Rate Limiting
|
||
```typescript
|
||
// /lib/security/rate-limiter.ts
|
||
export class RateLimiter {
|
||
// API 限流配置
|
||
static limits = {
|
||
'api/ai/generate': { requests: 10, window: 60 * 60 }, // 每小時10次
|
||
'api/flashcards': { requests: 100, window: 60 }, // 每分鐘100次
|
||
'api/study/record': { requests: 200, window: 60 } // 每分鐘200次
|
||
};
|
||
}
|
||
```
|
||
|
||
### 8.2 輸入驗證
|
||
```typescript
|
||
// /lib/validation/schemas.ts
|
||
import { z } from 'zod';
|
||
|
||
export const flashcardSchema = z.object({
|
||
word: z.string().min(1).max(255),
|
||
translation: z.string().min(1),
|
||
definition: z.string().min(1),
|
||
card_set_id: z.string().uuid(),
|
||
difficulty_level: z.enum(['A1', 'A2', 'B1', 'B2', 'C1', 'C2']).optional()
|
||
});
|
||
|
||
export const aiGenerationSchema = z.object({
|
||
input_text: z.string().min(10).max(5000),
|
||
extraction_type: z.enum(['vocabulary', 'smart']),
|
||
card_count: z.number().min(5).max(20)
|
||
});
|
||
```
|
||
|
||
## 9. 測試策略
|
||
|
||
### 9.1 API 測試
|
||
```typescript
|
||
// __tests__/api/flashcards.test.ts
|
||
describe('/api/flashcards', () => {
|
||
it('should create flashcard with valid data', async () => {
|
||
// 測試詞卡創建
|
||
});
|
||
|
||
it('should return 401 for unauthenticated requests', async () => {
|
||
// 測試認證
|
||
});
|
||
|
||
it('should validate input data', async () => {
|
||
// 測試輸入驗證
|
||
});
|
||
});
|
||
```
|
||
|
||
### 9.2 AI 服務測試
|
||
```typescript
|
||
// __tests__/lib/ai/gemini-generator.test.ts
|
||
describe('GeminiCardGenerator', () => {
|
||
it('should generate valid flashcards from text', async () => {
|
||
// 測試AI生成
|
||
});
|
||
|
||
it('should handle API errors gracefully', async () => {
|
||
// 測試錯誤處理
|
||
});
|
||
});
|
||
```
|
||
|
||
## 10. 部署配置
|
||
|
||
### 10.1 環境變數配置
|
||
```env
|
||
# Supabase
|
||
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
|
||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
|
||
|
||
# AI 服務
|
||
GEMINI_API_KEY=your_gemini_api_key
|
||
|
||
# 應用設定
|
||
NEXT_PUBLIC_APP_URL=https://your-domain.com
|
||
NODE_ENV=production
|
||
|
||
# 可選:Redis (Upstash)
|
||
REDIS_URL=your_redis_url
|
||
REDIS_TOKEN=your_redis_token
|
||
```
|
||
|
||
### 10.2 Vercel 部署設定
|
||
```json
|
||
// vercel.json
|
||
{
|
||
"functions": {
|
||
"app/api/**": {
|
||
"maxDuration": 30
|
||
}
|
||
},
|
||
"env": {
|
||
"NODE_ENV": "production"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 11. 開發階段規劃 (.NET Core 重寫版)
|
||
|
||
### ⚡ **重寫完成狀態**
|
||
**原 Next.js API Routes**: 已完全移除 ✅
|
||
**新 .NET Core API**: 開發中 🚀
|
||
|
||
### Phase 1: .NET Core 基礎建設 (第1週)
|
||
- [x] 清理 Next.js API 代碼
|
||
- [ ] 安裝 .NET 8 SDK
|
||
- [ ] 建立 ASP.NET Core Web API 專案
|
||
- [ ] Entity Framework Core 配置
|
||
- [ ] 基礎 CRUD API (卡組、詞卡)
|
||
|
||
### Phase 2: 核心業務邏輯 (第2週)
|
||
- [ ] SM-2 算法 C# 實現
|
||
- [ ] 學習系統 API (會話管理、記錄)
|
||
- [ ] JWT 認證系統
|
||
- [ ] 統一錯誤處理和驗證
|
||
|
||
### Phase 3: AI 和進階功能 (第3週)
|
||
- [ ] Google Gemini AI 服務整合
|
||
- [ ] 智能檢測和批量操作 API
|
||
- [ ] 統計分析系統
|
||
- [ ] 錯誤回報系統
|
||
|
||
### Phase 4: 前端整合和優化 (第4週)
|
||
- [ ] 前端 API 調用更新
|
||
- [ ] 認證流程調整
|
||
- [ ] 性能優化和快取
|
||
- [ ] 部署配置 (Azure/Railway)
|
||
|
||
**架構優勢**:
|
||
- 🚀 性能提升 30-50%
|
||
- 🛡️ 強型別安全
|
||
- 🏢 企業級架構
|
||
- 🔧 更好的開發體驗
|
||
|
||
## 12. API 回應格式標準
|
||
|
||
### 12.1 成功回應格式
|
||
```typescript
|
||
interface SuccessResponse<T> {
|
||
success: true;
|
||
data: T;
|
||
message?: string;
|
||
meta?: {
|
||
total?: number;
|
||
page?: number;
|
||
limit?: number;
|
||
};
|
||
}
|
||
```
|
||
|
||
### 12.2 錯誤回應格式
|
||
```typescript
|
||
interface ErrorResponse {
|
||
success: false;
|
||
error: {
|
||
code: string;
|
||
message: string;
|
||
details?: any;
|
||
};
|
||
timestamp: string;
|
||
}
|
||
```
|
||
|
||
### 12.3 分頁回應格式
|
||
```typescript
|
||
interface PaginatedResponse<T> {
|
||
success: true;
|
||
data: T[];
|
||
pagination: {
|
||
total: number;
|
||
page: number;
|
||
limit: number;
|
||
total_pages: number;
|
||
has_next: boolean;
|
||
has_prev: boolean;
|
||
};
|
||
}
|
||
```
|
||
|
||
## 13. 監控與維護
|
||
|
||
### 13.1 關鍵指標監控
|
||
- API 回應時間 (< 500ms 目標)
|
||
- 錯誤率 (< 1% 目標)
|
||
- AI API 配額使用率
|
||
- 資料庫連接池狀態
|
||
- 用戶活躍度
|
||
|
||
### 13.2 警報設定
|
||
- API 錯誤率超過 5%
|
||
- 回應時間超過 2 秒
|
||
- AI API 配額使用超過 80%
|
||
- 資料庫連接數超過閾值
|
||
|
||
### 13.3 備份策略
|
||
- 每日自動資料庫備份
|
||
- 用戶數據導出功能
|
||
- 災難恢復計劃
|
||
|
||
這個後端開發計劃完全基於已實作的前端功能,確保 API 能夠支援所有前端頁面的需求,包括儀表板統計、詞卡管理、學習系統、AI 生成和錯誤回報等核心功能。 |