dramaling-vocab-learning/docs/03_development/api/flashcard-management-api.md

945 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DramaLing 詞卡管理 API 規格書
## 1. API 概覽
### 1.1 基本資訊
- **基礎 URL**: `http://localhost:5008/api` (開發環境)
- **控制器**: `FlashcardsController`
- **路由前綴**: `/api/flashcards`
- **認證方式**: JWT Bearer Token (開發階段暫時關閉)
- **資料格式**: JSON (UTF-8)
### 1.2 架構依賴
> 📋 **技術架構參考文檔**
>
> 本 API 規格書依賴以下文檔,建議閱讀順序:
>
> **🏗️ 系統架構文檔**
> - [系統架構總覽](../../04_technical/system-architecture.md) - 了解整體架構設計
> - [後端架構詳細說明](../../04_technical/backend-architecture.md) - 了解 ASP.NET Core 架構細節
>
> **📋 需求規格文檔**
> - [詞卡管理功能產品需求規格](../../01_requirement/詞卡管理功能產品需求規格.md) - 了解功能需求和用戶故事
## 2. 資料模型定義
### 2.1 詞卡實體 (Flashcard Entity)
#### C# 實體模型
```csharp
public class Flashcard
{
// 基本識別
public Guid Id { get; set; }
public Guid UserId { get; set; }
public Guid? CardSetId { get; set; }
// 詞卡內容
[Required, MaxLength(255)]
public string Word { get; set; }
[Required]
public string Translation { get; set; }
[Required]
public string Definition { get; set; }
[MaxLength(50)]
public string? PartOfSpeech { get; set; }
[MaxLength(255)]
public string? Pronunciation { get; set; }
public string? Example { get; set; }
public string? ExampleTranslation { get; set; }
// SM-2 學習算法參數
public float EasinessFactor { get; set; } = 2.5f;
public int Repetitions { get; set; } = 0;
public int IntervalDays { get; set; } = 1;
public DateTime NextReviewDate { get; set; }
// 學習統計
[Range(0, 100)]
public int MasteryLevel { get; set; } = 0;
public int TimesReviewed { get; set; } = 0;
public int TimesCorrect { get; set; } = 0;
public DateTime? LastReviewedAt { get; set; }
// 狀態管理
public bool IsFavorite { get; set; } = false;
public bool IsArchived { get; set; } = false;
[MaxLength(10)]
public string? DifficultyLevel { get; set; } // A1-C2
// 時間戳記
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
```
#### TypeScript 前端型別定義
```typescript
interface Flashcard {
id: string;
word: string;
translation: string;
definition: string;
partOfSpeech: string;
pronunciation: string;
example: string;
exampleTranslation?: string;
masteryLevel: number; // 0-100
timesReviewed: number;
isFavorite: boolean;
nextReviewDate: string; // ISO Date
difficultyLevel: string; // A1, A2, B1, B2, C1, C2
createdAt: string; // ISO Date
updatedAt?: string; // ISO Date
}
interface CreateFlashcardRequest {
word: string;
translation: string;
definition: string;
pronunciation: string;
partOfSpeech: string;
example: string;
exampleTranslation?: string;
}
```
### 2.2 API 回應格式標準
#### 成功回應格式
```json
{
"success": true,
"data": {
// 實際資料內容
},
"message": "操作成功描述" // 可選
}
```
#### 錯誤回應格式
```json
{
"success": false,
"error": "錯誤描述",
"isDuplicate": true, // 特殊情況:重複資料
"existingCard": { /* 現有詞卡資料 */ } // 重複時的現有資料
}
```
## 3. API 端點規格
### 3.1 端點清單
| 方法 | 端點 | 描述 | 狀態 |
|------|------|------|------|
| GET | `/api/flashcards` | 取得詞卡列表 | ✅ 已實現 |
| GET | `/api/flashcards/{id}` | 取得單一詞卡 | ✅ 已實現 |
| POST | `/api/flashcards` | 創建新詞卡 | ✅ 已實現 |
| PUT | `/api/flashcards/{id}` | 更新詞卡 | ✅ 已實現 |
| DELETE | `/api/flashcards/{id}` | 刪除詞卡 | ✅ 已實現 |
| POST | `/api/flashcards/{id}/favorite` | 切換收藏狀態 | ✅ 已實現 |
### 3.2 詳細 API 規格
#### 📖 GET /api/flashcards
**功能**: 取得用戶的詞卡列表,支援搜尋和篩選
**查詢參數**:
```typescript
interface GetFlashcardsParams {
search?: string; // 搜尋關鍵字,搜尋範圍:詞彙、翻譯、定義
favoritesOnly?: boolean; // 僅顯示收藏詞卡 (預設: false)
}
```
**實際實現邏輯**:
```csharp
// 搜尋篩選邏輯
if (!string.IsNullOrEmpty(search))
{
query = query.Where(f =>
f.Word.Contains(search) ||
f.Translation.Contains(search) ||
(f.Definition != null && f.Definition.Contains(search)));
}
// 收藏篩選
if (favoritesOnly)
{
query = query.Where(f => f.IsFavorite);
}
// 排序:按創建時間降序
var flashcards = await query.OrderByDescending(f => f.CreatedAt).ToListAsync();
```
**成功回應**:
```json
{
"success": true,
"data": {
"flashcards": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"word": "sophisticated",
"translation": "精密的",
"definition": "Highly developed or complex",
"partOfSpeech": "adjective",
"pronunciation": "/səˈfɪstɪkeɪtɪd/",
"example": "A sophisticated system",
"exampleTranslation": "一個精密的系統",
"masteryLevel": 75,
"timesReviewed": 12,
"isFavorite": true,
"nextReviewDate": "2025-09-25T00:00:00Z",
"difficultyLevel": "C1",
"createdAt": "2025-09-20T08:30:00Z",
"updatedAt": "2025-09-24T10:15:00Z"
}
],
"count": 1
}
}
```
**請求範例**:
```bash
# 取得所有詞卡
curl "http://localhost:5008/api/flashcards"
# 搜尋包含 "sophisticated" 的詞卡
curl "http://localhost:5008/api/flashcards?search=sophisticated"
# 僅取得收藏詞卡
curl "http://localhost:5008/api/flashcards?favoritesOnly=true"
# 組合搜尋:搜尋收藏詞卡中包含 "精密" 的詞卡
curl "http://localhost:5008/api/flashcards?search=精密&favoritesOnly=true"
```
#### 📖 GET /api/flashcards/{id}
**功能**: 取得單一詞卡的完整資訊
**路徑參數**:
- `id`: 詞卡唯一識別碼 (GUID 格式)
**成功回應**:
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"word": "sophisticated",
// ... 完整詞卡資料,格式同列表 API
"createdAt": "2025-09-20T08:30:00Z",
"updatedAt": "2025-09-24T10:15:00Z"
}
}
```
**錯誤回應**:
```json
{
"success": false,
"error": "詞卡不存在"
}
```
#### ✏️ POST /api/flashcards
**功能**: 創建新的詞卡
**請求體**:
```json
{
"word": "elaborate",
"translation": "詳細說明",
"definition": "To explain in detail",
"pronunciation": "/ɪˈlæbərət/",
"partOfSpeech": "verb",
"example": "Please elaborate on your idea",
"exampleTranslation": "請詳細說明你的想法"
}
```
**實際實現邏輯**:
```csharp
// 1. 自動創建測試用戶 (開發階段)
var testUser = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);
if (testUser == null) {
// 自動創建測試用戶邏輯
}
// 2. 重複詞卡檢測
var existing = await _context.Flashcards
.FirstOrDefaultAsync(f => f.UserId == userId &&
f.Word.ToLower() == request.Word.ToLower() &&
!f.IsArchived);
// 3. 創建新詞卡
var flashcard = new Flashcard
{
Id = Guid.NewGuid(),
UserId = userId,
Word = request.Word,
Translation = request.Translation,
// ... 其他欄位
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
```
**成功回應**:
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"word": "elaborate",
// ... 完整創建的詞卡資料
"createdAt": "2025-09-24T10:30:00Z"
},
"message": "詞卡創建成功"
}
```
**重複詞卡回應**:
```json
{
"success": false,
"error": "詞卡已存在",
"isDuplicate": true,
"existingCard": {
"id": "existing-id",
"word": "elaborate",
// ... 現有詞卡資料
}
}
```
#### ✏️ PUT /api/flashcards/{id}
**功能**: 更新現有詞卡
**路徑參數**:
- `id`: 詞卡唯一識別碼 (GUID)
**請求體**: 與 POST 相同格式
**實際實現邏輯**:
```csharp
// 1. 查找現有詞卡
var flashcard = await _context.Flashcards
.FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId && !f.IsArchived);
if (flashcard == null)
{
return NotFound(new { Success = false, Error = "詞卡不存在" });
}
// 2. 更新欄位
flashcard.Word = request.Word;
flashcard.Translation = request.Translation;
// ... 更新其他欄位
flashcard.UpdatedAt = DateTime.UtcNow;
// 3. 保存變更
await _context.SaveChangesAsync();
```
**成功回應**:
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
// ... 更新後的完整詞卡資料
"updatedAt": "2025-09-24T10:35:00Z"
},
"message": "詞卡更新成功"
}
```
#### 🗑️ DELETE /api/flashcards/{id}
**功能**: 刪除詞卡 (軟刪除機制)
**路徑參數**:
- `id`: 詞卡唯一識別碼 (GUID)
**實際實現邏輯**:
```csharp
// 軟刪除:設定 IsArchived = true
var flashcard = await _context.Flashcards
.FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId && !f.IsArchived);
if (flashcard == null)
{
return NotFound(new { Success = false, Error = "詞卡不存在" });
}
flashcard.IsArchived = true;
flashcard.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
```
**成功回應**:
```json
{
"success": true,
"message": "詞卡已刪除"
}
```
#### ⭐ POST /api/flashcards/{id}/favorite
**功能**: 切換詞卡的收藏狀態
**路徑參數**:
- `id`: 詞卡唯一識別碼 (GUID)
**實際實現邏輯**:
```csharp
var flashcard = await _context.Flashcards
.FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId && !f.IsArchived);
if (flashcard == null)
{
return NotFound(new { Success = false, Error = "詞卡不存在" });
}
// 切換收藏狀態
flashcard.IsFavorite = !flashcard.IsFavorite;
flashcard.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
```
**成功回應**:
```json
{
"success": true,
"data": {
"isFavorite": true
},
"message": "已加入收藏"
}
```
## 4. 前端整合規格
### 4.1 FlashcardsService 類別
#### TypeScript 服務實現
```typescript
class FlashcardsService {
private readonly baseURL = `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5008'}/api`;
// 統一請求處理
private async makeRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const response = await fetch(`${this.baseURL}${endpoint}`, {
headers: {
'Content-Type': 'application/json',
...options.headers,
},
...options,
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Network error' }));
throw new Error(errorData.error || `HTTP ${response.status}`);
}
return response.json();
}
// API 方法實現
async getFlashcards(search?: string, favoritesOnly: boolean = false): Promise<ApiResponse<{flashcards: Flashcard[], count: number}>> {
const params = new URLSearchParams();
if (search) params.append('search', search);
if (favoritesOnly) params.append('favoritesOnly', 'true');
const queryString = params.toString();
const endpoint = `/flashcards${queryString ? `?${queryString}` : ''}`;
return await this.makeRequest<ApiResponse<{flashcards: Flashcard[], count: number}>>(endpoint);
}
async createFlashcard(data: CreateFlashcardRequest): Promise<ApiResponse<Flashcard>> {
return await this.makeRequest<ApiResponse<Flashcard>>('/flashcards', {
method: 'POST',
body: JSON.stringify(data),
});
}
async updateFlashcard(id: string, data: CreateFlashcardRequest): Promise<ApiResponse<Flashcard>> {
return await this.makeRequest<ApiResponse<Flashcard>>(`/flashcards/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
});
}
async deleteFlashcard(id: string): Promise<ApiResponse<void>> {
return await this.makeRequest<ApiResponse<void>>(`/flashcards/${id}`, {
method: 'DELETE',
});
}
async toggleFavorite(id: string): Promise<ApiResponse<void>> {
return await this.makeRequest<ApiResponse<void>>(`/flashcards/${id}/favorite`, {
method: 'POST',
});
}
}
export const flashcardsService = new FlashcardsService();
```
### 4.2 前端使用範例
#### 詞卡列表載入
```typescript
const loadFlashcards = async () => {
try {
setLoading(true);
const result = await flashcardsService.getFlashcards();
if (result.success && result.data) {
setFlashcards(result.data.flashcards);
} else {
setError(result.error || 'Failed to load flashcards');
}
} catch (err) {
setError('Failed to load flashcards');
} finally {
setLoading(false);
}
};
```
#### 詞卡保存 (含重複檢測)
```typescript
const handleSaveWord = async (word: string, analysis: any) => {
try {
const cardData = {
word: word,
translation: analysis.translation || '',
definition: analysis.definition || '',
pronunciation: analysis.pronunciation || `/${word}/`,
partOfSpeech: analysis.partOfSpeech || 'unknown',
example: `Example sentence with ${word}.`
};
const response = await flashcardsService.createFlashcard(cardData);
if (response.success) {
alert(`✅ 已成功將「${word}」保存到詞卡庫!`);
return { success: true };
} else if (response.error && response.error.includes('已存在')) {
alert(`⚠️ 詞卡「${word}」已經存在於詞卡庫中`);
return { success: false, error: 'duplicate' };
} else {
throw new Error(response.error || '保存失敗');
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '保存失敗';
alert(`❌ 保存詞卡失敗: ${errorMessage}`);
return { success: false, error: errorMessage };
}
};
```
## 5. 搜尋與篩選功能
### 5.1 後端搜尋實現
#### 支援的搜尋欄位
```csharp
// 目前實現的搜尋範圍
query = query.Where(f =>
f.Word.Contains(search) || // 詞彙本身
f.Translation.Contains(search) || // 中文翻譯
(f.Definition != null && f.Definition.Contains(search)) // 英文定義
);
// 未來可擴展的搜尋範圍
// f.Example.Contains(search) || // 例句內容
// f.ExampleTranslation.Contains(search) // 例句翻譯
```
#### 搜尋邏輯特性
- **大小寫敏感**: 目前使用 `Contains()` 進行大小寫敏感搜尋
- **部分匹配**: 支援關鍵字部分匹配
- **邏輯運算**: OR 邏輯 (任一欄位包含關鍵字即匹配)
### 5.2 前端搜尋與篩選
#### 即時搜尋實現
```typescript
// 前端即時篩選邏輯
const filteredCards = allCards.filter(card => {
// 基本文字搜尋
if (searchTerm) {
const searchLower = searchTerm.toLowerCase();
const matchesText =
card.word?.toLowerCase().includes(searchLower) ||
card.translation?.toLowerCase().includes(searchLower) ||
card.definition?.toLowerCase().includes(searchLower);
if (!matchesText) return false;
}
// CEFR 等級篩選
if (searchFilters.cefrLevel && card.difficultyLevel !== searchFilters.cefrLevel) {
return false;
}
// 詞性篩選
if (searchFilters.partOfSpeech && card.partOfSpeech !== searchFilters.partOfSpeech) {
return false;
}
// 掌握度篩選
if (searchFilters.masteryLevel) {
const mastery = card.masteryLevel || 0;
if (searchFilters.masteryLevel === 'high' && mastery < 80) return false;
if (searchFilters.masteryLevel === 'medium' && (mastery < 60 || mastery >= 80)) return false;
if (searchFilters.masteryLevel === 'low' && mastery >= 60) return false;
}
// 收藏篩選
if (searchFilters.onlyFavorites && !card.isFavorite) {
return false;
}
return true;
});
```
#### 進階篩選選項
```typescript
interface SearchFilters {
cefrLevel: string; // A1, A2, B1, B2, C1, C2
partOfSpeech: string; // noun, verb, adjective, adverb, preposition, interjection
masteryLevel: string; // high (80%+), medium (60-79%), low (<60%)
onlyFavorites: boolean; // 僅收藏詞卡
}
```
## 6. 錯誤處理機制
### 6.1 後端錯誤處理
#### 統一錯誤處理模式
```csharp
try
{
// API 邏輯
return Ok(new { Success = true, Data = result });
}
catch (DbUpdateException ex)
{
_logger.LogError(ex, "Database error during flashcard operation");
return StatusCode(500, new { Success = false, Error = "資料庫操作失敗" });
}
catch (ArgumentException ex)
{
_logger.LogWarning(ex, "Invalid argument for flashcard operation");
return BadRequest(new { Success = false, Error = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error during flashcard operation");
return StatusCode(500, new { Success = false, Error = "內部伺服器錯誤" });
}
```
#### 特殊情況處理
```csharp
// 重複詞卡檢測
if (existing != null)
{
return Ok(new
{
Success = false,
Error = "詞卡已存在",
IsDuplicate = true,
ExistingCard = new { /* 現有詞卡資料 */ }
});
}
// 詞卡不存在
if (flashcard == null)
{
return NotFound(new { Success = false, Error = "詞卡不存在" });
}
```
### 6.2 前端錯誤處理
#### API 服務層錯誤處理
```typescript
private async makeRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
try {
const response = await fetch(`${this.baseURL}${endpoint}`, options);
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Network error' }));
throw new Error(errorData.error || errorData.details || `HTTP ${response.status}`);
}
return response.json();
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
```
#### 用戶反饋機制
```typescript
// 成功操作反饋
if (response.success) {
alert(`✅ 已成功將「${word}」保存到詞卡庫!`);
return { success: true };
}
// 重複詞卡反饋
else if (response.error && response.error.includes('已存在')) {
alert(`⚠️ 詞卡「${word}」已經存在於詞卡庫中`);
return { success: false, error: 'duplicate' };
}
// 一般錯誤反饋
else {
alert(`❌ 保存詞卡失敗: ${response.error}`);
return { success: false, error: response.error };
}
```
## 7. 認證與授權
### 7.1 開發階段認證
#### 目前實現 (測試模式)
```csharp
[AllowAnonymous] // 暫時移除認證要求
public class FlashcardsController : ControllerBase
{
private Guid GetUserId()
{
// 使用固定測試用戶 ID
return Guid.Parse("00000000-0000-0000-0000-000000000001");
}
}
```
#### 自動測試用戶創建
```csharp
// 確保測試用戶存在
var testUser = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);
if (testUser == null)
{
testUser = new User
{
Id = userId,
Username = "testuser",
Email = "test@example.com",
DisplayName = "測試用戶",
SubscriptionType = "free",
EnglishLevel = "A2",
CreatedAt = DateTime.UtcNow
};
_context.Users.Add(testUser);
await _context.SaveChangesAsync();
}
```
### 7.2 生產環境認證 (未來啟用)
#### JWT Token 解析
```csharp
[Authorize] // 生產環境啟用
public class FlashcardsController : ControllerBase
{
private Guid GetUserId()
{
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");
}
}
```
## 8. 效能優化
### 8.1 資料庫查詢優化
#### 索引建議
```sql
-- 用戶詞卡查詢索引
CREATE INDEX IX_Flashcards_UserId_IsArchived ON Flashcards(UserId, IsArchived);
-- 搜尋優化索引
CREATE INDEX IX_Flashcards_Word ON Flashcards(Word);
CREATE INDEX IX_Flashcards_Translation ON Flashcards(Translation);
-- 收藏篩選索引
CREATE INDEX IX_Flashcards_IsFavorite ON Flashcards(IsFavorite);
-- 複合查詢索引
CREATE INDEX IX_Flashcards_UserId_IsFavorite_IsArchived ON Flashcards(UserId, IsFavorite, IsArchived);
```
#### 查詢優化技巧
```csharp
// 使用 AsNoTracking 提升查詢效能 (只讀查詢)
var flashcards = await query
.AsNoTracking()
.OrderByDescending(f => f.CreatedAt)
.ToListAsync();
// 選擇性載入欄位 (避免載入不必要的關聯資料)
.Select(f => new {
f.Id, f.Word, f.Translation, f.Definition,
// 僅選擇需要的欄位
})
```
### 8.2 快取策略 (未來實現)
#### 記憶體快取
```csharp
// 用戶詞卡列表快取 (30分鐘)
var cacheKey = $"flashcards:user:{userId}";
var cachedCards = await _cacheService.GetAsync<List<Flashcard>>(cacheKey);
if (cachedCards == null)
{
cachedCards = await LoadFlashcardsFromDatabase(userId);
await _cacheService.SetAsync(cacheKey, cachedCards, TimeSpan.FromMinutes(30));
}
```
#### 搜尋結果快取
```csharp
// 搜尋結果快取 (10分鐘)
var searchCacheKey = $"search:{userId}:{searchTerm}:{favoritesOnly}";
var cachedResults = await _cacheService.GetAsync<SearchResult>(searchCacheKey);
```
## 9. 測試規格
### 9.1 API 測試用例
#### 功能測試
```bash
# 測試詞卡創建
curl -X POST http://localhost:5008/api/flashcards \
-H "Content-Type: application/json" \
-d '{
"word": "test",
"translation": "測試",
"definition": "A trial or examination",
"pronunciation": "/test/",
"partOfSpeech": "noun",
"example": "This is a test sentence"
}'
# 測試搜尋功能
curl "http://localhost:5008/api/flashcards?search=test"
# 測試收藏功能
curl -X POST http://localhost:5008/api/flashcards/{id}/favorite
# 測試詞卡更新
curl -X PUT http://localhost:5008/api/flashcards/{id} \
-H "Content-Type: application/json" \
-d '{ /* 更新的詞卡資料 */ }'
# 測試詞卡刪除
curl -X DELETE http://localhost:5008/api/flashcards/{id}
```
#### 邊界條件測試
```bash
# 測試重複詞卡創建
curl -X POST http://localhost:5008/api/flashcards \
-d '{"word": "existing-word", ...}'
# 預期回應: success: false, isDuplicate: true
# 測試不存在的詞卡操作
curl http://localhost:5008/api/flashcards/non-existent-id
# 預期回應: 404 Not Found
# 測試空搜尋
curl "http://localhost:5008/api/flashcards?search="
# 預期回應: 返回所有詞卡
```
### 9.2 效能測試
#### 載入測試
```bash
# 測試大量詞卡載入 (1000+ 詞卡)
time curl "http://localhost:5008/api/flashcards"
# 預期: < 2秒
# 測試搜尋效能
time curl "http://localhost:5008/api/flashcards?search=sophisticated"
# 預期: < 300ms
```
## 10. 部署與監控
### 10.1 健康檢查
#### API 健康檢查端點
```csharp
// Program.cs 中配置
services.AddHealthChecks()
.AddDbContextCheck<DramaLingDbContext>();
app.MapHealthChecks("/health");
```
**健康檢查請求**:
```bash
curl http://localhost:5008/health
```
### 10.2 日誌監控
#### 結構化日誌
```csharp
_logger.LogInformation("Creating flashcard for user {UserId}, word: {Word}",
userId, request.Word);
_logger.LogError(ex, "Failed to create flashcard for user {UserId}", userId);
_logger.LogWarning("Duplicate flashcard creation attempt: {Word} for user {UserId}",
request.Word, userId);
```
#### 關鍵指標監控
- **API 響應時間**: 平均 < 200ms
- **成功率**: > 99.5%
- **重複詞卡檢測**: 準確率 100%
- **資料庫連接**: 健康狀態監控
---
**文檔版本**: v1.0
**建立日期**: 2025-09-24
**基於**: FlashcardsController.cs v1.0
**維護負責**: API 開發團隊
**更新頻率**: 控制器變更時同步更新
> 📋 **相關參考文檔**
>
> **📋 需求與規格**
> - [詞卡管理功能需求規格](../../01_requirement/詞卡管理功能產品需求規格.md) - 查看完整功能需求和用戶故事
>
> **🏗️ 技術架構**
> - [後端架構詳細說明](../../04_technical/backend-architecture.md) - 了解後端技術實現細節
> - [前端架構詳細說明](../../04_technical/frontend-architecture.md) - 了解前端整合方式