Refactor code structure for improved readability and maintainability

This commit is contained in:
masoodafar-web
2026-02-16 00:59:16 +03:30
parent 956a9ff6d6
commit ad31c8be97
10 changed files with 3388 additions and 3 deletions
+187
View File
@@ -0,0 +1,187 @@
# 🔐 فیکس فلوی ثبت‌نام / ورود 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) ✅
```