dramaling-vocab-learning/backend/DramaLing.Api/Controllers/SimplifiedFlashcardsControl...

256 lines
8.4 KiB
C#
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.

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using DramaLing.Api.Data;
using DramaLing.Api.Models.Entities;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
namespace DramaLing.Api.Controllers;
[ApiController]
[Route("api/flashcards-simple")]
[Authorize] // 恢復認證要求
public class SimplifiedFlashcardsController : ControllerBase
{
private readonly DramaLingDbContext _context;
private readonly ILogger<SimplifiedFlashcardsController> _logger;
public SimplifiedFlashcardsController(DramaLingDbContext context, ILogger<SimplifiedFlashcardsController> logger)
{
_context = context;
_logger = logger;
}
private Guid GetUserId()
{
var userIdString = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ??
User.FindFirst("sub")?.Value;
if (Guid.TryParse(userIdString, out var userId))
return userId;
// 如果解析失敗,記錄錯誤並拋出異常
_logger.LogError("Failed to extract valid user ID from token. Claims: {Claims}",
string.Join(", ", User.Claims.Select(c => $"{c.Type}={c.Value}")));
throw new UnauthorizedAccessException("Invalid user ID in token");
}
[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
.OrderByDescending(f => f.CreatedAt)
.Select(f => new
{
f.Id,
f.Word,
f.Translation,
f.Definition,
f.PartOfSpeech,
f.Pronunciation,
f.Example,
f.ExampleTranslation,
f.MasteryLevel,
f.TimesReviewed,
f.IsFavorite,
f.NextReviewDate,
f.DifficultyLevel,
f.CreatedAt,
f.UpdatedAt
// 移除 CardSet 屬性
})
.ToListAsync();
return Ok(new
{
Success = true,
Data = new
{
Flashcards = flashcards,
Count = flashcards.Count
}
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting flashcards for user");
return StatusCode(500, new { Success = false, Error = "Failed to load flashcards" });
}
}
[HttpPost]
public async Task<ActionResult> CreateFlashcard([FromBody] CreateSimpleFlashcardRequest request)
{
try
{
var userId = GetUserId();
// 檢測重複詞卡
var existing = await _context.Flashcards
.FirstOrDefaultAsync(f => f.UserId == userId &&
f.Word.ToLower() == request.Word.ToLower() &&
!f.IsArchived);
if (existing != null)
{
return Ok(new
{
Success = false,
Error = "詞卡已存在",
IsDuplicate = true,
ExistingCard = new
{
existing.Id,
existing.Word,
existing.Translation,
existing.CreatedAt
}
});
}
var flashcard = new Flashcard
{
Id = Guid.NewGuid(),
UserId = userId,
// 移除 CardSetId設為 Guid.Empty 或 null
CardSetId = Guid.Empty,
Word = request.Word,
Translation = request.Translation,
Definition = request.Definition ?? "",
PartOfSpeech = request.PartOfSpeech,
Pronunciation = request.Pronunciation,
Example = request.Example,
ExampleTranslation = request.ExampleTranslation,
MasteryLevel = 0,
TimesReviewed = 0,
IsFavorite = false,
NextReviewDate = DateTime.Today,
DifficultyLevel = "A2", // 預設等級
EasinessFactor = 2.5f,
IntervalDays = 1,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
_context.Flashcards.Add(flashcard);
await _context.SaveChangesAsync();
return Ok(new
{
Success = true,
Data = new
{
flashcard.Id,
flashcard.Word,
flashcard.Translation,
flashcard.Definition,
flashcard.CreatedAt
},
Message = "詞卡創建成功"
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating flashcard");
return StatusCode(500, new { Success = false, Error = "Failed to create 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移除 CardSetId
public class CreateSimpleFlashcardRequest
{
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; }
}