Handler.cs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Application.Abstractions.Messaging.Email;
  4. using Application.Common;
  5. using Domain.Entities.EmailVerification.ValueObject;
  6. using SharedKernel.Results;
  7. using Microsoft.EntityFrameworkCore;
  8. namespace Application.Features.Api.MyPage.VerifyEmail;
  9. internal sealed class Handler(IAppDbContext db, IMailService mailService) : IQueryHandler<Query, Result>
  10. {
  11. public async Task<Result> Handle(Query request, CancellationToken ct)
  12. {
  13. if (string.IsNullOrWhiteSpace(request.Token))
  14. {
  15. return Result.Failure(Error.Problem("MyPage.TokenRequired", "인증 토큰은 필수입니다."));
  16. }
  17. var verifyToken = await db.EmailVerifyToken.FirstOrDefaultAsync(t => t.Token == request.Token && t.Type == VerificationType.ChangedEmail, ct);
  18. if (verifyToken is null)
  19. {
  20. return Result.Failure(Error.NotFound("MyPage.TokenNotFound", "유효하지 않은 인증 토큰입니다."));
  21. }
  22. if (verifyToken.IsVerified)
  23. {
  24. return Result.Failure(Error.Problem("MyPage.AlreadyVerified", "이미 인증된 토큰입니다."));
  25. }
  26. if (verifyToken.Expiration < DateTime.UtcNow)
  27. {
  28. return Result.Failure(Error.Problem("MyPage.TokenExpired", "인증 토큰이 만료되었습니다."));
  29. }
  30. // AdditionalData.Email = 기존 이메일, verifyToken.Email = 새 이메일
  31. var oldEmail = verifyToken.AdditionalData?.Email;
  32. if (string.IsNullOrWhiteSpace(oldEmail))
  33. {
  34. return Result.Failure(Error.Problem("MyPage.InvalidToken", "인증 토큰 데이터가 올바르지 않습니다."));
  35. }
  36. var member = await db.Member.FirstOrDefaultAsync(m => m.Email == oldEmail, ct);
  37. if (member is null)
  38. {
  39. return Result.Failure(Error.NotFound("MyPage.MemberNotFound", "회원 정보를 찾을 수 없습니다."));
  40. }
  41. // 새 이메일이 다른 회원에게 사용 중인지 확인
  42. var newEmail = verifyToken.Email;
  43. var emailExists = await db.Member.AnyAsync(m => m.Email == newEmail && m.ID != member.ID, ct);
  44. if (emailExists)
  45. {
  46. return Result.Failure(Error.Conflict("MyPage.EmailExists", "이미 사용 중인 이메일입니다."));
  47. }
  48. verifyToken.MarkVerified();
  49. member.SetEmail(newEmail);
  50. member.MarkEmailVerified();
  51. await db.SaveChangesAsync(ct);
  52. // 이메일 변경 완료 안내 메일 (새 이메일로 발송)
  53. var emailTpl = (await db.Config.AsNoTracking().FirstOrDefaultAsync(ct))?.EmailTemplate;
  54. if (!string.IsNullOrWhiteSpace(emailTpl?.ChangedEmailFormContent))
  55. {
  56. var subject = string.IsNullOrWhiteSpace(emailTpl.ChangedEmailFormTitle)
  57. ? "이메일이 변경되었습니다"
  58. : emailTpl.ChangedEmailFormTitle;
  59. var content = emailTpl.ChangedEmailFormContent.Render(
  60. ("Email", newEmail),
  61. ("CreatedAt", DateTime.UtcNow.AddHours(9).ToString("yyyy-MM-dd HH:mm")));
  62. await mailService.SendAsync(new SendData(newEmail, subject, content), ct);
  63. }
  64. return Result.Success();
  65. }
  66. }