# 🔐 فیکس فلوی ثبت‌نام / ورود 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 و کاربر بر اساس شماره موبایل استفاده می‌کرد: ```csharp // ❌ قبل — 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 استفاده شود: ```csharp // ✅ بعد — ابتدا کاربر از 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`: ```csharp // ❌ قبل — GetAwaiter() بدون GetResult() عملیات async را اجرا نمی‌کند InitUserAuthInfo().GetAwaiter(); ``` `GetAwaiter()` فقط یک شیء awaiter برمی‌گرداند ولی عملیات را اجرا **نمی‌کند**. در نتیجه `_userAuthInfo` مقداردهی نمی‌شود و منوی کناری (`MainLayout.razor`) وضعیت نادرست نشان می‌دهد. ### رفع ```csharp // ✅ بعد — عملیات async را همگام اجرا می‌کند InitUserAuthInfo().GetAwaiter().GetResult(); ``` ### محل استفاده `MainLayout.razor` خطوط ۱۰۶ و ۱۰۹: ```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) ✅ ```