dramaling-vocab-learning/backend/DramaLing.Api/Controllers/FlashcardsController.cs

272 lines
8.6 KiB
C#

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using DramaLing.Api.Data;
using DramaLing.Api.Models.Entities;
using Microsoft.AspNetCore.Authorization;
namespace DramaLing.Api.Controllers;
[ApiController]
[Route("api/flashcards")]
[AllowAnonymous]
public class FlashcardsController : ControllerBase
{
private readonly DramaLingDbContext _context;
private readonly ILogger<FlashcardsController> _logger;
public FlashcardsController(
DramaLingDbContext context,
ILogger<FlashcardsController> logger)
{
_context = context;
_logger = logger;
}
private Guid GetUserId()
{
// 暫時使用固定測試用戶 ID
return Guid.Parse("00000000-0000-0000-0000-000000000001");
}
[HttpGet]
public async Task<ActionResult> GetFlashcards(
[FromQuery] string? search = null,
[FromQuery] bool favoritesOnly = false)
{
try
{
var userId = GetUserId();
var query = _context.Flashcards
.Where(f => f.UserId == userId && !f.IsArchived)
.AsQueryable();
// 搜尋篩選
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
.AsNoTracking()
.OrderByDescending(f => f.CreatedAt)
.Select(f => new
{
f.Id,
f.Word,
f.Translation,
f.Definition,
f.PartOfSpeech,
f.Pronunciation,
f.Example,
f.ExampleTranslation,
f.IsFavorite,
f.DifficultyLevel,
f.CreatedAt,
f.UpdatedAt
})
.ToListAsync();
return Ok(new
{
Success = true,
Data = new
{
Flashcards = flashcards,
Count = flashcards.Count
}
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting flashcards");
return StatusCode(500, new { Success = false, Error = "Failed to load flashcards" });
}
}
[HttpPost]
public async Task<ActionResult> CreateFlashcard([FromBody] CreateFlashcardRequest request)
{
try
{
var userId = GetUserId();
var flashcard = new Flashcard
{
Id = Guid.NewGuid(),
UserId = userId,
Word = request.Word,
Translation = request.Translation,
Definition = request.Definition ?? "",
PartOfSpeech = request.PartOfSpeech,
Pronunciation = request.Pronunciation,
Example = request.Example,
ExampleTranslation = request.ExampleTranslation,
DifficultyLevel = "A2", // 預設等級
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
_context.Flashcards.Add(flashcard);
await _context.SaveChangesAsync();
return Ok(new
{
Success = true,
Data = flashcard,
Message = "詞卡創建成功"
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating flashcard");
return StatusCode(500, new { Success = false, Error = "Failed to create flashcard" });
}
}
[HttpGet("{id}")]
public async Task<ActionResult> GetFlashcard(Guid id)
{
try
{
var userId = GetUserId();
var flashcard = await _context.Flashcards
.FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId);
if (flashcard == null)
{
return NotFound(new { Success = false, Error = "Flashcard not found" });
}
return Ok(new { Success = true, Data = flashcard });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting flashcard {FlashcardId}", id);
return StatusCode(500, new { Success = false, Error = "Failed to get flashcard" });
}
}
[HttpPut("{id}")]
public async Task<ActionResult> UpdateFlashcard(Guid id, [FromBody] CreateFlashcardRequest request)
{
try
{
var userId = GetUserId();
var flashcard = await _context.Flashcards
.FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId);
if (flashcard == null)
{
return NotFound(new { Success = false, Error = "Flashcard not found" });
}
// 更新詞卡資訊
flashcard.Word = request.Word;
flashcard.Translation = request.Translation;
flashcard.Definition = request.Definition ?? "";
flashcard.PartOfSpeech = request.PartOfSpeech;
flashcard.Pronunciation = request.Pronunciation;
flashcard.Example = request.Example;
flashcard.ExampleTranslation = request.ExampleTranslation;
flashcard.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return Ok(new
{
Success = true,
Data = flashcard,
Message = "詞卡更新成功"
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating flashcard {FlashcardId}", id);
return StatusCode(500, new { Success = false, Error = "Failed to update flashcard" });
}
}
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteFlashcard(Guid id)
{
try
{
var userId = GetUserId();
var flashcard = await _context.Flashcards
.FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId);
if (flashcard == null)
{
return NotFound(new { Success = false, Error = "Flashcard not found" });
}
_context.Flashcards.Remove(flashcard);
await _context.SaveChangesAsync();
return Ok(new { Success = true, Message = "詞卡已刪除" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting flashcard {FlashcardId}", id);
return StatusCode(500, new { Success = false, Error = "Failed to delete flashcard" });
}
}
[HttpPost("{id}/favorite")]
public async Task<ActionResult> ToggleFavorite(Guid id)
{
try
{
var userId = GetUserId();
var flashcard = await _context.Flashcards
.FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId);
if (flashcard == null)
{
return NotFound(new { Success = false, Error = "Flashcard not found" });
}
flashcard.IsFavorite = !flashcard.IsFavorite;
flashcard.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return Ok(new {
Success = true,
IsFavorite = flashcard.IsFavorite,
Message = flashcard.IsFavorite ? "已加入收藏" : "已取消收藏"
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error toggling favorite for flashcard {FlashcardId}", id);
return StatusCode(500, new { Success = false, Error = "Failed to toggle favorite" });
}
}
}
// DTO 類別
public class CreateFlashcardRequest
{
public string Word { get; set; } = string.Empty;
public string Translation { get; set; } = string.Empty;
public string Definition { get; set; } = string.Empty;
public string PartOfSpeech { get; set; } = string.Empty;
public string Pronunciation { get; set; } = string.Empty;
public string Example { get; set; } = string.Empty;
public string? ExampleTranslation { get; set; }
}