Files
docs/cms/REGISTRATION-FLOW-FIXES.md
T

8.7 KiB
Raw Blame History

🔐 فیکس فلوی ثبت‌نام / ورود FrontOffice

تاریخ: بهمن ۱۴۰۴ (February 2026)


📋 خلاصه

بررسی کامل فلوی ثبت‌نام و ورود FrontOffice از UI تا دیتابیس انجام شد. ۳ باگ بحرانی شناسایی و رفع شده:

# شدت مشکل فایل
1 🔴 بحرانی کاربران جدید ثبت‌نام نمی‌شوند UserCQ/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs
2 🔴 بحرانی امضای قرارداد همیشه شکست می‌خورد UserCQ/AcceptContract/AcceptContractCommandHandler.cs
3 🟡 متوسط منوی کناری وضعیت نادرست نشان می‌دهد FrontOffice.Main/Utilities/AuthService.cs

🔴 باگ ۱ — کاربران جدید ثبت‌نام نمی‌شوند

مشکل

FrontOffice از UserContract.UserContractClient (user.proto) استفاده می‌کند → UserCQ/VerifyOtpTokenCommandHandler. این handler وقتی کاربر یافت نمی‌شد فقط خطای «کاربر یافت نشد» برمی‌گرداند و کاربر جدید ایجاد نمی‌کرد.

لاجیک ایجاد کاربر (شامل: اعتبارسنجی کد معرف، درخت باینری، موقعیت شاخه) در OtpTokenCQ/VerifyOtpTokenCommandHandler بود — سرویسی که FrontOffice اصلاً از آن استفاده نمی‌کند.

رفع

اضافه شدن لاجیک کامل ایجاد کاربر جدید به UserCQ/VerifyOtpTokenCommandHandler:

if (user == null)
{
    // ۱. اعتبارسنجی کد معرف (ParentReferralCode) — الزامی
    // ۲. بررسی وجود معرف و فعال بودن عضویت باشگاه
    // ۳. بررسی ظرفیت (حداکثر ۲ زیرمجموعه مستقیم)
    // ۴. تعیین شاخه (چپ اول، بعد راست)
    // ۵. ایجاد User + UserRole + UserWallet
    // ۶. رویدادهای دامنه: CreateNewUserEvent, CreateNewUserRoleEvent, CreateNewUserWalletEvent
    // ۷. بارگذاری مجدد کاربر با روابط کامل
    // ۸. تولید JWT token
}

اعتبارسنجی‌ها

مرحله شرط پیام خطا
کد معرف خالی یا null «کد معرف الزامی است»
معرف وجود نداشته باشد «معرف وجود ندارد»
عضویت باشگاه غیرفعال باشد «لینک دعوت معرف فعال نیست»
ظرفیت بیش از ۱ فرزند «ظرفیت معرف تکمیل است»
شاخه هر دو پُر باشند «ظرفیت معرف تکمیل است»

فایل

CMS/src/CMSMicroservice.Application/UserCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs


🔴 باگ ۲ — امضای قرارداد همیشه شکست می‌خورد

مشکل

AcceptContractCommandHandler از _currentUserService.Username برای پیدا کردن OTP و کاربر بر اساس شماره موبایل استفاده می‌کرد:

// ❌ قبل — Username = "{FirstName} {LastName}" نه شماره موبایل!
var otpToken = await _context.OtpTokens
    .Where(x => x.Mobile == _currentUserService.Username ...)
var user = await _context.Users
    .Where(x => x.Mobile == _currentUserService.Username ...)

CurrentUserService.Username مقدار ClaimTypes.Name را برمی‌گرداند که در JWT به صورت "{FirstName} {LastName}" ذخیره شده — نه شماره موبایل!

رفع

اول کاربر بر اساس UserId (از ClaimTypes.NameIdentifier) پیدا شود، سپس از user.Mobile برای جستجوی OTP استفاده شود:

// ✅ بعد — ابتدا کاربر از UserId پیدا شود
var userId = long.Parse(_currentUserService.UserId);
var user = await _context.Users
    .Where(x => x.Id == userId)...

var otpToken = await _context.OtpTokens
    .Where(x => x.Mobile == user.Mobile ...)

فایل

CMS/src/CMSMicroservice.Application/UserCQ/Commands/AcceptContract/AcceptContractCommandHandler.cs


🟡 باگ ۳ — IsCompleteRegister() داده قدیمی می‌خواند

مشکل

متد sync در AuthService:

// ❌ قبل — GetAwaiter() بدون GetResult() عملیات async را اجرا نمی‌کند
InitUserAuthInfo().GetAwaiter();

GetAwaiter() فقط یک شیء awaiter برمی‌گرداند ولی عملیات را اجرا نمی‌کند. در نتیجه _userAuthInfo مقداردهی نمی‌شود و منوی کناری (MainLayout.razor) وضعیت نادرست نشان می‌دهد.

رفع

// ✅ بعد — عملیات async را همگام اجرا می‌کند
InitUserAuthInfo().GetAwaiter().GetResult();

محل استفاده

MainLayout.razor خطوط ۱۰۶ و ۱۰۹:

Disabled="@(!AuthService.IsCompleteRegister())"

فایل

FrontOffice/src/FrontOffice.Main/Utilities/AuthService.cs


🏗️ معماری فلوی ثبت‌نام (بعد از فیکس)

┌─────────────────────────────────────────────────────────────┐
│                    FrontOffice (Blazor WASM)                 │
│                                                             │
│  LoginPage → SendOtp → VerifyOtp(mobile, code, referral)   │
│      ↓ gRPC-Web                                            │
├─────────────────────────────────────────────────────────────┤
│                CMS Backend (gRPC)                           │
│                                                             │
│  UserContract.VerifyOtpToken                                │
│      ↓                                                      │
│  UserCQ/VerifyOtpTokenCommandHandler                        │
│      │                                                      │
│      ├── OTP صحیح؟ → ❌ خطا                                │
│      │                                                      │
│      ├── کاربر موجود؟ → ✅ تولید JWT Token                 │
│      │                                                      │
│      └── کاربر جدید؟                                       │
│           ├── اعتبارسنجی کد معرف                           │
│           ├── بررسی ظرفیت درخت باینری                      │
│           ├── ایجاد User + UserRole + UserWallet            │
│           ├── رویدادهای دامنه                              │
│           └── تولید JWT Token                               │
│                                                             │
│  بعد از ثبت‌نام → RegisterWizard:                          │
│    Step 1: اطلاعات شخصی                                    │
│    Step 2: امضای قرارداد (AcceptContract + OTP)             │
│    Step 3: خرید پکیج                                       │
├─────────────────────────────────────────────────────────────┤
│                     Database                                │
│                                                             │
│  Users ─── UserRoles ─── UserWallets                        │
│    └── NetworkParentId, LegPosition (Binary Tree)           │
│    └── UserContracts, ClubMembership                        │
└─────────────────────────────────────────────────────────────┘

📝 فایل‌های تغییر یافته

فایل تغییر
CMS/.../UserCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs اضافه شدن لاجیک ایجاد کاربر جدید (86→165 خط)
CMS/.../UserCQ/Commands/AcceptContract/AcceptContractCommandHandler.cs تغییر lookup از Username به UserId
FrontOffice/.../Utilities/AuthService.cs اضافه شدن .GetResult() به GetAwaiter()

بیلد

CMS:         0 Error(s)  ✅
FrontOffice: 0 Error(s)  ✅
BackOffice:  0 Error(s)  ✅