This commit is contained in:
masoodafar-web
2026-01-03 18:27:49 +03:30
parent 0369292d7f
commit 5965b98728
156 changed files with 16082 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+547
View File
@@ -0,0 +1,547 @@
# 🎯 اسپرینت جاری (Current Sprint)
**تاریخ شروع**: ۱۴ آذر ۱۴۰۴
**آخرین بروزرسانی**: ۲۸ آذر ۱۴۰۴
**مدت**: 2 هفته
**هدف**: تکمیل BackOffice UI و رفع Anti-Patterns معماری
---
## 🔧 تغییرات اخیر (۲۸ آذر) - Session 2
### ✅ سیستم مدیریت موجودی محصولات
**CMS Handler**: چک موجودی در `SubmitShopBuyOrderCommandHandler`
- چک موجودی قبل از تأیید سفارش
- کاهش `RemainingCount` و افزایش `SaleCount` بعد از پرداخت
- پیام خطای فارسی با جزئیات محصول و تعداد
**FrontOffice ProductDetail**:
- `MaxQty` داینامیک بر اساس موجودی واقعی
- نمایش Chip موجودی (سبز/قرمز) با تعداد
- غیرفعال کردن دکمه افزودن وقتی ناموجود
### ✅ ویژگی‌های باشگاه (ClubFeatures) - اصلاح معماری
**حذف فیلدها از UserClubFeature**:
- `DetailedDescriptionHtml`, `Icon`, `Color` فقط در `ClubFeature` (جدول قالب)
- `UserClubFeature` فقط: `IsActive`, `GrantedAt`, `Notes`
**فایل‌های اصلاح‌شده**:
- CMS: `UserClubFeatureDto`, `clubmembership.proto`, `ClubFeatureProfile`
- BFF: `GetClubFeaturesQueryHandler`, `GetClubFeaturesResponseDto`, `configuration.proto`, `ConfigurationProfile`
- FrontOffice: `ClubConfigurationService`, `FeaturesPage`
**MembershipPage - مزایای عضویت**:
1. شارژ ۵۶ میلیون تومان کیف پول فروشگاه تخفیفی
2. عضویت در شبکه بازاریابی و دریافت پورسانت
3. امکان جذب زیرمجموعه
### ✅ رفع باگ مدال آدرس‌ها
- **Snackbar تکراری**: حذف inject از code-behind (global در `_Imports.razor`)
- **NullReferenceException**: null check برای `dialog.Result`
### ✅ VAT و Cart Services
- **VATService**: نرخ پیش‌فرض 9.99% برای debug
- **CartService**: `EnsureInitializedAsync` + `IsAuthenticatedAsync` برای لود فقط برای کاربران لاگین‌شده
---
## 🔧 تغییرات قبلی (۲۸ آذر) - Session 1
### ✅ نمودار درختی شبکه در FrontOffice با d3-org-chart
**کتابخانه‌ها**: d3-org-chart v3 + d3.js v7 + d3-flextree v2.1.2
**ویژگی‌ها**:
- نمایش درختی باینری شبکه
- کلیک روی نود → نمایش درخت زیرمجموعه
- دکمه بازگشت به نود قبلی
- دکمه "درخت من" برای بازگشت به درخت کاربر
- انتخاب عمق درخت با MudSelect (2-10 سطح)
- FitToScreen برای تناسب با صفحه
- Responsive با MudBlazor components
### ✅ API جدید GetSubordinateTree
**Proto**: `GetSubordinateTreeRequest` با `target_user_id` و `max_depth`
**BFF Handler**: `GetSubordinateTreeQueryHandler.cs`
**Frontend Service**: `GetSubordinateTreeAsync(targetUserId, maxDepth)`
**امنیت**: JWT Authentication (بدون check سنگین subordinate)
### ✅ رفع مشکل Encoding فارسی در Geography
**Entity های تغییر یافته**: Country, State, City
**تغییرات Configuration**: `NVARCHAR` با `Persian_100_CI_AI` collation
**Migration**: `FixPersianCollation_Geography`
---
## 🔧 تغییرات قبلی (۱۷ آذر)
### ✅ رفع Anti-Pattern معماری در BackOffice.BFF
**مشکل**: BackOffice.BFF.WebApi از پکیج‌های Protobuf مربوط به CMS استفاده می‌کرد
**راه‌حل**:
- ساخت Protobuf های اختصاصی BackOffice.BFF (ClubMembership, Commission, Configuration, NetworkMembership)
- تغییر namespace از `CMSMicroservice` به `Foursat.BackOffice.BFF.*`
- تغییر GrpcServices از "Client" به "Both" (برای پشتیبانی هم از Server و هم Client)
- نسخه‌های منتشر شده: 0.0.6 (ClubMembership, Commission, NetworkMembership), 1.0.6 (Configuration)
### ✅ اضافه شدن HTTP Annotations به Protobuf
- افزودن `google/api/annotations.proto` به 4 پروژه Protobuf
- پیاده‌سازی HTTP endpoints برای Swagger: 33 endpoint
- ClubMembership: 7 endpoints
- Commission: 14 endpoints
- Configuration: 5 endpoints
- NetworkMembership: 7 endpoints
- نصب `Google.Api.CommonProtos v2.10.0`
### ✅ رفع مشکل Mapster با Immutable Types
- ساخت `NetworkMembershipProfile.cs` با استفاده از `MapWith()`
- مپینگ دستی برای `RepeatedField` و `Timestamp`
- رفع خطای "Cannot convert immutable type"
### ✅ پشتیبانی از Multi-Role Authorization
- تغییر `AuthorizationService` برای خواندن چندین رول از JWT
- اضافه شدن متد `GetUserRolesAsync()`
- استفاده از `user.FindAll(ClaimTypes.Role)` بجای `FindFirst`
- پشتیبانی از رول‌های آرایه‌ای در `ApiAuthenticationStateProvider`
### ✅ نمایش درختی شبکه (Network Tree Visualization)
- پیاده‌سازی درخت تعاملی با D3.js v7
- ویژگی‌های درخت:
- Zoom & Pan با mouse/touch
- دکمه Reset برای بازگشت به حالت اولیه
- رنگ‌بندی: سبز (فعال), قرمز (غیرفعال), سبز (چپ), نارنجی (راست)
- کلیک روی node برای بارگذاری درخت آن کاربر
- Responsive با viewBox و preserveAspectRatio
- اضافه شدن `UserAutoComplete` برای جستجوی کاربر
- جستجوی همزمان در Mobile, FirstName, LastName, NationalCode
- نمایش نام + موبایل در لیست
### ✅ رفع مشکلات UI
- رفع NullReferenceException در `NetworkTreeViewer` (JoinedAt null check)
- رفع timing issue در render درخت (StateHasChanged + Task.Delay)
- رفع خطای JSInterop با استفاده از `setDotNetReference`
---
## ⚠️ یادآوری مهم: Proto Package Workflow
**قبل از هر کاری این را بخوانید!**
### قانون اجباری برای تغییر Proto (ALL Services):
```
┌─────────────────────────────────────────┐
│ تغییر Proto File │
│ ↓ │
│ افزایش Version در csproj │
│ ↓ │
│ dotnet pack -c Release │
│ ↓ │
│ Update Version در لایه بالاتر │
└─────────────────────────────────────────┘
```
**این برای همه سرویس‌ها اجباری است:**
- ✅ CMS Proto changes → Update BFF
- ✅ BackOffice.BFF Proto changes → Update BackOffice UI
- ✅ FrontOffice.BFF Proto changes → Update FrontOffice UI
**عدم رعایت = ساعت‌ها Debug و سردرگمی بیهوده!**
GitLab Registry: `https://git.afrino.co/api/packages/FourSat/nuget/index.json`
---
## 📋 وضعیت کلی پروژه
### Backend:
-**CMS Microservice**: ~98% Complete
-**Daya Loan Integration**: 100% Complete (Real API implemented - Dec 6, 2025)
-**BackOffice.BFF**: 100% Complete (35+ Handlers)
-**Architecture Fixed**: Anti-pattern با CMS Protobuf رفع شد
-**HTTP Annotations**: 33 endpoint با Swagger support
-**Mapster Profiles**: NetworkMembership, Products با MapWith()
-**FrontOffice.BFF**: 98% Complete (همه سرویس‌های مورد نیاز مشتری پیاده‌سازی شده)
### Frontend:
-**BackOffice UI**: ~97% Complete (65+ صفحه)
-**Multi-Role Authorization**: پشتیبانی از چندین نقش همزمان
-**Network Tree Visualization**: نمایش درختی تعاملی با D3.js
-**User AutoComplete**: جستجوی پیشرفته کاربران
-**FrontOffice UI**: **98% Complete** (تمام صفحات مورد نیاز مشتری پیاده‌سازی شده)
---
## 🎉 جمع‌بندی نهایی FrontOffice (سمت مشتری)
### ✅ صفحات پیاده‌سازی شده و متصل به API:
#### 🔐 احراز هویت:
| صفحه | Route | وضعیت |
|------|-------|-------|
| ثبت‌نام (OTP) | `/register` | ✅ متصل به gRPC |
| ورود (OTP) | Dialog | ✅ متصل به gRPC |
| تایید قرارداد | `/register` Step 3 | ✅ متصل به gRPC |
#### 👤 پروفایل:
| صفحه | Route | وضعیت |
|------|-------|-------|
| داشبورد پروفایل | `/profile` | ✅ متصل به gRPC |
| ویرایش اطلاعات شخصی | `/profile/personal` | ✅ متصل به gRPC |
| مدیریت آدرس‌ها | `/profile/addresses` | ✅ متصل به gRPC |
| تنظیمات | `/profile/settings` | ✅ متصل به gRPC |
| کیف پول | `/profile/wallet` | ✅ متصل به gRPC |
| درخت شبکه | `/profile/tree` | ✅ متصل به gRPC |
#### 🛒 فروشگاه:
| صفحه | Route | وضعیت |
|------|-------|-------|
| لیست محصولات | `/products` | ✅ متصل به gRPC |
| جزئیات محصول | `/products/{id}` | ✅ متصل به gRPC |
| دسته‌بندی‌ها | `/categories` | ✅ متصل به gRPC |
| سبد خرید | `/cart` | ✅ متصل به gRPC |
| تایید خرید (VAT) | `/checkout` | ✅ متصل به gRPC |
#### 📦 سفارشات:
| صفحه | Route | وضعیت |
|------|-------|-------|
| لیست سفارشات | `/orders` | ✅ متصل به gRPC |
| جزئیات سفارش (VAT) | `/orders/{id}` | ✅ متصل به gRPC |
| پیگیری سفارش | `/order-tracking/{id}` | ✅ متصل به gRPC |
#### 💰 کمیسیون:
| صفحه | Route | وضعیت |
|------|-------|-------|
| داشبورد کمیسیون | `/commission` | ✅ متصل به gRPC |
| تاریخچه پرداخت‌ها | `/commission/history` | ✅ متصل به gRPC |
| تعادل هفتگی | `/commission/weekly-balance` | ✅ متصل به gRPC |
#### 🌐 شبکه:
| صفحه | Route | وضعیت |
|------|-------|-------|
| آمار شبکه | `/network/statistics` | ✅ متصل به gRPC |
| درخت شبکه | `/profile/tree` | ✅ متصل به gRPC |
#### 🏆 باشگاه:
| صفحه | Route | وضعیت |
|------|-------|-------|
| عضویت باشگاه | `/club/membership` | ✅ متصل به gRPC |
| امکانات باشگاه | `/club/features` | ✅ Static |
#### 📦 پکیج‌ها:
| صفحه | Route | وضعیت |
|------|-------|-------|
| لیست پکیج‌ها | `/packages` | ✅ متصل به gRPC |
| پکیج‌های من | `/my-packages` | ✅ متصل به gRPC |
| جزئیات پکیج | `/packages/{id}` | ✅ متصل به gRPC |
#### 📄 سایر صفحات:
| صفحه | Route | وضعیت |
|------|-------|-------|
| صفحه اصلی | `/` | ✅ Complete |
| درباره ما | `/about` | ✅ Static |
| سوالات متداول | `/faq` | ✅ Static |
| تماس با ما | `/contact` | ⚠️ UI آماده، Mock |
---
### ⚠️ موارد Mock (نیاز به CMS):
| قابلیت | وضعیت UI | نیاز CMS |
|--------|----------|----------|
| **فرم تماس با ما** | ✅ UI کامل | Entity `ContactMessage` |
| **نظرات محصول** | ❌ | Entity `Review` |
| **کد تخفیف** | ❌ | RPC `ValidateDiscountCode` |
---
### ❌ موارد حذف شده (نیاز نیست):
| قابلیت | دلیل |
|--------|------|
| `ChangePassword` | سیستم فقط OTP دارد، رمز عبور نداریم |
| `ForgotPassword` | سیستم فقط OTP دارد |
> **نکته**: صفحه `ChangePassword.razor` ساخته شده ولی فعلاً کاربردی ندارد چون لاگین فقط با OTP است.
---
## 📊 مقایسه CMS vs FrontOffice.BFF
### سرویس‌های CMS که در FrontOffice.BFF پیاده‌سازی شده:
| CMS Service | FrontOffice.BFF | وضعیت |
|-------------|-----------------|-------|
| UserService | ✅ UserService | کامل |
| OtpTokenService | ✅ (در UserService) | کامل |
| UserAddressService | ✅ UserAddressService | کامل |
| ProductsService | ✅ ProductsService | کامل |
| CategoryService | ✅ CategoriesService | کامل |
| UserCartsService | ✅ ShopingCartService | کامل |
| UserOrderService | ✅ UserOrderService | کامل |
| UserWalletService | ✅ UserWalletService | کامل |
| TransactionsService | ✅ TransactionService | کامل |
| CommissionService | ✅ CommissionService | کامل |
| ClubMembershipService | ✅ ClubMembershipService | کامل |
| NetworkMembershipService | ✅ NetworkMembershipService | کامل |
| PackageService | ✅ PackageService | کامل |
| DiscountShopServices | ✅ DiscountShopService | کامل |
| UserContractService | ✅ (در UserService - AcceptContract) | کامل |
### سرویس‌های CMS که فقط برای Admin هستند (نیاز مشتری نیست):
| CMS Service | توضیح |
|-------------|-------|
| ConfigurationService | تنظیمات سیستم (Admin) |
| ContractService | مدیریت قراردادها (Admin) |
| ManualPaymentService | پرداخت دستی (Admin) |
| RoleService | مدیریت نقش‌ها (Admin) |
| UserRoleService | اختصاص نقش (Admin) |
| TagService | مدیریت تگ‌ها (Admin) |
| ProductTagService | اختصاص تگ به محصول (Admin) |
| PublicMessageService | پیام‌های عمومی (Admin) |
| ProductImagesService | مدیریت تصاویر (Admin) |
| ProductGalleriesService | گالری محصول (Admin) |
| ProductCategoryService | دسته‌بندی محصول (Admin) |
| FactorDetailsService | جزئیات فاکتور (Admin) |
| UserWalletChangeLogService | لاگ تغییرات کیف پول (Admin) |
---
### 2. ✅ Package Purchase System - UI Implementation
**Status**: ✅ تکمیل شد
**Owner**: FrontOffice Team
**تاریخ تکمیل**: ۱۵ آذر ۱۴۰۴
#### CMS Status:
- ✅ Entities (5) - `PackagePurchase`, `PackagePurchaseItem`, etc.
- ✅ Commands (6) - `CreatePackagePurchaseCommand`, etc.
- ✅ Queries (3) - `GetPackagePurchaseByIdQuery`, etc.
- ✅ Protobuf (4 RPCs) - 264 lines
#### تغییرات انجام شده:
-**PackageService.cs**: سرویس gRPC برای دریافت پکیج‌ها
-**RouteConstants.cs**: اضافه شدن مسیرهای `/packages` و `/my-packages`
-**ConfigureServices.cs**: ثبت `PackageService` در DI
-**Packages.razor**: صفحه لیست پکیج‌ها با Grid View، جستجو، و Loading States
-**MyPackages.razor**: صفحه پکیج‌های کاربر با نمایش وضعیت خرید
**Build Status**: ✅ FrontOffice Build Succeeded - 0 Errors
**Reference**: `01-BUSINESS/package-purchase-system.md`
---
### 3. ✅ اصلاح محاسبه کمیسیون هفتگی (بحرانی)
**Status**: ✅ تکمیل شد
**Owner**: CMS Team
**تاریخ شروع**: ۱۴ آذر ۱۴۰۴
**تاریخ تکمیل**: ۱۴ آذر ۱۴۰۴
#### شرح مشکلات رفع شده:
| # | مشکل | وضعیت |
|---|------|-------|
| 1 | محدودیت لول (15) پیاده‌سازی نشده | ✅ Fixed |
| 2 | فقط تعادل شخصی حساب می‌شود (نه زیرمجموعه) | ✅ Fixed |
#### فرمول پیاده‌سازی شده:
```
کمیسیون = (تعادل_شخص + SUM(تعادل_زیرمجموعه تا 15 لول)) × ارزش_هر_تعادل
```
#### تسک‌های تکمیل شده:
| فاز | شرح | وضعیت |
|-----|------|-------|
| 1 | اضافه کردن `Commission.MaxNetworkLevel` به Config | ✅ |
| 2 | اصلاح `CalculateWeeklyBalancesCommandHandler` - محدودیت لول | ✅ |
| 3 | اصلاح `ProcessUserPayoutsCommandHandler` - تعادل زیرمجموعه | ✅ |
| 4 | تست و Build | ✅ (0 Errors) |
#### تغییرات انجام شده:
- **Config**: اضافه کردن `Commission.MaxNetworkLevel = 15`
- **CalculateWeeklyBalances**: پارامتر `maxLevel` به متدهای بازگشتی اضافه شد
- **ProcessUserPayouts**: متد `SumSubordinateBalancesAsync` برای جمع تعادل زیرمجموعه‌ها تا 15 لول
**Reference**: `01-BUSINESS/commission-calculation-fix.md`
---
### 4. ✅ اصلاح سقف تعادل هفتگی (MaxWeeklyBalances)
**Status**: ✅ تکمیل شد
**Owner**: CMS Team
**تاریخ تکمیل**: ۱۴ آذر ۱۴۰۴
#### شرح مشکل:
سقف تعادل هفتگی در پیاده‌سازی فعلی **300 کل** بود، اما باید **300 برای هر دست** باشد.
| توضیح | منطق قدیم | منطق جدید |
|-------|-----------|-----------|
| سقف | 300 کل | 300 هر دست |
| Max Total | 300 | 300+300=600 |
#### تغییرات انجام شده:
**CMS Changes:**
-**SystemConfiguration**: تغییر کلید از `MaxWeeklyBalancesPerUser` به `MaxWeeklyBalancesPerLeg`
-**CalculateWeeklyBalancesCommandHandler**: اصلاح منطق محاسبه سقف
- اعمال سقف روی هر دست جداگانه قبل از محاسبه MIN
- باقیمانده = مازاد سقف هر دست (نه نصف تعادل کل)
-**ApplicationDbContextInitialiser**: اصلاح Seed Data
**منطق جدید:**
```csharp
// ✅ سقف روی هر دست جداگانه
var cappedLeftTotal = Math.Min(leftTotal, maxBalancesPerLeg);
var cappedRightTotal = Math.Min(rightTotal, maxBalancesPerLeg);
var totalBalances = Math.Min(cappedLeftTotal, cappedRightTotal);
var leftRemainder = leftTotal - cappedLeftTotal; // مازاد سقف
var rightRemainder = rightTotal - cappedRightTotal;
```
**Build Status**: ✅ CMS Build Succeeded - 0 Errors
**مستندات:**
-`01-BUSINESS/balance-calculation-rules.md` - آپدیت شد
---
## 🟢 Low Priority (هفته بعد)
### 5. ✅ VAT System Implementation
**Status**: ✅ تکمیل شد
**Owner**: CMS + BFF + FrontOffice UI
**تاریخ تکمیل**: ۱۵ آذر ۱۴۰۴
#### تغییرات انجام شده:
**CMS:**
-**OrderVAT Entity**: قبلاً موجود بود
-**GetUserOrderResponseDto**: اضافه شدن `VatInfo` با فیلدهای `VatRate`, `BaseAmount`, `VatAmount`, `TotalAmount`, `IsPaid`
-**GetUserOrderQueryHandler**: اضافه شدن `.Include(i => i.OrderVAT)` و mapping VAT
-**CMS Proto**: اضافه شدن `OrderVATInfo` message
**FrontOffice.BFF:**
-**Proto**: اضافه شدن `OrderVATInfo` و `vat_info` field
-**GetUserOrderResponseDto**: اضافه شدن `OrderVATInfoDto`
**FrontOffice UI:**
-**CheckoutSummary.razor**: نمایش جزئیات مالی شامل:
- جمع کالاها
- مالیات بر ارزش افزوده (۹%)
- مبلغ قابل پرداخت
-**OrderDetail.razor**: نمایش جزئیات مالی مشابه
**Build Status**: ✅ All Projects - 0 Errors
---
### 6. Manual Payment System
**Status**: 🟡 In Progress (CMS + BackOffice.BFF)
**Owner**: CMS + BackOffice.BFF
**Estimate**: 2 روز (UI باقیمانده)
#### وضعیت فعلی:
- ✅ CMS Domain & Application: ManualPayment + CreateManualPayment/ApproveManualPayment/RejectManualPayment + GetAllManualPayments
- ✅ CMS gRPC: ManualPaymentContract (Create/Approve/Reject/GetAllManualPayments)
- ✅ BackOffice.BFF: 4 Handler (CreateManualPayment, ApproveManualPayment, RejectManualPayment, GetManualPayments)
- ⏳ BackOffice UI: صفحه لیست Manual Payments + Dialog جزئیات/Approve/Reject
- ⏳ FrontOffice Flow (CreateManualPaymentRequest از سمت کاربر)
---
### 7. RBAC System Implementation
**Status**: 🟡 In Progress (BackOffice.BFF Permission Layer پیاده‌سازی شده؛ CMS + BackOffice UI باقی مانده)
**Owner**: CMS + BFF Teams
**Estimate**: 1.5 هفته
#### Requirements:
- تعریف Roles و Permissions (CMS Domain/Application)
- Policy-based Authorization (CMS + BackOffice UI)
- Admin Panel برای مدیریت دسترسی‌ها
- ✅ BackOffice.BFF: IPermissionService + CheckUserPermissionHandler / GetUserRolesHandler + gRPC PermissionInterceptor
---
## ⚠️ Blockers & Dependencies
### 🟢 Resolved:
1. ~~**Protobuf Mismatch** (FrontOffice.BFF ↔ CMS)~~ ✅ Fixed
2. ~~**WalletService Mock Data**~~ ✅ Connected to gRPC
3. ~~**Tree Builder Missing** (NetworkMembership)~~ ✅ Implemented
### 🟡 Minor Issues:
1. **MudBlazor Warnings** - ~100 warnings (غیر critical)
2. **Missing CMS Entities** - Review, ContactMessage, DiscountValidation
---
## 📊 Progress Summary
### ✅ وضعیت نهایی FrontOffice (سمت مشتری):
**FrontOffice UI: 98% Complete** 🎉
-**28+ صفحه** پیاده‌سازی شده
-**همه سرویس‌ها** به gRPC متصل
-**Build موفق** بدون Error
- ⚠️ فقط **فرم تماس با ما** Mock است (نیاز به CMS entity)
### کارهای تمام شده این اسپرینت (۱۴-۱۵ آذر):
**روز اول (۱۴ آذر):**
- ✅ FrontOffice Proto Projects: 5 پروژه جدید
- ✅ FrontOffice UI Services: 4 سرویس به gRPC وصل شدند
- ✅ FrontOffice.BFF Handlers: 15+ handler
- ✅ Commission Calculation Fix
- ✅ Balance Cap Fix: 300 per leg
**روز دوم (۱۵ آذر):**
- ✅ Package Purchase UI: Packages.razor + MyPackages.razor
- ✅ VAT System: CheckoutSummary + OrderDetail
- ✅ OrderTracking: Timeline پیگیری سفارش
- ✅ ChangePassword: UI آماده (برای آینده)
- ✅ Documentation: جمع‌بندی نهایی
---
## 🗂️ Backlog (کارهای آینده)
### Low Priority - FrontOffice:
| تسک | توضیحات | اولویت |
|-----|---------|--------|
| **Password Authentication** | اضافه کردن لاگین با رمز عبور علاوه بر OTP | 🟡 Low |
| **ForgotPassword** | بازیابی رمز عبور (نیاز به CMS RPC) | 🟡 Low |
| **Contact Form Backend** | اتصال فرم تماس به CMS | 🟡 Low |
| **Product Reviews** | سیستم نظرات محصول | 🟡 Low |
| **Discount Code** | اعتبارسنجی کد تخفیف | 🟡 Low |
### High Priority - BackOffice (Admin):
| تسک | توضیحات | اولویت |
|-----|---------|--------|
| **Manual Payment UI** | صفحه مدیریت پرداخت‌های دستی | 🔴 High |
| **RBAC System** | سیستم کنترل دسترسی | 🔴 High |
---
## 🎯 Definition of Done
### برای هر Task:
- [x] Code Review Complete
- [x] Build با 0 error
- [x] Manual Testing انجام شده
- [x] Documentation بروز شده
---
## 📝 نتیجه‌گیری نهایی
### سمت مشتری (FrontOffice):
**آماده Production** - تمام قابلیت‌های اصلی پیاده‌سازی شده
### سمت ادمین (BackOffice):
🟡 **95% Complete** - Manual Payment و RBAC باقیمانده
---
**آخرین بروزرسانی**: ۱۵ آذر ۱۴۰۴ 🚀
@@ -0,0 +1,507 @@
# 🛒 Discount Shop - Implementation Plan
**تاریخ ایجاد**: ۱۰ دی ۱۴۰۴ (30 December 2025)
**وضعیت**: 🚧 در حال اجرا
**اولویت**: 🔴 بالا
---
## 📊 وضعیت فعلی
### ✅ موارد کامل شده:
| بخش | فایل‌ها | وضعیت |
|-----|---------|-------|
| **Domain Entities** | `DiscountProduct`, `DiscountCategory`, `DiscountOrder`, `DiscountOrderDetail`, `DiscountShoppingCart`, `DiscountProductCategory` | ✅ کامل |
| **CMS Commands** | Create/Update/Delete برای Product, Category, Order, Cart | ✅ کامل |
| **CMS Queries** | GetProducts, GetCategories, GetUserOrders, GetUserCart, GetOrderById | ✅ کامل |
| **Proto Files** | `discountproduct.proto`, `discountcategory.proto`, `discountorder.proto`, `discountshoppingcart.proto` | ✅ کامل |
| **BackOffice UI** | DiscountProductsMainPage, DiscountCategoriesMainPage, DiscountOrdersMainPage, SalesReports | ✅ کامل |
| **BackOffice Services** | IDiscountProductService, IDiscountCategoryService, IDiscountOrderService | ✅ کامل |
| **UI کامپوننت گالری** | ProductImageGallery.razor (فقط کلاینت، بدون Backend) | ⚠️ ناقص |
### ❌ موارد باقیمانده:
| # | مورد | توضیح | تخمین زمان |
|---|------|-------|------------|
| 1 | گالری تصاویر محصول | Entity + CRUD + Proto + Backend | 4 ساعت |
| 2 | API لیست سفارشات ادمین | GetAllDiscountOrders با فیلترها | 2 ساعت |
| 3 | محاسبه VAT | فعال‌سازی مالیات 10% در سفارشات | 1 ساعت |
| 4 | گزارش فروش | API آماری برای SalesReports | 2 ساعت |
| 5 | اتصال گالری به Backend | Upload + Service در BackOffice | 2 ساعت |
| **جمع** | | | **11 ساعت** |
---
## 📋 مراحل پیاده‌سازی
---
## مرحله ۱: گالری تصاویر محصول (Backend)
### 1.1 ایجاد Entity
**فایل**: `CMS/src/CMSMicroservice.Domain/Entities/DiscountShop/DiscountProductImage.cs`
```csharp
namespace CMSMicroservice.Domain.Entities.DiscountShop;
/// <summary>
/// تصویر گالری محصول تخفیفی
/// </summary>
public class DiscountProductImage : BaseAuditableEntity
{
/// <summary>
/// شناسه محصول
/// </summary>
public long DiscountProductId { get; set; }
/// <summary>
/// محصول
/// </summary>
public virtual DiscountProduct DiscountProduct { get; set; }
/// <summary>
/// عنوان تصویر
/// </summary>
public string? Title { get; set; }
/// <summary>
/// متن جایگزین (Alt)
/// </summary>
public string? AltText { get; set; }
/// <summary>
/// مسیر تصویر اصلی
/// </summary>
public string ImagePath { get; set; }
/// <summary>
/// مسیر تصویر کوچک
/// </summary>
public string? ThumbnailPath { get; set; }
/// <summary>
/// ترتیب نمایش
/// </summary>
public int SortOrder { get; set; }
/// <summary>
/// آیا تصویر اصلی محصول است؟
/// </summary>
public bool IsPrimary { get; set; }
}
```
### 1.2 ایجاد Configuration
**فایل**: `CMS/src/CMSMicroservice.Infrastructure/Persistence/Configurations/DiscountShop/DiscountProductImageConfiguration.cs`
```csharp
using CMSMicroservice.Domain.Entities.DiscountShop;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations.DiscountShop;
public class DiscountProductImageConfiguration : IEntityTypeConfiguration<DiscountProductImage>
{
public void Configure(EntityTypeBuilder<DiscountProductImage> builder)
{
builder.ToTable("DiscountProductImages");
builder.HasKey(x => x.Id);
builder.Property(x => x.Title)
.HasMaxLength(200);
builder.Property(x => x.AltText)
.HasMaxLength(500);
builder.Property(x => x.ImagePath)
.IsRequired()
.HasMaxLength(500);
builder.Property(x => x.ThumbnailPath)
.HasMaxLength(500);
builder.HasOne(x => x.DiscountProduct)
.WithMany(p => p.Images)
.HasForeignKey(x => x.DiscountProductId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(x => x.DiscountProductId);
builder.HasIndex(x => new { x.DiscountProductId, x.SortOrder });
}
}
```
### 1.3 به‌روزرسانی DiscountProduct Entity
**فایل**: `CMS/src/CMSMicroservice.Domain/Entities/DiscountShop/DiscountProduct.cs`
اضافه کردن Navigation Property:
```csharp
/// <summary>
/// تصاویر گالری محصول
/// </summary>
public virtual ICollection<DiscountProductImage> Images { get; set; }
```
### 1.4 به‌روزرسانی DbContext
**فایل**: `CMS/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs`
```csharp
public DbSet<DiscountProductImage> DiscountProductImages { get; set; }
```
### 1.5 ایجاد Migration
```bash
cd CMS/src/CMSMicroservice.Infrastructure
dotnet ef migrations add AddDiscountProductImages -s ../CMSMicroservice.WebApi
```
### 1.6 ایجاد Commands
**پوشه**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Commands/`
| Command | فایل‌ها |
|---------|---------|
| `AddDiscountProductImage` | Command.cs, Handler.cs, Validator.cs |
| `UpdateDiscountProductImage` | Command.cs, Handler.cs, Validator.cs |
| `DeleteDiscountProductImage` | Command.cs, Handler.cs |
| `ReorderDiscountProductImages` | Command.cs, Handler.cs |
### 1.7 ایجاد Query
**پوشه**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Queries/GetDiscountProductImages/`
| فایل | توضیح |
|------|-------|
| `GetDiscountProductImagesQuery.cs` | Request با ProductId |
| `GetDiscountProductImagesQueryHandler.cs` | Handler |
| `GetDiscountProductImagesResponseDto.cs` | Response با لیست تصاویر |
### 1.8 به‌روزرسانی Proto
**فایل**: `CMS/src/CMSMicroservice.Protobuf/Protos/discountproduct.proto`
```protobuf
// اضافه کردن به service DiscountProductContract:
rpc AddDiscountProductImage(AddDiscountProductImageRequest) returns (AddDiscountProductImageResponse);
rpc UpdateDiscountProductImage(UpdateDiscountProductImageRequest) returns (google.protobuf.Empty);
rpc DeleteDiscountProductImage(DeleteDiscountProductImageRequest) returns (google.protobuf.Empty);
rpc ReorderDiscountProductImages(ReorderDiscountProductImagesRequest) returns (google.protobuf.Empty);
rpc GetDiscountProductImages(GetDiscountProductImagesRequest) returns (GetDiscountProductImagesResponse);
// Messages:
message AddDiscountProductImageRequest {
int64 product_id = 1;
string title = 2;
string alt_text = 3;
string image_path = 4;
string thumbnail_path = 5;
int32 sort_order = 6;
bool is_primary = 7;
}
message AddDiscountProductImageResponse {
int64 image_id = 1;
}
message UpdateDiscountProductImageRequest {
int64 image_id = 1;
string title = 2;
string alt_text = 3;
string image_path = 4;
string thumbnail_path = 5;
int32 sort_order = 6;
bool is_primary = 7;
}
message DeleteDiscountProductImageRequest {
int64 image_id = 1;
}
message ReorderDiscountProductImagesRequest {
int64 product_id = 1;
repeated int64 image_ids = 2; // ترتیب جدید
}
message GetDiscountProductImagesRequest {
int64 product_id = 1;
}
message GetDiscountProductImagesResponse {
repeated DiscountProductImageDto images = 1;
}
message DiscountProductImageDto {
int64 id = 1;
int64 product_id = 2;
string title = 3;
string alt_text = 4;
string image_path = 5;
string thumbnail_path = 6;
int32 sort_order = 7;
bool is_primary = 8;
}
```
### 1.9 ایجاد gRPC Service Methods
**فایل**: `CMS/src/CMSMicroservice.WebApi/Services/DiscountProductService.cs`
اضافه کردن 5 متد جدید برای Image CRUD.
---
## مرحله ۲: API لیست سفارشات ادمین
### 2.1 ایجاد Query
**پوشه**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Queries/GetAllDiscountOrders/`
```csharp
// GetAllDiscountOrdersQuery.cs
public class GetAllDiscountOrdersQuery : IRequest<GetAllDiscountOrdersResponseDto>
{
public long? UserId { get; set; }
public bool? PaymentCompleted { get; set; }
public int? DeliveryStatus { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public string? SearchQuery { get; set; } // جستجو در شماره سفارش
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 20;
}
```
### 2.2 به‌روزرسانی Proto
**فایل**: `CMS/src/CMSMicroservice.Protobuf/Protos/discountorder.proto`
```protobuf
// اضافه کردن به service:
rpc GetAllDiscountOrders(GetAllDiscountOrdersRequest) returns (GetAllDiscountOrdersResponse);
message GetAllDiscountOrdersRequest {
google.protobuf.Int64Value user_id = 1;
google.protobuf.BoolValue payment_completed = 2;
google.protobuf.Int32Value delivery_status = 3;
google.protobuf.Timestamp from_date = 4;
google.protobuf.Timestamp to_date = 5;
google.protobuf.StringValue search_query = 6;
int32 page_number = 7;
int32 page_size = 8;
}
message GetAllDiscountOrdersResponse {
messages.MetaData meta_data = 1;
repeated OrderSummaryDto models = 2;
}
```
### 2.3 به‌روزرسانی BackOffice Service
**فایل**: `BackOffice/src/BackOffice/Services/DiscountOrder/DiscountOrderService.cs`
تغییر `GetOrdersAsync` برای استفاده از API جدید (GetAllDiscountOrders به جای GetUserOrders با UserId=0).
---
## مرحله ۳: فعال‌سازی محاسبه VAT
### 3.1 به‌روزرسانی PlaceOrderCommandHandler
**فایل**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Commands/PlaceOrder/PlaceOrderCommandHandler.cs`
```csharp
// محاسبه VAT (10%)
const decimal VatRate = 0.10m;
var vatAmount = (long)(totalAmount * VatRate);
order.VatAmount = vatAmount;
// مبلغ نهایی شامل VAT
var finalAmount = totalAmount + vatAmount;
```
### 3.2 به‌روزرسانی Proto Response
**فایل**: `CMS/src/CMSMicroservice.Protobuf/Protos/discountorder.proto`
اضافه کردن `int64 vat_amount` به `PlaceOrderResponse` و `GetOrderByIdResponse`.
### 3.3 به‌روزرسانی UI
**فایل**: `BackOffice/src/BackOffice/Pages/DiscountShop/Components/OrderDetailsDialog.razor`
نمایش VAT در جزئیات سفارش.
---
## مرحله ۴: گزارش فروش (Statistics API)
### 4.1 ایجاد Query
**پوشه**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Queries/GetDiscountShopStatistics/`
```csharp
public class GetDiscountShopStatisticsResponseDto
{
// خلاصه کلی
public long TotalSales { get; set; }
public int TotalOrders { get; set; }
public int TotalProducts { get; set; }
public int TotalCustomers { get; set; }
// گزارش بازه زمانی
public long PeriodSales { get; set; }
public int PeriodOrders { get; set; }
// پرفروش‌ترین‌ها
public List<TopProductDto> TopProducts { get; set; }
// فروش روزانه (برای نمودار)
public List<DailySalesDto> DailySales { get; set; }
}
public class TopProductDto
{
public long ProductId { get; set; }
public string Title { get; set; }
public int SalesCount { get; set; }
public long TotalRevenue { get; set; }
}
public class DailySalesDto
{
public DateTime Date { get; set; }
public long Amount { get; set; }
public int OrderCount { get; set; }
}
```
### 4.2 به‌روزرسانی Proto
**فایل**: `CMS/src/CMSMicroservice.Protobuf/Protos/discountorder.proto`
```protobuf
rpc GetDiscountShopStatistics(GetDiscountShopStatisticsRequest) returns (GetDiscountShopStatisticsResponse);
message GetDiscountShopStatisticsRequest {
google.protobuf.Timestamp from_date = 1;
google.protobuf.Timestamp to_date = 2;
}
message GetDiscountShopStatisticsResponse {
int64 total_sales = 1;
int32 total_orders = 2;
int32 total_products = 3;
int32 total_customers = 4;
int64 period_sales = 5;
int32 period_orders = 6;
repeated TopProductDto top_products = 7;
repeated DailySalesDto daily_sales = 8;
}
```
### 4.3 تکمیل SalesReports.razor
**فایل**: `BackOffice/src/BackOffice/Pages/DiscountShop/SalesReports.razor`
اتصال به API جدید و نمایش:
- کارت‌های آماری (Total Sales, Orders, Products, Customers)
- جدول پرفروش‌ترین محصولات
- نمودار فروش روزانه (MudChart)
---
## مرحله ۵: اتصال گالری به Backend
### 5.1 ایجاد Service در BackOffice
**فایل**: `BackOffice/src/BackOffice/Services/DiscountProduct/IDiscountProductService.cs`
اضافه کردن متدهای:
```csharp
Task<List<DiscountProductImageDto>> GetProductImagesAsync(long productId);
Task<long> AddProductImageAsync(AddProductImageDto dto);
Task UpdateProductImageAsync(UpdateProductImageDto dto);
Task DeleteProductImageAsync(long imageId);
Task ReorderProductImagesAsync(long productId, List<long> imageIds);
```
### 5.2 به‌روزرسانی ProductFormDialog
**فایل**: `BackOffice/src/BackOffice/Pages/DiscountShop/Components/ProductFormDialog.razor`
- در حالت Edit: بارگذاری تصاویر موجود از API
- اتصال کامپوننت `ProductImageGallery` به متدهای Service
- ذخیره تغییرات گالری همزمان با ذخیره محصول
### 5.3 آپلود فایل
بررسی سیستم آپلود موجود در پروژه:
- اگر MinIO/S3 استفاده می‌شود: استفاده از همان سرویس
- اگر فایل‌سیستم: ایجاد endpoint آپلود در CMS
---
## 📝 Checklist
### مرحله ۱: گالری تصاویر
- [ ] ایجاد Entity `DiscountProductImage`
- [ ] ایجاد Configuration
- [ ] به‌روزرسانی `DiscountProduct` Entity
- [ ] به‌روزرسانی DbContext
- [ ] ایجاد و اجرای Migration
- [ ] ایجاد Commands (Add, Update, Delete, Reorder)
- [ ] ایجاد Query (GetImages)
- [ ] به‌روزرسانی Proto
- [ ] پیاده‌سازی gRPC Service Methods
- [ ] تست با Postman/gRPCurl
### مرحله ۲: API سفارشات ادمین
- [ ] ایجاد Query `GetAllDiscountOrders`
- [ ] به‌روزرسانی Proto
- [ ] پیاده‌سازی gRPC Service Method
- [ ] به‌روزرسانی BackOffice Service
- [ ] تست UI DiscountOrdersMainPage
### مرحله ۳: محاسبه VAT
- [ ] به‌روزرسانی `PlaceOrderCommandHandler`
- [ ] به‌روزرسانی Proto (VatAmount در responses)
- [ ] به‌روزرسانی UI نمایش سفارش
- [ ] تست محاسبه VAT
### مرحله ۴: گزارش فروش
- [ ] ایجاد Query `GetDiscountShopStatistics`
- [ ] به‌روزرسانی Proto
- [ ] پیاده‌سازی gRPC Service Method
- [ ] ایجاد Service در BackOffice
- [ ] تکمیل UI `SalesReports.razor`
### مرحله ۵: اتصال گالری
- [ ] اضافه کردن متدهای Image به Service
- [ ] به‌روزرسانی ProductFormDialog
- [ ] پیاده‌سازی/اتصال به سیستم آپلود
- [ ] تست کامل گالری
---
## 🔗 فایل‌های مرتبط
| فایل | توضیح |
|------|-------|
| `totalDoc/01-BUSINESS/discount-shop-business.md` | مستندات اصلی طراحی |
| `totalDoc/03-BACKEND/BackOffice.BFF/discount-shop-integration.md` | پلن اولیه integration |
| `CMS/src/CMSMicroservice.Domain/Entities/DiscountShop/` | Entity های موجود |
| `CMS/src/CMSMicroservice.Application/DiscountShopCQ/` | Commands و Queries موجود |
| `BackOffice/src/BackOffice/Pages/DiscountShop/` | صفحات UI موجود |
---
**آخرین به‌روزرسانی**: ۱۰ دی ۱۴۰۴
@@ -0,0 +1,617 @@
# 📋 Task List - توضیحات جدید بیزینس 2025-12-08
**تاریخ ایجاد**: 2025-12-08
**آخرین به‌روزرسانی**: 2025-12-09
**منبع**: تحلیل توضیحات شفاهی جدید بیزینس
**وضعیت**: ✅ Task #0 Complete, بقیه آماده برای اجرا
---
## ✅ Completed Tasks
### ~~Task #0: اصلاح محاسبات تعادل و فلش~~ ✅
**شرح**:
کد محاسبه تعادل با توضیحات دقیق بیزینس تطبیق داده شد.
**انجام شده**:
- ✅ ترتیب محاسبات اصلاح شد (تعادل → باقیمانده → سقف → فلش)
- ✅ فلش از هر دو طرف محاسبه می‌شود
- ✅ باقیمانده جداگانه ذخیره می‌شود (چپ و راست)
- ✅ Documentation به‌روزرسانی شد
- ✅ مثال‌های 5 لول عمقی اضافه شد
**فایل‌های تغییر یافته**:
```
CMS/src/.../CalculateWeeklyBalancesCommandHandler.cs (اصلاح شد)
totalDoc/01-BUSINESS/balance-calculation-rules.md (به‌روزرسانی شد)
totalDoc/01-BUSINESS/balance-calculation-examples-5-levels.md (جدید)
```
**تاریخ اتمام**: 2025-12-09
---
## 🔥 Priority 1: Critical Tasks
### Task #1: پیاده‌سازی Worker حذف خودکار کاربران غیرفعال
**شرح**:
کاربرانی که تا 2 هفته بعد از ثبت نام هیچکدام از موارد زیر را انجام ندادند باید به صورت خودکار حذف شوند:
- وام دایا نگرفتند
- پرداخت مستقیم 56 میلیون نکردند
**Acceptance Criteria**:
- [ ] Worker روزانه یک بار اجرا شود (مثلاً ساعت 3 صبح)
- [ ] کاربرانی با `CreatedAt < Now - 14 days` و `IsActive = false` و `ClubMembershipId = null` شناسایی شوند
- [ ] کاربر به صورت Soft Delete حذف شود (یا Hard Delete بر اساس تصمیم)
- [ ] جایگاه شبکه (Network Position) آزاد شود
- [ ] معرف (Parent) بتواند دوباره کاربر جدید جذب کند
- [ ] Log کامل عملیات حذف ثبت شود
**فایل‌های نیاز به ایجاد/تغییر**:
```
CMS/src/CMSMicroservice.WebApi/BackgroundWorkers/
└── DeleteInactiveUsersJob.cs (جدید)
CMS/src/CMSMicroservice.Application/UserCQ/Commands/
└── DeleteInactiveUser/
├── DeleteInactiveUserCommand.cs (جدید)
└── DeleteInactiveUserCommandHandler.cs (جدید)
CMS/src/CMSMicroservice.WebApi/Program.cs
└── services.AddHostedService<DeleteInactiveUsersJob>();
```
**کد پیشنهادی**:
```csharp
public class DeleteInactiveUsersJob : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<DeleteInactiveUsersJob> _logger;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// محاسبه زمان اجرا (3 صبح)
var now = DateTime.Now;
var next3AM = now.Date.AddDays(1).AddHours(3);
var delay = next3AM - now;
await Task.Delay(delay, stoppingToken);
using var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<IApplicationDbContext>();
var twoWeeksAgo = DateTime.Now.AddDays(-14);
var inactiveUsers = await context.Users
.Where(u => u.Created < twoWeeksAgo
&& u.ClubMembershipId == null
&& !u.IsActive)
.ToListAsync(stoppingToken);
_logger.LogInformation($"🧹 حذف {inactiveUsers.Count} کاربر غیرفعال بیش از 2 هفته");
foreach (var user in inactiveUsers)
{
// حذف کاربر
user.IsDeleted = true; // Soft Delete
user.DeletedAt = DateTime.Now;
// آزادسازی جایگاه شبکه
// (NetworkParentId را null نکنید چون تاریخچه نیاز دارد)
_logger.LogWarning($"❌ حذف کاربر: {user.Id} - {user.UserName}");
}
await context.SaveChangesAsync(stoppingToken);
}
}
}
```
**تست**:
1. کاربر جدید با `CreatedAt = DateTime.Now.AddDays(-15)` ایجاد کنید
2. `IsActive = false`, `ClubMembershipId = null`
3. Worker را مجبور به اجرا کنید (یا زمان را تغییر دهید)
4. چک کنید: `user.IsDeleted = true`
**تخمین زمان**: 4-6 ساعت
---
### Task #2: الزامی کردن دیالوگ باشگاه مشتریان
**شرح**:
بعد از پرداخت موفق 56 میلیون تومان، کاربر **باید** قرارداد باشگاه مشتریان را امضا کند. تا زمانی که امضا نکند، نمی‌تواند به سایر بخش‌های سیستم دسترسی داشته باشد و لینک معرفی خود را ببیند.
**Acceptance Criteria**:
- [ ] بعد از تأیید پرداخت، Modal/Dialog باشگاه مشتریان باز شود
- [ ] دکمه Close غیرفعال باشد (یا Modal با `disableBackdropClick` باز شود)
- [ ] کاربر نتواند از دیالوگ خارج شود (ESC هم کار نکند)
- [ ] بعد از امضای قرارداد:
- `ClubMembership` record ایجاد شود
- `User.ClubMembershipId` Set شود
- 25 میلیون تومان به `WeeklyCommissionPool` اضافه شود
- [ ] بعد از امضا، redirect به Dashboard
- [ ] در Dashboard لینک معرفی نمایش داده شود
**فایل‌های نیاز به تغییر**:
```
FrontOffice/src/FrontOffice.Main/Pages/
└── Payment/PaymentSuccess.razor
└── Payment/PaymentSuccess.razor.cs
FrontOffice/src/FrontOffice.Main/Components/
└── ClubMembershipDialog.razor (جدید یا اصلاح)
CMS/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/
└── CreateClubMembership/
├── CreateClubMembershipCommand.cs
└── CreateClubMembershipCommandHandler.cs
```
**کد پیشنهادی (Frontend)**:
```razor
@* PaymentSuccess.razor *@
@if (_showClubDialog)
{
<MudDialog @bind-IsVisible="_showClubDialog"
Options="@(new DialogOptions {
DisableBackdropClick = true,
CloseButton = false
})">
<DialogContent>
<h3>عضویت در باشگاه مشتریان</h3>
<p>برای ادامه، لطفاً قرارداد باشگاه مشتریان را مطالعه و امضا کنید.</p>
<MudPaper Class="pa-4 my-4" Elevation="2">
<p>متن قرارداد...</p>
</MudPaper>
<MudCheckBox @bind-Checked="_agreedToTerms">
متن قرارداد را مطالعه کردم و با آن موافقم
</MudCheckBox>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Disabled="!_agreedToTerms"
OnClick="SignContract">
امضای قرارداد
</MudButton>
</DialogActions>
</MudDialog>
}
```
```csharp
// PaymentSuccess.razor.cs
private bool _showClubDialog = false;
private bool _agreedToTerms = false;
protected override async Task OnInitializedAsync()
{
// بعد از تأیید پرداخت
if (PaymentConfirmed && !User.ClubMembershipId.HasValue)
{
_showClubDialog = true;
}
}
private async Task SignContract()
{
var request = new CreateClubMembershipRequest
{
UserId = User.Id,
InitialContribution = 25000000
};
await ClubMembershipContract.CreateClubMembershipAsync(request);
_showClubDialog = false;
NavigationManager.NavigateTo("/dashboard");
}
```
**تست**:
1. پرداخت 56M انجام دهید
2. بعد از موفقیت، باید Dialog باز شود
3. سعی کنید Close کنید → نشود
4. بدون tick نزدن → دکمه غیرفعال باشد
5. tick بزنید و امضا کنید → redirect به Dashboard
6. لینک معرفی نمایش داده شود
**تخمین زمان**: 6-8 ساعت
---
### Task #3: شرط نمایش لینک معرفی
**شرح**:
لینک معرفی فقط باید برای کاربرانی نمایش داده شود که:
1. پرداخت کرده‌اند (`IsActive = true`)
2. عضو باشگاه مشتریان شده‌اند (`ClubMembershipId != null`)
3. عضویت باشگاه فعال است (`ClubMembership.IsActive = true`)
**Acceptance Criteria**:
- [ ] در صفحه Dashboard یا Profile، شرط بالا چک شود
- [ ] اگر شرایط برقرار نیست:
- پیغام: "برای دریافت لینک معرفی، ابتدا عضو باشگاه مشتریان شوید"
- دکمه "عضویت در باشگاه" (در صورت عدم عضویت)
- [ ] اگر شرایط برقرار است:
- لینک معرفی نمایش داده شود
- دکمه کپی
- QR Code (اختیاری)
**فایل‌های نیاز به تغییر**:
```
FrontOffice/src/FrontOffice.Main/Pages/
└── Dashboard/Dashboard.razor
└── Dashboard/Dashboard.razor.cs
یا
FrontOffice/src/FrontOffice.Main/Pages/
└── Profile/MyProfile.razor
```
**کد پیشنهادی**:
```razor
@if (CanShowReferralLink)
{
<MudCard Class="my-4">
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.h6">🔗 لینک معرفی شما</MudText>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent>
<MudTextField @bind-Value="_referralLink"
ReadOnly="true"
Adornment="Adornment.End"
AdornmentIcon="@Icons.Material.Filled.ContentCopy"
OnAdornmentClick="CopyLink"/>
</MudCardContent>
</MudCard>
}
else
{
<MudAlert Severity="Severity.Warning" Class="my-4">
برای دریافت لینک معرفی، ابتدا عضو باشگاه مشتریان شوید.
@if (!User.ClubMembershipId.HasValue)
{
<MudButton Color="Color.Primary"
Variant="Variant.Filled"
Class="mt-2"
OnClick="OpenClubDialog">
عضویت در باشگاه
</MudButton>
}
</MudAlert>
}
```
```csharp
private bool CanShowReferralLink =>
User.IsActive
&& User.ClubMembershipId.HasValue
&& User.ClubMembership?.IsActive == true;
```
**تست**:
1. کاربر بدون `ClubMembership` → Alert نمایش داده شود
2. کاربر با `ClubMembership` فعال → لینک نمایش داده شود
3. دکمه کپی کار کند
**تخمین زمان**: 3-4 ساعت
---
## ⚠️ Priority 2: Medium Tasks
### Task #4: Validation دقیق‌تر محدودیت 2 فرزند فعال
**شرح**:
در هنگام ثبت نام با کد معرف، باید بررسی شود که آیا Parent حداکثر **2 فرزند فعال** دارد یا نه (نه فقط 2 فرزند).
**Acceptance Criteria**:
- [ ] Validation در `CreateUserCommandHandler` یا `NetworkPlacementService`
- [ ] شمارش فرزندان با شرط:
```csharp
u.NetworkParentId == parentId
&& u.IsActive
&& u.ClubMembershipId != null
```
- [ ] اگر `activeChildCount >= 2`:
- Exception: "این کاربر تعداد زیرمجموعه‌هاش پر شده و شما نمی‌تونید جزو زیرمجموعه این آدم بشید"
- یا Auto-Placement (بر اساس تصمیم)
**فایل‌های نیاز به تغییر**:
```
CMS/src/CMSMicroservice.Application/Services/
└── NetworkPlacementService.cs
CMS/src/CMSMicroservice.Application/UserCQ/Commands/CreateUser/
└── CreateUserCommandHandler.cs
└── CreateUserCommandValidator.cs
```
**کد پیشنهادی**:
```csharp
public async Task<NetworkLeg?> CalculateLegPositionAsync(long parentId, CancellationToken cancellationToken)
{
var activeChildrenCount = await _context.Users
.CountAsync(u => u.NetworkParentId == parentId
&& u.IsActive
&& u.ClubMembershipId != null,
cancellationToken);
if (activeChildrenCount >= 2)
{
throw new InvalidOperationException(
"این کاربر تعداد زیرمجموعه‌هاش پر شده و شما نمی‌تونید جزو زیرمجموعه این آدم بشید");
}
// بررسی Left و Right
var hasLeft = await _context.Users
.AnyAsync(u => u.NetworkParentId == parentId
&& u.LegPosition == NetworkLeg.Left
&& u.IsActive
&& u.ClubMembershipId != null,
cancellationToken);
if (!hasLeft) return NetworkLeg.Left;
var hasRight = await _context.Users
.AnyAsync(u => u.NetworkParentId == parentId
&& u.LegPosition == NetworkLeg.Right
&& u.IsActive
&& u.ClubMembershipId != null,
cancellationToken);
if (!hasRight) return NetworkLeg.Right;
return null; // هر دو پر است
}
```
**تست**:
1. Parent با 2 فرزند فعال
2. ثبت نام با کد این Parent
3. باید Exception بیاید
**تخمین زمان**: 3-4 ساعت
---
### Task #5: بهبود پیغام خطای کد معرف پر
**شرح**:
در صفحه ثبت نام، اگر کاربر کد معرفی وارد کند که ظرفیتش پر است، باید پیغام خطای واضح و فارسی نمایش داده شود.
**Acceptance Criteria**:
- [ ] در Frontend، بعد از وارد کردن کد معرف، validation شود
- [ ] اگر کد پر بود، پیغام:
> "این کد معرف ظرفیتش پر شده، لطفا از کد معرف دیگری استفاده کنید"
- [ ] Snackbar یا Alert با Severity.Warning
- [ ] فیلد کد معرف هایلایت شود (قرمز)
**فایل‌های نیاز به تغییر**:
```
FrontOffice/src/FrontOffice.Main/Pages/
└── Register.razor
└── Register.razor.cs
```
**کد پیشنهادی**:
```csharp
private async Task ValidateReferralCode()
{
if (string.IsNullOrWhiteSpace(_referralCode))
return;
try
{
var request = new ValidateReferralCodeRequest
{
ReferralCode = _referralCode
};
var response = await UserContract.ValidateReferralCodeAsync(request);
if (!response.IsValid)
{
_referralCodeError = "کد معرف نامعتبر است";
}
else if (response.IsFull)
{
_referralCodeError = "این کد معرف ظرفیتش پر شده، لطفا از کد معرف دیگری استفاده کنید";
Snackbar.Add(_referralCodeError, Severity.Warning);
}
}
catch (Exception ex)
{
_referralCodeError = "خطا در بررسی کد معرف";
}
}
```
**تست**:
1. Parent پر را پیدا کنید
2. کد معرف او را در Register وارد کنید
3. پیغام واضح نمایش داده شود
**تخمین زمان**: 2-3 ساعت
---
## 📝 Priority 3: Documentation Tasks
### Task #6: به‌روزرسانی مستندات
**شرح**:
با توجه به توضیحات جدید، داکیومنت‌های زیر باید Update شوند.
**فایل‌های نیاز به تغییر**:
#### 1. `totalDoc/01-BUSINESS/network-commission-system.md`
```markdown
# اضافه کردن بخش جدید:
## ۱۰. حذف خودکار کاربران غیرفعال
کاربرانی که تا 2 هفته بعد از ثبت نام:
- وام دایا نگرفته‌اند
- پرداخت مستقیم 56 میلیون نکرده‌اند
به صورت خودکار حذف می‌شوند.
**Worker**: `DeleteInactiveUsersJob`
**زمان اجرا**: روزانه ساعت 3 صبح
**منطق**: `CreatedAt < Now - 14 days && !IsActive && ClubMembershipId == null`
---
## ۱۱. شرایط نمایش لینک معرفی
لینک معرفی فقط برای کاربرانی نمایش داده می‌شود که:
1. پرداخت کرده‌اند (IsActive = true)
2. عضو باشگاه مشتریان شده‌اند (ClubMembershipId != null)
3. عضویت باشگاه فعال است (ClubMembership.IsActive = true)
**تا زمانی که این شرایط برقرار نباشد، کاربر نمی‌تواند لینک معرفی خود را ببیند.**
---
## ۱۲. الزامی بودن دیالوگ باشگاه مشتریان
بعد از پرداخت موفق 56 میلیون تومان، کاربر **باید** قرارداد باشگاه مشتریان را امضا کند.
**فرآیند**:
1. پرداخت موفق
2. Dialog باشگاه مشتریان باز می‌شود
3. کاربر نمی‌تواند Dialog را ببندد
4. باید قرارداد را بخواند و امضا کند
5. بعد از امضا → redirect به Dashboard
6. لینک معرفی نمایش داده می‌شود
```
#### 2. `totalDoc/01-BUSINESS/binary-tree-guide.md`
```markdown
# اصلاح بخش Validation:
### محدودیت 2 فرزند **فعال**
هر Parent فقط می‌تواند **2 فرزند فعال** داشته باشد.
**تعریف فعال**:
- IsActive = true
- ClubMembershipId != null
- عضویت باشگاه فعال است
**نکته مهم**: کاربرانی که ثبت نام کرده‌اند اما هنوز فعال نشده‌اند، در شمارش 2 فرزند محسوب نمی‌شوند.
```
#### 3. `totalDoc/03-BACKEND/CMS/implementation-status.md`
```markdown
# افزودن به بخش Background Workers:
### ✅ DeleteInactiveUsersWorker (NEW - 2025-12-08)
**وضعیت**: 🔴 نیاز به پیاده‌سازی
**شرح**: حذف خودکار کاربران غیرفعال بعد از 2 هفته
**منطق**:
- روزانه ساعت 3 صبح اجرا می‌شود
- کاربرانی که `CreatedAt < Now - 14 days`
- و `IsActive = false`
- و `ClubMembershipId = null`
- به صورت Soft Delete حذف می‌شوند
**فایل**: `CMS/BackgroundWorkers/DeleteInactiveUsersJob.cs`
**Dependencies**:
- IApplicationDbContext
- ILogger
```
#### 4. `totalDoc/05-TASKS/BACKLOG.md`
```markdown
# اضافه کردن این 5 Task به Backlog
## 🔥 Critical
- [ ] Task #1: پیاده‌سازی DeleteInactiveUsersWorker (6h)
- [ ] Task #2: الزامی کردن دیالوگ باشگاه (8h)
- [ ] Task #3: شرط نمایش لینک معرفی (4h)
## ⚠️ Medium
- [ ] Task #4: Validation 2 فرزند فعال (4h)
- [ ] Task #5: پیغام خطای کد معرف پر (3h)
## 📝 Low
- [ ] Task #6: Update Documentation (2h)
**زمان کل**: 27 ساعت (~4 روز کاری)
```
**تخمین زمان**: 2-3 ساعت
---
## 📊 خلاصه Task ها
| # | عنوان | Priority | زمان | وضعیت |
|---|--------|----------|------|--------|
| 1 | DeleteInactiveUsersWorker | 🔥 Critical | 6h | ⬜ Todo |
| 2 | الزامی دیالوگ باشگاه | 🔥 Critical | 8h | ⬜ Todo |
| 3 | شرط لینک معرفی | 🔥 Critical | 4h | ⬜ Todo |
| 4 | Validation 2 فرزند فعال | ⚠️ Medium | 4h | ⬜ Todo |
| 5 | پیغام کد معرف پر | ⚠️ Medium | 3h | ⬜ Todo |
| 6 | Update Documentation | 📝 Low | 3h | ⬜ Todo |
**مجموع زمان**: 28 ساعت (~4 روز کاری)
---
## 🎯 پلان اجرا (پیشنهادی)
### روز 1 (8 ساعت):
- [ ] Task #1: DeleteInactiveUsersWorker (6h)
- [ ] شروع Task #2 (2h)
### روز 2 (8 ساعت):
- [ ] ادامه Task #2: Dialog الزامی (6h)
- [ ] شروع Task #3 (2h)
### روز 3 (8 ساعت):
- [ ] ادامه Task #3: شرط لینک (2h)
- [ ] Task #4: Validation (4h)
- [ ] شروع Task #5 (2h)
### روز 4 (4 ساعت):
- [ ] ادامه Task #5 (1h)
- [ ] Task #6: Documentation (3h)
---
## ✅ Definition of Done
هر Task زمانی Complete حساب می‌شود که:
1. ✅ کد نوشته شده و Build موفق
2. ✅ Unit Test / Manual Test انجام شده
3. ✅ Code Review شده
4. ✅ Documentation به‌روز شده
5. ✅ Merge به Main Branch
---
**تهیه‌کننده**: AI Assistant
**تاریخ**: 2025-12-08
**نسخه**: 1.0
+250
View File
@@ -0,0 +1,250 @@
# گزارش بررسی تطبیق بیزینس با کد
**تاریخ بررسی**: _________
**بررسی‌کننده**: _________
**نسخه کد**: _________
---
## ✅ بیزینس 1: Binary Tree (درخت دودویی)
### بررسی کد:
```bash
# دستور اجرا شده:
cd CMS/src/CMSMicroservice.Domain/Entities
grep -A 20 "class User" User.cs | grep -E "Parent|Left|Right"
```
### نتیجه:
- [ ] ✅ User دارای Parent, LeftChild, RightChild است
- [ ] ✅ Spillover Logic پیاده‌سازی شده
- [ ] ✅ Depth محاسبه می‌شود
- [ ] ✅ Parent تغییر نمی‌کند
### تست عملی:
```
ثبت‌نام 7 کاربر:
- User1 (Root)
- User2 (Left of 1)
- User3 (Right of 1)
- User4 (Left of 2) ✓
- User5 (Right of 2) ✓
- User6 (Left of 3) ✓
- User7 (Right of 3) ✓
نتیجه: [✅ موفق] [❌ ناموفق]
```
### تطبیق با داکیومنت:
- فایل: `totalDoc/CMS/binary-tree-registration-guide.md`
- وضعیت: [✅ تطبیق کامل] [⚠️ نیاز به به‌روزرسانی] [❌ عدم تطبیق]
- توضیحات: _________________
---
## 💰 بیزینس 2: محاسبه کمیسیون
### بررسی کد:
```bash
# Cron Job
cd CMS/src/CMSMicroservice.Application/BackgroundWorkers
grep "Cron.*Sunday" -r .
# فرمول
grep "TotalPV.*Percentage" -r .
```
### نتیجه:
- [ ] ✅ Cron: یکشنبه 00:05 UTC
- [ ] ✅ فرمول: Commission = TotalPV × Percentage
- [ ] ✅ MinimumPV چک می‌شود
- [ ] ✅ CarryOver به هفته بعد
- [ ] ✅ MaxCommission رعایت می‌شود
### تست عملی:
```
User: TestUser1
PV این هفته: 1000
Percentage: 10%
MinimumPV: 500
محاسبه شده: _______
انتظار: 100
نتیجه: [✅ موفق] [❌ ناموفق]
```
### تطبیق با داکیومنت:
- فایل: `totalDoc/CMS/network-club-commission-system-v1.1.md`
- وضعیت: [✅ تطبیق کامل] [⚠️ نیاز به به‌روزرسانی] [❌ عدم تطبیق]
- توضیحات: _________________
---
## 🏆 بیزینس 3: سطوح باشگاه
### بررسی کد:
```bash
cd CMS/src/CMSMicroservice.Domain/Entities
grep -A 10 "ClubLevel" ClubMembership.cs
```
### نتیجه:
- [ ] ✅ 4 سطح: Bronze, Silver, Gold, Platinum
- [ ] ✅ شرط ارتقا پیاده‌سازی شده
- [ ] ✅ سطح پایین نمی‌آید
- [ ] ✅ Duration (ماهانه/سالانه)
### تست عملی:
```
User: TestUser2
PV فعلی: 5000 (Bronze)
شرط Silver: 10000 PV
بعد از رسیدن به 10000:
- سطح فعلی: _______
- انتظار: Silver
نتیجه: [✅ موفق] [❌ ناموفق]
```
### تطبیق با داکیومنت:
- فایل: `totalDoc/CMS/network-club-commission-system-v1.1.md`
- وضعیت: [✅ تطبیق کامل] [⚠️ نیاز به به‌روزرسانی] [❌ عدم تطبیق]
- توضیحات: _________________
---
## 💳 بیزینس 4: برداشت (Withdrawal)
### بررسی کد:
```bash
cd CMS/src/CMSMicroservice.Application/WithdrawalCQ
ls -1 *Command.cs
```
### نتیجه:
- [ ] ✅ حداقل موجودی چک می‌شود
- [ ] ✅ کارمزد محاسبه می‌شود
- [ ] ✅ وضعیت‌ها: Pending/Approved/Rejected
- [ ] ✅ فقط مدیر می‌تواند تأیید کند
- [ ] ✅ واریز بعد از Approve
### تست عملی:
```
موجودی: 200,000
درخواست برداشت: 150,000
کارمزد 2%: 3,000
مبلغ نهایی: 147,000
نتیجه: [✅ موفق] [❌ ناموفق]
```
### تطبیق با داکیومنت:
- فایل: `totalDoc/CMS/implementation-progress.md`
- وضعیت: [✅ تطبیق کامل] [⚠️ نیاز به به‌روزرسانی] [❌ عدم تطبیق]
- توضیحات: _________________
---
## 📧 بیزینس 5: اطلاع‌رسانی (Email/SMS)
### بررسی کد:
```bash
cd CMS/src/CMSMicroservice.Application/Common/Services
ls -1 *Notification*
```
### نتیجه:
- [ ] ✅ Email برای Commission ارسال می‌شود
- [ ] ✅ SMS برای تأیید موبایل
- [ ] ✅ Template های HTML
- [ ] ✅ ارسال بلافاصله بعد از event
### تست عملی:
```
Event: Commission Calculated
User Email: test@example.com
Email دریافت شد؟ [✅ بله] [❌ خیر]
محتوای Email صحیح؟ [✅ بله] [❌ خیر]
نتیجه: [✅ موفق] [❌ ناموفق]
```
### تطبیق با داکیومنت:
- فایل: `totalDoc/CMS/email-sms-configuration-guide.md`
- وضعیت: [✅ تطبیق کامل] [⚠️ نیاز به به‌روزرسانی] [❌ عدم تطبیق]
- توضیحات: _________________
---
## 🛒 بیزینس 6: سفارش و فاکتور
### بررسی کد:
```bash
cd CMS/src/CMSMicroservice.Domain/Entities
grep -A 20 "class UserOrder" UserOrder.cs
```
### نتیجه:
- [ ] ✅ UserOrder و FactorDetail
- [ ] ✅ محاسبه PV
- [ ] ✅ وضعیت سفارش
- [ ] ✅ VatPercentage اضافه شده
### تست عملی:
```
محصول 1: قیمت 100,000، PV: 50
محصول 2: قیمت 200,000، PV: 100
جمع PV: _______
انتظار: 150
VAT 10%: _______
انتظار: 30,000
نتیجه: [✅ موفق] [❌ ناموفق]
```
### تطبیق با داکیومنت:
- فایل: `totalDoc/CMS/network-club-commission-system-v1.1.md`
- وضعیت: [✅ تطبیق کامل] [⚠️ نیاز به به‌روزرسانی] [❌ عدم تطبیق]
- توضیحات: _________________
---
## 📊 خلاصه نتایج
### آمار کلی:
- تعداد بیزینس بررسی شده: 6
- تطبیق کامل: _____ (___%)
- نیاز به به‌روزرسانی داکیومنت: _____
- عدم تطبیق (Bug): _____
### موارد نیازمند اقدام فوری:
1. _________________
2. _________________
3. _________________
### موارد نیازمند به‌روزرسانی داکیومنت:
1. _________________
2. _________________
3. _________________
---
## 🎯 اقدامات بعدی
### این هفته:
- [ ] _________________
- [ ] _________________
### ماه آینده:
- [ ] _________________
- [ ] _________________
---
**امضا**: _________
**تاریخ تکمیل گزارش**: _________