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

227 lines
6.6 KiB
C#

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/[controller]")]
[Authorize]
public class CardSetsController : ControllerBase
{
private readonly DramaLingDbContext _context;
public CardSetsController(DramaLingDbContext context)
{
_context = context;
}
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");
}
[HttpGet]
public async Task<ActionResult> GetCardSets()
{
try
{
var userId = GetUserId();
var cardSets = await _context.CardSets
.Where(cs => cs.UserId == userId)
.OrderByDescending(cs => cs.CreatedAt)
.Select(cs => new
{
cs.Id,
cs.Name,
cs.Description,
cs.Color,
cs.CardCount,
cs.CreatedAt,
cs.UpdatedAt,
// 計算進度 (簡化版)
Progress = cs.CardCount > 0 ?
_context.Flashcards
.Where(f => f.CardSetId == cs.Id)
.Average(f => (double?)f.MasteryLevel) ?? 0 : 0,
LastStudied = cs.UpdatedAt,
Tags = new string[] { } // Phase 1 簡化
})
.ToListAsync();
return Ok(new
{
Success = true,
Data = new { Sets = cardSets }
});
}
catch (UnauthorizedAccessException)
{
return Unauthorized(new { Success = false, Error = "Unauthorized" });
}
catch (Exception ex)
{
return StatusCode(500, new
{
Success = false,
Error = "Failed to fetch card sets",
Timestamp = DateTime.UtcNow
});
}
}
[HttpPost]
public async Task<ActionResult> CreateCardSet([FromBody] CreateCardSetRequest request)
{
try
{
var userId = GetUserId();
if (string.IsNullOrWhiteSpace(request.Name))
return BadRequest(new { Success = false, Error = "Name is required" });
if (request.Name.Length > 255)
return BadRequest(new { Success = false, Error = "Name must be less than 255 characters" });
var cardSet = new CardSet
{
Id = Guid.NewGuid(),
UserId = userId,
Name = request.Name.Trim(),
Description = request.Description?.Trim(),
Color = request.Color ?? "bg-blue-500"
};
_context.CardSets.Add(cardSet);
await _context.SaveChangesAsync();
return Ok(new
{
Success = true,
Data = cardSet,
Message = "Card set created successfully"
});
}
catch (UnauthorizedAccessException)
{
return Unauthorized(new { Success = false, Error = "Unauthorized" });
}
catch (Exception ex)
{
return StatusCode(500, new
{
Success = false,
Error = "Failed to create card set",
Timestamp = DateTime.UtcNow
});
}
}
[HttpPut("{id}")]
public async Task<ActionResult> UpdateCardSet(Guid id, [FromBody] UpdateCardSetRequest request)
{
try
{
var userId = GetUserId();
var cardSet = await _context.CardSets
.FirstOrDefaultAsync(cs => cs.Id == id && cs.UserId == userId);
if (cardSet == null)
return NotFound(new { Success = false, Error = "Card set not found" });
if (!string.IsNullOrEmpty(request.Name))
cardSet.Name = request.Name.Trim();
if (request.Description != null)
cardSet.Description = request.Description?.Trim();
if (!string.IsNullOrEmpty(request.Color))
cardSet.Color = request.Color;
cardSet.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return Ok(new
{
Success = true,
Data = cardSet,
Message = "Card set updated successfully"
});
}
catch (UnauthorizedAccessException)
{
return Unauthorized(new { Success = false, Error = "Unauthorized" });
}
catch (Exception ex)
{
return StatusCode(500, new
{
Success = false,
Error = "Failed to update card set",
Timestamp = DateTime.UtcNow
});
}
}
[HttpDelete("{id}")]
public async Task<ActionResult> DeleteCardSet(Guid id)
{
try
{
var userId = GetUserId();
var cardSet = await _context.CardSets
.Include(cs => cs.Flashcards)
.FirstOrDefaultAsync(cs => cs.Id == id && cs.UserId == userId);
if (cardSet == null)
return NotFound(new { Success = false, Error = "Card set not found" });
_context.CardSets.Remove(cardSet);
await _context.SaveChangesAsync();
return Ok(new
{
Success = true,
Message = "Card set deleted successfully"
});
}
catch (UnauthorizedAccessException)
{
return Unauthorized(new { Success = false, Error = "Unauthorized" });
}
catch (Exception ex)
{
return StatusCode(500, new
{
Success = false,
Error = "Failed to delete card set",
Timestamp = DateTime.UtcNow
});
}
}
}
// Request DTOs
public class CreateCardSetRequest
{
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public string? Color { get; set; }
}
public class UpdateCardSetRequest
{
public string? Name { get; set; }
public string? Description { get; set; }
public string? Color { get; set; }
}