using CMSMicroservice.Domain.Events; using Microsoft.Extensions.Configuration; namespace CMSMicroservice.Application.OtpTokenCQ.Commands.VerifyOtpToken; public class VerifyOtpTokenCommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IConfiguration _cfg; private readonly IHashService _hashService; public VerifyOtpTokenCommandHandler(IApplicationDbContext context, IConfiguration cfg, IHashService hashService) { _context = context; _cfg = cfg; _hashService = hashService; } const int MaxAttempts = 5; // محدودیت تلاش public async Task Handle(VerifyOtpTokenCommand request, CancellationToken cancellationToken) { var mobile = request.Mobile.NormalizeIranMobile(); var purpose = request.Purpose?.ToLowerInvariant() ?? "signup"; var now = DateTime.Now; var otp = await _context.OtpTokens .Where(o => o.Mobile == mobile && o.Purpose == purpose && !o.IsUsed && o.ExpiresAt > now) .OrderByDescending(o => o.Created) .FirstOrDefaultAsync(cancellationToken); if (otp is null) return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد پیدا نشد یا منقضی شده است." }; if (otp.Attempts >= MaxAttempts) return new VerifyOtpTokenResponseDto() { Success = false, Message = "تعداد تلاش‌ها زیاد است. دوباره کد بگیرید." }; otp.Attempts++; var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set"); if (!_hashService.VerifyHmacSha256Hex(request.Code, otp.CodeHash, secret)) { await _context.SaveChangesAsync(cancellationToken); return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد نادرست است." }; } // موفق otp.IsUsed = true; // کاربر را بساز/به‌روزرسانی کن var user = await _context.Users.FirstOrDefaultAsync(u => u.Mobile == mobile, cancellationToken); if (user is null) { if (request.ParentReferralCode == null) return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد معرف الزامی است." }; var parent = await _context.Users.FirstOrDefaultAsync(u => u.ReferralCode == request.ParentReferralCode, cancellationToken: cancellationToken); if (parent == null) return new VerifyOtpTokenResponseDto() { Success = false, Message = "معرف وجود ندارد." }; if (await _context.Users.CountAsync(x => x.NetworkParentId == parent.Id, cancellationToken: cancellationToken) > 1) return new VerifyOtpTokenResponseDto() { Success = false, Message = "ظرفیت معرف تکمیل است!!" }; user = new User { Mobile = mobile, ReferralCode = UtilExtensions.Generate(digits: 10, firstDigitNonZero: true), IsMobileVerified = true, MobileVerifiedAt = now, IsRulesAccepted = true, RulesAcceptedAt = now, NetworkParentId = parent.Id }; await _context.Users.AddAsync(user, cancellationToken); user.AddDomainEvent(new CreateNewUserEvent(user)); await _context.SaveChangesAsync(cancellationToken); var userRole = new UserRole { UserId = user.Id, RoleId = 1, //UserRoleEnum.User }; await _context.UserRoles.AddAsync(userRole, cancellationToken); user.AddDomainEvent(new CreateNewUserRoleEvent(userRole)); var userWallet = new UserWallet { UserId = user.Id, Balance = 0, NetworkBalance = 0 }; await _context.UserWallets.AddAsync(userWallet, cancellationToken); user.AddDomainEvent(new CreateNewUserWalletEvent(userWallet)); await _context.SaveChangesAsync(cancellationToken); } else { user.IsMobileVerified = true; user.MobileVerifiedAt ??= now; _context.Users.Update(user); user.AddDomainEvent(new UpdateUserEvent(user)); await _context.SaveChangesAsync(cancellationToken); } return new VerifyOtpTokenResponseDto() { Success = true, Message = "اعتبارسنجی موفق.", UserId = user.Id, RemainingAttempts = MaxAttempts, RemainingSeconds = (otp.ExpiresAt - now).Seconds }; } }