188 lines
8.7 KiB
Markdown
188 lines
8.7 KiB
Markdown
# 🔐 فیکس فلوی ثبتنام / ورود 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) ✅
|
||
```
|