Handler.cs 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. using System.Text.RegularExpressions;
  2. using Application.Abstractions.Data;
  3. using Application.Abstractions.Messaging;
  4. using Microsoft.EntityFrameworkCore;
  5. using SharedKernel.Results;
  6. namespace Application.Features.Api.Studio.IssueDonationCode;
  7. internal sealed class Handler(IAppDbContext db) : ICommandHandler<Command, Result<Response>>
  8. {
  9. public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
  10. {
  11. if (string.IsNullOrWhiteSpace(request.Code))
  12. {
  13. return Result.Failure<Response>(Error.Problem("DonationCode.Required", "후원 코드를 입력해 주세요."));
  14. }
  15. var trimmed = request.Code.Trim();
  16. if (!Regex.IsMatch(trimmed, "^[A-Za-z0-9]{4,7}$"))
  17. {
  18. return Result.Failure<Response>(Error.Problem("DonationCode.InvalidFormat", "후원 코드는 4~7자 영문/숫자만 가능합니다."));
  19. }
  20. var channel = await db.Channel.FirstOrDefaultAsync(c => c.MemberID == request.MemberID && c.IsActive, ct);
  21. if (channel is null)
  22. {
  23. return Result.Failure<Response>(Error.NotFound("Channel.NotFound", "활성 채널을 찾을 수 없습니다."));
  24. }
  25. if (channel.DonationCode is not null)
  26. {
  27. return Result.Failure<Response>(Error.Conflict("DonationCode.AlreadyIssued", "이미 후원 코드가 발급되어 변경할 수 없습니다."));
  28. }
  29. var normalized = trimmed.ToUpperInvariant();
  30. var duplicate = await db.Channel.AsNoTracking().AnyAsync(c => c.DonationCode == normalized, ct);
  31. if (duplicate)
  32. {
  33. return Result.Failure<Response>(Error.Conflict("DonationCode.Duplicate", "이미 사용 중인 후원 코드입니다. 다른 코드를 시도해 주세요."));
  34. }
  35. try
  36. {
  37. channel.IssueDonationCode(normalized);
  38. await db.SaveChangesAsync(ct);
  39. }
  40. catch (DbUpdateException)
  41. {
  42. return Result.Failure<Response>(Error.Conflict("DonationCode.Duplicate", "이미 사용 중인 후원 코드입니다. 다른 코드를 시도해 주세요."));
  43. }
  44. return Result.Success(new Response(channel.DonationCode!));
  45. }
  46. }