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

188 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🔐 فیکس فلوی ثبت‌نام / ورود 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) ✅
```