Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -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) ✅
|
||||
```
|
||||
Reference in New Issue
Block a user