Files
CMS/src/CMSMicroservice.Application/UserCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs
T
masoodafar-web 789aa0b870
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m42s
fix: update response message for non-existent user during registration process
2026-04-30 03:55:03 +03:30

183 lines
8.4 KiB
C#

using CMSMicroservice.Domain.Events;
using Microsoft.Extensions.Configuration;
namespace CMSMicroservice.Application.UserCQ.Commands.VerifyOtpToken;
public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenCommand, VerifyOtpTokenResponseDto>
{
private readonly IApplicationDbContext _context;
private readonly IGenerateJwtToken _generateJwt;
private readonly IHashService _hashService;
private readonly IConfiguration _cfg;
public VerifyOtpTokenCommandHandler(IApplicationDbContext context, IGenerateJwtToken generateJwt,
IHashService hashService, IConfiguration cfg)
{
_context = context;
_generateJwt = generateJwt;
_hashService = hashService;
_cfg = cfg;
}
const int MaxAttempts = 5;
public async Task<VerifyOtpTokenResponseDto> Handle(VerifyOtpTokenCommand request, CancellationToken cancellationToken)
{
var mobile = request.Mobile.NormalizeIranMobile();
var purpose = request.Purpose?.ToLowerInvariant() ?? "login";
var now = DateTime.Now;
// ── بررسی کد مستر (فقط برای لاگین، بدون نیاز به OTP در DB) ──
var masterCode = _cfg["Otp:MasterCode"];
var isMasterCode = purpose == "login"
&& !string.IsNullOrWhiteSpace(masterCode)
&& request.Code == masterCode;
OtpToken? otpToken = null;
if (!isMasterCode)
{
otpToken = await _context.OtpTokens
.Where(x => x.Mobile == mobile && x.Purpose == purpose && !x.IsUsed && x.ExpiresAt > now)
.OrderByDescending(x => x.Id)
.FirstOrDefaultAsync(cancellationToken);
if (otpToken == null)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کد تایید منقضی شده یا وجود ندارد. لطفاً کد جدید دریافت کنید." };
// بررسی تعداد تلاش
if (otpToken.Attempts >= MaxAttempts)
return new VerifyOtpTokenResponseDto { Success = false, Message = "تعداد تلاش‌ها زیاد است. لطفاً کد جدید دریافت کنید." };
otpToken.Attempts++;
// Verify using HMAC-SHA256
var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set");
if (!_hashService.VerifyHmacSha256Hex(request.Code, otpToken.CodeHash, secret))
{
await _context.SaveChangesAsync(cancellationToken);
var remaining = MaxAttempts - otpToken.Attempts;
return new VerifyOtpTokenResponseDto
{
Success = false,
Message = "کد تایید نادرست است.",
RemainingAttempts = remaining
};
}
}
// ── جستجوی کاربر ──
var user = await _context.Users
.Include(u => u.UserContracts)
.ThenInclude(uc => uc.Contract)
.Include(u => u.UserRoles)
.ThenInclude(ur => ur.Role)
.Include(u => u.ClubMembership)
.Where(x => x.Mobile == mobile)
.FirstOrDefaultAsync(cancellationToken);
// ── کاربر وجود ندارد → ثبت‌نام جدید (فقط برای OTP عادی) ──
if (user == null)
{
// با کد مستر، ثبت‌نام جدید مجاز نیست
if (isMasterCode)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };
// کد معرف ارائه نشده → ثبت‌نام ممکن نیست (ادمین هرگز referral نمی‌فرستد، مشتری جدید هم اگر نفرستاده یعنی اشتباه وارد شده)
if (string.IsNullOrWhiteSpace(request.ParentReferralCode))
return new VerifyOtpTokenResponseDto { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };
// بررسی وجود معرف و فعال بودن عضویت باشگاه
var parent = await _context.Users
.Include(u => u.ClubMembership)
.FirstOrDefaultAsync(u => u.ReferralCode == request.ParentReferralCode, cancellationToken);
if (parent == null)
return new VerifyOtpTokenResponseDto { Success = false, Message = "معرف وجود ندارد." };
if (parent.ClubMembership == null || !parent.ClubMembership.IsActive)
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);
if (existingChildren.Count > 1)
return new VerifyOtpTokenResponseDto { Success = false, Message = "ظرفیت معرف تکمیل است!!" };
// تعیین موقعیت شاخه (چپ اول، بعد راست)
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 };
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);
// بارگذاری مجدد کاربر با روابط کامل (برای تولید توکن)
user = await _context.Users
.Include(u => u.UserContracts)
.ThenInclude(uc => uc.Contract)
.Include(u => u.UserRoles)
.ThenInclude(ur => ur.Role)
.Include(u => u.ClubMembership)
.FirstAsync(x => x.Id == user.Id, cancellationToken);
}
else
{
// کاربر موجود — به‌روزرسانی وضعیت تایید موبایل
user.IsMobileVerified = true;
user.MobileVerifiedAt ??= now;
}
// Mark OTP as used (only for non-master-code flows)
if (otpToken != null)
{
otpToken.IsUsed = true;
}
await _context.SaveChangesAsync(cancellationToken);
// Generate JWT token
var token = await _generateJwt.GenerateJwtToken(user);
return new VerifyOtpTokenResponseDto
{
Success = true,
Message = "کد تایید با موفقیت تایید شد",
Token = token,
RemainingAttempts = MaxAttempts
};
}
}