using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Domain.Entities.EmailVerification.ValueObject; using SharedKernel.Results; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.Auth.VerifyEmail; internal sealed class Handler( IAppDbContext db ) : ICommandHandler> { public async Task> Handle(Command request, CancellationToken ct) { // 이메일 유효성 검사 if (string.IsNullOrWhiteSpace(request.Email)) { return Result.Failure(Error.Problem("Auth.EmailRequired", "이메일은 필수입니다.")); } if (string.IsNullOrWhiteSpace(request.Code)) { return Result.Failure(Error.Problem("Auth.CodeRequired", "인증번호는 필수입니다.")); } var email = request.Email.Trim().ToLower(); // 인증번호 조회 (미인증, 만료되지 않은 것) var verifyNumber = await db.EmailVerifyNumber.Where(e => e.Email == email && e.Type == request.Type && !e.IsVerified && e.Expiration > DateTime.UtcNow).OrderByDescending(e => e.CreatedAt).FirstOrDefaultAsync(ct); if (verifyNumber is null) { return Result.Failure(Error.NotFound("Auth.CodeNotFound", "인증번호가 올바르지 않거나 만료되었습니다.")); } // 코드 일치 확인 if (verifyNumber.Code != request.Code.Trim()) { return Result.Failure(Error.Problem("Auth.CodeMismatch", "인증번호가 일치하지 않습니다.")); } // 인증 완료 처리 verifyNumber.MarkVerified(); // 회원가입 인증인 경우 이메일 인증 완료 처리 if (request.Type == VerificationType.Registration) { var member = await db.Member.FirstOrDefaultAsync(m => m.Email == email, ct); if (member is not null && !member.IsEmailVerified) { member.MarkEmailVerified(); } } await db.SaveChangesAsync(ct); return Result.Success(request.Type); } }