Files
CMS/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs
T
2025-12-22 22:39:15 +03:30

152 lines
6.2 KiB
C#

using CMSMicroservice.Domain.Enums;
using CMSMicroservice.Domain.Events;
using Microsoft.Extensions.Configuration;
namespace CMSMicroservice.Application.OtpTokenCQ.Commands.VerifyOtpToken;
public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenCommand, VerifyOtpTokenResponseDto>
{
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<VerifyOtpTokenResponseDto> 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
.Include(u => u.ClubMembership)
.FirstOrDefaultAsync(u => u.ReferralCode == request.ParentReferralCode,
cancellationToken: cancellationToken);
if (parent == null)
return new VerifyOtpTokenResponseDto() { Success = false, Message = "معرف وجود ندارد." };
// بررسی فعالیت باشگاه معرف
if (parent.ClubMembership == null || !parent.ClubMembership.IsActive)
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 = "ظرفیت معرف تکمیل است!!" };
// تعیین موقعیت در شبکه (چپ یا راست)
var existingChildren = await _context.Users
.Where(x => x.NetworkParentId == parent.Id && !x.IsDeleted)
.Select(x => x.LegPosition)
.ToListAsync(cancellationToken);
NetworkLeg newUserLegPosition;
if (!existingChildren.Any(x => x == NetworkLeg.Left))
{
newUserLegPosition = NetworkLeg.Left;
}
else if (!existingChildren.Any(x => x == NetworkLeg.Right))
{
newUserLegPosition = NetworkLeg.Right;
}
else
{
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,
LegPosition = newUserLegPosition
};
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
};
}
}