Compare commits

..

10 Commits

Author SHA1 Message Date
masoodafar-web 5965b98728 update 2026-01-03 18:27:49 +03:30
masoodafar-web 0369292d7f feat: Update inventory system plan with completion date and BFF synchronization details 2026-01-03 16:00:37 +03:30
masoodafar-web 86c4d9ce70 Refactor code structure for improved readability and maintainability 2026-01-03 07:38:05 +03:30
masoodafar-web 73e1971cc3 feat: Implement Discount Shop Completion Plan with Product Image Gallery, Admin APIs, VAT Calculation, and Sales Reports
- Added DiscountProductImage entity and related configurations for product image gallery.
- Created commands and queries for managing product images.
- Developed GetAllDiscountOrders API for admin order management with various filters.
- Implemented VAT calculation service and integrated it into order processing.
- Created Sales Reports API with support for daily, weekly, and monthly reports.
- Completed gRPC services for BackOffice.BFF to expose new APIs.
- Updated Proto files and project references accordingly.
2026-01-02 00:46:08 +03:30
masoodafar-web df650c3886 feat: Update changelog and documentation for version 2.7, including Commission Data Flow Fix, UI improvements, and terminology cleanup 2025-12-29 00:40:13 +03:30
masoodafar-web 4f999033bd feat: Update documentation with recent fixes and enhancements including Commission System and mapping issues 2025-12-27 22:03:21 +03:30
masoodafar-web 6220049161 feat: Enhance CMS Microservice with SystemConstants and SmsTemplates
- Added SystemConstants class to centralize hardcoded values for club configuration, commission configuration, and package amounts.
- Introduced SmsTemplates class to manage SMS message templates for various user notifications.
- Implemented automatic SMS sending for Daya Loan approval notifications.
- Updated BackOffice UI to include App Version management features.
- Fixed mapping issues in Mapster profiles for improved data handling.
- Updated changelog and documentation to reflect recent changes and configurations.
2025-12-27 05:07:33 +03:30
masoodafar-web 6380517ba2 docs: Add CHANGELOG-2025-12-26 and update documentation
- Add CHANGELOG-2025-12-26.md for App Version Management feature
- Update 00-INDEX.md with latest achievements and version 2.5
- Update BackOffice README with App Version Management info
- Update BackOffice.BFF README with new package versions
- Update FrontOffice README with ReferralCode feature
2025-12-26 06:12:24 +03:30
masoodafar-web 858933daa5 docs: add CHANGELOG-2025-12-25 - Chatika flag, DayaLoan fix, BackOffice tree rewrite with org-chart, SP_GetNetworkTree 2025-12-25 02:23:00 +03:30
masoodafar-web e7d979117c feat: Add Chatika integration and Club Features system documentation
- Implement Chatika account activation via background job
- Create IChatikaApiService interface and its implementation
- Add Club Features system documentation detailing features and entities
- Introduce ClubFeatureType enum to replace hardcoded IDs
- Update SQL scripts for Club Membership migration
- Fix various bugs in BackOffice UI and improve Products page functionality
2025-12-24 01:07:49 +03:30
156 changed files with 27419 additions and 183 deletions
+90 -9
View File
@@ -1,7 +1,7 @@
# 📚 FourSat Project - فهرست جامع مستندات
> **نسخه**: 2.1
> **آخرین بروزرسانی**: ۲۹ آذر ۱۴۰۴ (December 19, 2025)
> **نسخه**: 2.8
> **آخرین بروزرسانی**: ۱۱ دی ۱۴۰۴ (December 31, 2025)
> **وضعیت**: ✅ تجمیع و بازسازی کامل
---
@@ -28,7 +28,7 @@
### Backend Services:
| سرویس | وضعیت | تکمیل | فایل مرجع |
|-------|------|------|-----------|
| **CMS Microservice** | ✅ Production Ready | 95% | [`03-BACKEND/CMS/implementation-status.md`](03-BACKEND/CMS/implementation-status.md) |
| **CMS Microservice** | ✅ Production Ready | 98% | [`03-BACKEND/CMS/implementation-status.md`](03-BACKEND/CMS/implementation-status.md) |
| **BackOffice.BFF** | ✅ Production Ready | 100% | [`03-BACKEND/BackOffice.BFF/handlers-status.md`](03-BACKEND/BackOffice.BFF/handlers-status.md) |
| **FrontOffice.BFF** | 🚧 In Progress | 60% | [`03-BACKEND/FrontOffice.BFF/README.md`](03-BACKEND/FrontOffice.BFF/README.md) |
@@ -38,11 +38,45 @@
| **BackOffice UI** | ✅ Production Ready | 100% | [`04-FRONTEND/BackOffice/ui-status.md`](04-FRONTEND/BackOffice/ui-status.md) |
| **FrontOffice UI** | 🚧 In Progress | 75% | [`04-FRONTEND/FrontOffice/README.md`](04-FRONTEND/FrontOffice/README.md) |
### آخرین دستاوردها (۱۴ آذر):
- ✅ **FrontOffice UI**: 7 صفحه جدید (Club, Network, Commission)
- ✅ **FrontOffice.BFF**: 3 ماژول جدید (ClubMembership, NetworkMembership, Commission)
- ✅ **Build**: موفق با 0 خطا
- ✅ **Documentation**: بازسازی کامل ساختار
### آخرین دستاوردها (۱۱ دی):
- ✅ **Discount Shop BFF Complete**: پیاده‌سازی کامل لایه BFF شامل WebApi Services
- ✅ **Product Image Gallery**: گالری تصاویر محصولات فروشگاه تخفیفی (5 API)
- ✅ **Admin Order Reports**: گزارشات مدیریتی سفارشات (GetAll + SalesReport)
- ✅ **VAT Calculation**: محاسبه مالیات بر ارزش افزوده در سفارشات
- ✅ **gRPC Services**: DiscountProductService + DiscountOrderService
### دستاوردهای ۹ دی:
- ✅ **Commission Carryover Fix**: رفع مشکل نمایش 0 برای carryover در weekly-balance
- ✅ **WeekSelector Autocomplete**: انتخابگر هفته با جستجو در داشبورد کمیسیون
- ✅ **Responsive Commission Pages**: بهبود UI با MudGrid و Summary Stats
- ✅ **Merged Dashboard/History**: ادغام دو صفحه تکراری با dual routing
- ✅ **Terminology Cleanup**: جایگزینی کلمات MLM-حساس (کمیسیون→پاداش، شبکه→تیم)
### دستاوردهای ۷ دی:
- ✅ **SystemConstants**: انتقال مقادیر hardcode (56M) به کلاس مرکزی
- ✅ **SmsTemplates**: متمرکز کردن همه قالب‌های پیامک در یک فایل
- ✅ **Daya Loan SMS**: ارسال پیامک خودکار هنگام تأیید وام دایا
- ✅ **AppVersion UI Complete**: صفحه مدیریت نسخه با قابلیت افزودن جدید
- ✅ **Mapping Fixes**: رفع مشکلات Mapster (Unit→Empty, WeeklyPools)
- ✅ **Commission Status Refactor**: انتقال تبدیل Status از BFF به FrontOffice client
- ✅ **ProcessWithdrawal Fix**: رفع خطای "PayoutId invalid" در BackOffice
- ✅ **WeekDisplayName Fix**: نمایش "هفته چهلم" به جای "1404-W40"
- ✅ **Withdrawals Page Fix**: رفع مشکل لود نشدن صفحه تأیید برداشت‌ها
- ✅ **Network Balances Enhanced**: نمایش نام کاربر + Carryover breakdown با Tooltip
- ✅ **WeekDefinitionId Fix**: رفع مشکل ارسال 0 به جای مقدار صحیح (Int64Value.Value)
### دستاوردهای ۶ دی:
- ✅ **App Version Management**: سیستم کامل مدیریت نسخه اپلیکیشن‌های موبایل
- ✅ **ReferralCode در درخت**: نمایش کد معرف در نودهای درخت شبکه FrontOffice
- ✅ **BackOffice Settings Page**: صفحه `/settings/app-versions` با UI کامل
### دستاوردهای ۵ دی:
- ✅ **Chatika Enabled Flag**: قابلیت فعال/غیرفعال کردن Worker چتیکا از Config
- ✅ **DayaLoan Fix**: جلوگیری از استعلام مجدد مشتریان با قرارداد
- ✅ **BackOffice Tree Rewrite**: بازنویسی کامل صفحه درخت شبکه با d3-org-chart
- ✅ **Node Tooltip**: نمایش اطلاعات کاربر روی hover
- ✅ **Week Filter Visual**: تمایز بصری کاربران فعال شده در هفته فیلتر شده
- ✅ **GetNetworkTree SP**: Stored Procedure برای بهبود سرعت + حذف محدودیت عمق
---
@@ -95,6 +129,11 @@
| [`payment-gateway.md`](03-BACKEND/CMS/payment-gateway.md) | درگاه پرداخت | ZarinPal, Daya Integration |
| [`daya-api-implementation.md`](03-BACKEND/CMS/daya-api-implementation.md) | ✨ Daya API Guide | Complete Real API Implementation (Dec 6) |
| [`club-membership-migration.md`](03-BACKEND/CMS/club-membership-migration.md) | ✨ Migration Scripts | اسکریپت‌های مهاجرت باشگاه مشتریان (Dec 9) |
| [`chatika-integration.md`](03-BACKEND/CMS/chatika-integration.md) | 🤖 Chatika Integration | Worker خودکار فعال‌سازی حساب AI (Dec 23) |
| [`club-features-system.md`](03-BACKEND/CMS/club-features-system.md) | 🎁 Club Features | Enum، Handler ها، UserClubFeatures (Dec 23) |
| [`INVENTORY-SYSTEM-PLAN.md`](03-BACKEND/INVENTORY-SYSTEM-PLAN.md) | 📦 سیستم انبارداری | پلن یکپارچه‌سازی موجودی (Jan 1, 2026) |
| [`PRODUCT-BUNDLE-FEATURE.md`](03-BACKEND/CMS/PRODUCT-BUNDLE-FEATURE.md) | 📦 پکیج محصولات | ⏸️ Postponed - بسته‌بندی محصولات (Jan 1, 2026) |
| [`MANUAL-CLUB-MEMBERSHIP-TASKS.md`](03-BACKEND/CMS/MANUAL-CLUB-MEMBERSHIP-TASKS.md) | 👤 عضویت دستی | ⏳ تسک‌های پیاده‌سازی عضویت دستی باشگاه (Jan 1, 2026) |
**Key Stats**:
- **Entities**: 50+ Domain Entities
@@ -166,11 +205,12 @@
|------|-------|-------|
| [`CURRENT-SPRINT.md`](05-TASKS/CURRENT-SPRINT.md) | اسپرینت جاری | TODO های High/Medium/Low Priority |
| [`BACKLOG.md`](05-TASKS/BACKLOG.md) | Backlog | کارهای آینده، Feature Requests |
| [`DISCOUNT-SHOP-COMPLETION-PLAN.md`](05-TASKS/DISCOUNT-SHOP-COMPLETION-PLAN.md) | تکمیل فروشگاه تخفیفی | گالری تصاویر، VAT، گزارش فروش |
| [`verification-template.md`](05-TASKS/verification-template.md) | چک‌لیست QA | تست‌های Business Verification |
**Current Sprint Highlights**:
- 🔥 **High Priority**: Discount Shop Completion (گالری، VAT، گزارش)
- 🔥 **High Priority**: FrontOffice UI Integration (7 صفحه)
- 🔥 **High Priority**: Protobuf Mismatch Fixes (3 Handler)
- 🟡 **Medium**: WalletService Implementation (5 متد)
- 🟡 **Medium**: Package Purchase UI (4 صفحه)
@@ -274,6 +314,47 @@
## 📝 تاریخچه تغییرات
### نسخه 2.8 (۱۱ دی ۱۴۰۴ / Dec 31, 2025):
- ✅ **Discount Shop BFF Complete**: پیاده‌سازی کامل لایه BFF شامل WebApi Services
- ✅ **Product Image Gallery**: گالری تصاویر محصولات (5 API جدید)
- ✅ **Admin Order Reports**: گزارشات مدیریتی سفارشات (GetAll + SalesReport)
- ✅ **VAT Calculation**: محاسبه مالیات بر ارزش افزوده
- ✅ **gRPC Services**: DiscountProductService + DiscountOrderService
- ✅ Changelog جدید: `CHANGELOG-2025-12-31.md`
### نسخه 2.7 (۹ دی ۱۴۰۴ / Dec 29, 2025):
- ✅ **Commission Carryover Fix**: رفع مشکل نمایش carryover
- ✅ **WeekSelector Autocomplete**: انتخابگر هفته با جستجو
- ✅ **Responsive Commission Pages**: بهبود UI با MudGrid
- ✅ Changelog جدید: `CHANGELOG-2025-12-29.md`
### نسخه 2.5 (۶ دی ۱۴۰۴ / Dec 26, 2025):
- ✅ **App Version Management**: سیستم کامل مدیریت نسخه اپلیکیشن‌های موبایل
- ✅ **BackOffice UI**: صفحه `/settings/app-versions` با MudBlazor
- ✅ **ReferralCode Display**: نمایش کد معرف در درخت شبکه FrontOffice
- ✅ Changelog جدید: `CHANGELOG-2025-12-26.md`
### نسخه 2.4 (۵ دی ۱۴۰۴ / Dec 25, 2025):
- ✅ **Chatika Enabled Flag**: قابلیت فعال/غیرفعال کردن Worker چتیکا
- ✅ **DayaLoan Fix**: جلوگیری از استعلام مجدد مشتریان با قرارداد
- ✅ **BackOffice Tree Rewrite**: بازنویسی کامل با d3-org-chart
- ✅ **Node Tooltip**: نمایش اطلاعات کاربر روی hover
- ✅ **GetNetworkTree SP**: Stored Procedure برای بهبود سرعت
- ✅ Changelog جدید: `CHANGELOG-2025-12-25.md`
### نسخه 2.3 (۳ دی ۱۴۰۴ / Dec 23, 2025):
- ✅ **Chatika Integration**: Worker خودکار فعال‌سازی حساب چتیکا
- ✅ **ClubFeatureType Enum**: جایگزینی hardcoded IDs با Enum قابل نگهداری
- ✅ **LegPosition Logic**: تنظیم خودکار دست چپ/راست در ثبت‌نام شبکه
- ✅ **Handler Sync**: همگام‌سازی AcceptContract و ActivateMembership
- ✅ مستندات جدید: `chatika-integration.md`, `club-features-system.md`
- ✅ Changelog جدید: `CHANGELOG-2025-12-23.md`
### نسخه 2.2 (۳۰ آذر ۱۴۰۴ / Dec 20, 2025):
- ✅ رفع باگ‌های /network/balances, /club/members, /club/statistics
- ✅ فعال‌سازی Products: CreateNew, Update, Gallery, Tags
- ✅ Changelog جدید: `CHANGELOG-2025-12-20.md`
### نسخه 2.0 (۱۴ آذر ۱۴۰۴):
- ✅ بازسازی کامل ساختار مستندات
- ✅ تجمیع اسناد تکراری
@@ -17,10 +17,10 @@ BackOffice.BFF از قراردادهای Protobuf **اختصاصی خودش** ا
| Project | Version | Namespace | Purpose |
|---------|---------|-----------|----------|
| BackOffice.BFF.ClubMembership.Protobuf | 0.0.6 | Foursat.BackOffice.BFF.ClubMembership.Protos | باشگاه مشتریان |
| BackOffice.BFF.Commission.Protobuf | 0.0.6 | Foursat.BackOffice.BFF.Commission.Protos | کمیسیون |
| BackOffice.BFF.Configuration.Protobuf | 1.0.6 | Foursat.BackOffice.BFF.Configuration.Protos | تنظیمات |
| BackOffice.BFF.NetworkMembership.Protobuf | 0.0.6 | Foursat.BackOffice.BFF.NetworkMembership.Protos | شبکه |
| BackOffice.BFF.ClubMembership.Protobuf | 0.0.7 | Foursat.BackOffice.BFF.ClubMembership.Protos | باشگاه مشتریان |
| BackOffice.BFF.Commission.Protobuf | 0.0.13 | Foursat.BackOffice.BFF.Commission.Protos | کمیسیون |
| BackOffice.BFF.Configuration.Protobuf | 1.0.20 | BackOffice.BFF.Configuration.Protobuf.Protos | تنظیمات + AppVersion |
| BackOffice.BFF.NetworkMembership.Protobuf | 0.0.11 | Foursat.BackOffice.BFF.NetworkMembership.Protos | شبکه |
**تغییر معماری (۱۷ آذر ۱۴۰۴)**:
- ❌ **قبلا**: استفاده مستقیم از `CMSMicroservice.Protobuf` (Anti-Pattern)
@@ -1,8 +1,8 @@
# BackOffice.BFF - Discount Shop Integration Plan
**تاریخ ایجاد**: 1403/09/13 (2024-12-04)
**آخرین بروزرسانی**: 1403/09/14 (2024-12-05)
**وضعیت**: ✅ پیاده‌سازی شده (Plan اجرا شده است)
**آخرین بروزرسانی**: 1403/10/11 (2024-12-31)
**وضعیت**: ✅ پیاده‌سازی کامل (شامل Image Gallery و Admin Reports)
**اولویت در زمان طراحی**: 🔴 بالا
---
@@ -17,8 +17,21 @@
- 4 gRPC Services
- Migration: AddDiscountShopSystem
### ⏳ وضعیت در BackOffice.BFF (به‌روزرسانی)
- **19 Handler** برای 4 سرویس جدید → ✅ پیاده‌سازی و متصل به CMS
- **Phase 10: Product Image Gallery** - 100% ✅ (جدید)
- 5 عملیات جدید: Add/Update/Delete/Reorder/Get Images
- 2 Command + 3 RPC جدید
- پشتیبانی از چندین تصویر برای هر محصول
- **Phase 11: Admin Order Reports** - 100% ✅ (جدید)
- GetAllDiscountOrders: لیست کامل سفارشات با فیلتر و صفحه‌بندی
- GetDiscountSalesReport: گزارش فروش با فیلتر تاریخ و نوع گزارش
### ✅ وضعیت در BackOffice.BFF (تکمیل شده)
- **26 Handler** برای 6 سرویس → ✅ پیاده‌سازی و متصل به CMS
- 19 Handler اصلی + 5 Handler گالری تصاویر + 2 Handler گزارش سفارشات
- **2 gRPC Service در WebApi** → ✅ جدید
- `DiscountProductService.cs` با 10 RPC endpoint
- `DiscountOrderService.cs` با 7 RPC endpoint
- **4 Client Interface** در `IApplicationContractContext` → ✅ اضافه و در `ApplicationContractContext` پیاده‌سازی شده
- **Test و Validation** → ✅ در BackOffice UI (DiscountShop صفحات و سرویس‌ها) در حال استفاده عملی
@@ -293,6 +306,153 @@
---
### 5️⃣ مدیریت گالری تصاویر محصولات (5 API جدید) 🆕
**سرویس**: `DiscountProductContract`
> **توجه**: این APIها برای مدیریت چندین تصویر برای هر محصول استفاده می‌شوند (گالری تصاویر).
#### الف. افزودن تصویر به گالری ⭐ **مهم**
- **Handler**: `AddDiscountProductImageCommandHandler`
- **Command**: `AddDiscountProductImageCommand`
- **Request**:
```csharp
- ProductId (Guid)
- Image (ImageFileModel)
* File (byte[])
* FileName (string)
* Mime (string)
- SortOrder (int - ترتیب نمایش)
- IsMain (bool - آیا تصویر اصلی است؟)
```
- **Response**: ImageId (Guid)
- **کاربرد Admin**: افزودن تصاویر جدید به گالری محصول
#### ب. ویرایش تصویر گالری
- **Handler**: `UpdateDiscountProductImageCommandHandler`
- **Command**: `UpdateDiscountProductImageCommand`
- **Request**:
```csharp
- ImageId (Guid)
- ProductId (Guid)
- NewImage (ImageFileModel - اختیاری)
- SortOrder (int)
- IsMain (bool)
```
- **Response**: Success/Failure
- **کاربرد Admin**: تغییر تصویر موجود یا تغییر ترتیب/اصلی بودن
#### ج. حذف تصویر از گالری
- **Handler**: `DeleteDiscountProductImageCommandHandler`
- **Command**: `DeleteDiscountProductImageCommand`
- **Request**: ImageId (Guid), ProductId (Guid)
- **Response**: Success/Failure
- **کاربرد Admin**: حذف تصویر از گالری محصول
#### د. تغییر ترتیب تصاویر ⭐ **مهم**
- **Handler**: `ReorderDiscountProductImagesCommandHandler`
- **Command**: `ReorderDiscountProductImagesCommand`
- **Request**:
```csharp
- ProductId (Guid)
- ImageOrders (List)
* ImageId (Guid)
* SortOrder (int)
```
- **Response**: Success/Failure
- **کاربرد Admin**: تغییر ترتیب نمایش تصاویر با drag & drop
#### ه. دریافت لیست تصاویر محصول
- **Handler**: `GetDiscountProductImagesQueryHandler`
- **Query**: `GetDiscountProductImagesQuery`
- **Request**: ProductId (Guid)
- **Response**:
```csharp
- List<ProductImageDto>
* ImageId (Guid)
* ImagePath (string)
* SortOrder (int)
* IsMain (bool)
* CreatedAt (DateTime)
```
- **کاربرد Admin**: نمایش گالری تصاویر محصول
---
### 6️⃣ گزارشات مدیریتی سفارشات (2 API جدید) 🆕
**سرویس**: `DiscountOrderContract`
> **توجه**: این APIها برای گزارش‌گیری و مدیریت کلی سفارشات توسط ادمین استفاده می‌شوند.
#### الف. لیست کامل سفارشات ⭐⭐⭐ **خیلی مهم**
- **Handler**: `GetAllDiscountOrdersQueryHandler`
- **Query**: `GetAllDiscountOrdersQuery`
- **Request**:
```csharp
- PageNumber (int - پیش‌فرض: 1)
- PageSize (int - پیش‌فرض: 10)
- UserId (Guid? - فیلتر کاربر)
- Status (DeliveryStatus? - فیلتر وضعیت)
- FromDate (DateTime? - از تاریخ)
- ToDate (DateTime? - تا تاریخ)
- SearchTerm (string? - جستجو در شماره سفارش/نام کاربر)
```
- **Response**:
```csharp
- MetaData (PaginationMetaData)
* PageNumber
* PageSize
* TotalCount
* TotalPages
- Models (List<OrderSummaryDto>)
* OrderId
* UserId
* UserName
* TotalPrice
* DiscountBalanceUsed
* GatewayAmount
* VatAmount (مالیات ارزش افزوده)
* DeliveryStatus
* ItemsCount
* OrderDate
* PaymentDate
```
- **کاربرد Admin**: مشاهده و فیلتر تمام سفارشات فروشگاه تخفیفی
#### ب. گزارش فروش ⭐⭐ **مهم**
- **Handler**: `GetDiscountSalesReportQueryHandler`
- **Query**: `GetDiscountSalesReportQuery`
- **Request**:
```csharp
- FromDate (DateTime? - شروع بازه)
- ToDate (DateTime? - پایان بازه)
- ReportType (enum: Daily, Weekly, Monthly)
```
- **Response**:
```csharp
- SalesReportDto
* TotalOrders (int - تعداد کل سفارشات)
* TotalRevenue (decimal - مجموع درآمد)
* TotalVat (decimal - مجموع مالیات)
* TotalDiscountUsed (decimal - مجموع تخفیف استفاده شده)
* AverageOrderValue (decimal - میانگین ارزش سفارش)
* TopSellingProducts (List)
- ProductId
- ProductTitle
- TotalSold
- TotalRevenue
* OrdersByStatus (Dictionary<DeliveryStatus, int>)
* DailyBreakdown (List - جزئیات روزانه)
- Date
- OrderCount
- Revenue
- VatAmount
```
- **کاربرد Admin**: تحلیل عملکرد فروش و گزارش‌گیری دوره‌ای
---
## 📋 لیست کامل Handlerهای مورد نیاز
### ✅ موجود در BackOffice.BFF (35 Handler)
@@ -305,7 +465,7 @@
7. Network Membership (3)
8. Club Membership (4)
### ⏳ نیاز به ایجاد (19 Handler)
### ✅ تکمیل شده (26 Handler)
#### گروه 1: Discount Product (5 Handlers)
1. ✅ `CreateDiscountProductHandler`
@@ -334,6 +494,17 @@
18. ✅ `GetOrderByIdHandler` ⭐⭐⭐ **خیلی مهم**
19. ✅ `GetUserOrdersHandler` ⭐⭐ **مهم**
#### گروه 5: Product Image Gallery (5 Handlers) 🆕
20. ✅ `AddDiscountProductImageCommandHandler` ⭐ **جدید**
21. ✅ `UpdateDiscountProductImageCommandHandler` **جدید**
22. ✅ `DeleteDiscountProductImageCommandHandler` **جدید**
23. ✅ `ReorderDiscountProductImagesCommandHandler` ⭐ **جدید**
24. ✅ `GetDiscountProductImagesQueryHandler` **جدید**
#### گروه 6: Admin Order Reports (2 Handlers) 🆕
25. ✅ `GetAllDiscountOrdersQueryHandler` ⭐⭐⭐ **جدید - خیلی مهم**
26. ✅ `GetDiscountSalesReportQueryHandler` ⭐⭐ **جدید - مهم**
---
## 🏗️ تغییرات مورد نیاز در BackOffice.BFF
@@ -567,6 +738,12 @@ BackOffice.BFF.Application/
- اگر پرداخت ناموفق باشد: باید موجودی برگردانده شود (در CompleteOrderPayment)
- Admin باید بتواند موجودی را دستی تغییر دهد
### ⚠️ نکته 5: VAT Calculation 🆕
- مالیات ارزش افزوده (VAT) 10% برای هر سفارش محاسبه می‌شود
- VAT روی قیمت نهایی (بعد از تخفیف) محاسبه می‌شود
- فیلد `VatAmount` در هر سفارش ذخیره می‌شود
- در گزارش فروش، مجموع VAT جداگانه نمایش داده می‌شود
---
## 📚 مستندات مرتبط
@@ -574,20 +751,25 @@ BackOffice.BFF.Application/
- [CMS Implementation Progress](../CMS/implementation-progress.md) - Phase 9 Details
- [REMAINING-TASKS-CONSOLIDATED](../REMAINING-TASKS-CONSOLIDATED.md) - Overall Project Status
- [BackOffice.BFF CMS Integration](./cms-integration.md) - Existing Integration Guide
- [CHANGELOG-2025-12-31](../../CHANGELOG-2025-12-31.md) - تغییرات این سشن 🆕
---
## ✅ Checklist پیاده‌سازی
### Backend (BackOffice.BFF)
- [ ] آپدیت IApplicationContractContext (4 Client)
- [ ] آپدیت ApplicationContractContext (Implementation)
- [ ] ایجاد 5 Handler محصولات
- [ ] ایجاد 4 Handler دسته‌بندی
- [ ] ایجاد 5 Handler سبد خرید
- [ ] ایجاد 5 Handler سفارشات
- [ ] تست تمام Handlerها
- [ ] آپدیت مستندات cms-integration.md
### Backend (BackOffice.BFF) - ✅ تکمیل شده
- [x] آپدیت IApplicationContractContext (4 Client)
- [x] آپدیت ApplicationContractContext (Implementation)
- [x] ایجاد 5 Handler محصولات
- [x] ایجاد 4 Handler دسته‌بندی
- [x] ایجاد 5 Handler سبد خرید
- [x] ایجاد 5 Handler سفارشات
- [x] ایجاد 5 Handler گالری تصاویر 🆕
- [x] ایجاد 2 Handler گزارش سفارشات 🆕
- [x] ایجاد DiscountProductService (gRPC) 🆕
- [x] ایجاد DiscountOrderService (gRPC) 🆕
- [x] تست تمام Handlerها
- [x] آپدیت مستندات cms-integration.md
### Frontend (BackOffice UI)
- [ ] صفحه لیست محصولات
@@ -596,8 +778,21 @@ BackOffice.BFF.Application/
- [ ] صفحه لیست سفارشات
- [ ] صفحه جزئیات سفارش
- [ ] صفحه Support سبد خرید
- [ ] صفحه گالری تصاویر محصول 🆕
- [ ] صفحه گزارش فروش 🆕
- [ ] تست UI با داده واقعی
---
**آماده شروع پیاده‌سازی؟** 🚀
## 🎉 وضعیت نهایی
**Backend کاملاً آماده!** ✅
تمام APIهای لازم برای:
- مدیریت محصولات (CRUD + گالری تصاویر)
- مدیریت دسته‌بندی‌ها
- پشتیبانی سبد خرید
- مدیریت سفارشات
- گزارش‌گیری فروش
در لایه‌های BFF Application و WebApi پیاده‌سازی شده‌اند. 🚀
@@ -1,9 +1,24 @@
# BackOffice Development Plan - Network & Commission System
**Date**: 2025-12-01
**Version**: 2.3
**Date**: ۳۰ آذر ۱۴۰۴ (2025-12-20)
**Version**: 2.4
**Status**: 🟢 **Production Ready - 100% Complete**
**Last Updated**: 2025-12-01
**Last Updated**: 2025-12-20
---
## 🆕 آخرین تغییرات (۳۰ آذر ۱۴۰۴)
### رفع باگ‌های Mapster:
- ✅ `CommissionProfile.cs`: اضافه شدن mapping برای `GetUserWeeklyBalancesRequest`
- ✅ `ClubMembershipProfile.cs`: بازنویسی کامل با mappings جدید
### اضافه شدن Service Override:
- ✅ `ClubMembershipService.cs`: اضافه شدن `GetClubStatistics` override
### اضافه شدن فیلد RemainingCount:
- ✅ `CreateNewProductsCommand.cs`: اضافه شدن `RemainingCount`
- ✅ `UpdateProductsCommand.cs`: اضافه شدن `RemainingCount`
---
@@ -0,0 +1,269 @@
# 📦 CHANGELOG - سیستم انبارداری Phase 2
> **تاریخ:** ۱۲ دی ۱۴۰۴ (1 January 2026)
> **نوع:** Feature Implementation
> **وضعیت:** ✅ Build Successful
---
## 🎯 خلاصه
پیاده‌سازی کامل **Phase 2** سیستم انبارداری شامل:
- Repository Pattern برای سه Entity اصلی
- CQRS Commands و Queries کامل
- Handlers برای تمام عملیات
- DI Configuration
---
## ✅ تغییرات انجام شده
### 1. Repository Interfaces (Application Layer)
| فایل | توضیح |
|------|-------|
| `IInventoryItemRepository.cs` | اینترفیس repository برای مدیریت موجودی |
| `IStockMovementRepository.cs` | اینترفیس repository برای حرکات انبار |
| `IWarehouseRepository.cs` | اینترفیس repository برای انبارها |
**متدهای کلیدی `IInventoryItemRepository`:**
- `GetByIdAsync`, `GetByProductIdAsync`, `GetByDiscountProductIdAsync`
- `GetLowStockItemsAsync`, `GetOutOfStockItemsAsync`
- `UpdateQuantityAsync`, `ReserveQuantityAsync`, `ReleaseReservedQuantityAsync`
- `BulkUpdateQuantityAsync`, `BulkReserveQuantityAsync`
---
### 2. Repository Implementations (Infrastructure Layer)
| فایل | توضیح |
|------|-------|
| `InventoryItemRepository.cs` | پیاده‌سازی کامل با EF Core |
| `StockMovementRepository.cs` | پیاده‌سازی با analytics queries |
| `WarehouseRepository.cs` | پیاده‌سازی با statistics |
**ویژگی‌های خاص:**
- استفاده از `BaseAuditableEntity.Created` (نه CreatedAt)
- پشتیبانی از `ProductType.RegularProduct` و `ProductType.DiscountProduct`
- متدهای bulk operation برای عملکرد بهتر
---
### 3. CQRS Commands
#### InventoryItem Commands (8 عدد):
```
✅ CreateInventoryItemCommand
✅ UpdateInventoryItemCommand
✅ UpdateInventoryQuantityCommand
✅ ReserveInventoryCommand
✅ ReleaseReservedInventoryCommand
✅ ReduceInventoryCommand
✅ IncreaseInventoryCommand
✅ DeleteInventoryItemCommand
```
#### StockMovement Commands (3 عدد):
```
✅ CreateStockMovementCommand
✅ BulkCreateStockMovementCommand
✅ DeleteStockMovementCommand
```
#### Warehouse Commands (6 عدد):
```
✅ CreateWarehouseCommand
✅ UpdateWarehouseCommand
✅ DeleteWarehouseCommand
✅ SetDefaultWarehouseCommand
✅ ActivateWarehouseCommand
✅ BulkCreateWarehousesCommand
```
---
### 4. CQRS Queries
#### InventoryItem Queries (10 عدد):
```
✅ GetInventoryItemByIdQuery
✅ GetInventoryItemByProductIdQuery
✅ GetInventoryItemByDiscountProductIdQuery
✅ SearchInventoryItemsQuery
✅ GetInventoryItemsCountQuery
✅ GetLowStockItemsQuery
✅ GetOutOfStockItemsQuery
✅ CheckInventoryAvailabilityQuery
✅ GetAvailableQuantityQuery
✅ GetWarehouseInventoryItemsQuery
```
#### StockMovement Queries (12 عدد):
```
✅ GetStockMovementByIdQuery
✅ GetInventoryItemMovementHistoryQuery
✅ GetStockMovementsByOrderQuery
✅ GetStockMovementsByDiscountOrderQuery
✅ GetStockMovementsByReferenceQuery
✅ GetStockMovementsByTypeQuery
✅ GetRecentStockMovementsQuery
✅ SearchStockMovementsQuery
✅ GetStockMovementsCountQuery
✅ GetMovementSummaryQuery
✅ GetDailyMovementVolumeQuery
✅ GetTopMovingProductsQuery
```
#### Warehouse Queries (10 عدد):
```
✅ GetWarehouseByIdQuery
✅ GetWarehouseByCodeQuery
✅ GetDefaultWarehouseQuery
✅ GetActiveWarehousesQuery
✅ GetAllWarehousesQuery
✅ SearchWarehousesQuery
✅ GetWarehousesCountQuery
✅ WarehouseExistsQuery
✅ WarehouseExistsByCodeQuery
✅ GetWarehouseStatisticsQuery
```
---
### 5. Handlers
| فایل | Handlers |
|------|----------|
| `InventoryItemCommandHandlers.cs` | 8 handler برای commands |
| `InventoryItemQueryHandlers.cs` | 10 handler برای queries |
| `StockMovementCommandHandlers.cs` | 3 handler برای commands |
| `StockMovementQueryHandlers.cs` | 12 handler برای queries |
| `WarehouseCommandHandlers.cs` | 6 handler برای commands |
| `WarehouseQueryHandlers.cs` | 10 handler برای queries |
---
### 6. DI Configuration
فایل `DependencyInjection.cs` آپدیت شد:
```csharp
// Inventory Repositories
services.AddScoped<IInventoryItemRepository, InventoryItemRepository>();
services.AddScoped<IStockMovementRepository, StockMovementRepository>();
services.AddScoped<IWarehouseRepository, WarehouseRepository>();
```
---
## 🐛 باگ‌های رفع شده
| مشکل | راه‌حل |
|------|--------|
| `ProductType.Normal` not found | تغییر به `ProductType.RegularProduct` |
| `ProductType.Discount` not found | تغییر به `ProductType.DiscountProduct` |
| `.CreatedAt` not found | تغییر به `.Created` (BaseAuditableEntity) |
| Namespace `Persistence.Context` | تغییر به `Persistence` |
| Interface mismatch errors | بازنویسی کامل repositories |
---
## 📊 آمار نهایی
| متریک | مقدار |
|--------|-------|
| **Total Commands** | 17 |
| **Total Queries** | 32 |
| **Total Handlers** | 49 |
| **Repository Interfaces** | 3 |
| **Repository Implementations** | 3 |
| **Build Errors** | 0 ✅ |
| **Build Warnings** | 466 |
---
## ⏳ مراحل بعدی (باقی‌مانده از Plan)
### Phase 3: Business Services (اولویت بالا)
- [ ] `IInventoryService` interface
- [ ] `InventoryService` implementation
- [ ] `InitializeInventoryAsync` - ایجاد موجودی برای محصول جدید
- [ ] `ReserveStockAsync` - رزرو برای سفارش
- [ ] `ReleaseReservationAsync` - آزادسازی رزرو
- [ ] `ConfirmSaleAsync` - تایید فروش
- [ ] `SyncRemainingCountAsync` - همگام‌سازی با Product.RemainingCount
### Phase 4: Integration
- [ ] یکپارچه‌سازی با `CreateProductCommandHandler`
- [ ] یکپارچه‌سازی با `PlaceOrderCommandHandler`
- [ ] یکپارچه‌سازی با `CompletePaymentHandler`
### Phase 5: Data Migration
- [ ] Migration script برای Products موجود
- [ ] Migration script برای DiscountProducts موجود
### Phase 6: Proto/gRPC
- [ ] `inventory.proto`
- [ ] gRPC Service
### Phase 7: Tests
- [ ] Unit tests
- [ ] Integration tests
---
## 📁 ساختار فایل‌ها
```
CMSMicroservice.Application/
├── Common/
│ └── Interfaces/
│ ├── IInventoryItemRepository.cs ✅
│ ├── IStockMovementRepository.cs ✅
│ └── IWarehouseRepository.cs ✅
└── Features/
├── InventoryItems/
│ ├── Commands/
│ │ └── InventoryItemCommands.cs ✅
│ ├── Handlers/
│ │ ├── InventoryItemCommandHandlers.cs ✅
│ │ └── InventoryItemQueryHandlers.cs ✅
│ └── Queries/
│ └── InventoryItemQueries.cs ✅
├── StockMovements/
│ ├── Commands/
│ │ └── StockMovementCommands.cs ✅
│ ├── Handlers/
│ │ ├── StockMovementCommandHandlers.cs ✅
│ │ └── StockMovementQueryHandlers.cs ✅
│ └── Queries/
│ └── StockMovementQueries.cs ✅
└── Warehouses/
├── Commands/
│ └── WarehouseCommands.cs ✅
├── Handlers/
│ ├── WarehouseCommandHandlers.cs ✅
│ └── WarehouseQueryHandlers.cs ✅
└── Queries/
└── WarehouseQueries.cs ✅
CMSMicroservice.Infrastructure/
├── DependencyInjection.cs ✅ (updated)
└── Persistence/
└── Repositories/
├── InventoryItemRepository.cs ✅
├── StockMovementRepository.cs ✅
└── WarehouseRepository.cs ✅
```
---
## 🔗 مستندات مرتبط
- [INVENTORY-SYSTEM-PLAN.md](../INVENTORY-SYSTEM-PLAN.md) - Plan اصلی
- [development-plan.md](./development-plan.md) - پلن توسعه CMS
---
**نویسنده:** GitHub Copilot
**تاریخ آخرین بروزرسانی:** 1 January 2026
@@ -0,0 +1,407 @@
# عضویت دستی باشگاه مشتریان - Manual Club Membership
## 📋 خلاصه نیازمندی
ادمین بتواند برای یک کاربر **عضویت دستی باشگاه مشتریان** ایجاد کند که:
- کیف پول با **56 میلیون (Balance)** + **112 میلیون (DiscountBalance)** شارژ شود
- تراکنش و لاگ کیف پول ثبت شود
- فیلد `User.PackagePurchaseMethod = DirectPurchase` تنظیم شود
- مسیر تصویر فیش واریزی ذخیره شود
- بدون نیاز به تایید دو مرحله‌ای (ادمین ایجاد می‌کند = تایید شده)
---
## 🔢 فرمول‌های محاسبه
```
BasePackageAmount = 56,000,000 ریال (SystemConstants)
Balance (شارژ اصلی) = BasePackageAmount = 56M
DiscountBalance (تخفیف) = BasePackageAmount × 2 = 112M
مجموع شارژ = 56M + 112M = 168M ریال
```
---
## 📁 فایل‌های مورد نیاز برای تغییر
| # | فایل | نوع تغییر | اولویت |
|---|------|----------|--------|
| 1 | `ManualPayment.cs` | اضافه کردن `ImagePath` | بالا |
| 2 | `CreateManualPaymentCommand.cs` | اضافه کردن `ImagePath` | بالا |
| 3 | `manualpayment.proto` (CMS) | اضافه کردن `image_path` | بالا |
| 4 | `manualpayment.proto` (BFF) | اضافه کردن `image_path` | بالا |
| 5 | `CreateManualPaymentCommandHandler.cs` (CMS) | بازنویسی کامل | بالا |
| 6 | `CreateManualPaymentCommandHandler.cs` (BFF) | اضافه کردن `ImagePath` | متوسط |
| 7 | **جدید:** `GetManualMembershipPaymentsQuery` | Query برای لیست | کم |
---
## ✅ تسک 1: اضافه کردن ImagePath به Entity
**فایل:** `CMS/src/CMSMicroservice.Domain/Entities/Payment/ManualPayment.cs`
**تغییر:** بعد از `ReferenceNumber` اضافه شود:
```csharp
/// <summary>
/// مسیر تصویر فیش واریزی (اختیاری)
/// </summary>
public string? ImagePath { get; set; }
```
**محل دقیق:**
```csharp
/// <summary>
/// شماره مرجع یا شماره فیش (اختیاری)
/// </summary>
public string? ReferenceNumber { get; set; }
// ⬇️ اینجا اضافه شود ⬇️
/// <summary>
/// مسیر تصویر فیش واریزی (اختیاری)
/// </summary>
public string? ImagePath { get; set; }
/// <summary>
/// وضعیت تایید
/// </summary>
public ManualPaymentStatus Status { get; set; } = ManualPaymentStatus.Pending;
```
---
## ✅ تسک 2: اضافه کردن ImagePath به Command
**فایل:** `CMS/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommand.cs`
**تغییر:** بعد از `ReferenceNumber` اضافه شود:
```csharp
/// <summary>
/// مسیر تصویر فیش واریزی (اختیاری)
/// </summary>
public string? ImagePath { get; set; }
```
---
## ✅ تسک 3: آپدیت Proto - CMS
**فایل:** `CMS/src/CMSMicroservice.Protobuf/Protos/manualpayment.proto`
**تغییر در `CreateManualPaymentRequest`:**
```protobuf
message CreateManualPaymentRequest
{
int64 user_id = 1;
int64 amount = 2;
ManualPaymentType type = 3;
string description = 4;
google.protobuf.StringValue reference_number = 5;
google.protobuf.StringValue image_path = 6; // ⬅️ اضافه شود
}
```
**تغییر در `ManualPaymentModel`:**
```protobuf
message ManualPaymentModel
{
// ... existing fields ...
google.protobuf.Timestamp created = 19;
google.protobuf.StringValue image_path = 20; // ⬅️ اضافه شود
}
```
---
## ✅ تسک 4: آپدیت Proto - BFF
**فایل:** `BackOffice.BFF/src/Protobufs/BackOffice.BFF.ManualPayment.Protobuf/Protos/manualpayment.proto`
**همان تغییرات تسک 3**
---
## ✅ تسک 5: بازنویسی Handler (CMS) - مهم‌ترین تسک
**فایل:** `CMS/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs`
**کد جدید کامل:**
```csharp
using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Entities.Payment;
using CMSMicroservice.Domain.Enums;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.CreateManualPayment;
public class CreateManualPaymentCommandHandler : IRequestHandler<CreateManualPaymentCommand, long>
{
private readonly IApplicationDbContext _context;
private readonly ICurrentUserService _currentUser;
private readonly ILogger<CreateManualPaymentCommandHandler> _logger;
public CreateManualPaymentCommandHandler(
IApplicationDbContext context,
ICurrentUserService currentUser,
ILogger<CreateManualPaymentCommandHandler> logger)
{
_context = context;
_currentUser = currentUser;
_logger = logger;
}
public async Task<long> Handle(
CreateManualPaymentCommand request,
CancellationToken cancellationToken)
{
try
{
_logger.LogInformation(
"Creating manual membership payment for UserId: {UserId}, Type: {Type}",
request.UserId,
request.Type
);
// 1. بررسی Admin فعلی
var currentUserId = _currentUser.UserId;
if (string.IsNullOrEmpty(currentUserId))
{
throw new UnauthorizedAccessException("کاربر احراز هویت نشده است");
}
if (!long.TryParse(currentUserId, out var adminUserId))
{
throw new UnauthorizedAccessException("شناسه کاربر نامعتبر است");
}
// 2. بررسی وجود کاربر
var user = await _context.Users
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
if (user == null)
{
_logger.LogWarning("User not found: {UserId}", request.UserId);
throw new NotFoundException(nameof(User), request.UserId);
}
// 3. پیدا کردن کیف پول
var wallet = await _context.UserWallets
.FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken);
if (wallet == null)
{
_logger.LogError("Wallet not found for UserId: {UserId}", request.UserId);
throw new NotFoundException($"کیف پول کاربر {request.UserId} یافت نشد");
}
// 4. محاسبه مبالغ
var balanceAmount = SystemConstants.BasePackageAmount; // 56M
var discountBalanceAmount = SystemConstants.BasePackageAmount * 2; // 112M
var totalAmount = balanceAmount + discountBalanceAmount; // 168M
// 5. ثبت تراکنش
var transaction = new Transaction
{
Amount = totalAmount,
Description = $"عضویت دستی باشگاه مشتریان - {request.Description} - مرجع: {request.ReferenceNumber}",
PaymentStatus = PaymentStatus.Success,
PaymentDate = DateTime.Now,
RefId = request.ReferenceNumber,
Type = TransactionType.DepositExternal1
};
_context.Transactions.Add(transaction);
await _context.SaveChangesAsync(cancellationToken);
// 6. ایجاد ManualPayment با وضعیت Approved (بدون نیاز به تایید دو مرحله‌ای)
var manualPayment = new ManualPayment
{
UserId = request.UserId,
Amount = totalAmount,
Type = request.Type,
Description = request.Description,
ReferenceNumber = request.ReferenceNumber,
ImagePath = request.ImagePath,
Status = ManualPaymentStatus.Approved,
RequestedBy = adminUserId,
ApprovedBy = adminUserId,
ApprovedAt = DateTime.Now,
TransactionId = transaction.Id
};
_context.ManualPayments.Add(manualPayment);
// 7. اعمال تغییرات بر کیف پول
var oldBalance = wallet.Balance;
var oldDiscountBalance = wallet.DiscountBalance;
wallet.Balance += balanceAmount; // +56M
wallet.DiscountBalance += discountBalanceAmount; // +112M
// 8. ثبت لاگ کیف پول
var walletLog = new UserWalletChangeLog
{
WalletId = wallet.Id,
CurrentBalance = wallet.Balance,
ChangeValue = balanceAmount,
CurrentNetworkBalance = wallet.NetworkBalance,
ChangeNerworkValue = 0,
CurrentDiscountBalance = wallet.DiscountBalance,
ChangeDiscountValue = discountBalanceAmount,
IsIncrease = true,
RefrenceId = transaction.Id
};
await _context.UserWalletChangeLogs.AddAsync(walletLog, cancellationToken);
// 9. تنظیم روش خرید پکیج
user.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;
// 10. ذخیره همه تغییرات
await _context.SaveChangesAsync(cancellationToken);
_logger.LogInformation(
"Manual membership payment created successfully. " +
"ManualPaymentId: {Id}, UserId: {UserId}, TransactionId: {TransactionId}, " +
"Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}",
manualPayment.Id,
request.UserId,
transaction.Id,
oldBalance,
wallet.Balance,
oldDiscountBalance,
wallet.DiscountBalance
);
return manualPayment.Id;
}
catch (Exception ex) when (ex is not NotFoundException && ex is not UnauthorizedAccessException)
{
_logger.LogError(
ex,
"Error creating manual membership payment for UserId: {UserId}",
request.UserId
);
throw;
}
}
}
```
---
## ✅ تسک 6: آپدیت Handler (BFF)
**فایل:** `BackOffice.BFF/src/BackOffice.BFF.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs`
**تغییر:** اضافه کردن `ImagePath` به gRPC request:
```csharp
var grpcRequest = new CreateManualPaymentRequest
{
UserId = request.UserId,
Amount = request.Amount,
Type = (ManualPaymentType)request.Type,
Description = request.Description
};
if (!string.IsNullOrWhiteSpace(request.ReferenceNumber))
{
grpcRequest.ReferenceNumber = request.ReferenceNumber;
}
// ⬇️ اضافه شود ⬇️
if (!string.IsNullOrWhiteSpace(request.ImagePath))
{
grpcRequest.ImagePath = request.ImagePath;
}
```
**همچنین:** فایل `CreateManualPaymentCommand.cs` در BFF هم باید `ImagePath` اضافه شود.
---
## ✅ تسک 7: ایجاد Query برای لیست (اختیاری)
**فایل‌های جدید:**
- `GetManualMembershipPaymentsQuery.cs`
- `GetManualMembershipPaymentsQueryHandler.cs`
- `ManualMembershipPaymentDto.cs`
> این تسک **اختیاری** است چون در حال حاضر `GetAllManualPayments` وجود دارد که می‌تواند با فیلتر `Type` استفاده شود.
---
## 🔄 ترتیب اجرای تسک‌ها
```mermaid
graph TD
A[1. Entity - ImagePath] --> B[2. Command - ImagePath]
B --> C[3. Proto CMS - image_path]
C --> D[4. Proto BFF - image_path]
D --> E[5. CMS Handler - Full Rewrite]
E --> F[6. BFF Handler - ImagePath]
F --> G[7. Build & Test]
G --> H[8. Query - اختیاری]
```
---
## 📝 نکات مهم
### 1. تفاوت با ProcessManualMembershipPayment
| معیار | CreateManualPayment (این تسک) | ProcessManualMembershipPayment |
|-------|------------------------------|--------------------------------|
| کاربرد | ادمین ایجاد می‌کند | مشتری از طریق درگاه پرداخت می‌کند |
| Amount | از `SystemConstants` (ثابت) | از `request` (متغیر) |
| DiscountBalance | `BasePackageAmount × 2` | `Amount` (همان مبلغ) |
| ImagePath | ✅ دارد | ❌ ندارد |
### 2. مقادیر SystemConstants
```csharp
// فایل: CMSMicroservice.Domain/Common/SystemConstants.cs
public const long BasePackageAmount = 56_000_000; // 56 میلیون ریال
```
### 3. ManualPaymentType پیشنهادی
برای این کاربرد می‌توان از `CashDeposit` یا یک نوع جدید مثل `ClubMembership` استفاده کرد.
---
## ⏱️ برآورد زمانی
| تسک | زمان تقریبی |
|-----|-------------|
| تسک 1-4 (فیلدها و Proto) | ~15 دقیقه |
| تسک 5 (Handler CMS) | ~20 دقیقه |
| تسک 6 (Handler BFF) | ~10 دقیقه |
| Build & Test | ~10 دقیقه |
| **مجموع** | **~55 دقیقه** |
---
## 🧪 تست نهایی
بعد از اتمام تسک‌ها:
1. **Build:** `dotnet build` در هر دو پروژه
2. **Migration:** اگر نیاز بود برای `ImagePath`
3. **تست API:** ایجاد یک Manual Payment برای کاربر تست
4. **بررسی:** Balance و DiscountBalance کاربر
---
**تاریخ ایجاد:** 2026-01-01
**نویسنده:** GitHub Copilot
**وضعیت:** ⏳ در انتظار اجرا
@@ -0,0 +1,303 @@
# 📦 Product Bundle Feature (پکیج محصولات)
> **وضعیت:** ⏸️ Postponed - مستند شده برای پیاده‌سازی آینده
>
> **تاریخ:** ۱۲ دی ۱۴۰۴ (1 January 2026)
---
## 📋 خلاصه نیازمندی
امکان ایجاد **پکیج محصولات** که:
- یک محصول با نوع "پکیج" ایجاد می‌شود (همه فیلدها مثل محصول عادی)
- این پکیج شامل **چند محصول** است
- هنگام **خرید پکیج**، موجودی **تمام محصولات داخل** کم می‌شود
- هنگام **مرجوعی**، موجودی تمام محصولات برمی‌گردد
---
## 🏗️ تغییرات مورد نیاز
### 1. Domain Layer
#### 1.1 Enum جدید: `ProductTypeCategory`
```csharp
// CMSMicroservice.Domain/Enums/ProductTypeCategory.cs
public enum ProductTypeCategory
{
Simple = 1, // محصول ساده
Bundle = 2 // پکیج (بسته محصولات)
}
```
#### 1.2 فیلد جدید در `Product` Entity
```csharp
// Product.cs - اضافه کردن فیلد
public ProductTypeCategory TypeCategory { get; set; } = ProductTypeCategory.Simple;
```
#### 1.3 Entity جدید: `ProductBundleItem` (جدول واسط)
```csharp
// CMSMicroservice.Domain/Entities/ProductBundleItem.cs
public class ProductBundleItem : BaseAuditableEntity
{
/// <summary>
/// شناسه محصول پکیج (والد)
/// </summary>
public long BundleProductId { get; set; }
public virtual Product BundleProduct { get; set; } = null!;
/// <summary>
/// شناسه محصول داخل پکیج (فرزند)
/// </summary>
public long ChildProductId { get; set; }
public virtual Product ChildProduct { get; set; } = null!;
/// <summary>
/// تعداد این محصول در پکیج
/// </summary>
public int Quantity { get; set; } = 1;
}
```
### 2. Infrastructure Layer
#### 2.1 DbContext Configuration
```csharp
// ApplicationDbContext.cs
public DbSet<ProductBundleItem> ProductBundleItems => Set<ProductBundleItem>();
// Configuration
modelBuilder.Entity<ProductBundleItem>(entity =>
{
entity.ToTable("ProductBundleItems", "CMS");
entity.HasOne(x => x.BundleProduct)
.WithMany(p => p.BundleItems)
.HasForeignKey(x => x.BundleProductId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(x => x.ChildProduct)
.WithMany()
.HasForeignKey(x => x.ChildProductId)
.OnDelete(DeleteBehavior.Restrict);
// یک محصول فقط یکبار در یک پکیج
entity.HasIndex(x => new { x.BundleProductId, x.ChildProductId }).IsUnique();
});
```
#### 2.2 آپدیت `InventoryService.ConfirmSaleAsync()`
```csharp
public async Task<bool> ConfirmSaleAsync(
long productId,
ProductType productType,
int quantity,
long? orderId = null,
CancellationToken ct = default)
{
// چک کردن آیا محصول پکیج است
var product = await _dbContext.Products
.Include(p => p.BundleItems)
.ThenInclude(bi => bi.ChildProduct)
.FirstOrDefaultAsync(p => p.Id == productId, ct);
if (product?.TypeCategory == ProductTypeCategory.Bundle)
{
// کم کردن موجودی تمام محصولات داخل پکیج
foreach (var bundleItem in product.BundleItems)
{
await ConfirmSaleForSingleProduct(
bundleItem.ChildProductId,
productType,
quantity * bundleItem.Quantity, // ضرب در تعداد خرید شده
orderId,
ct);
}
return true;
}
// محصول ساده - روال عادی
return await ConfirmSaleForSingleProduct(productId, productType, quantity, orderId, ct);
}
```
### 3. Application Layer
#### 3.1 آپدیت `CreateNewProductsCommand`
```csharp
public record CreateNewProductsCommand : IRequest<long>
{
// ... existing fields ...
public ProductTypeCategory TypeCategory { get; init; } = ProductTypeCategory.Simple;
/// <summary>
/// لیست محصولات داخل پکیج (فقط وقتی TypeCategory == Bundle)
/// </summary>
public List<BundleItemDto>? BundleItems { get; init; }
}
public record BundleItemDto
{
public long ProductId { get; init; }
public int Quantity { get; init; } = 1;
}
```
#### 3.2 Repository جدید: `IProductBundleItemRepository`
```csharp
public interface IProductBundleItemRepository : IRepository<ProductBundleItem>
{
Task<List<ProductBundleItem>> GetByBundleProductIdAsync(long bundleProductId, CancellationToken ct = default);
Task SetBundleItemsAsync(long bundleProductId, List<(long ProductId, int Quantity)> items, CancellationToken ct = default);
}
```
### 4. Proto/gRPC Layer
#### 4.1 آپدیت `products.proto`
```protobuf
enum ProductTypeCategory {
PRODUCT_TYPE_SIMPLE = 0;
PRODUCT_TYPE_BUNDLE = 1;
}
message BundleItemMessage {
int64 product_id = 1;
int32 quantity = 2;
}
message CreateNewProductsRequest {
// ... existing fields ...
ProductTypeCategory type_category = 15;
repeated BundleItemMessage bundle_items = 16;
}
message ProductDto {
// ... existing fields ...
ProductTypeCategory type_category = 20;
repeated BundleItemMessage bundle_items = 21;
}
```
---
## 📊 دیاگرام رابطه‌ها
```
┌─────────────────┐
│ Products │
├─────────────────┤
│ Id │◄──────────────────┐
│ Title │ │
│ TypeCategory │ ← Simple/Bundle │
│ ... │ │
└────────┬────────┘ │
│ │
│ 1:N (Bundle → Items) │
▼ │
┌─────────────────────┐ │
│ ProductBundleItems │ │
├─────────────────────┤ │
│ Id │ │
│ BundleProductId (FK)│───────────────┘
│ ChildProductId (FK) │───────────────┐
│ Quantity │ │
└─────────────────────┘ │
┌────────────────────────────┘
┌─────────────────┐
│ Products │
│ (Child Item) │
└─────────────────┘
```
---
## 🔄 Flow خرید پکیج
```
1. کاربر پکیج را به سبد اضافه می‌کند
└── CartItem { ProductId: 100, Count: 2 } // پکیج شامل 3 محصول
2. سفارش ثبت می‌شود
└── PlaceOrderCommandHandler.ReserveStock()
├── Check: Product.TypeCategory == Bundle
├── Get: BundleItems = [
│ { ChildProductId: 10, Quantity: 1 },
│ { ChildProductId: 20, Quantity: 2 },
│ { ChildProductId: 30, Quantity: 1 }
│ ]
└── Reserve:
├── Product 10: Reserve 2×1 = 2 عدد
├── Product 20: Reserve 2×2 = 4 عدد
└── Product 30: Reserve 2×1 = 2 عدد
3. پرداخت موفق
└── ConfirmSaleAsync()
├── Product 10: -2 از موجودی
├── Product 20: -4 از موجودی
└── Product 30: -2 از موجودی
4. مرجوعی (در صورت نیاز)
└── ProcessReturnAsync()
├── Product 10: +2 به موجودی
├── Product 20: +4 به موجودی
└── Product 30: +2 به موجودی
```
---
## ⚠️ محدودیت‌ها و قوانین
1. **محصول پکیج خودش موجودی ندارد** - فقط موجودی محصولات داخلش مهم است
2. **پکیج داخل پکیج ممنوع** - فقط محصولات ساده (`Simple`) می‌توانند داخل پکیج باشند
3. **حذف محصول از پکیج** - اگر محصولی در پکیج استفاده شده، نمی‌تواند حذف شود
4. **موجودی قابل فروش پکیج** = `MIN(موجودی هر محصول داخل / تعداد آن در پکیج)`
---
## 📁 فایل‌های جدید/تغییریافته
### فایل‌های جدید:
- `CMSMicroservice.Domain/Enums/ProductTypeCategory.cs`
- `CMSMicroservice.Domain/Entities/ProductBundleItem.cs`
- `CMSMicroservice.Application/Features/ProductBundleItems/*`
- `CMSMicroservice.Infrastructure/Repositories/ProductBundleItemRepository.cs`
### فایل‌های تغییریافته:
- `CMSMicroservice.Domain/Entities/Product.cs` - اضافه کردن `TypeCategory` و `BundleItems`
- `CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs` - DbSet و Configuration
- `CMSMicroservice.Infrastructure/Services/InventoryService.cs` - منطق پکیج
- `CMSMicroservice.Application/ProductsCQ/Commands/CreateNewProducts/*`
- `CMSMicroservice.Protobuf/Protos/products.proto`
- Order Handlers (Reserve, Confirm, Release)
---
## ⏱️ تخمین زمان
| تسک | زمان تخمینی |
|-----|-------------|
| Domain entities & enums | 30 دقیقه |
| EF Migration | 15 دقیقه |
| Repository | 30 دقیقه |
| InventoryService update | 1 ساعت |
| CQRS handlers | 1 ساعت |
| Proto & gRPC | 45 دقیقه |
| تست و دیباگ | 1 ساعت |
| **جمع** | **~5 ساعت** |
---
## 📝 یادداشت‌ها
- این فیچر با پکیج عضویت (`Package` entity موجود) متفاوت است
- نیاز به تست دقیق منطق انبارداری دارد
- UI نیاز به multi-select برای انتخاب محصولات داخل پکیج دارد
---
*این داکیومنت برای پیاده‌سازی آینده نگهداری می‌شود.*
@@ -1,66 +1,52 @@
# CMS Microservice - Network & Club Commission System
[![Status](https://img.shields.io/badge/Status-Production%20Ready-success)]()
[![Progress](https://img.shields.io/badge/Progress-85%25-blue)]()
[![Progress](https://img.shields.io/badge/Progress-98%25-blue)]()
[![MVP](https://img.shields.io/badge/MVP-100%25%20Complete-brightgreen)]()
## 📊 Project Status (2025-12-18)
## 📊 Project Status (2025-12-27)
**Overall Progress**: 85% Complete (7/10 phases)
**Production Readiness**: 95%
**Overall Progress**: 98% Complete
**Production Readiness**: 99%
**MVP Status**: ✅ 100% Complete
### ✅ Completed Phases (7)
### ✅ Completed Phases
1. ✅ Domain Layer (Entities, Enums, Value Objects)
2. ✅ Club Membership System
3. ✅ Binary Network Tree
4. ✅ **Commission Calculation & Background Worker** (MVP)
4. ✅ Commission Calculation & Background Worker (MVP)
5. ✅ Protobuf gRPC Services
6. ✅ History & Configuration Management
7. ✅ Database Migration & Seed Data
8. ✅ App Version Management
9. ✅ SMS Templates & SystemConstants
### 🟡 Partially Complete (1)
- Phase 10: Withdrawal & Settlement (40%)
### 🟡 Partially Complete
- Withdrawal & Settlement (40%)
- ✅ Commands & Database
- ❌ Payment Gateway Integration
### ❌ Not Started (1)
- Phase 9: Club Shop & Product Integration (0%)
### ⏸️ Postponed (1)
- Phase 7: Testing (Unit, Integration, Load tests)
---
## 🚀 Recent Updates (2025-12-18 / ۲۸ آذر)
## 🚀 Recent Updates (2025-12-27 / ۷ دی)
### Entity Configuration - Persian Encoding Fix
- ✅ **Geography Entities**: Country, State, City
- ✅ **Change**: All string columns now `NVARCHAR` with `Persian_100_CI_AI` collation
- ✅ **Migration**: `FixPersianCollation_Geography`
- ✅ **Fixes**: Persian characters display correctly in Geography tables
### SystemConstants - مقادیر ثابت
- ✅ **فایل جدید**: `Domain/Common/SystemConstants.cs`
- ✅ `GoldenPackageAmount = 56_000_000` - پکیج طلایی
- ✅ `DayaLoanAmount = 56_000_000` - وام دایا
- ✅ حذف مقادیر hardcode از همه handlers
### Previous Updates (2025-12-01)
- ✅ **MailKit 4.14.1** for Email (SMTP with HTML templates)
- ✅ **Kavenegar 1.2.5** for SMS (Iranian SMS gateway)
- ✅ User.Email field added with migration
- ✅ 3 notification types: Commission, Club activation, Errors
- ✅ Persian RTL templates with rich formatting
- ✅ Production configuration guide created
### SmsTemplates - قالب‌های پیامک ✅
- ✅ **فایل جدید**: `Domain/Common/SmsTemplates.cs`
- ✅ قالب‌ها: DayaLoan, ClubActivated, PackagePurchased, Commission, Withdrawal, OTP, Welcome
- ✅ ارسال SMS خودکار هنگام تأیید وام دایا
### Hangfire Job Scheduling - COMPLETED
- ✅ Dashboard UI at `/hangfire`
- ✅ Cron schedule: Sunday 00:05 UTC
- ✅ SQL Server persistence
- ✅ Manual trigger API endpoints
- ✅ Distributed execution support
### Mapping Fixes
- ✅ `AppVersionProfile.cs` - Map List to GetAllAppVersionsResponse
### Infrastructure Enhancements - COMPLETED ✅
- ✅ Health Check endpoints (`/health`, `/health/ready`, `/health/live`)
- ✅ AlertService (structured logging for Sentry/Slack)
- ✅ Retry logic (Polly 8.5.0 with exponential backoff)
- ✅ WorkerExecutionLog (database audit trail)
- ✅ CurrentUserService (JWT authentication context)
### Previous Updates (2025-12-26)
- ✅ **App Version Management**: Entity, gRPC, Handlers
- ✅ **ReferralCode in Network Tree**: SP + Proto update
---
@@ -0,0 +1,424 @@
# 🤖 Chatika Integration Guide
> **آخرین بروزرسانی**: ۳ دی ۱۴۰۴ (23 December 2025)
> **وضعیت**: ✅ Production Ready
---
## 📋 فهرست
1. [معرفی](#معرفی)
2. [معماری](#معماری)
3. [API چتیکا](#api-چتیکا)
4. [پیاده‌سازی](#پیاده‌سازی)
5. [تنظیمات](#تنظیمات)
6. [نحوه کار Worker](#نحوه-کار-worker)
7. [Troubleshooting](#troubleshooting)
---
## معرفی
چتیکا یک سرویس هوش مصنوعی است که به عنوان اولین فیچر باشگاه مشتریان به کاربران ارائه می‌شود. هنگام فعال‌سازی باشگاه، به صورت خودکار یک حساب در چتیکا برای کاربر ایجاد می‌شود.
### ویژگی‌ها:
- ✅ فعال‌سازی خودکار حساب
- ✅ جلوگیری از ثبت تکراری
- ✅ Retry با Exponential Backoff
- ✅ Logging کامل
---
## معماری
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ User Activates │───▶│ ClubMembership │───▶│ UserClubFeature │
│ Club Package │ │ (IsActive=true) │ │ (Chatika, Id=1)│
└─────────────────┘ └──────────────────┘ │ Notes = NULL │
└────────┬────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Hangfire Scheduler │
│ Cron: */5 * * * * (Every 5 minutes) │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ ChatikaAccountActivationJob │
│ │
│ Query: SELECT * FROM UserClubFeatures │
│ WHERE ClubFeatureId = 1 (Chatika) │
│ AND ClubMembership.IsActive = true │
│ AND Notes IS NULL │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ ChatikaApiService │
│ POST https://api.chatika.ir/api/v1/organizations/register-user │
│ Header: X-API-Key: {ApiKey} │
│ Body: { "mobile_number": "09123456789" } │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Update UserClubFeature │
│ Notes = "🎉 تبریک! حساب هوش مصنوعی چتیکا شما فعال شد..." │
│ IsActive = true │
└─────────────────────────────────────────────────────────────────┘
```
---
## API چتیکا
### Endpoint
```
POST /api/v1/organizations/register-user
```
### Headers
| Header | Value |
|--------|-------|
| `X-API-Key` | Organization API Key |
| `Content-Type` | `application/json` |
### Request Body
```json
{
"mobile_number": "09123456789"
}
```
### Success Response (200 OK)
```json
{
"id": 1,
"mobile_number": "09123456789",
"organization_id": 1,
"organization_title": "FourSat",
"wallet_balance": 100.0,
"is_new_user": true,
"credit_charged": 100.0
}
```
### Error Responses
| Status | Error Code | Description |
|--------|-----------|-------------|
| 401 | `INVALID_API_KEY` | API Key نامعتبر |
| 403 | `ORGANIZATION_DISABLED` | سازمان غیرفعال شده |
| 403 | `ORGANIZATION_EXPIRED` | سازمان منقضی شده |
| 400 | `INVALID_MOBILE_FORMAT` | فرمت شماره موبایل نامعتبر |
---
## پیاده‌سازی
### 1. Interface
**فایل**: `CMSMicroservice.Application/Common/Interfaces/IChatikaApiService.cs`
```csharp
public interface IChatikaApiService
{
Task<ChatikaAccountResult> CreateAccountAsync(
string mobileNumber,
string fullName,
CancellationToken cancellationToken = default);
}
public class ChatikaAccountResult
{
public bool IsSuccess { get; set; }
public string? ErrorMessage { get; set; }
public string? ChatikaUserId { get; set; }
public string? AccessUrl { get; set; }
public static ChatikaAccountResult Success(...) => ...;
public static ChatikaAccountResult Failure(string error) => ...;
}
```
### 2. Service Implementation
**فایل**: `CMSMicroservice.Infrastructure/Services/ChatikaApiService.cs`
```csharp
public class ChatikaApiService : IChatikaApiService
{
private readonly HttpClient _httpClient;
private readonly ILogger<ChatikaApiService> _logger;
public async Task<ChatikaAccountResult> CreateAccountAsync(
string mobileNumber,
string fullName,
CancellationToken cancellationToken = default)
{
var request = new { mobile_number = mobileNumber };
var response = await _httpClient.PostAsJsonAsync(
"/api/v1/organizations/register-user",
request,
cancellationToken);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<ChatikaRegisterResponse>();
return ChatikaAccountResult.Success(result?.Id.ToString(), "https://chatika.ir");
}
return ChatikaAccountResult.Failure($"Error: {response.StatusCode}");
}
}
```
### 3. Background Job
**فایل**: `CMSMicroservice.Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs`
```csharp
public class ChatikaAccountActivationJob
{
private const string ChatikaFeatureDescription =
"🎉 تبریک! حساب هوش مصنوعی چتیکا شما فعال شد.\n\n" +
"برای استفاده از امکانات رایگان چتیکا:\n" +
"1️⃣ به وب‌سایت chatika.ir مراجعه کنید\n" +
"2️⃣ شماره موبایل خود را وارد کنید\n" +
"3️⃣ از دستیار هوشمند چتیکا لذت ببرید!\n\n" +
"🔗 لینک ورود: https://chatika.ir";
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
// 1. پیدا کردن کاربران در انتظار
var pendingUsers = await _context.UserClubFeatures
.Include(ucf => ucf.User)
.Include(ucf => ucf.ClubMembership)
.Where(ucf =>
ucf.ClubFeatureId == (long)ClubFeatureType.Chatika &&
ucf.ClubMembership.IsActive &&
!ucf.IsDeleted &&
ucf.IsActive &&
(ucf.Notes == null || ucf.Notes == ""))
.ToListAsync(cancellationToken);
// 2. پردازش هر کاربر
foreach (var userFeature in pendingUsers)
{
var user = userFeature.User;
var fullName = $"{user.FirstName} {user.LastName}".Trim();
// 3. کال API با Retry
var result = await _retryPipeline.ExecuteAsync(
async ct => await _chatikaApiService.CreateAccountAsync(
user.Mobile, fullName, ct),
cancellationToken);
// 4. آپدیت فیچر
if (result.IsSuccess)
{
userFeature.Notes = ChatikaFeatureDescription;
userFeature.IsActive = true;
await _context.SaveChangesAsync(cancellationToken);
}
}
}
}
```
---
## تنظیمات
### appsettings.json
```json
{
"Chatika": {
"BaseUrl": "https://api.chatika.ir",
"ApiKey": "YOUR_ORGANIZATION_API_KEY"
}
}
```
### DI Registration
**فایل**: `ConfigureServices.cs`
```csharp
// Chatika API Service
services.AddHttpClient<IChatikaApiService, ChatikaApiService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
.ConfigureHttpClient((sp, client) =>
{
client.Timeout = TimeSpan.FromSeconds(30);
});
// Background Job
services.AddScoped<ChatikaAccountActivationJob>();
```
### Hangfire Registration
**فایل**: `Program.cs`
```csharp
// Chatika Account Activation: Every 5 minutes
recurringJobManager.AddOrUpdate<ChatikaAccountActivationJob>(
recurringJobId: "chatika-account-activation",
methodCall: job => job.ExecuteAsync(CancellationToken.None),
cronExpression: "*/5 * * * *",
options: new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc });
```
---
## نحوه کار Worker
### Flowchart
```
┌──────────────────────────────────────────────────────────────┐
│ START (Every 5 min) │
└──────────────────────────┬───────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Query: Users with Chatika feature & Notes = NULL │
└──────────────────────────┬───────────────────────────────────┘
┌─────────────┐
│ Any Users? │
└──────┬──────┘
┌────────────┴────────────┐
│ NO │ YES
▼ ▼
┌──────────┐ ┌───────────────┐
│ END │ │ For each user │
└──────────┘ └───────┬───────┘
┌────────────────────┐
│ Call Chatika API │
│ (with 3x Retry) │
└────────┬───────────┘
┌─────────┴─────────┐
│ SUCCESS │ FAILURE
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Update Notes │ │ Log Warning │
│ IsActive=true │ │ Continue │
└───────────────┘ └───────────────┘
┌────────────────┐
│ Next User │
└────────────────┘
```
### Retry Policy
```csharp
// Polly Retry: 3 attempts with exponential backoff
_retryPipeline = new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions
{
MaxRetryAttempts = 3,
Delay = TimeSpan.FromSeconds(30),
BackoffType = DelayBackoffType.Exponential,
UseJitter = true
})
.Build();
```
**Retry Timeline:**
- Attempt 1: Immediate
- Attempt 2: ~30 seconds later
- Attempt 3: ~60 seconds later
---
## Troubleshooting
### 1. API Key Invalid
**خطا**: `INVALID_API_KEY`
**راه‌حل**:
1. بررسی `appsettings.json`
2. تأیید API Key در داشبورد چتیکا
3. چک کردن header name: باید `X-API-Key` باشد
### 2. Users Not Being Processed
**علت احتمالی**:
1. `ClubMembership.IsActive = false`
2. `UserClubFeature.Notes` قبلاً پر شده
3. `ClubFeatureId != 1`
**Debug Query**:
```sql
SELECT ucf.*, u.Mobile, cm.IsActive
FROM UserClubFeatures ucf
JOIN Users u ON ucf.UserId = u.Id
JOIN ClubMemberships cm ON ucf.ClubMembershipId = cm.Id
WHERE ucf.ClubFeatureId = 1
AND ucf.IsDeleted = 0
AND (ucf.Notes IS NULL OR ucf.Notes = '')
```
### 3. Hangfire Job Not Running
**راه‌حل**:
1. چک کردن Hangfire Dashboard: `/hangfire`
2. بررسی لاگ‌ها در Seq
3. تأیید ثبت Job در `Program.cs`
### 4. Network Timeout
**علت**: سرور چتیکا در دسترس نیست
**راه‌حل**:
- Retry Policy خودکار 3 بار تلاش می‌کند
- بررسی لاگ‌ها برای خطای دقیق
- تماس با پشتیبانی چتیکا
---
## 📊 Monitoring
### Logs to Watch
```
🚀 Starting Chatika account activation job
📋 Found {Count} users pending Chatika activation
🤖 Creating Chatika account for mobile: 0912***
✅ Chatika account activated for user {UserId}
⚠️ Failed to create Chatika account for user {UserId}: {Error}
❌ Network error calling Chatika API
🏁 Chatika activation job completed. Success: {X}, Failed: {Y}
```
### Seq Query
```
ApplicationName = "CMSMicroservice" AND Message LIKE "%Chatika%"
```
---
## 📚 مستندات مرتبط
- [Club Features System](./club-features-system.md)
- [Hangfire Jobs Guide](./hangfire-jobs.md)
- [Commission System](./commission-system.md)
@@ -0,0 +1,340 @@
# 🎁 Club Features System
> **آخرین بروزرسانی**: ۳ دی ۱۴۰۴ (23 December 2025)
> **وضعیت**: ✅ Production Ready
---
## 📋 فهرست
1. [معرفی](#معرفی)
2. [فیچرهای باشگاه](#فیچرهای-باشگاه)
3. [Entity ها](#entity-ها)
4. [Enum ClubFeatureType](#enum-clubfeaturetype)
5. [فرآیند فعال‌سازی](#فرآیند-فعال‌سازی)
6. [API ها](#api-ها)
---
## معرفی
سیستم فیچرهای باشگاه مشتریان، امکانات ویژه‌ای را برای اعضای باشگاه فراهم می‌کند. هر کاربر با فعال‌سازی باشگاه، به تمام 4 فیچر دسترسی پیدا می‌کند.
---
## فیچرهای باشگاه
| Id | نام | عنوان فارسی | توضیح |
|----|-----|-------------|-------|
| 1 | **Chatika** | چتیکا | دستیار هوش مصنوعی - حساب خودکار ایجاد می‌شود |
| 2 | **Bime** | بیمه | خدمات بیمه‌ای |
| 3 | **Trip** | تریپ | خدمات سفر و گردشگری |
| 4 | **Learn** | لرن | آموزش و یادگیری |
---
## Entity ها
### ClubFeature (تعریف فیچرها)
```csharp
public class ClubFeature : BaseAuditableEntity
{
public string Title { get; set; }
public string? Description { get; set; }
public bool IsActive { get; set; }
public int SortOrder { get; set; }
public virtual ICollection<UserClubFeature>? UserClubFeatures { get; set; }
}
```
### UserClubFeature (فیچرهای کاربر)
```csharp
public class UserClubFeature : BaseAuditableEntity
{
public long UserId { get; set; }
public virtual User User { get; set; }
public long ClubMembershipId { get; set; }
public virtual ClubMembership ClubMembership { get; set; }
public long ClubFeatureId { get; set; }
public virtual ClubFeature ClubFeature { get; set; }
public DateTime GrantedAt { get; set; }
public bool IsActive { get; set; } = true;
public string? Notes { get; set; } // توضیحات اختیاری یا وضعیت فعال‌سازی
}
```
### Database Schema
```sql
CREATE TABLE ClubFeatures (
Id BIGINT PRIMARY KEY IDENTITY,
Title NVARCHAR(200) NOT NULL,
Description NVARCHAR(MAX),
IsActive BIT DEFAULT 1,
SortOrder INT DEFAULT 0,
-- BaseAuditableEntity fields
Created DATETIME2,
CreatedBy NVARCHAR(100),
LastModified DATETIME2,
LastModifiedBy NVARCHAR(100),
IsDeleted BIT DEFAULT 0
);
CREATE TABLE UserClubFeatures (
Id BIGINT PRIMARY KEY IDENTITY,
UserId BIGINT NOT NULL FOREIGN KEY REFERENCES Users(Id),
ClubMembershipId BIGINT NOT NULL FOREIGN KEY REFERENCES ClubMemberships(Id),
ClubFeatureId BIGINT NOT NULL FOREIGN KEY REFERENCES ClubFeatures(Id),
GrantedAt DATETIME2 NOT NULL,
IsActive BIT DEFAULT 1,
Notes NVARCHAR(MAX),
-- BaseAuditableEntity fields
Created DATETIME2,
CreatedBy NVARCHAR(100),
LastModified DATETIME2,
LastModifiedBy NVARCHAR(100),
IsDeleted BIT DEFAULT 0
);
-- Seed Data
INSERT INTO ClubFeatures (Id, Title, Description, IsActive, SortOrder)
VALUES
(1, N'چتیکا', N'دستیار هوش مصنوعی', 1, 1),
(2, N'بیمه', N'خدمات بیمه‌ای', 1, 2),
(3, N'تریپ', N'خدمات سفر و گردشگری', 1, 3),
(4, N'لرن', N'آموزش و یادگیری', 1, 4);
```
---
## Enum ClubFeatureType
برای جلوگیری از hardcoded IDs، از Enum استفاده می‌شود:
**فایل**: `CMSMicroservice.Domain/Enums/ClubFeatureType.cs`
```csharp
namespace CMSMicroservice.Domain.Enums;
/// <summary>
/// انواع ویژگی‌های باشگاه مشتریان
/// </summary>
public enum ClubFeatureType
{
/// <summary>
/// چتیکا - دستیار هوش مصنوعی
/// </summary>
Chatika = 1,
/// <summary>
/// بیمه - خدمات بیمه‌ای
/// </summary>
Bime = 2,
/// <summary>
/// تریپ - خدمات سفر و گردشگری
/// </summary>
Trip = 3,
/// <summary>
/// لرن - آموزش و یادگیری
/// </summary>
Learn = 4
}
/// <summary>
/// Extension methods برای ClubFeatureType
/// </summary>
public static class ClubFeatureTypeExtensions
{
/// <summary>
/// دریافت تمام مقادیر ClubFeatureType به صورت آرایه long
/// </summary>
public static long[] GetAllFeatureIds()
{
return Enum.GetValues<ClubFeatureType>()
.Select(f => (long)f)
.ToArray();
}
/// <summary>
/// دریافت عنوان فارسی ویژگی
/// </summary>
public static string GetPersianTitle(this ClubFeatureType featureType)
{
return featureType switch
{
ClubFeatureType.Chatika => "چتیکا",
ClubFeatureType.Bime => "بیمه",
ClubFeatureType.Trip => "تور و سفر",
ClubFeatureType.Learn => "آموزش",
_ => featureType.ToString()
};
}
}
```
### استفاده در کد
```csharp
// ❌ قبل - Hardcoded
var featureIds = new long[] { 1, 2, 3, 4 };
// ✅ بعد - با Enum
var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
// دسترسی به یک فیچر خاص
var chatikaId = (long)ClubFeatureType.Chatika; // = 1
var title = ClubFeatureType.Bime.GetPersianTitle(); // = "بیمه"
```
---
## فرآیند فعال‌سازی
هنگام فعال‌سازی باشگاه مشتریان، فیچرها به این ترتیب اختصاص داده می‌شوند:
```
┌─────────────────────────────────────────────────────────────────┐
│ ActivateClubMembershipCommandHandler │
│ یا │
│ AcceptClubMembershipContractCommandHandler │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ // 8. اختصاص فیچرهای باشگاه │
│ var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds(); │
│ foreach (var featureId in featureIds) │
│ { │
│ _context.UserClubFeatures.Add(new UserClubFeature │
│ { │
│ UserId = user.Id, │
│ ClubMembershipId = membership.Id, │
│ ClubFeatureId = featureId, │
│ GrantedAt = DateTime.Now, │
│ IsActive = true, │
│ Notes = null // برای چتیکا بعداً توسط Worker پر میشود │
│ }); │
│ } │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 4 UserClubFeature Records │
│ ┌─────────────┬──────────────┬────────────┬─────────────┐ │
│ │ ClubFeatureId │ GrantedAt │ IsActive │ Notes │ │
│ ├─────────────┼──────────────┼────────────┼─────────────┤ │
│ │ 1 (Chatika) │ 2025-12-23 │ true │ NULL → پر │ │
│ │ 2 (Bime) │ 2025-12-23 │ true │ NULL │ │
│ │ 3 (Trip) │ 2025-12-23 │ true │ NULL │ │
│ │ 4 (Learn) │ 2025-12-23 │ true │ NULL │ │
│ └─────────────┴──────────────┴────────────┴─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
▼ (برای چتیکا)
┌─────────────────────────────────────────────────────────────────┐
│ ChatikaAccountActivationJob (Worker) │
│ - هر 5 دقیقه اجرا می‌شود │
│ - کاربران با Notes = NULL و ClubFeatureId = 1 را پیدا می‌کند │
│ - API چتیکا را کال می‌کند │
│ - Notes را با توضیحات فارسی پر می‌کند │
└─────────────────────────────────────────────────────────────────┘
```
---
## API ها
### GetUserClubFeatures
دریافت لیست فیچرهای فعال کاربر:
```protobuf
rpc GetUserClubFeatures (GetUserClubFeaturesRequest) returns (GetUserClubFeaturesResponse);
message GetUserClubFeaturesRequest {
int64 user_id = 1;
}
message GetUserClubFeaturesResponse {
repeated UserClubFeatureModel features = 1;
}
message UserClubFeatureModel {
int64 id = 1;
int64 club_feature_id = 2;
string feature_title = 3;
string feature_description = 4;
google.protobuf.Timestamp granted_at = 5;
bool is_active = 6;
string notes = 7;
}
```
### ToggleUserClubFeature
فعال/غیرفعال کردن فیچر توسط ادمین:
```protobuf
rpc ToggleUserClubFeature (ToggleUserClubFeatureRequest) returns (ToggleUserClubFeatureResponse);
message ToggleUserClubFeatureRequest {
int64 user_club_feature_id = 1;
bool is_active = 2;
}
```
---
## 📊 Query های مفید
### تعداد فیچرهای فعال هر کاربر
```sql
SELECT u.Mobile, COUNT(ucf.Id) as FeatureCount
FROM Users u
JOIN UserClubFeatures ucf ON u.Id = ucf.UserId
WHERE ucf.IsActive = 1 AND ucf.IsDeleted = 0
GROUP BY u.Mobile
```
### کاربران بدون فیچر چتیکا فعال
```sql
SELECT u.Id, u.Mobile
FROM Users u
JOIN ClubMemberships cm ON u.Id = cm.UserId
WHERE cm.IsActive = 1
AND NOT EXISTS (
SELECT 1 FROM UserClubFeatures ucf
WHERE ucf.UserId = u.Id
AND ucf.ClubFeatureId = 1
AND ucf.IsActive = 1
)
```
### وضعیت فعال‌سازی چتیکا
```sql
SELECT
CASE WHEN Notes IS NOT NULL THEN 'Activated' ELSE 'Pending' END as Status,
COUNT(*) as Count
FROM UserClubFeatures
WHERE ClubFeatureId = 1 AND IsDeleted = 0
GROUP BY CASE WHEN Notes IS NOT NULL THEN 'Activated' ELSE 'Pending' END
```
---
## 📚 مستندات مرتبط
- [Chatika Integration](./chatika-integration.md)
- [Club Membership Migration](./club-membership-migration.md)
- [Commission System](./commission-system.md)
+309
View File
@@ -0,0 +1,309 @@
# CMS Microservice Development Plan - Updated January 2026
## 📋 Project Overview
پروژه CMS Microservice با معماری Clean Architecture و الگوهای Domain-Driven Design برای مدیریت محصولات و موجودی انبار.
**تکنولوژی‌های اصلی:**
- .NET 9.0
- Entity Framework Core 9.x
- MediatR 13.0.0 (CQRS)
- SQL Server
## 🎯 Current Status: Phase 2 Complete ✅
---
## Phase 1: Infrastructure & Domain Layer ✅ COMPLETED
**Duration:** ✅ Completed
**Status:** ✅ All tasks finished successfully
### 📦 Domain Entities
-`InventoryItem` - مدیریت کالاهای موجود در انبار
-`StockMovement` - ردیابی حرکات موجودی
-`Warehouse` - مدیریت انبارها
### 🔧 Domain Enums
-`StockMovementType` - انواع حرکات موجودی
### 🗄️ Database Infrastructure
- ✅ Entity Framework Core configurations
- ✅ ApplicationDbContext setup
- ✅ Database migrations created and applied
- ✅ SQL Server compatibility ensured
---
## Phase 2: Repository Pattern & CQRS ✅ COMPLETED
**Duration:** ✅ Completed
**Status:** ✅ All tasks finished successfully
### 🏛️ Repository Pattern Implementation
#### Repository Interfaces:
-`IInventoryItemRepository` - 25+ methods for inventory management
-`IStockMovementRepository` - Movement tracking and analytics
-`IWarehouseRepository` - Warehouse management operations
#### Repository Implementations:
-`InventoryItemRepository` - Complete CRUD with business logic
-`StockMovementRepository` - Movement tracking with analytics
-`WarehouseRepository` - Warehouse management with statistics
### 🔄 CQRS Pattern Implementation
#### Commands:
**InventoryItem Commands:**
-`CreateInventoryItemCommand` - Create new inventory item
-`UpdateInventoryItemCommand` - Update inventory details
-`UpdateInventoryQuantityCommand` - Adjust quantity with audit
-`ReserveInventoryCommand` - Reserve stock for orders
-`ReleaseReservedInventoryCommand` - Release reserved stock
-`ReduceInventoryCommand` - Reduce stock (sales)
-`IncreaseInventoryCommand` - Increase stock (purchases)
-`DeleteInventoryItemCommand` - Delete inventory item
**StockMovement Commands:**
-`CreateStockMovementCommand` - Record stock movement
-`BulkCreateStockMovementCommand` - Bulk movement recording
-`DeleteStockMovementCommand` - Delete movement record
**Warehouse Commands:**
-`CreateWarehouseCommand` - Create new warehouse
-`UpdateWarehouseCommand` - Update warehouse details
-`DeleteWarehouseCommand` - Delete warehouse
-`SetDefaultWarehouseCommand` - Set default warehouse
-`ActivateWarehouseCommand` - Activate/deactivate warehouse
-`BulkCreateWarehousesCommand` - Bulk warehouse creation
#### Queries:
**InventoryItem Queries:**
-`GetInventoryItemByIdQuery` - Get by ID
-`GetInventoryItemByProductIdQuery` - Get by product
-`SearchInventoryItemsQuery` - Advanced search with filters
-`GetLowStockItemsQuery` - Low stock alerts
-`GetOutOfStockItemsQuery` - Out of stock items
-`CheckInventoryAvailabilityQuery` - Availability check
-`GetAvailableQuantityQuery` - Available quantity calculation
**StockMovement Queries:**
-`GetInventoryItemMovementHistoryQuery` - Movement history
-`GetStockMovementsByOrderQuery` - Order-based movements
-`SearchStockMovementsQuery` - Advanced search
-`GetMovementSummaryQuery` - Movement analytics
-`GetDailyMovementVolumeQuery` - Daily volume reports
-`GetTopMovingProductsQuery` - Top moving products
**Warehouse Queries:**
-`GetWarehouseByIdQuery` - Get by ID
-`GetDefaultWarehouseQuery` - Get default warehouse
-`GetActiveWarehousesQuery` - Get active warehouses
-`SearchWarehousesQuery` - Warehouse search
-`GetWarehouseStatisticsQuery` - Warehouse statistics
-`GetWarehouseLowStockItemsQuery` - Low stock by warehouse
### 🎭 Command/Query Handlers
#### Command Handlers:
-**InventoryItem Handlers:** 8 handlers with complete business logic
-**StockMovement Handlers:** 3 handlers with validation
-**Warehouse Handlers:** 6 handlers with business rules
#### Query Handlers:
-**InventoryItem Handlers:** 10 handlers for all queries
-**StockMovement Handlers:** 12 handlers with analytics
-**Warehouse Handlers:** 13 handlers with statistics
### 🔧 Infrastructure Services
- ✅ Dependency Injection configuration
- ✅ Repository registrations
- ✅ Database context configuration
---
## Phase 3: Business Services Layer 🚧 IN PROGRESS
**Duration:** In Progress
**Status:** 🔄 Ready to start
### 📋 Services to Implement:
-`IInventoryManagementService` - High-level inventory operations
-`IStockMovementService` - Movement orchestration
-`IWarehouseService` - Warehouse business logic
-`IInventoryReportingService` - Advanced reporting
-`IInventoryValidationService` - Business rule validation
### 🎯 Business Logic Features:
- ⏳ Automated reorder point calculations
- ⏳ Bulk operations with transaction management
- ⏳ Advanced inventory allocation strategies
- ⏳ Multi-warehouse transfer operations
- ⏳ Inventory forecasting and analytics
---
## Phase 4: DTOs & AutoMapper 📋 PLANNED
**Duration:** Planned
**Status:** ⏳ Pending
### 📦 DTOs to Create:
- ⏳ Request DTOs for API inputs
- ⏳ Response DTOs for API outputs
- ⏳ Search/Filter DTOs
- ⏳ Report DTOs
### 🔄 Mapping Configuration:
- ⏳ AutoMapper profiles
- ⏳ Domain to DTO mappings
- ⏳ DTO to Domain mappings
---
## Phase 5: Web API Controllers 🌐 PLANNED
**Duration:** Planned
**Status:** ⏳ Pending
### 🎮 Controllers to Implement:
-`InventoryController` - Inventory CRUD operations
-`WarehouseController` - Warehouse management
-`StockMovementController` - Movement tracking
-`ReportsController` - Analytics and reporting
### 🔒 API Features:
- ⏳ RESTful API design
- ⏳ Input validation
- ⏳ Error handling
- ⏳ API documentation (Swagger)
- ⏳ Authentication/Authorization integration
---
## 🚀 Key Features Implemented
### ✅ **Complete Inventory Management:**
- Multi-warehouse support with default warehouse designation
- Product and discount product inventory tracking
- Quantity management with min/max thresholds
- Reserved quantity handling for order processing
- Comprehensive audit trail for all movements
### ✅ **Advanced Stock Movement Tracking:**
- 8 different movement types (Purchase, Sale, Transfer, etc.)
- Automatic movement recording for all inventory changes
- Reference number and user tracking
- Bulk movement processing capabilities
- Analytics and reporting ready
### ✅ **Robust Repository Pattern:**
- Generic repository interfaces with specific implementations
- Transaction support for complex operations
- Optimized querying with Entity Framework Core
- Bulk operations for performance
- Comprehensive search and filtering
### ✅ **Clean CQRS Implementation:**
- Clear separation of commands and queries
- MediatR integration for loose coupling
- Comprehensive validation in command handlers
- Rich query capabilities with filtering and pagination
- Analytics queries for business intelligence
### ✅ **Database-First Approach:**
- Entity Framework Core with SQL Server
- Proper indexing for performance
- Foreign key relationships maintained
- Migration support for schema evolution
---
## 🎯 Business Capabilities Enabled
### **Inventory Operations:**
- ✅ Real-time inventory tracking
- ✅ Multi-warehouse inventory management
- ✅ Automatic low stock alerts
- ✅ Order fulfillment with reservation system
- ✅ Purchase order processing with stock increases
### **Analytics & Reporting:**
- ✅ Movement history and audit trails
- ✅ Daily/weekly/monthly movement reports
- ✅ Top moving products analysis
- ✅ Warehouse utilization statistics
- ✅ Low stock and out-of-stock reporting
### **Business Rules:**
- ✅ Automatic stock movement recording
- ✅ Reservation system for order processing
- ✅ Warehouse transfer capabilities
- ✅ Min/max quantity enforcement
- ✅ Default warehouse management
---
## 📊 Technical Metrics
### **Code Coverage:**
-**Repository Layer:** 100% implemented with business logic
-**CQRS Layer:** 100% commands/queries with handlers
-**Infrastructure:** 100% DI configuration complete
- 🔄 **Business Services:** 0% - Next phase
-**API Layer:** 0% - Future phase
### **Performance Considerations:**
- ✅ Optimized Entity Framework queries
- ✅ Bulk operations for large datasets
- ✅ Proper database indexing
- ✅ Transaction management for consistency
- ✅ Pagination support for large result sets
### **Testing Strategy:**
- 🔄 Unit tests for business logic - Planned
- 🔄 Integration tests for repositories - Planned
- 🔄 API tests for controllers - Planned
- 🔄 Performance tests - Planned
---
## 🔮 Next Steps
### **Immediate (Phase 3):**
1. Implement Business Services layer
2. Add advanced business logic and validations
3. Create service abstractions for complex operations
### **Short Term (Phase 4-5):**
1. Design and implement DTOs with AutoMapper
2. Create RESTful API controllers
3. Add comprehensive API documentation
### **Long Term:**
1. Performance optimization and caching
2. Advanced analytics and reporting
3. Integration with external systems
4. Microservice deployment strategies
---
## 🏗️ Architecture Summary
```
📁 CMS Microservice
├── 🎯 Domain Layer (✅ Complete)
│ ├── Entities (InventoryItem, StockMovement, Warehouse)
│ └── Enums (StockMovementType)
├── 📚 Application Layer (✅ Complete)
│ ├── Features/
│ │ ├── InventoryItems/ (Commands, Queries, Handlers)
│ │ ├── StockMovements/ (Commands, Queries, Handlers)
│ │ └── Warehouses/ (Commands, Queries, Handlers)
│ └── Common/Interfaces/Repositories/
├── 🏗️ Infrastructure Layer (✅ Complete)
│ ├── Persistence/
│ │ ├── Context/ (ApplicationDbContext)
│ │ ├── Configurations/ (EF Core configs)
│ │ ├── Repositories/ (Repository implementations)
│ │ └── Migrations/ (Database migrations)
│ └── DependencyInjection
└── 🌐 API Layer (⏳ Planned)
├── Controllers/ (REST APIs)
├── DTOs/ (Data Transfer Objects)
└── Mapping/ (AutoMapper profiles)
```
**Project Status:** 50% Complete - Ready for Business Services Implementation 🚀
@@ -1,5 +1,65 @@
# راهنمای پیکربندی Email و SMS
## قالب‌های پیامک (SmsTemplates)
> **فایل**: `CMSMicroservice.Domain/Common/SmsTemplates.cs`
همه قالب‌های پیامک در یک کلاس متمرکز شده‌اند:
```csharp
public static class SmsTemplates
{
// وام دایا
public static string DayaLoanReceived(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، مبلغ {amount:N0} ریال وام دایا به کیف پول شما واریز شد. کارابازار";
// فعال‌سازی باشگاه
public static string ClubActivated(string? firstName)
=> $"{GetUserName(firstName)} عزیز، حساب باشگاه شما فعال شد. کارابازار";
// خرید پکیج
public static string PackagePurchased(string? firstName, string packageName)
=> $"{GetUserName(firstName)} عزیز، پکیج {packageName} با موفقیت خریداری شد. کارابازار";
// واریز کمیسیون
public static string CommissionDeposited(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، مبلغ {amount:N0} ریال کمیسیون به کیف پول شما واریز شد. کارابازار";
// برداشت موفق
public static string WithdrawalSuccess(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، درخواست برداشت {amount:N0} ریال با موفقیت انجام شد. کارابازار";
// پیوستن به شبکه
public static string NetworkJoined(string? firstName, string referrerName)
=> $"{GetUserName(firstName)} عزیز، به شبکه {referrerName} پیوستید. کارابازار";
// زیرمجموعه جدید
public static string NewDownline(string? firstName, string newMemberName)
=> $"{GetUserName(firstName)} عزیز، {newMemberName} به زیرمجموعه شما اضافه شد. کارابازار";
// کد OTP
public static string OtpCode(string code)
=> $"کد تأیید شما: {code}\nکارابازار";
// خوش‌آمدگویی
public static string Welcome(string? firstName)
=> $"{GetUserName(firstName)} عزیز، به کارابازار خوش آمدید!";
}
```
### نحوه استفاده:
```csharp
// تزریق سرویس
private readonly IKavenegarService _smsService;
// ارسال پیامک
var message = SmsTemplates.DayaLoanReceived(user.FirstName, 56_000_000);
await _smsService.SendAsync(user.PhoneNumber, message);
```
---
## تنظیمات Email (Gmail)
### مرحله 1: ایجاد App Password در Gmail
@@ -4,8 +4,8 @@
**Project**: CMS Microservice - Network & Club System
**Architecture**: Clean Architecture (Domain → Application → Infrastructure → WebApi/Protobuf)
**Last Updated**: 2024-12-04
**Current Phase**: Club Discount Shop System Complete
**Last Updated**: 2025-12-25
**Current Phase**: Performance Optimization & Integration Enhancements
### 🎯 Completion Statistics
- ✅ **Fully Completed**: 12 phases (100%)
@@ -21,7 +21,46 @@
---
## 🆕 Recent Updates (2025-12-09)
## 🆕 Recent Updates (2025-12-25)
### ✅ Chatika Integration Enhancements
**Date**: 2025-12-25
**Feature**: بهبودات یکپارچگی چتیکا
**Changes**:
1. **Enabled Flag**: اضافه شدن `Chatika.Enabled` در appsettings.json برای فعال/غیرفعال کردن Worker
2. **ChatikaAccountActivationJob**: چک کردن فلگ Enabled قبل از اجرای Job
### ✅ DayaLoan Worker Fix
**Date**: 2025-12-25
**Feature**: اصلاح DayaLoanCheckWorker
**Problem**: Worker برای مشتریانی که قبلاً قرارداد گرفته‌اند مجدداً استعلام می‌زد
**Solution**: فیلتر کردن کاربرانی که `ContractNumber` دارند از لیست پردازش
### ✅ GetNetworkTree Stored Procedure
**Date**: 2025-12-25
**Feature**: بهبود Performance درخت شبکه با Stored Procedure
**Changes**:
1. **SP_GetNetworkTree.sql**: Stored Procedure با CTE recursive
2. **GetNetworkTreeQueryHandler**: بازنویسی برای استفاده از SP
3. **NetworkTreeNodeDto**: DTO جدید برای نتیجه SP
4. **No Depth Limit**: حذف محدودیت عمق با `OPTION (MAXRECURSION 0)`
**Parameters**:
- `@RootUserId`: کاربر ریشه
- `@MaxDepth`: حداکثر عمق (پیش‌فرض: 100)
- `@IsClubActive`: فیلتر فعال بودن باشگاه
- `@ActivationWeekDefinitionId`: فیلتر هفته فعال‌سازی
---
## 🆕 Previous Updates (2025-12-09)
### ✅ Club Membership Auto-Features Enhancement
+120
View File
@@ -0,0 +1,120 @@
# 🔧 SystemConstants - مقادیر ثابت سیستم
> **فایل**: `CMSMicroservice.Domain/Common/SystemConstants.cs`
> **آخرین بروزرسانی**: ۷ دی ۱۴۰۴
---
## 📋 هدف
این کلاس شامل تمام مقادیر ثابت سیستم است که در چندین جای مختلف استفاده می‌شوند.
به جای hardcode کردن اعداد در کد، از این ثابت‌ها استفاده کنید.
---
## 📊 مقادیر موجود
### Club Configuration
| ثابت | مقدار | توضیح |
|------|-------|-------|
| `ClubJoiningPercentage` | 0.35 (35%) | درصد کمیسیون پیوستن به باشگاه |
| `ClubActivationThreshold` | 0.5 (50%) | آستانه فعال‌سازی باشگاه |
### Commission Configuration
| ثابت | مقدار | توضیح |
|------|-------|-------|
| `MaxCalculationAttempts` | 3 | حداکثر تلاش برای محاسبه کمیسیون |
| `DefaultCommissionPoolDays` | 7 | تعداد روزهای استخر کمیسیون |
### Package Amounts
| ثابت | مقدار | توضیح |
|------|-------|-------|
| `GoldenPackageAmount` | 56,000,000 | مبلغ پکیج طلایی (56 میلیون ریال) |
| `DayaLoanAmount` | 56,000,000 | مبلغ وام دایا (56 میلیون ریال) |
---
## 💻 کد
```csharp
namespace CMSMicroservice.Domain.Common;
/// <summary>
/// مقادیر ثابت سیستم که در چند جای مختلف استفاده می‌شوند
/// </summary>
public static class SystemConstants
{
// Club Configuration
public const decimal ClubJoiningPercentage = 0.35m; // 35% کمیسیون پیوستن به باشگاه
public const decimal ClubActivationThreshold = 0.5m; // 50% آستانه فعال‌سازی
// Commission Configuration
public const int MaxCalculationAttempts = 3; // حداکثر تلاش محاسبه
public const int DefaultCommissionPoolDays = 7; // روزهای استخر کمیسیون
// Package Amounts
public const long GoldenPackageAmount = 56_000_000; // 56 میلیون - پکیج طلایی
public const long DayaLoanAmount = 56_000_000; // 56 میلیون - وام دایا
}
```
---
## 🔍 نحوه استفاده
### در Handler ها:
```csharp
using CMSMicroservice.Domain.Common;
public class ProcessDayaLoanApprovalCommandHandler
{
public async Task<Unit> Handle(...)
{
// به جای: var amount = 56_000_000;
var amount = SystemConstants.DayaLoanAmount;
await DepositToWallet(userId, amount);
}
}
```
### در Validation ها:
```csharp
public class ValidateGoldenPackagePurchaseQueryHandler
{
public async Task<bool> Handle(...)
{
var requiredAmount = SystemConstants.GoldenPackageAmount;
return user.WalletBalance >= requiredAmount;
}
}
```
---
## ⚠️ قوانین
1. **همیشه از ثابت‌ها استفاده کنید** - هرگز مقادیر magic number در کد ننویسید
2. **تغییر مقادیر** - برای تغییر یک مقدار، فقط این فایل را تغییر دهید
3. **ثابت‌های جدید** - اگر مقداری در بیش از یک جا استفاده می‌شود، به این فایل اضافه کنید
4. **نام‌گذاری** - از نام‌های توصیفی استفاده کنید (مثلاً `GoldenPackageAmount` نه `Amount1`)
---
## 📁 فایل‌های مرتبط
- `SmsTemplates.cs` - قالب‌های پیامک
- `ProcessDayaLoanApprovalCommandHandler.cs` - استفاده از DayaLoanAmount
- `ValidateGoldenPackagePurchaseQueryHandler.cs` - استفاده از GoldenPackageAmount
---
## 🔗 Related Docs
- [email-sms-configuration.md](email-sms-configuration.md) - تنظیمات SMS و قالب‌ها
- [CHANGELOG-2025-12-27.md](../../CHANGELOG-2025-12-27.md) - تاریخچه تغییرات
File diff suppressed because it is too large Load Diff
+244
View File
@@ -0,0 +1,244 @@
# 🔧 Mapster - مشکلات رایج و راه‌حل‌ها
> **آخرین بروزرسانی**: ۷ دی ۱۴۰۴
> **نسخه Mapster**: 7.4.0
---
## 📋 فهرست مشکلات
1. [Protobuf Int64Value Mapping](#1-protobuf-int64value-mapping)
2. [MediatR Unit to Empty](#2-mediatr-unit-to-empty)
3. [Repeated Fields (List) Mapping](#3-repeated-fields-list-mapping)
4. [Property Name Mismatch](#4-property-name-mismatch)
5. [Nullable Types](#5-nullable-types)
---
## 1. Protobuf Int64Value Mapping
### مشکل
فیلدهای `google.protobuf.Int64Value` (یا `StringValue`, `BoolValue` و غیره) که wrapper types هستند، در mapping مستقیم کار نمی‌کنند.
### نشانه‌ها
- مقدار همیشه `0` یا `null` می‌شود
- Value در client ست شده ولی در server نادرست دریافت می‌شود
### Proto:
```protobuf
import "google/protobuf/wrappers.proto";
message GetMyWeeklyBalancesRequest {
google.protobuf.Int64Value week_definition_id = 3;
}
```
### ❌ کد اشتباه:
```csharp
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
.Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId); // WRONG!
```
### ✅ کد صحیح:
```csharp
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
.Map(dest => dest.WeekDefinitionId,
src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : null);
```
### توضیح
`Int64Value` یک class wrapper است نه primitive type. باید `.Value` را extract کنید.
---
## 2. MediatR Unit to Empty
### مشکل
`MediatR.Unit` نمی‌تواند به `google.protobuf.WellKnownTypes.Empty` map شود.
### نشانه‌ها
- Exception: `No mapping found for MediatR.Unit`
- gRPC call با void return کار نمی‌کند
### ❌ کد اشتباه:
```csharp
// No mapping defined - will fail at runtime
return await _mediator.Send(command).Adapt<Empty>();
```
### ✅ راه‌حل:
```csharp
// در GeneralMapping.cs یا هر Profile
config.NewConfig<MediatR.Unit, Google.Protobuf.WellKnownTypes.Empty>()
.MapWith(_ => new Google.Protobuf.WellKnownTypes.Empty());
```
### محل فایل:
`BackOffice.BFF.WebApi/Common/Mappings/GeneralMapping.cs`
---
## 3. Repeated Fields (List) Mapping
### مشکل
فیلدهای `repeated` در protobuf به property `RepeatedField<T>` تبدیل می‌شوند که `add-only` هستند.
### نشانه‌ها
- لیست همیشه خالی
- Exception: `Cannot set RepeatedField`
### Proto:
```protobuf
message GetAllAppVersionsResponse {
repeated AppVersionItem items = 1;
}
```
### ❌ کد اشتباه:
```csharp
config.NewConfig<List<AppVersionItemDto>, GetAllAppVersionsResponse>()
.Map(dest => dest.Items, src => src); // WRONG - Items is read-only
```
### ✅ کد صحیح:
```csharp
config.NewConfig<List<AppVersionItemDto>, GetAllAppVersionsResponse>()
.MapWith(src => CreateResponse(src));
private static GetAllAppVersionsResponse CreateResponse(List<AppVersionItemDto> items)
{
var response = new GetAllAppVersionsResponse();
foreach (var item in items)
{
response.Items.Add(item.Adapt<AppVersionItem>());
}
return response;
}
```
---
## 4. Property Name Mismatch
### مشکل
نام property در DTO با نام در proto message یکی نیست.
### نشانه‌ها
- فیلد همیشه `null` یا default value
- Auto-mapping کار نمی‌کند
### مثال:
```protobuf
message MetaData {
int32 total_page = 2; // -> TotalPage in C#
}
```
```csharp
public class MetaDataDto
{
public int TotalPages { get; set; } // WRONG: should be TotalPage
}
```
### ✅ راه‌حل 1 - اصلاح نام:
```csharp
public class MetaDataDto
{
public int TotalPage { get; set; } // Match proto
}
```
### ✅ راه‌حل 2 - Explicit mapping:
```csharp
config.NewConfig<MetaData, MetaDataDto>()
.Map(dest => dest.TotalPages, src => src.TotalPage);
```
---
## 5. Nullable Types
### مشکل
Nullable types در C# نیاز به handling خاص دارند.
### Proto با nullable:
```protobuf
google.protobuf.Int64Value nullable_id = 1;
```
### ✅ در DTO:
```csharp
public long? NullableId { get; set; }
```
### ✅ Mapping:
```csharp
.Map(dest => dest.NullableId,
src => src.NullableId != null ? (long?)src.NullableId.Value : null)
```
---
## 🎯 Best Practices
### 1. همیشه Explicit Mapping برای Complex Types
```csharp
// بهتر است همیشه explicit باشد
config.NewConfig<SourceType, DestType>()
.Map(dest => dest.Prop1, src => src.Prop1)
.Map(dest => dest.Prop2, src => src.Prop2);
```
### 2. استفاده از MapWith برای Custom Logic
```csharp
config.NewConfig<Source, Dest>()
.MapWith(src => new Dest
{
// full control
});
```
### 3. فایل Profile مجزا برای هر Domain
```
Common/Mappings/
├── CommissionProfile.cs
├── NetworkProfile.cs
├── ClubProfile.cs
└── GeneralMapping.cs // for common types like Unit -> Empty
```
### 4. تست Mapping ها
```csharp
[Fact]
public void Should_Map_Request_To_Query()
{
// Arrange
var request = new GetMyWeeklyBalancesRequest { WeekDefinitionId = 7 };
// Act
var query = request.Adapt<GetMyWeeklyBalancesQuery>();
// Assert
Assert.Equal(7, query.WeekDefinitionId);
}
```
---
## 📁 فایل‌های Profile در پروژه
| پروژه | مسیر | محتوا |
|-------|------|-------|
| CMS | `WebApi/Common/Mappings/` | CommissionProfile, AppVersionProfile |
| BackOffice.BFF | `Application/Common/Mappings/` | CommissionProfile |
| BackOffice.BFF | `WebApi/Common/Mappings/` | GeneralMapping |
| FrontOffice.BFF | `WebApi/Common/Mappings/` | CommissionProfile |
---
## 🔗 منابع
- [Mapster Documentation](https://github.com/MapsterMapper/Mapster)
- [Protobuf Well-Known Types](https://protobuf.dev/reference/csharp/api-docs/class/google/protobuf/well-known-types/)
- [CHANGELOG-2025-12-27.md](../CHANGELOG-2025-12-27.md) - جزئیات بیشتر
@@ -1,8 +1,8 @@
# BackOffice - Network & Commission Management System
**Version**: 2.2
**Last Updated**: 2025-12-05
**Status**: 🟡 **Build In Progress - ~12 Errors Remaining**
**Version**: 2.3
**Last Updated**: 2025-12-26
**Status**: **Build Successful - Production Ready**
---
@@ -14,28 +14,24 @@ BackOffice is a comprehensive Blazor WebAssembly application for managing networ
## 🎯 Current Status
### **Build Status: 🔴 FAILING (~12 errors)**
### **Build Status: ✅ SUCCESSFUL (0 Errors, ~246 Warnings)**
> See `BackOffice/docs/BUILD-FIX-STATUS.md` for detailed error list
### **Recent Changes (2025-12-26)**:
- ✅ **NEW**: App Version Management page (`/settings/app-versions`)
- ✅ Network Tree rewritten with d3-org-chart
- ✅ All proto dependencies resolved
- ✅ MudBlazor upgraded to 8.x with proper T parameter
### **Recent Changes (2025-12-05)**:
- ⚠️ Migrated from .NET 8 to .NET 9
- ⚠️ Updated MudBlazor to 8.14.0 (requires T parameter for generics)
- ⚠️ Multiple files excluded from build (missing proto dependencies)
- ⚠️ Products.Protobuf switched from NuGet to ProjectReference
### **Recent Changes (2025-12-25)**:
- ✅ Network Tree Viewer complete rewrite with d3-org-chart
- ✅ Tooltip on hover for node details
- ✅ Week filter visual distinction
- ✅ Search and navigation features
### **Known Issues**:
- Missing proto projects: DiscountProduct, DiscountCategory, DiscountOrder, Tag, ProductTag
- Some UserOrder methods missing in proto (CancelOrder, ApplyDiscount, UpdateOrderStatus)
- PaginationState namespace conflicts
- Int32Value/Int64Value binding issues in WithdrawalReports
### **Excluded Files** (see `BackOffice/docs/EXCLUDED-FILES.md`):
- `Pages/DiscountShop/**` - needs new proto projects
- `Pages/Tag/**` - needs Tag.Protobuf
- `Pages/Products/Components/*Dialog*` - needs proto updates
- `Pages/UserOrder/Components/*Dialog*` - needs proto updates
- Several other pages with missing dependencies
### **Previous Known Issues (Now Resolved)**:
- ~~Missing proto projects~~ ✅ Fixed
- ~~UserOrder methods missing~~ ✅ Fixed
- ~~PaginationState conflicts~~ ✅ Fixed
---
@@ -57,7 +53,10 @@ BackOffice/
│ │ ├── Club/ # 3 pages (Members, Statistics)
│ │ ├── SystemManagement/ # 4 pages (Worker, Alerts, Health, Config)
│ │ ├── Dashboard/ # 1 page (SystemOverview)
│ │ └── Settings/ # 1 page (UserSettings)
│ │ └── Settings/ # 2 pages (UserSettings, AppVersions)
│ ├── Services/
│ │ ├── AppVersion/ # App version management
│ │ └── ...
│ └── Components/ # Reusable dialogs
└── README.md
```
@@ -107,6 +106,13 @@ Access at: `https://localhost:7001`
- Health dashboard
- Configuration editor
### **5. App Version Management** 📱 (NEW)
- Mobile app version control
- Force update configuration
- Min required version setting
- Cache clear requirements
- Update messages & release notes
---
## 🔧 Technical Stack
@@ -1,7 +1,7 @@
# گزارش وضعیت UI پنل مدیریت (BackOffice)
تاریخ گزارش: 2024-12-04
وضعیت کلی: **آماده برای Production - 95% کامل** 🎉
تاریخ گزارش: ۵ دی ۱۴۰۴ (2025-12-25)
وضعیت کلی: **آماده برای Production - 100% کامل** 🎉
---
@@ -14,8 +14,48 @@
| Services Backend | 8 فایل (4 Interface + 4 Implementation) | ✅ کامل |
| Dialog Components | 6 کامپوننت | ✅ کامل |
| اتصالات CRUD | همه عملیات | ✅ کامل |
| گزارش‌ها و نمودارهای مالی | 2 صفحه | 🟡 تکمیل پایه (PDF باقیمانده) |
| **جمع کل** | **60 صفحه + 8 سرویس + 6 دیالوگ** | **95% آماده** 🎉 |
| گزارش‌ها و نمودارهای مالی | 2 صفحه | ✅ کامل |
| **جمع کل** | **60 صفحه + 8 سرویس + 6 دیالوگ** | **100% آماده** 🎉 |
---
## 🆕 آخرین تغییرات (۵ دی ۱۴۰۴)
### بازنویسی صفحه درخت شبکه با d3-org-chart:
- ✅ `Network/NetworkTreeViewer.razor` - بازنویسی کامل با d3-org-chart
- ✅ `wwwroot/js/admin-org-chart.js` - Wrapper جدید برای org-chart
- ✅ `wwwroot/css/admin-org-chart.css` - استایل کارت‌های نود
- ✅ Tooltip روی hover با اطلاعات کامل کاربر
- ✅ فیلتر بصری هفته فعال‌سازی (disabled style برای کاربران خارج از هفته)
- ✅ Navigation history (breadcrumb) برای drill-down
- ✅ Export to PNG
- ✅ DataGrid زیر چارت
### کتابخانه‌های جدید:
- ✅ `d3-org-chart3.js` - کپی از FrontOffice
- ✅ `d3-flextree.min.js` - Dependency
---
## 🆕 تغییرات قبلی (۳۰ آذر ۱۴۰۴)
### باگ‌های رفع شده:
- ✅ `/network/balances` - ValidationException برطرف شد (Mapster mapping)
- ✅ `/club/members` - مشکل لود داده‌ها برطرف شد (ClubMembershipProfile)
- ✅ `/club/statistics` - خطای Unimplemented برطرف شد (GetClubStatistics override)
### قابلیت‌های فعال شده در Products:
- ✅ `CreateNew()` - ایجاد محصول جدید با CreateDialog
- ✅ `Update()` - ویرایش محصول با UpdateDialog
- ✅ `OpenGallery()` - مدیریت گالری با GalleryDialog
- ✅ `OpenTagAssignment()` - اختصاص تگ با AssignTagsDialog
### فیلد موجودی محصولات:
- ✅ فیلد "تعداد موجودی" در CreateDialog و UpdateDialog
- ✅ ستون موجودی در لیست با نمایش رنگی:
- 🔴 **ناموجود** (موجودی ≤ 0)
- 🟡 **کم موجود** (موجودی < 10)
- 🟢 **موجود** (موجودی ≥ 10)
---
@@ -32,7 +72,7 @@
- ✅ Commission/Withdrawals.razor - درخواست‌های برداشت (لیست، فیلتر وضعیت، دکمه‌های Approve/Reject/Process متصل به API، نمایش BankReferenceId / TrackingCode / PaymentFailureReason)
### 3. شبکه (3 صفحه)
- ✅ Network/Tree.razor - درخت شبکه
- ✅ Network/Tree.razor - درخت شبکه (بازنویسی شده با d3-org-chart)
- ✅ Network/Balances.razor - گزارش موجودی‌ها
- ✅ Network/Statistics.razor - آمار شبکه
@@ -2,11 +2,63 @@
> **FrontOffice**: رابط کاربری Blazor Server برای مشتریان نهایی سیستم FourSat
>
> **آخرین بروزرسانی**: ۲۸ آذر ۱۴۰۴ (18 دسامبر 2025)
> **آخرین بروزرسانی**: ۹ دی ۱۴۰۴ (29 دسامبر 2025)
---
## 🆕 تغییرات اخیر (۲۸ آذر ۱۴۰۴)
## 🆕 تغییرات اخیر (۹ دی ۱۴۰۴)
### ✅ Commission Data Flow Fix
- **مشکل**: صفحه weekly-balance مقادیر carryover را 0 نشان می‌داد
- **حل**: استفاده از مقادیر واقعی سرور به جای محاسبه محلی
- **فایل‌ها**:
- `CommissionService.cs` - استفاده از `balance.LeftLegCarryover`
- `CommissionDtos.cs` - Properties جدید carryover و new_members
### ✅ WeekSelector Autocomplete
- **کامپوننت**: `MudAutocomplete` برای انتخاب هفته
- **قابلیت**: جستجو در لیست هفته‌ها
- **صفحه**: `CommissionDashboardPage.razor`
### ✅ Responsive UI Improvements
- **MudGrid**: استفاده از breakpoints (`xs`, `sm`, `md`)
- **Summary Stats**: کارت‌های آماری در بالای صفحه
- **MudHidden**: جدول در دسکتاپ، کارت در موبایل
### ✅ Merged Dashboard & History Pages
- **قبل**: دو صفحه جداگانه تکراری
- **بعد**: یک صفحه با dual routing
- **فایل حذف شده**: `CommissionHistoryPage.razor[.cs]`
### ✅ Terminology Cleanup (MLM-Sensitive Words)
جایگزینی کلمات حساس:
| قبلی | جدید |
|------|------|
| کمیسیون | پاداش |
| شبکه‌سازی | تیم‌سازی |
| مشاهده شبکه | مشاهده تیم |
| آمار شبکه | آمار تیم |
| شبکه‌های فروش | تیم‌های فروش |
**فایل‌های تغییر یافته**: WeeklyBalancePage, CommissionDashboardPage, MyPackages, Packages, Index, About, Footer, NetworkStatisticsPage
---
## 🆕 تغییرات قبلی (۶ دی ۱۴۰۴)
### ✅ نمایش کد معرف در درخت شبکه
- **کد معرف**: نمایش ReferralCode برای کاربران فعال باشگاه
- **دکمه کپی**: امکان کپی کد معرف با یک کلیک
- **استایل**: طراحی زیبا با رنگ سبز برای کد معرف
- **فایل‌های تغییر یافته**:
- `Utilities/NetworkMembershipDtos.cs` - فیلد ReferralCode
- `Utilities/NetworkMembershipService.cs` - Mapping
- `wwwroot/js/org-chart.js` - نمایش در نود
- `wwwroot/css/org-chart.css` - استایل‌ها
---
## 🆕 تغییرات قبلی (۲۸ آذر ۱۴۰۴)
### ✅ نمودار درختی شبکه با d3-org-chart
- **کتابخانه**: d3-org-chart v3 + d3.js v7 + d3-flextree
@@ -66,17 +118,16 @@ FrontOffice/
│ │ │ ├── MembershipPage.razor
│ │ │ ├── FeaturesPage.razor
│ │ │ └── Components/ActivationSection.razor
│ │ ├── Network/ # ✅ شبکه (2 صفحه)
│ │ ├── Network/ # ✅ تیم (2 صفحه)
│ │ │ ├── NetworkStatisticsPage.razor
│ │ │ └── (Tree در Profile است)
│ │ └── Commission/ # ✅ کمیسیون (3 صفحه)
│ │ ├── CommissionDashboardPage.razor
│ │ ├── CommissionHistoryPage.razor
│ │ └── Commission/ # ✅ پاداش (2 صفحه)
│ │ ├── CommissionDashboardPage.razor # dual: /dashboard + /history
│ │ └── WeeklyBalancePage.razor
│ └── Utilities/ # Services & DTOs
│ ├── ClubMembershipService.cs # ⚠️ Mock Data
│ ├── NetworkMembershipService.cs # ⚠️ Mock Data
│ ├── CommissionService.cs # ⚠️ Mock Data
│ ├── CommissionService.cs # ✅ Real Data
│ └── WalletService.cs # ⚠️ 4 متد کامنت شده
└── FrontOffice.BFF/ # Backend for Frontend
├── FrontOffice.BFF.sln
@@ -156,26 +207,26 @@ FrontOffice/
- MudChart.Donut برای توزیع
- کارت آخرین عضو (آواتار، موقعیت، تاریخ)
### 💰 Commission (3 صفحه) - **جدید**
- ✅ **CommissionDashboardPage.razor**: داشبورد پرداخت‌ها
- فیلترهای هفته و وضعیت
### 💰 Commission (2 صفحه) - **بروزرسانی ۹ دی**
- ✅ **CommissionDashboardPage.razor**: داشبورد پاداش‌ها (merged با History)
- **Dual routing**: `/commission/dashboard` + `/commission/history`
- **WeekSelector Autocomplete**: انتخابگر هفته با جستجو
- **Summary Stats Cards**: کل پاداش، پرداخت شده، در انتظار، میانگین
- جدول + نمای موبایل (MudHidden responsive)
- Pagination با MudPagination
- لینک به صفحات تاریخچه و تعادل هفتگی
- ✅ **CommissionHistoryPage.razor**: تاریخچه کامل پرداخت‌ها
- کارت‌های خلاصه (مجموع، تعداد هفته، میانگین)
- جدول کامل با FixedHeader
- لینک به جزئیات تعادل هر هفته
- لینک به صفحه تعادل هفتگی
- ✅ **WeeklyBalancePage.razor**: جزئیات تعادل هفتگی
- انتخابگر هفته با دکمه "هفته جاری"
- کارت‌های تعادل چپ/راست با Progress bar
- پنل محاسبات (Min balance, Count, Commission)
- کارت‌های تعادل تیم اول/دوم با Progress bar
- **Carryover Breakdown**: نمایش اعضای جدید + انتقال از هفته قبل
- پنل محاسبات (Min balance, Count, پاداش)
- هشدار Carryover (اگر باشد)
- MudChart.Bar مقایسه چپ/راست/Min
- MudChart.Bar مقایسه تیم اول/دوم/Min
- پشتیبانی Query parameter (?week=45)
- ❌ **CommissionHistoryPage.razor**: حذف شده (merged با Dashboard)
---
## 🛠️ Services (8 سرویس)
@@ -4,38 +4,53 @@
---
## 🔴 کارهای فوری BackOffice (Proto Projects Missing)
## 🟢 BackOffice UI - فعال‌سازی فرانت‌اند فروشگاه تخفیفی (تکمیل شده ✅)
> **تاریخ ثبت**: ۱۵ آذر ۱۴۰۴
> **تاریخ تکمیل**: ۱۲ دی ۱۴۰۴ (1 January 2026)
BackOffice UI نیاز به Proto Projects زیر دارد که هنوز ساخته نشده‌اند:
تمام سرویس‌ها و صفحات فروشگاه تخفیفی در BackOffice UI فعال شدند:
| Proto Project | وضعیت | اولویت |
|---------------|-------|--------|
| `BackOffice.BFF.DiscountProduct.Protobuf` | ❌ Missing | 🔴 High |
| `BackOffice.BFF.DiscountCategory.Protobuf` | ❌ Missing | 🔴 High |
| `BackOffice.BFF.DiscountOrder.Protobuf` | ❌ Missing | 🔴 High |
| `BackOffice.BFF.Tag.Protobuf` | ❌ Missing | 🔴 High |
| `BackOffice.BFF.ProductTag.Protobuf` | ❌ Missing | 🔴 High |
| `BackOffice.BFF.PublicMessage.Protobuf` | ❌ Missing | 🔴 High |
| صفحه | Route | وضعیت |
|------|-------|-------|
| مدیریت محصولات تخفیفی | `/discount-products` | ✅ فعال |
| مدیریت دسته‌بندی‌ها | `/discount-categories` | ✅ فعال |
| مدیریت سفارشات | `/discount-orders` | ✅ فعال |
| گزارش فروش | `/sales-reports` | ✅ فعال |
| مدیریت تگ‌ها | `/tags` | ✅ فعال |
| پیام‌های عمومی | `/public-messages` | ✅ فعال |
**صفحات تأثیرپذیر:**
- `Pages/DiscountShop/*` - تمام صفحات فروشگاه تخفیفی
- `Pages/Tag/*` - مدیریت تگ‌ها
- `Pages/PublicMessages/*` - پیام‌های عمومی
- `Pages/Payment/ManualPayments.razor` - پرداخت دستی
**تغییرات در `ConfigureService.cs` (1 January 2026):**
1. فعال‌سازی Using statements برای proto clients
2. ثبت gRPC Clients در DI container
3. ثبت Application Services (DiscountProduct, DiscountCategory, DiscountOrder, Tag, PublicMessage)
**راه‌حل موقت:**
صفحات مربوطه از Build exclude شده‌اند تا proto ها ساخته شوند.
---
**TODO:**
1. [ ] ساخت `BackOffice.BFF.DiscountProduct.Protobuf`
2. [ ] ساخت `BackOffice.BFF.DiscountCategory.Protobuf`
3. [ ] ساخت `BackOffice.BFF.DiscountOrder.Protobuf`
4. [ ] ساخت `BackOffice.BFF.Tag.Protobuf`
5. [ ] ساخت `BackOffice.BFF.ProductTag.Protobuf`
6. [ ] ساخت `BackOffice.BFF.PublicMessage.Protobuf`
7. [ ] Re-include صفحات exclude شده
## 🟢 Proto Projects - BackOffice (تکمیل شده ✅)
> **تاریخ تکمیل**: ۲۳ دی ۱۴۰۴ (13 January 2025)
تمام Proto Projects مورد نیاز BackOffice UI ساخته و فعال شدند:
| Proto Project | وضعیت | توضیحات |
|---------------|-------|---------|
| `BackOffice.BFF.DiscountProduct.Protobuf` | ✅ Done | WebApi Service ساخته شد |
| `BackOffice.BFF.DiscountCategory.Protobuf` | ✅ Done | WebApi Service ساخته شد |
| `BackOffice.BFF.DiscountOrder.Protobuf` | ✅ Done | WebApi Service قبلاً موجود بود |
| `BackOffice.BFF.DiscountShoppingCart.Protobuf` | ✅ Done | WebApi Service + Handlers اصلاح شد |
| `BackOffice.BFF.Tag.Protobuf` | ✅ Done | WebApi Service ساخته شد |
| `BackOffice.BFF.ProductTag.Protobuf` | ✅ Done | WebApi Service + Handlers ساخته شد |
| `BackOffice.BFF.PublicMessage.Protobuf` | ✅ Done | WebApi Service قبلاً موجود بود |
| `BackOffice.BFF.ManualPayment.Protobuf` | ✅ Done | WebApi Service قبلاً موجود بود |
**تغییرات اصلی (13 January 2025):**
1. ساخت `DiscountCategoryService.cs` (4 RPCs)
2. ساخت `DiscountShoppingCartService.cs` (5 RPCs)
3. ساخت `TagService.cs` (6 RPCs)
4. ساخت `ProductTagService.cs` (5 RPCs)
5. ساخت Handlers برای ProductTag (Update, Delete, Get, GetAll)
6. اصلاح Handlers برای DiscountShoppingCart (مطابقت با CMS proto)
7. فعال‌سازی `DiscountShoppingCartCQ` از exclude شده در csproj
---
@@ -107,8 +122,8 @@ strong {
}
</style>
> **تاریخ به‌روزرسانی**: ۱۵ آذر ۱۴۰۴ (2025-12-05)
> **وضعیت کلی**: FrontOffice 98% | BackOffice 95%
> **تاریخ به‌روزرسانی**: ۱۲ دی ۱۴۰۴ (2026-01-01)
> **وضعیت کلی**: FrontOffice 98% | BackOffice 100% ✅
> **مسیر توسعه**: CMS → BackOffice.BFF → BackOffice (UI) → FrontOffice.BFF → FrontOffice
---
@@ -117,11 +132,23 @@ strong {
### ✅ تکمیل شده
- **CMS Microservice**: 96% (تمام سرویس‌های مورد نیاز مشتری پیاده‌سازی شده)
- **BackOffice.BFF**: 100% (35+ Handlers تکمیل)
- **BackOffice UI**: 95% (60+ صفحه آماده Production)
- **BackOffice.BFF**: 100% (35+ Handlers تکمیل + WebApi Services)
- **BackOffice UI**: **100%** (60+ صفحه آماده Production - همه ماژول‌ها فعال)
- **FrontOffice.BFF**: **98%** ✅ (همه سرویس‌های مورد نیاز مشتری پیاده‌سازی شده)
- **FrontOffice UI**: **98%** ✅ (28+ صفحه متصل به gRPC - آماده Production)
### 🎉 سمت ادمین (BackOffice) - آماده Production ✅
تمام قابلیت‌های اصلی برای ادمین پیاده‌سازی و فعال شده:
- ✅ مدیریت محصولات + دسته‌بندی
- ✅ فروشگاه تخفیفی (محصولات، دسته‌بندی، سفارشات)
- ✅ مدیریت تگ‌ها
- ✅ پیام‌های عمومی
- ✅ کمیسیون + برداشت
- ✅ شبکه + باشگاه
- ✅ پکیج‌ها
- ✅ کاربران + نقش‌ها
### 🎉 سمت مشتری (FrontOffice) - آماده Production
تمام قابلیت‌های اصلی برای مشتری پیاده‌سازی شده:
@@ -133,9 +160,9 @@ strong {
- ✅ شبکه + کمیسیون
- ✅ باشگاه + پکیج‌ها
### 🔴 کارهای باقیمانده
### 🟡 کارهای باقیمانده (اولویت پایین)
#### FrontOffice (اولویت پایین):
#### FrontOffice:
| تسک | توضیحات | اولویت |
|-----|---------|--------|
| **Password Auth** | لاگین با رمز عبور (علاوه بر OTP) | 🟡 Low |
@@ -144,11 +171,10 @@ strong {
| **Reviews** | نظرات محصول | 🟡 Low |
| **Discount Code** | کد تخفیف | 🟡 Low |
#### BackOffice (اولویت بالا):
#### BackOffice:
| تسک | توضیحات | اولویت |
|-----|---------|--------|
| **Manual Payment UI** | صفحه پرداخت دستی | 🔴 High |
| **RBAC System** | کنترل دسترسی | 🔴 High |
| **Transactions API** | صفحه تراکنش‌ها (UI آماده، API نیاز) | 🟡 Low |
---
@@ -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 موجود |
---
**آخرین به‌روزرسانی**: ۱۰ دی ۱۴۰۴
+501
View File
@@ -0,0 +1,501 @@
# BackOffice Build Fix Status
> آخرین بروزرسانی: December 20, 2025
## وضعیت فعلی
**Build Status**: ✅ SUCCESS - 0 Error
### BackOffice.BFF Solution:
- **Build**: ✅ موفق - 0 Error
- **Proto Projects فعال**:
- ✅ BackOffice.BFF.Tag.Protobuf
- ✅ BackOffice.BFF.ProductTag.Protobuf
- ✅ BackOffice.BFF.DiscountProduct.Protobuf
- ✅ BackOffice.BFF.DiscountCategory.Protobuf
- ✅ BackOffice.BFF.DiscountOrder.Protobuf
- ✅ BackOffice.BFF.DiscountShoppingCart.Protobuf
- ✅ BackOffice.BFF.PublicMessage.Protobuf
- ✅ BackOffice.BFF.ManualPayment.Protobuf
- ✅ BackOffice.BFF.ClubMembership.Protobuf
- ✅ BackOffice.BFF.Commission.Protobuf
### BackOffice UI:
- **Build**: ✅ موفق - 0 Error
- **Framework**: Blazor WebAssembly .NET 9.0
- **UI Library**: MudBlazor 8.14.0
### CMS Microservice:
- **Build**: ✅ موفق - 0 Error
**پیشرفت کلی**: از 60+ خطا به 0 خطا رسیدیم ✨
---
## ⚠️ ملاحظات مهم Proto Packages
> **هشدار مهم**: هر تغییری در Proto files نیاز به این 3 مرحله دارد:
### چک‌لیست اجباری بعد از تغییر Proto:
1. **افزایش Version** در `.csproj`:
```xml
<Version>0.0.142</Version> → <Version>0.0.143</Version>
```
2. **Pack کردن** Proto project:
```bash
cd path/to/proto/project
dotnet pack -c Release
# ✅ خودکار push می‌شه به GitLab Registry
```
3. **Update Version** در پروژه‌های وابسته (لایه بالاتر):
```xml
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.143" />
```
**مثال**: تغییر در CMS Proto → Pack → Update در BFF Protos → Pack → Update در UI
**⚠️ فراموش کردن این مراحل = Build Error یا Runtime Bug**
---
## ماژول‌های فعال شده (Enabled Modules)
### ✅ کاملاً فعال و تست شده:
1. **DiscountShop Module** (فروشگاه تخفیفی)
- ✅ DiscountProductsMainPage - مدیریت محصولات تخفیفی
- ✅ DiscountCategoriesMainPage - مدیریت دسته‌بندی‌ها (با MudDataGrid)
- ✅ DiscountOrdersMainPage - مدیریت سفارشات
- ✅ SalesReports - گزارش فروش
- ✅ ProductImageGallery - گالری تصاویر (با MudBlazor 8 fixes)
- Services: IDiscountProductService, IDiscountCategoryService, IDiscountOrderService
2. **PublicMessages Module** (پیام‌های عمومی)
- ✅ PublicMessagesMainPage - مدیریت پیام‌ها
- ✅ MessageFormDialog - فرم ایجاد/ویرایش
- ✅ MessageViewDialog - نمایش جزئیات
- ✅ MessageTemplatesDialog - قالب‌های آماده
- Services: IPublicMessageService
- Proto: BackOffice.BFF.PublicMessage.Protobuf
3. **ManualPayment Module** (پرداخت‌های دستی)
- ✅ ManualPayments - صفحه اصلی مدیریت
- ✅ ManualPaymentDialog - فرم ایجاد و تایید/رد
- Services: Direct gRPC to ManualPaymentContract
- Proto: BackOffice.BFF.ManualPayment.Protobuf
4. **Tag Module** (برچسب‌ها)
- ✅ TagManagementPage - مدیریت تگ‌ها
- ✅ TagEditDialog - ویرایش تگ
- Services: ITagService, IProductTagService
- Proto: BackOffice.BFF.Tag.Protobuf, BackOffice.BFF.ProductTag.Protobuf
5. **Dashboard Widgets**
- ✅ DiscountShopWidget - آمار فروشگاه تخفیفی (7 روز اخیر)
6. **Payment Pages**
- ✅ Transactions - صفحه تراکنش‌ها
7. **DragDrop Pages**
- ✅ CategoryProductsDragDropPage - مدیریت محصولات دسته
- ✅ ProductCategoriesDragDropPage - مدیریت دسته‌های محصول
8. **BulkEdit Module**
- ✅ BulkEdit - ویرایش گروهی محصولات (قیمت، موجودی، وضعیت)
- Proto: BackOffice.BFF.Products.Protobuf (BulkUpdateProductPrices, BulkUpdateProductStock, ToggleProductStatus)
- Note: استفاده از `BackOffice.BFF.Protobuf.Common.PaginationState` با using alias
9. **Product Image Management** - ✅ FULLY OPERATIONAL
- ✅ GalleryDialog - گالری تصاویر محصول
- ✅ CreateDialog - ایجاد محصول با آپلود تصویر
- ✅ UpdateDialog - ویرایش محصول با آپلود تصویر
- ✅ Proto: GetProductGallery, AddProductImage, RemoveProductImage
- ✅ Messages: ImageFileModel, ProductGalleryItem
- ✅ Backend: ProductsService methods uncommented and active
- ✅ CQRS Handlers: AddProductImageCommandHandler, GetProductGalleryQueryHandler, RemoveProductImageCommandHandler
- ✅ CMS Integration: ProductGalleries microservice connected
- ✅ Image Optimization: SixLabors.ImageSharp (1200x1200 + 300x300 thumbnail)
---
## ماژول‌های Exclude شده (نیاز به کار اضافی)
**هیچ فایلی Exclude نیست!** ✅
تمامی صفحات و کامپوننت‌ها build می‌شوند. فقط Backend implementation برای Image Upload لازمه.
---
## تغییرات مهم MudBlazor 8
### Breaking Changes برطرف شده:
1. **MudDialogInstance → IMudDialogInstance**
```csharp
// قبلی:
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
// جدید:
[CascadingParameter] IMudDialogInstance MudDialog { get; set; }
```
2. **MudSwitch نیاز به T parameter**
```razor
<!-- قبلی: -->
<MudSwitch @bind-Checked="Model.IsActive" />
<!-- جدید: -->
<MudSwitch T="bool" @bind-Value="Model.IsActive" />
```
3. **MudChip نیاز به T parameter**
```razor
<!-- قبلی: -->
<MudChip>Text</MudChip>
<!-- جدید: -->
<MudChip T="string">Text</MudChip>
```
4. **MudTreeView تغییر API**
- راه‌حل: جایگزینی با `MudDataGrid` در DiscountCategoriesMainPage
5. **MudFileUpload تغییر signature**
```csharp
// FilesChanged حالا IBrowserFile می‌گیرد نه IReadOnlyList
<MudFileUpload T="IReadOnlyList<IBrowserFile>" FilesChanged="OnFilesSelected" />
```
6. **DragEventArgs.PreventDefault() حذف شد**
```razor
<!-- استفاده از directive attribute: -->
@ondragover:preventDefault
```
---
## تغییرات Proto
### 1. Google.Protobuf.WellKnownTypes Simplification
در همه جا از wrapper به مقدار مستقیم تغییر یافت:
```csharp
// قبلی (اشتباه):
request.UserId = new Google.Protobuf.WellKnownTypes.Int64Value { Value = userId };
request.Status = new Google.Protobuf.WellKnownTypes.Int32Value { Value = status };
request.ReferenceNumber = new Google.Protobuf.WellKnownTypes.StringValue { Value = refNum };
// جدید (صحیح):
request.UserId = userId;
request.Status = status;
request.ReferenceNumber = refNum;
```
### 2. Timestamp to DateTime Conversion
```csharp
// Proto Timestamp به DateTime تبدیل می‌شود:
var dateTime = timestamp.ToDateTime(); // به جای ToLocalTime()
```
---
## تغییرات معماری
### BasePageComponent Pattern
صفحات با فیلتر از `BasePageComponent` استفاده می‌کنند ولی `ReloadAsync()` ندارد.
راه‌حل: استفاده مستقیم از `MudDataGrid.ReloadServerData()`:
```csharp
private MudDataGrid<ModelType>? _dataGrid;
private async Task OnFilterSubmit()
{
if (_dataGrid != null)
await _dataGrid.ReloadServerData();
}
```
---
- `ProductGalleryImage`
- `GetCategoriesRequest/Response`
- `UpdateProductCategoriesRequest`
- `GetProductsForCategoryRequest/Response`
- `UpdateCategoryProductsRequest`
### 3. تغییرات csproj
**Products از NuGet به ProjectReference تغییر کرد**:
```xml
<!-- قبلی: -->
<PackageReference Include="Foursat.BackOffice.BFF.Products.Protobuf" Version="0.0.8" />
<!-- جدید: -->
<ProjectReference Include="../../../BackOffice.BFF/src/Protobufs/BackOffice.BFF.Products.Protobuf/BackOffice.BFF.Products.Protobuf.csproj" />
```
### 4. فیکس‌های MudBlazor
**MudSwitch T parameter**:
- `Pages/Settings/UserSettings.razor`
- `Pages/Club/ClubMembers.razor`
- `Pages/Configuration/Configuration.razor`
```razor
<!-- قبلی: -->
<MudSwitch @bind-Value="..." />
<!-- جدید: -->
<MudSwitch T="bool" @bind-Value="..." />
```
### 5. فیکس Snackbar Duplicate
در فایل‌های زیر `[Inject] ISnackbar Snackbar` حذف شد (چون در `_Imports.razor` inject شده):
- `ApplyDiscountDialog.razor.cs`
- `CancelOrderDialog.razor.cs`
- `ChangeOrderStatusDialog.razor.cs`
### 6. فیکس ConfigureService.cs
Using های زیر comment شدند:
```csharp
// using BackOffice.Services.DiscountProduct;
// using BackOffice.Services.DiscountCategory;
// using BackOffice.Services.DiscountOrder;
// using BackOffice.Services.Tag;
// using BackOffice.Services.ProductTag;
// using BackOffice.Services.PublicMessage;
```
---
## کارهای باقیمانده (TODO)
### فوری - نیاز به Proto Methods:
#### 1. Product Image Management
**فایل‌های Excluded**:
- `Pages/Products/Components/GalleryDialog.razor`
- `Pages/Products/Components/CreateDialog.razor`
- `Pages/Products/Components/UpdateDialog.razor`
**Proto Methods مورد نیاز در `products.proto`**:
```protobuf
service ProductsContract {
// برای GalleryDialog:
rpc AddProductImage(AddProductImageRequest) returns (AddProductImageResponse);
rpc RemoveProductImage(RemoveProductImageRequest) returns (google.protobuf.Empty);
// برای Create/Update Dialogs:
rpc CreateProductWithImage(CreateProductWithImageRequest) returns (CreateProductResponse);
rpc UpdateProductWithImage(UpdateProductWithImageRequest) returns (google.protobuf.Empty);
}
message ImageFileModel {
bytes file = 1;
string mime = 2;
string file_name = 3;
}
message AddProductImageRequest {
int64 product_id = 1;
string title = 2;
ImageFileModel image_file = 3;
}
message AddProductImageResponse {
int64 product_gallery_id = 1;
}
message RemoveProductImageRequest {
int64 product_gallery_id = 1;
}
message CreateProductWithImageRequest {
// ... سایر فیلدهای محصول
ImageFileModel image_file = 1;
ImageFileModel thumbnail_file = 2;
}
message UpdateProductWithImageRequest {
int64 id = 1;
// ... سایر فیلدها
ImageFileModel image_file = 2;
ImageFileModel thumbnail_file = 3;
}
```
**وضعیت**: 🔴 نیاز به پیاده‌سازی در Backend
---
#### 2. BulkEdit Refactoring
**فایل Excluded**: `Pages/Products/BulkEdit.razor`
**مشکل**: استفاده مستقیم از `CMSMicroservice.Protobuf.Protos`
**راه‌حل**:
1. حذف dependency به `CMSMicroservice.Protobuf`
2. افزودن bulk update methods به `products.proto`:
```protobuf
service ProductsContract {
rpc BulkUpdateProducts(BulkUpdateProductsRequest) returns (BulkUpdateProductsResponse);
}
message BulkUpdateProductsRequest {
repeated int64 product_ids = 1;
google.protobuf.Int64Value new_price = 2;
google.protobuf.Int32Value new_discount = 3;
google.protobuf.Int32Value new_club_discount_percent = 4;
StockUpdateOperation stock_operation = 5;
google.protobuf.BoolValue status_enable = 6;
}
enum StockUpdateOperation {
STOCK_NO_CHANGE = 0;
STOCK_SET = 1;
STOCK_ADD = 2;
STOCK_SUBTRACT = 3;
}
message BulkUpdateProductsResponse {
int32 updated_count = 1;
repeated int64 failed_product_ids = 2;
}
```
**وضعیت**: 🔴 نیاز به پیاده‌سازی در Backend
---
### اختیاری - بهبودها:
#### 3. Transactions API Implementation
**فایل**: `Pages/Payment/Transactions.razor`
**وضعیت فعلی**: ✅ Enabled ولی متد `LoadData` فقط `TODO` دارد
**نیاز**: پیاده‌سازی Transaction API در Backend
---
## آمار نهایی
### ماژول‌های فعال: 7 ✅
1. DiscountShop (Products, Categories, Orders, Reports)
2. PublicMessages
3. ManualPayments
4. Tag Management
5. Dashboard DiscountShopWidget
6. Transactions Page
7. DragDrop Pages (Category ↔ Products)
### ماژول‌های Excluded: 3 ❌
1. GalleryDialog (نیاز به Image Upload API)
2. CreateDialog/UpdateDialog (نیاز به Image Upload API)
3. BulkEdit (نیاز به Refactoring + Bulk API)
### Build Errors: 0 🎉
### Proto Projects: 14 فعال
### صفحات فعال: ~30+
### کامپوننت‌های فعال: ~50+
---
---
## Handler های موقتاً Exclude شده در BackOffice.BFF.Application
### فایل‌های Exclude شده:
```xml
<Compile Remove="DiscountOrderCQ/**/*.cs" />
<Compile Remove="DiscountShoppingCartCQ/**/*.cs" />
<Compile Remove="ManualPaymentCQ/**/*.cs" />
<Compile Remove="ConfigurationCQ/**/*.cs" />
<Compile Remove="CommissionCQ/Commands/ProcessWithdrawal/**/*.cs" />
```
### دلیل Exclude:
این Handler ها فیلدهای متفاوتی با proto های CMS دارند و نیاز به بازنویسی دارند.
### مثال عدم تطابق DiscountOrder:
**Handler انتظار دارد:**
- Request: `UserId`, `AddressId`, `DiscountBalanceAmount`, `GatewayAmount`
- Response: `OrderId`, `TrackingCode`, `RequiresGatewayPayment`, `GatewayPayableAmount`
**Proto CMS دارد:**
- Request: `user_id`, `user_address_id`, `discount_balance_to_use`, `notes`
- Response: `success`, `message`, `order_id`, `gateway_amount`, `payment_url`
---
## Proto Update های مورد نیاز
### UserOrder.Protobuf
متدهای زیر باید اضافه شوند:
- `CancelOrderAsync(CancelOrderRequest)`
- `ApplyDiscountToOrderAsync(ApplyDiscountToOrderRequest)`
- `UpdateOrderStatusAsync(UpdateOrderStatusRequest)`
فیلدهای زیر باید اضافه شوند:
- `VatAmount`
- `VatPercentage`
- `VatBaseAmount`
- `VatTotalAmount`
- `PaymentStatus.None`
### Products.Protobuf
متدهای زیر باید اضافه شوند:
- `AddProductImageAsync`
- `RemoveProductImageAsync`
فیلدهای زیر باید اضافه شوند:
- `ImageFile` (bytes)
- `ThumbnailFile` (bytes)
- `ImageFileModel` message
---
## دستورات برای ادامه کار
### 1. اجرای build برای دیدن خطاهای فعلی:
```bash
cd /home/masoud/Apps/project/FourSat/BackOffice/src/BackOffice
dotnet build 2>&1 | grep -E "error CS|Error"
```
### 2. فایل‌های مهم برای بررسی:
- `BackOffice.csproj` - لیست exclude ها و references
- `ConfigureService.cs` - DI registrations
- `_Imports.razor` - global using و inject ها
### 3. Proto فایل‌های مهم:
- `BackOffice.BFF/src/Protobufs/BackOffice.BFF.Products.Protobuf/Protos/products.proto`
- `BackOffice.BFF/src/Protobufs/BackOffice.BFF.UserOrder.Protobuf/Protos/userorder.proto`
---
## چک‌لیست برای chat جدید
- [ ] خطاهای build رو چک کن
- [ ] `PaginationState` namespace رو فیکس کن
- [ ] `WithdrawalReports` binding رو فیکس کن
- [ ] `OpenGalleryDialog` رو comment کن در `ProductsMainPage`
- [ ] `DiscountShopWidget` رو از `SystemOverview` حذف کن
- [ ] تست build موفق
---
## نکات مهم
1. **هیچ فایلی حذف نشده** - فقط از build exclude شدند
2. **Proto های local** از ProjectReference استفاده می‌کنند نه NuGet
3. **MudBlazor 8.14.0** نیاز به `T` parameter برای generic components دارد
4. **Snackbar** در `_Imports.razor` inject شده، نباید در component ها duplicate بشه
+174
View File
@@ -0,0 +1,174 @@
# 📝 Changelog - ۲۳ دی ۱۴۰۴ (13 January 2025)
> **Session**: تکمیل BackOffice.BFF WebApi Services برای Discount Shop
---
## 🎯 خلاصه تغییرات
تکمیل لایه gRPC Services برای BackOffice.BFF - بخش فروشگاه تخفیفی
---
## 🆕 WebApi Services جدید
### DiscountCategoryService.cs
**مسیر**: `BackOffice.BFF.WebApi/Services/DiscountCategoryService.cs`
| RPC | Command/Query |
|-----|---------------|
| `Create` | `CreateDiscountCategoryCommand` |
| `Update` | `UpdateDiscountCategoryCommand` |
| `Delete` | `DeleteDiscountCategoryCommand` |
| `GetDiscountCategories` | `GetDiscountCategoriesQuery` |
---
### DiscountShoppingCartService.cs
**مسیر**: `BackOffice.BFF.WebApi/Services/DiscountShoppingCartService.cs`
| RPC | Command/Query |
|-----|---------------|
| `AddToCart` | `AddToCartCommand` |
| `RemoveFromCart` | `RemoveFromCartCommand` |
| `UpdateCartItemCount` | `UpdateCartItemCountCommand` |
| `GetUserCart` | `GetUserCartQuery` |
| `ClearCart` | `ClearCartCommand` |
**توضیح**: این سرویس برای مدیریت سبد خرید کاربران توسط پشتیبانی/ادمین استفاده می‌شود.
---
### TagService.cs
**مسیر**: `BackOffice.BFF.WebApi/Services/TagService.cs`
| RPC | Command/Query |
|-----|---------------|
| `Create` | `CreateTagCommand` |
| `Update` | `UpdateTagCommand` |
| `Delete` | `DeleteTagCommand` |
| `Get` | `GetTagQuery` |
| `GetAll` | `GetAllTagsQuery` |
| `GetProductsByTag` | `GetProductsByTagQuery` |
---
### ProductTagService.cs
**مسیر**: `BackOffice.BFF.WebApi/Services/ProductTagService.cs`
| RPC | Command/Query |
|-----|---------------|
| `CreateNewProductTag` | `CreateProductTagCommand` |
| `UpdateProductTag` | `UpdateProductTagCommand` |
| `DeleteProductTag` | `DeleteProductTagCommand` |
| `GetProductTag` | `GetProductTagQuery` |
| `GetAllProductTagByFilter` | `GetAllProductTagsQuery` |
---
## 🔧 تغییرات Application Layer
### Handlers جدید ایجاد شده
#### ProductTagCQ/Commands/UpdateProductTag/
- `UpdateProductTagCommand.cs`
- `UpdateProductTagCommandHandler.cs`
#### ProductTagCQ/Commands/DeleteProductTag/
- `DeleteProductTagCommand.cs`
- `DeleteProductTagCommandHandler.cs`
#### ProductTagCQ/Queries/GetProductTag/
- `GetProductTagQuery.cs`
- `GetProductTagQueryHandler.cs`
#### ProductTagCQ/Queries/GetAllProductTags/
- `GetAllProductTagsQuery.cs`
- `GetAllProductTagsQueryHandler.cs`
---
### DiscountShoppingCartCQ - اصلاحات Proto
#### فایل‌های تغییر یافته:
**RemoveFromCartCommand.cs**
```diff
- public long CartItemId { get; init; }
+ public long ProductId { get; init; }
```
**UpdateCartItemCountCommand.cs**
```diff
- public long CartItemId { get; init; }
+ public long ProductId { get; init; }
```
**GetUserCartResponseDto.cs**
```diff
- public long UserId { get; set; }
- public long TotalDiscountedPrice { get; set; }
- public long TotalSavings { get; set; }
+ public long TotalDiscountAmount { get; set; }
+ public long FinalPrice { get; set; }
```
**CartItemDto**
```diff
- public long Id { get; set; }
- public long DiscountedPrice { get; set; }
- public DateTime AddedAt { get; set; }
+ public long DiscountAmount { get; set; }
+ public long FinalPrice { get; set; }
+ public int ProductRemainingCount { get; set; }
+ public DateTime Created { get; set; }
```
---
## 📦 تغییرات Proto Projects
### csproj تغییر یافته (GrpcServices: Client → Both)
- `BackOffice.BFF.DiscountCategory.Protobuf.csproj`
- `BackOffice.BFF.DiscountShoppingCart.Protobuf.csproj`
- `BackOffice.BFF.Tag.Protobuf.csproj`
- `BackOffice.BFF.ProductTag.Protobuf.csproj`
### WebApi.csproj - References اضافه شده
```xml
<ProjectReference Include="..\Protobufs\BackOffice.BFF.DiscountCategory.Protobuf\..." />
<ProjectReference Include="..\Protobufs\BackOffice.BFF.DiscountShoppingCart.Protobuf\..." />
<ProjectReference Include="..\Protobufs\BackOffice.BFF.Tag.Protobuf\..." />
<ProjectReference Include="..\Protobufs\BackOffice.BFF.ProductTag.Protobuf\..." />
```
---
## 🗑️ Excludes حذف شده
### BackOffice.BFF.Application.csproj
```diff
- <Compile Remove="DiscountShoppingCartCQ/**/*.cs" />
+ <!-- All excluded handlers have been enabled -->
```
---
## 📊 آمار نهایی
| متریک | مقدار |
|--------|-------|
| Services جدید | 4 |
| Handlers جدید | 8 |
| فایل‌های اصلاح شده | 12 |
| Proto projects تنظیم شده | 4 |
---
## ✅ نتیجه Build
```
Build succeeded.
177 Warning(s)
0 Error(s)
```
+180
View File
@@ -0,0 +1,180 @@
# 📝 Changelog - ۳۰ آذر ۱۴۰۴ (20 December 2025)
> **Session**: رفع باگ‌های BackOffice UI و فعال‌سازی قابلیت‌های Products
---
## 🎯 خلاصه Session
این session شامل رفع چندین باگ در صفحات BackOffice و فعال‌سازی قابلیت‌های صفحه Products بود.
---
## 🐛 Bug Fixes
### 1. صفحه `/network/balances` - ValidationException ✅
**مشکل**: خطای ValidationException هنگام لود صفحه بالانس‌های هفتگی
**علت**: Mapster mapping برای `GetUserWeeklyBalancesRequest``GetUserWeeklyBalancesQuery` وجود نداشت
**راه‌حل**: اضافه کردن mapping در `CommissionProfile.cs`:
```csharp
config.NewConfig<GetUserWeeklyBalancesRequest, GetUserWeeklyBalancesQuery>()
.Map(dest => dest.PaginationState, src => src.PaginationState);
```
**فایل**: `BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs`
---
### 2. صفحه `/club/members` - داده‌ها لود نمی‌شدند ✅
**مشکل**: صفحه خالی بود و هیچ داده‌ای نمایش نمی‌داد
**علت**: Mapster mappings برای `GetAllClubMemberships` در CMS و BFF وجود نداشت
**راه‌حل**: ایجاد `ClubMembershipProfile.cs` در هر دو لایه
**فایل‌های جدید/تغییر یافته**:
- `CMS/src/CMSMicroservice.WebApi/Common/Mappings/ClubMembershipProfile.cs` (NEW)
- `BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/ClubMembershipProfile.cs` (REWRITTEN)
**Mappings اضافه شده**:
```csharp
// CMS
GetAllClubMembershipsRequest GetAllClubMembershipsQuery
GetAllClubMembershipsResponseDto GetAllClubMembershipsResponse
// BFF
BFF.GetAllClubMembershipsRequest GetAllClubMembershipsQuery
CMS.GetAllClubMembershipsResponse BFF.GetAllClubMembershipsResponse
```
---
### 3. صفحه `/club/statistics` - Unimplemented Error ✅
**مشکل**: خطای `Status(StatusCode="Unimplemented", Detail="Method cms.ClubMembershipContract/GetClubStatistics is unimplemented")`
**علت**: متد `GetClubStatistics` در BFF Service override نشده بود
**راه‌حل**:
1. اضافه کردن override در `ClubMembershipService.cs`:
```csharp
public override async Task<GetClubStatisticsResponse> GetClubStatistics(
GetClubStatisticsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetClubStatisticsRequest, GetClubStatisticsQuery, GetClubStatisticsResponse>(request, context);
}
```
2. اضافه کردن mappings برای Statistics در هر دو Profile
**فایل‌های تغییر یافته**:
- `BackOffice.BFF/src/BackOffice.BFF.WebApi/Services/ClubMembershipService.cs`
- `CMS/src/CMSMicroservice.WebApi/Common/Mappings/ClubMembershipProfile.cs`
- `BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/ClubMembershipProfile.cs`
---
## ✨ New Features
### 4. فعال‌سازی قابلیت‌های صفحه Products ✅
**قبل**: همه دکمه‌ها پیام "در حال توسعه" نشان می‌دادند (کد comment شده بود)
**بعد**: همه قابلیت‌ها فعال شدند
**متدهای فعال شده در `ProductsMainPage.razor.cs`**:
-`CreateNew()` - ایجاد محصول جدید با CreateDialog
-`Update()` - ویرایش محصول با UpdateDialog
-`OpenGallery()` - مدیریت گالری تصاویر با GalleryDialog
-`OpenTagAssignment()` - اختصاص تگ به محصول با AssignTagsDialog
**فایل**: `BackOffice/src/BackOffice/Pages/Products/ProductsMainPage.razor.cs`
---
### 5. فیلد "تعداد موجودی" در Products ✅
**اضافات در UI**:
| فایل | تغییر |
|------|-------|
| `CreateDialog.razor` | اضافه شدن فیلد `RemainingCount` |
| `UpdateDialog.razor` | اضافه شدن فیلد `RemainingCount` |
| `ProductsMainPage.razor` | اضافه شدن ستون موجودی با رنگ‌بندی |
**نمایش موجودی در لیست**:
- 🔴 **ناموجود** - اگر موجودی `0` یا کمتر (Chip قرمز)
- 🟡 **کم موجود** - اگر موجودی کمتر از `10` (Chip زرد)
- 🟢 **موجود** - اگر موجودی `10` یا بیشتر (Chip سبز)
**فیکس در BFF** (مهم!):
فیلد `RemainingCount` در BFF Application Commands نبود و باعث می‌شد مقدار ارسال/دریافت نشه:
```csharp
// BackOffice.BFF.Application/ProductsCQ/Commands/CreateNewProducts/CreateNewProductsCommand.cs
public int RemainingCount { get; init; } // ← اضافه شد
// BackOffice.BFF.Application/ProductsCQ/Commands/UpdateProducts/UpdateProductsCommand.cs
public int RemainingCount { get; init; } // ← اضافه شد
```
---
## 📁 لیست کامل فایل‌های تغییر یافته
### BackOffice.BFF (5 فایل)
| فایل | تغییر |
|------|-------|
| `WebApi/Common/Mappings/CommissionProfile.cs` | اضافه شدن mapping برای GetUserWeeklyBalances |
| `WebApi/Common/Mappings/ClubMembershipProfile.cs` | بازنویسی کامل با mappings جدید |
| `WebApi/Services/ClubMembershipService.cs` | اضافه شدن GetClubStatistics override |
| `Application/.../CreateNewProductsCommand.cs` | اضافه شدن RemainingCount |
| `Application/.../UpdateProductsCommand.cs` | اضافه شدن RemainingCount |
### CMS (1 فایل جدید)
| فایل | تغییر |
|------|-------|
| `WebApi/Common/Mappings/ClubMembershipProfile.cs` | فایل جدید با mappings کامل |
### BackOffice UI (4 فایل)
| فایل | تغییر |
|------|-------|
| `Pages/Products/ProductsMainPage.razor.cs` | فعال‌سازی CreateNew, Update, OpenGallery, OpenTagAssignment |
| `Pages/Products/ProductsMainPage.razor` | اضافه شدن ستون موجودی |
| `Pages/Products/Components/CreateDialog.razor` | اضافه شدن فیلد موجودی |
| `Pages/Products/Components/UpdateDialog.razor` | اضافه شدن فیلد موجودی |
---
## 📊 Build Status
```
✅ BackOffice.BFF: Build succeeded (0 errors)
✅ CMS: Build succeeded (0 errors)
✅ BackOffice UI: Build succeeded (0 errors)
```
---
## 🔧 نکات فنی
### Mapster با Proto Types
برای proto types که immutable هستند، از `MapWith` استفاده کنید:
```csharp
config.NewConfig<SourceDto, ProtoResponse>()
.MapWith(src => new ProtoResponse
{
Field1 = src.Field1,
RepeatedField = { src.List?.Select(...) ?? Enumerable.Empty<...>() }
});
```
### Alias Imports برای Proto Disambiguation
```csharp
using BffProtos = BackOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership;
using CmsProtos = CMSMicroservice.Protobuf.Protos.ClubMembership;
```
+310
View File
@@ -0,0 +1,310 @@
# 📝 Changelog - ۳ دی ۱۴۰۴ (23 December 2025)
> **Session**: فعال‌سازی چتیکا، اصلاح ثبت‌نام شبکه، بهبود فرایند باشگاه مشتریان
---
## 🎯 خلاصه Session
این session شامل موارد زیر بود:
1. اصلاح SQL Scripts برای مهاجرت باشگاه مشتریان
2. اضافه کردن `LegPosition` در ثبت‌نام کاربران جدید
3. همگام‌سازی `AcceptClubMembershipContractCommandHandler` با `ActivateClubMembershipCommandHandler`
4. ایجاد `ClubFeatureType` Enum برای جایگزینی hardcoded IDs
5. **پیاده‌سازی Worker چتیکا** - فعال‌سازی خودکار حساب هوش مصنوعی
---
## 🐛 Bug Fixes
### 1. SQL Script - `IsActive` Column Missing ✅
**مشکل**: خطای `Invalid column name 'IsActive'` در stage database
**علت**: ستون `IsActive` در جدول `ClubMemberships` وجود نداشت
**راه‌حل**: اجرای migration script یا حذف ستون از INSERT
**فایل**: `dbbkup/MigrateUsersToClubMembership.sql`
---
### 2. SQL Script - `WeekNumber` → `WeekDefinitionId` Migration ✅
**مشکل**: خطای `Invalid column name 'WeekNumber'` در `WeeklyCommissionPools`
**علت**: ستون قبلاً به `WeekDefinitionId` تغییر نام داده شده بود
**راه‌حل**: تغییر Query برای استفاده از `WeekDefinitions` table:
```sql
-- قبل
INSERT INTO WeeklyCommissionPools (WeekNumber, ...) VALUES (1, ...)
-- بعد
DECLARE @CurrentWeekId BIGINT
SELECT @CurrentWeekId = Id FROM WeekDefinitions
WHERE StartDate <= GETDATE() AND EndDate >= GETDATE()
INSERT INTO WeeklyCommissionPools (WeekDefinitionId, ...) VALUES (@CurrentWeekId, ...)
```
**فایل**: `dbbkup/MigrateSpecificUsersToClubMembership.sql`
---
### 3. SQL Script - `DECLARE/SET` Syntax Error ✅
**مشکل**: خطای syntax در `DECLARE @var = value`
**علت**: FreeTDS/older SQL Server syntax نیاز به جداسازی DECLARE و SET دارد
**راه‌حل**:
```sql
-- قبل
DECLARE @CurrentWeekId BIGINT = (SELECT ...)
-- بعد
DECLARE @CurrentWeekId BIGINT
SET @CurrentWeekId = (SELECT ...)
```
**فایل**: `dbbkup/MigrateSpecificUsersToClubMembership.sql`
---
## ✨ New Features
### 4. اضافه کردن `LegPosition` در ثبت‌نام کاربران جدید ✅
**مشکل**: کاربران جدید بدون `LegPosition` در درخت شبکه ثبت می‌شدند
**راه‌حل**: اضافه کردن منطق تعیین دست چپ/راست:
```csharp
// تعیین موقعیت در دست چپ یا راست
var existingChildren = await _context.Users
.Where(x => x.NetworkParentId == parent.Id && !x.IsDeleted)
.Select(x => x.LegPosition)
.ToListAsync(cancellationToken);
NetworkLeg newUserLegPosition;
if (!existingChildren.Any(x => x == NetworkLeg.Left))
newUserLegPosition = NetworkLeg.Left;
else if (!existingChildren.Any(x => x == NetworkLeg.Right))
newUserLegPosition = NetworkLeg.Right;
else
return Error("Parent already has both legs filled");
user = new User { ..., LegPosition = newUserLegPosition };
```
**فایل**: `CMS/src/CMSMicroservice.Application/OtpTokenCQ/Commands/VerifyOtpToken/VerifyOtpTokenCommandHandler.cs`
---
### 5. همگام‌سازی `AcceptClubMembershipContractCommandHandler` ✅
**مشکل**: منطق فعال‌سازی باشگاه در `AcceptClubMembershipContractCommandHandler` ناقص بود
**راه‌حل**: کپی کامل منطق از `ActivateClubMembershipCommandHandler`:
**اضافات**:
- خواندن `GiftValue` و `ActivationFee` از Configuration
- ثبت `ClubMembershipHistory`
- ثبت در `WeeklyCommissionPool`
- اختصاص `UserClubFeatures` (همه 4 فیچر)
```csharp
// 6. ثبت تاریخچه
var history = new ClubMembershipHistory
{
ClubMembershipId = membership.Id,
ActionType = ClubMembershipActionType.Activated,
PerformedBy = _currentUserService.UserId ?? membership.UserId,
...
};
// 7. ثبت در استخر کمیسیون هفتگی
var currentWeek = _weekRepository.GetCurrentWeek();
var pool = new WeeklyCommissionPool { ... };
// 8. اختصاص فیچرهای باشگاه
var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
foreach (var featureId in featureIds) { ... }
```
**فایل**: `CMS/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs`
---
### 6. ایجاد `ClubFeatureType` Enum ✅
**مشکل**: استفاده از آرایه hardcoded `new long[] { 1, 2, 3, 4 }` در کد
**راه‌حل**: ایجاد Enum با Extension Method:
```csharp
public enum ClubFeatureType
{
Chatika = 1, // چتیکا - دستیار هوش مصنوعی
Bime = 2, // بیمه - خدمات بیمه‌ای
Trip = 3, // تریپ - خدمات سفر و گردشگری
Learn = 4 // لرن - آموزش و یادگیری
}
public static class ClubFeatureTypeExtensions
{
public static long[] GetAllFeatureIds() =>
Enum.GetValues<ClubFeatureType>().Select(f => (long)f).ToArray();
public static string GetPersianTitle(this ClubFeatureType featureType) => featureType switch
{
ClubFeatureType.Chatika => "چتیکا",
ClubFeatureType.Bime => "بیمه",
ClubFeatureType.Trip => "تور و سفر",
ClubFeatureType.Learn => "آموزش",
_ => featureType.ToString()
};
}
```
**فایل جدید**: `CMS/src/CMSMicroservice.Domain/Enums/ClubFeatureType.cs`
**فایل‌های آپدیت شده**:
- `ActivateClubMembershipCommandHandler.cs`
- `AcceptClubMembershipContractCommandHandler.cs`
---
### 7. 🤖 پیاده‌سازی Worker چتیکا (Chatika Account Activation) ✅
**نیاز**: فعال‌سازی خودکار حساب چتیکا برای اعضای جدید باشگاه
**معماری**:
```
┌─────────────────────────────────────────────────────────────┐
│ Hangfire Scheduler │
│ (Every 5 minutes) │
└─────────────────────┬───────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ ChatikaAccountActivationJob │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. Find users with: │ │
│ │ - ClubMembership.IsActive = true │ │
│ │ - ClubFeatureId = 1 (Chatika) │ │
│ │ - Notes IS NULL (not processed yet) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 2. For each user: │ │
│ │ - Call Chatika API │ │
│ │ - Update Notes with description │ │
│ │ - Set IsActive = true │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Chatika External API │
│ POST /api/v1/organizations/register-user │
│ Header: X-API-Key │
│ Body: { "mobile_number": "09..." } │
└─────────────────────────────────────────────────────────────┘
```
**فایل‌های جدید**:
| فایل | توضیح |
|------|-------|
| `IChatikaApiService.cs` | Interface سرویس چتیکا |
| `ChatikaApiService.cs` | پیاده‌سازی با HttpClient |
| `ChatikaAccountActivationJob.cs` | Hangfire Background Job |
**تنظیمات** (`appsettings.json`):
```json
"Chatika": {
"BaseUrl": "https://api.chatika.ir",
"ApiKey": "tIukvL8dnV4cB3yVWcCD9Xyfbj8rBxm5wPt2mLyJCgTsBBoMTWjt6mFEqQwpw-er"
}
```
**توضیحات فیچر چتیکا** (در `Notes` ذخیره می‌شود):
```
🎉 تبریک! حساب هوش مصنوعی چتیکا شما فعال شد.
برای استفاده از امکانات رایگان چتیکا:
1️⃣ به وب‌سایت chatika.ir مراجعه کنید
2️⃣ شماره موبایل خود را وارد کنید
3️⃣ از دستیار هوشمند چتیکا لذت ببرید!
🔗 لینک ورود: https://chatika.ir
```
**جلوگیری از تکرار**: با چک کردن `Notes != null` از کال مجدد API جلوگیری می‌شود
---
## 📁 فایل‌های تغییر یافته
### CMS Microservice
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `Domain/Enums/ClubFeatureType.cs` | **NEW** | Enum برای فیچرهای باشگاه |
| `Application/Common/Interfaces/IChatikaApiService.cs` | **NEW** | Interface سرویس چتیکا |
| `Infrastructure/Services/ChatikaApiService.cs` | **NEW** | پیاده‌سازی API چتیکا |
| `Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs` | **NEW** | Hangfire Job |
| `Infrastructure/ConfigureServices.cs` | Modified | رجیستر سرویس‌ها |
| `WebApi/Program.cs` | Modified | ثبت Recurring Job |
| `WebApi/appsettings.json` | Modified | تنظیمات چتیکا |
| `OtpTokenCQ/.../VerifyOtpTokenCommandHandler.cs` | Modified | اضافه شدن LegPosition |
| `ClubMembershipCQ/.../AcceptClubMembershipContractCommandHandler.cs` | **REWRITTEN** | منطق کامل فعال‌سازی |
| `ClubMembershipCQ/.../ActivateClubMembershipCommandHandler.cs` | Modified | استفاده از Enum |
### SQL Scripts
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `dbbkup/MigrateUsersToClubMembership.sql` | Modified | اضافه شدن PerformedBy, GiftValue |
| `dbbkup/MigrateSpecificUsersToClubMembership.sql` | Modified | تغییر WeekNumber به WeekDefinitionId |
---
## 🔧 تنظیمات جدید
### appsettings.json
```json
{
"Chatika": {
"BaseUrl": "https://api.chatika.ir",
"ApiKey": "YOUR_API_KEY_HERE"
}
}
```
---
## ✅ Build Status
```bash
cd /home/masoud/Apps/project/FourSat/CMS/src
dotnet build CMSMicroservice.WebApi/CMSMicroservice.WebApi.csproj --no-restore
# Result: Build succeeded. 0 Error(s)
```
---
## 📊 آمار Session
| متریک | مقدار |
|-------|-------|
| فایل‌های جدید | 4 |
| فایل‌های تغییر یافته | 8 |
| خطاهای رفع شده | 3 |
| قابلیت‌های جدید | 4 |
| خطوط کد اضافه شده | ~400 |
+421
View File
@@ -0,0 +1,421 @@
# 📝 Changelog - ۵ دی ۱۴۰۴ (25 December 2025)
> **Session**: فلگ فعال‌سازی چتیکا، اصلاح DayaLoan، بازنویسی صفحه درخت شبکه با org-chart، Stored Procedure برای GetNetworkTree
---
## 🎯 خلاصه Session
این session شامل موارد زیر بود:
1. **Chatika Enabled Flag** - اضافه کردن فلگ فعال/غیرفعال برای Worker چتیکا
2. **DayaLoanCheckWorker Fix** - جلوگیری از استعلام مجدد مشتریان با قرارداد
3. **BackOffice Network Tree Rewrite** - بازنویسی کامل با d3-org-chart
4. **Tooltip on Hover** - نمایش اطلاعات کاربر روی hover
5. **Week Filter Visual** - تمایز بصری کاربران فعال شده در هفته فیلتر شده
6. **Stored Procedure** - SP برای GetNetworkTree جهت بهبود performance
---
## ✨ New Features
### 1. Chatika Enabled Flag ✅
**نیاز**: قابلیت غیرفعال کردن موقت Worker چتیکا از طریق Config
**راه‌حل**: اضافه کردن فلگ `Enabled` در تنظیمات
**تغییرات در `appsettings.json`**:
```json
"Chatika": {
"Enabled": false, // ← فلگ جدید
"BaseUrl": "https://api.chatika.ir",
"ApiKey": "..."
}
```
**تغییرات در `ChatikaAccountActivationJob.cs`**:
```csharp
public async Task ExecuteAsync()
{
// Check if Chatika integration is enabled
var isEnabled = _configuration.GetValue<bool>("Chatika:Enabled", false);
if (!isEnabled)
{
_logger.LogDebug("Chatika integration is disabled. Skipping job execution.");
return;
}
// ... rest of the job
}
```
**فایل‌های تغییر یافته**:
- `CMSMicroservice.WebApi/appsettings.json`
- `CMSMicroservice.Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs`
---
### 2. DayaLoanCheckWorker - Exclude Existing Contracts ✅
**مشکل**: Worker استعلام دایا برای مشتریانی که قبلاً قرارداد گرفته‌اند مجدداً کال می‌شد
**راه‌حل**: فیلتر کردن کاربرانی که `ContractNumber` دارند
**کد اضافه شده**:
```csharp
// Get national codes of users who already have contracts
var existingContractNationalCodes = await _dbContext.DayaLoanContracts
.Where(c => !c.IsDeleted && !string.IsNullOrEmpty(c.ContractNumber))
.Select(c => c.NationalCode)
.Distinct()
.ToListAsync(stoppingToken);
// Filter out users who already have contracts
var usersToProcess = eligibleUsers
.Where(u => !existingContractNationalCodes.Contains(u.NationalCode))
.ToList();
_logger.LogInformation(
"Filtered users: {TotalEligible} eligible, {WithContract} already have contracts, {ToProcess} to process",
eligibleUsers.Count,
eligibleUsers.Count - usersToProcess.Count,
usersToProcess.Count);
```
**فایل**: `CMSMicroservice.WebApi/Workers/DayaLoanCheckWorker.cs`
---
### 3. 🌳 BackOffice Network Tree - Complete Rewrite with d3-org-chart ✅
**نیاز**: استفاده از پلاگین org-chart به جای D3 دستی برای نمایش درخت شبکه در پنل ادمین
**معماری جدید**:
```
┌─────────────────────────────────────────────────────────────┐
│ NetworkTreeViewer.razor │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MudToolBar: │ │
│ │ [Search] [Week Filter] [Expand] [Collapse] [Export] │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Navigation History (Breadcrumb) │ │
│ │ User 123 → User 456 → User 789 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ d3-org-chart Container (#admin-org-chart) │ │
│ │ │ │
│ │ ┌──────┐ │ │
│ │ │ Root │ │ │
│ │ └──┬───┘ │ │
│ │ ┌───┴───┐ │ │
│ │ ┌──┴──┐ ┌──┴──┐ │ │
│ │ │Left │ │Right│ │ │
│ │ └─────┘ └─────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MudDataGrid (Table View) │ │
│ │ Mobile | Name | Level | Position | Status | Actions │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
**فایل‌های جدید**:
| فایل | توضیح |
|------|-------|
| `wwwroot/js/admin-org-chart.js` | Wrapper برای d3-org-chart با امکانات ادمین |
| `wwwroot/css/admin-org-chart.css` | استایل کارت‌های نود با رنگ‌بندی چپ/راست |
| `wwwroot/js/d3-org-chart3.js` | کتابخانه d3-org-chart (کپی از FrontOffice) |
| `wwwroot/js/d3-flextree.min.js` | Dependency برای org-chart |
**فایل‌های تغییر یافته**:
- `NetworkTreeViewer.razor` - بازنویسی کامل
- `wwwroot/index.html` - اضافه شدن رفرنس‌های JS/CSS
---
### 4. 💬 Tooltip on Hover ✅
**نیاز**: نمایش اطلاعات کامل کاربر هنگام hover روی نود
**پیاده‌سازی در `admin-org-chart.js`**:
```javascript
// Tooltip container
const tooltip = d3.select('body').append('div')
.attr('class', 'admin-org-tooltip')
.style('opacity', 0);
// Node hover events
.on('mouseover', function(event, d) {
tooltip.transition().duration(200).style('opacity', .95);
tooltip.html(`
<div class="tooltip-header">${d.data.firstName} ${d.data.lastName}</div>
<div class="tooltip-row"><span>📱</span> ${d.data.mobile}</div>
<div class="tooltip-row"><span>📊</span> سطح: ${d.data.networkLevel}</div>
<div class="tooltip-row"><span>📍</span> ${d.data.legPosition === 1 ? 'چپ' : 'راست'}</div>
${d.data.isClubActive ?
`<div class="tooltip-row active"><span>✅</span> باشگاه فعال</div>` :
`<div class="tooltip-row inactive"><span>❌</span> باشگاه غیرفعال</div>`
}
${d.data.activationWeekDisplayName ?
`<div class="tooltip-row"><span>📅</span> ${d.data.activationWeekDisplayName}</div>` : ''
}
`)
.style('left', (event.pageX + 15) + 'px')
.style('top', (event.pageY - 10) + 'px');
})
```
**استایل Tooltip**:
```css
.admin-org-tooltip {
position: absolute;
background: rgba(33, 33, 33, 0.95);
color: white;
padding: 12px 16px;
border-radius: 8px;
font-size: 13px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
pointer-events: none;
z-index: 10000;
direction: rtl;
}
```
---
### 5. 🎨 Week Filter Visual Distinction ✅
**نیاز**: وقتی هفته فیلتر می‌شه، کاربران فعال شده در اون هفته متمایز باشن
**پیاده‌سازی**:
```javascript
// Apply week filter styling
applyWeekFilter: function(weekId) {
if (!this.chart || !this.chartData) return;
d3.selectAll('.admin-node-card').each(function() {
const nodeData = d3.select(this).datum();
if (nodeData && nodeData.data) {
if (weekId && nodeData.data.activationWeekDefinitionId !== weekId) {
d3.select(this).classed('disabled', true);
} else {
d3.select(this).classed('disabled', false);
}
}
});
}
```
**استایل**:
```css
/* کاربرانی که در هفته فیلتر شده فعال نشدن */
.admin-node-card.disabled {
opacity: 0.35;
filter: grayscale(70%);
}
/* کاربران فعال شده در هفته هدف */
.admin-node-card:not(.disabled) {
animation: target-glow 2s ease-in-out infinite;
}
@keyframes target-glow {
0%, 100% { box-shadow: 0 0 5px rgba(76, 175, 80, 0.3); }
50% { box-shadow: 0 0 20px rgba(76, 175, 80, 0.6); }
}
```
---
### 6. ⚡ Stored Procedure for GetNetworkTree ✅
**نیاز**: بهبود سرعت لود درخت شبکه + حذف محدودیت عمق
**Stored Procedure** (`dbbkup/SP_GetNetworkTree.sql`):
```sql
CREATE PROCEDURE [CMS].[GetNetworkTree]
@RootUserId BIGINT,
@MaxDepth INT = 100,
@IsClubActive BIT = NULL,
@ActivationWeekDefinitionId BIGINT = NULL
AS
BEGIN
SET NOCOUNT ON;
-- CTE برای پیمایش درخت باینری به صورت recursive
;WITH NetworkTreeCTE AS (
-- Base case: ریشه درخت
SELECT
u.Id AS UserId,
u.Mobile,
u.FirstName,
u.LastName,
u.LegPosition,
u.NetworkParentId AS ParentId,
0 AS NetworkLevel,
u.Created AS UserCreated
FROM [CMS].[Users] u
WHERE u.Id = @RootUserId AND u.IsDeleted = 0
UNION ALL
-- Recursive case: فرزندان
SELECT
u.Id, u.Mobile, u.FirstName, u.LastName, u.LegPosition,
u.NetworkParentId, parent.NetworkLevel + 1, u.Created
FROM [CMS].[Users] u
INNER JOIN NetworkTreeCTE parent ON u.NetworkParentId = parent.UserId
WHERE u.IsDeleted = 0 AND parent.NetworkLevel < @MaxDepth
)
-- Join با ClubMemberships و WeekDefinitions
SELECT
t.UserId, t.Mobile, t.FirstName, t.LastName, t.LegPosition,
t.ParentId, t.NetworkLevel,
cm.ActivatedAt AS ClubActivatedAt,
ISNULL(cm.IsActive, 0) AS IsClubActive,
wd.Id AS ActivationWeekDefinitionId,
wd.DisplayName AS ActivationWeekDisplayName,
CASE WHEN @ActivationWeekDefinitionId IS NOT NULL
AND wd.Id = @ActivationWeekDefinitionId THEN 1 ELSE 0
END AS IsActivatedInTargetWeek,
t.UserCreated
FROM NetworkTreeCTE t
LEFT JOIN [CMS].[ClubMemberships] cm ON cm.UserId = t.UserId AND cm.IsDeleted = 0
LEFT JOIN [CMS].[WeekDefinitions] wd ON cm.ActivatedAt >= wd.StartDate
AND cm.ActivatedAt < wd.EndDate
WHERE (@IsClubActive IS NULL OR ISNULL(cm.IsActive, 0) = @IsClubActive
OR t.NetworkLevel = 0)
ORDER BY t.NetworkLevel, t.ParentId, t.LegPosition
OPTION (MAXRECURSION 0); -- بدون محدودیت recursion
END
```
**تغییرات در Application Layer**:
**فایل جدید**: `NetworkTreeNodeDto.cs`
```csharp
public class NetworkTreeNodeDto
{
public long UserId { get; set; }
public string? Mobile { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public int? LegPosition { get; set; }
public long? ParentId { get; set; }
public int NetworkLevel { get; set; }
public DateTime? ClubActivatedAt { get; set; }
public bool IsClubActive { get; set; }
public long? ActivationWeekDefinitionId { get; set; }
public string? ActivationWeekDisplayName { get; set; }
public bool IsActivatedInTargetWeek { get; set; }
public DateTimeOffset UserCreated { get; set; }
}
```
**بازنویسی `GetNetworkTreeQueryHandler.cs`**:
- استفاده از ADO.NET برای اجرای SP
- تبدیل نتیجه flat به ساختار درختی
- پشتیبانی از همه پارامترهای فیلتر
**پکیج جدید**:
```xml
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.11" />
```
---
## 📁 فایل‌های تغییر یافته
### CMS Microservice
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `WebApi/appsettings.json` | Modified | اضافه شدن `Chatika.Enabled` |
| `Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs` | Modified | چک فلگ Enabled |
| `WebApi/Workers/DayaLoanCheckWorker.cs` | Modified | فیلتر کاربران با قرارداد |
| `Application/CMSMicroservice.Application.csproj` | Modified | پکیج Relational |
| `Application/Common/Interfaces/IApplicationDbContext.cs` | Modified | اضافه شدن DatabaseFacade |
| `Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs` | **REWRITTEN** | استفاده از SP |
| `Application/NetworkMembershipCQ/Queries/GetNetworkTree/NetworkTreeNodeDto.cs` | **NEW** | DTO برای نتیجه SP |
### BackOffice
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `Pages/Network/NetworkTreeViewer.razor` | **REWRITTEN** | استفاده از org-chart |
| `wwwroot/index.html` | Modified | رفرنس‌های JS/CSS |
| `wwwroot/js/admin-org-chart.js` | **NEW** | Wrapper برای org-chart |
| `wwwroot/css/admin-org-chart.css` | **NEW** | استایل نودها |
| `wwwroot/js/d3-org-chart3.js` | **NEW** | کتابخانه org-chart |
| `wwwroot/js/d3-flextree.min.js` | **NEW** | Dependency |
### SQL Scripts
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `dbbkup/SP_GetNetworkTree.sql` | **NEW** | Stored Procedure |
---
## 🔧 تنظیمات جدید
### appsettings.json - Chatika
```json
{
"Chatika": {
"Enabled": false, // ← جدید: فعال/غیرفعال کردن Worker
"BaseUrl": "https://api.chatika.ir",
"ApiKey": "YOUR_API_KEY_HERE"
}
}
```
---
## 🗄️ Database Changes
### اجرای Stored Procedure
```bash
# اجرای اسکریپت در SQL Server
sqlcmd -S <server> -d <database> -i dbbkup/SP_GetNetworkTree.sql
```
---
## ✅ Build Status
```bash
cd /home/masoud/Apps/project/FourSat/CMS/src
dotnet build
# Result: Build succeeded. 0 Error(s)
cd /home/masoud/Apps/project/FourSat/BackOffice/src
dotnet build BackOffice/BackOffice.csproj
# Result: Build succeeded
```
---
## 📊 آمار Session
| متریک | مقدار |
|-------|-------|
| فایل‌های جدید | 6 |
| فایل‌های تغییر یافته | 8 |
| قابلیت‌های جدید | 6 |
| خطوط کد اضافه شده | ~800 |
| Stored Procedure | 1 |
---
## 🔗 Related Changelogs
- [CHANGELOG-2025-12-23.md](CHANGELOG-2025-12-23.md) - پیاده‌سازی Worker چتیکا
- [CHANGELOG-2025-12-20.md](CHANGELOG-2025-12-20.md) - بهبودات قبلی
+383
View File
@@ -0,0 +1,383 @@
# 📝 Changelog - ۶ دی ۱۴۰۴ (26 December 2025)
> **Session**: سیستم مدیریت نسخه اپلیکیشن (App Version Management) + نمایش کد معرف در درخت شبکه
---
## 🎯 خلاصه Session
این session شامل موارد زیر بود:
1. **App Version Management** - سیستم کامل مدیریت نسخه اپلیکیشن‌های موبایل
2. **ReferralCode در NetworkTree** - نمایش کد معرف در درخت شبکه FrontOffice
3. **BackOffice UI** - صفحه مدیریت نسخه‌ها در پنل ادمین
---
## ✨ New Features
### 1. 📱 سیستم مدیریت نسخه اپلیکیشن (App Version Management) ✅
**نیاز**: مدیریت نسخه‌های اپلیکیشن‌های موبایل و کنترل به‌روزرسانی اجباری
**معماری**:
```
┌─────────────────────────────────────────────────────────────┐
│ Flow Diagram │
├─────────────────────────────────────────────────────────────┤
│ │
│ BackOffice UI ──(gRPC)──► BackOffice.BFF ──(gRPC)──► CMS │
│ (Blazor) (API Gateway) (Database) │
│ │
│ /settings/app-versions AppVersionService AppVersions │
│ Table │
└─────────────────────────────────────────────────────────────┘
```
---
#### 1.1 CMS Layer (Database + gRPC) ✅
**Entity**: `AppVersion.cs` (موجود)
```csharp
public class AppVersion : BaseAuditableEntity
{
public string AppName { get; set; } // FoursatMarketApp, FoursatClubApp
public string CurrentVersion { get; set; } // 1.2.0
public string MinRequiredVersion { get; set; } // 1.0.0
public bool RequiresFullCacheClear { get; set; }
public string UpdateMessage { get; set; }
public string ReleaseNotes { get; set; }
public bool IsActive { get; set; }
}
```
**Protobuf**: `appversion.proto`
```protobuf
service AppVersionContract {
rpc GetAllAppVersions(GetAllAppVersionsRequest) returns (GetAllAppVersionsResponse);
rpc GetAppVersion(GetAppVersionRequest) returns (GetAppVersionResponse);
rpc UpdateAppVersion(UpdateAppVersionRequest) returns (google.protobuf.Empty);
}
```
**فایل‌ها**:
- `CMSMicroservice.Protobuf/Protos/appversion.proto`
- `CMSMicroservice.WebApi/Services/AppVersionService.cs`
- پکیج NuGet: `CMSMicroservice.Protobuf` v0.0.159
---
#### 1.2 BackOffice.BFF Layer ✅
**Protobuf اختصاصی**: `BackOffice.BFF.Configuration.Protobuf/Protos/appversion.proto`
```protobuf
option csharp_namespace = "BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion";
service AppVersionContract {
rpc GetAllAppVersions(GetAllAppVersionsRequest) returns (GetAllAppVersionsResponse) {
option (google.api.http) = { get: "/AppVersion/GetAll" };
};
rpc GetAppVersion(GetAppVersionRequest) returns (GetAppVersionResponse) {
option (google.api.http) = { get: "/AppVersion/Get" };
};
rpc UpdateAppVersion(UpdateAppVersionRequest) returns (google.protobuf.Empty) {
option (google.api.http) = { post: "/AppVersion/Update" body: "*" };
};
}
```
**CQRS Components**:
| Component | Path | Description |
|-----------|------|-------------|
| `GetAllAppVersionsQuery` | ConfigurationCQ/Queries/ | Query با `IncludeInactive` |
| `GetAllAppVersionsResponseDto` | ConfigurationCQ/Queries/ | Response DTO با لیست آیتم‌ها |
| `GetAllAppVersionsQueryHandler` | ConfigurationCQ/Queries/ | Handler که CMS رو کال می‌کنه |
| `UpdateAppVersionCommand` | ConfigurationCQ/Commands/ | Command برای آپدیت |
| `UpdateAppVersionCommandHandler` | ConfigurationCQ/Commands/ | Handler که CMS رو کال می‌کنه |
**gRPC Service**: `AppVersionService.cs`
```csharp
public class AppVersionService : AppVersionContract.AppVersionContractBase
{
public override async Task<GetAllAppVersionsResponse> GetAllAppVersions(...)
public override async Task<GetAppVersionResponse> GetAppVersion(...)
public override async Task<Empty> UpdateAppVersion(...)
}
```
**Mapster Profile**: `AppVersionProfile.cs`
**فایل‌های جدید**:
- `src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/*`
- `src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/*`
- `src/BackOffice.BFF.WebApi/Services/AppVersionService.cs`
- `src/BackOffice.BFF.WebApi/Common/Mappings/AppVersionProfile.cs`
- `src/Protobufs/BackOffice.BFF.Configuration.Protobuf/Protos/appversion.proto`
**پکیج NuGet**: `Foursat.BackOffice.BFF.Configuration.Protobuf` v1.0.20
---
#### 1.3 BackOffice UI (Blazor) ✅
**مسیر صفحه**: `/settings/app-versions`
**Service Layer**:
`IAppVersionService.cs`:
```csharp
public interface IAppVersionService
{
Task<List<AppVersionDto>> GetAllAsync(bool includeInactive = false);
Task<AppVersionDto?> GetByNameAsync(string appName);
Task UpdateAsync(UpdateAppVersionDto dto);
}
```
`AppVersionDto.cs`:
```csharp
public class AppVersionDto
{
public long Id { get; set; }
public string AppName { get; set; }
public string AppNameDisplay { get; } // ترجمه فارسی
public string CurrentVersion { get; set; }
public string MinRequiredVersion { get; set; }
public bool RequiresFullCacheClear { get; set; }
public string UpdateMessage { get; set; }
public string ReleaseNotes { get; set; }
public bool IsActive { get; set; }
public DateTime? Created { get; set; }
public DateTime? LastModified { get; set; }
}
```
**UI Components**:
| Component | Description |
|-----------|-------------|
| `AppVersions.razor` | صفحه اصلی با جدول و کارت‌ها |
| `AppVersionEditDialog.razor` | Dialog ویرایش نسخه |
**ویژگی‌های صفحه**:
- 📋 جدول MudDataGrid با اطلاعات همه نسخه‌ها
- 🔍 فیلتر نمایش نسخه‌های غیرفعال
- 🃏 کارت خلاصه برای هر اپلیکیشن فعال
- ✏️ ویرایش با Dialog شامل:
- نسخه فعلی
- حداقل نسخه مورد نیاز
- نیاز به پاکسازی کش
- پیام به‌روزرسانی
- یادداشت‌های انتشار
- دلیل تغییر (برای لاگ)
**فایل‌های جدید**:
- `Services/AppVersion/IAppVersionService.cs`
- `Services/AppVersion/AppVersionService.cs`
- `Pages/Settings/AppVersions.razor`
- `Pages/Settings/Components/AppVersionEditDialog.razor`
**تغییرات در فایل‌های موجود**:
- `Common/Configure/ConfigureService.cs` - ثبت service و gRPC client
- `BackOffice.csproj` - آپدیت پکیج به v1.0.20
---
### 2. 🔗 نمایش کد معرف در درخت شبکه FrontOffice ✅
**نیاز**: نمایش کد معرف افرادی که در باشگاه فعالند کنار هر نود
**تغییرات**:
#### 2.1 CMS - Stored Procedure
`SP_GetNetworkTree.sql` - فیلد `ReferralCode` اضافه شده:
```sql
SELECT
...
u.ReferralCode,
...
FROM NetworkTree_CTE t
JOIN AspNetUsers u ON t.UserId = u.Id
```
#### 2.2 FrontOffice DTOs
`NetworkMembershipDtos.cs`:
```csharp
public class NetworkNodeDto
{
// ... existing fields
public string? ReferralCode { get; set; } // جدید
}
public class FlatNetworkNodeDto
{
// ... existing fields
public string? ReferralCode { get; set; } // جدید
}
```
#### 2.3 JavaScript - org-chart.js
```javascript
// فقط برای کاربران فعال باشگاه نمایش بده
${d.isClubActive && d.referralCode ? `
<div class="node-referral">
<span class="referral-label">کد معرف:</span>
<span class="referral-code">${d.referralCode}</span>
<button class="copy-btn" onclick="copyReferralCode('${d.referralCode}', event)">
<i class="fas fa-copy"></i>
</button>
</div>
` : ''}
```
#### 2.4 CSS - org-chart.css
```css
.node-referral {
display: flex;
align-items: center;
gap: 4px;
margin-top: 4px;
padding: 3px 6px;
background: rgba(76, 175, 80, 0.1);
border-radius: 4px;
font-size: 11px;
}
.referral-code {
font-weight: bold;
color: #4CAF50;
font-family: monospace;
}
.copy-btn {
background: transparent;
border: none;
cursor: pointer;
padding: 2px;
color: #666;
}
.copy-toast {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 8px 16px;
border-radius: 4px;
z-index: 10000;
}
```
**فایل‌های تغییر یافته**:
- `FrontOffice/Utilities/NetworkMembershipDtos.cs`
- `FrontOffice/Utilities/NetworkMembershipService.cs`
- `FrontOffice/wwwroot/js/org-chart.js`
- `FrontOffice/wwwroot/css/org-chart.css`
---
## 📦 Package Updates
| Package | From | To | Project |
|---------|------|-----|---------|
| `CMSMicroservice.Protobuf` | 0.0.156 | 0.0.159 | BackOffice.BFF.Domain |
| `Foursat.BackOffice.BFF.Configuration.Protobuf` | 1.0.7 | 1.0.20 | BackOffice |
---
## 🗄️ Git Commits
### BackOffice.BFF
```
commit 6bc45e4
feat(bff): Add App Version management endpoints
- Add appversion.proto with GetAll, Get, Update RPCs
- Add GetAllAppVersionsQuery and handler
- Add UpdateAppVersionCommand and handler
- Add AppVersionService gRPC service
- Add AppVersionProfile for Mapster mappings
- Update IApplicationContractContext with AppVersions client
- Bump Configuration.Protobuf to 1.0.20
```
### BackOffice
```
commit 6b140c3
feat(backoffice): Add App Version management UI
- Add IAppVersionService interface and implementation
- Add AppVersions.razor page for managing app versions
- Add AppVersionEditDialog component for editing versions
- Register AppVersion gRPC client and service in DI
- Update Foursat.BackOffice.BFF.Configuration.Protobuf to 1.0.20
```
### CMS
```
commit bca3b7f
feat: Add ReferralCode to NetworkTree and NetworkMembershipProfile
- Add ReferralCode to SP_GetNetworkTree stored procedure
- Map ReferralCode in NetworkMembershipProfile
- Update networkmembership.proto with referral_code field
```
---
## ✅ Build Status
```bash
# CMS
cd /home/masoud/Apps/project/FourSat/CMS/src
dotnet build CMSMicroservice.WebApi/CMSMicroservice.WebApi.csproj
# Result: Build succeeded. 0 Error(s)
# BackOffice.BFF
cd /home/masoud/Apps/project/FourSat/BackOffice.BFF/src
dotnet build BackOffice.BFF.WebApi/BackOffice.BFF.WebApi.csproj
# Result: Build succeeded. 200 Warning(s), 0 Error(s)
# BackOffice
cd /home/masoud/Apps/project/FourSat/BackOffice/src
dotnet build BackOffice/BackOffice.csproj
# Result: Build succeeded. 246 Warning(s), 0 Error(s)
```
---
## 📊 آمار Session
| متریک | مقدار |
|-------|-------|
| فایل‌های جدید | 12 |
| فایل‌های تغییر یافته | 10 |
| قابلیت‌های جدید | 2 |
| خطوط کد اضافه شده | ~1000 |
| پکیج‌های NuGet آپدیت شده | 2 |
| Commits | 3 |
---
## 📍 مسیرهای دسترسی
| Feature | URL | Project |
|---------|-----|---------|
| مدیریت نسخه اپ‌ها | `/settings/app-versions` | BackOffice |
| درخت شبکه با کد معرف | `/profile/tree` | FrontOffice |
---
## ⚠️ یادآوری‌ها
1. **Stored Procedure**: اسکریپت `SP_GetNetworkTree.sql` باید روی دیتابیس production اجرا شود
2. **منوی BackOffice**: صفحه `/settings/app-versions` در منوی سایدبار اضافه نشده - در صورت نیاز `NavMenu.razor` آپدیت شود
---
## 🔗 Related Changelogs
- [CHANGELOG-2025-12-25.md](CHANGELOG-2025-12-25.md) - درخت شبکه BackOffice با d3-org-chart
- [CHANGELOG-2025-12-23.md](CHANGELOG-2025-12-23.md) - پیاده‌سازی Worker چتیکا
+632
View File
@@ -0,0 +1,632 @@
# 📝 Changelog - ۷ دی ۱۴۰۴ (27 December 2025)
> **Session**: بهینه‌سازی‌های Mapping + SystemConstants + SMS Templates + AppVersion UI + Commission System Fixes
---
## 🎯 خلاصه Session
این session شامل موارد زیر بود:
1. **SystemConstants** - انتقال مقادیر ثابت از hardcode به کلاس مرکزی
2. **SMS Templates** - متمرکز کردن همه قالب‌های پیامک
3. **SMS for Daya Loan** - ارسال پیامک هنگام تأیید وام دایا
4. **AppVersion UI** - تکمیل صفحه مدیریت نسخه در BackOffice
5. **Mapping Fixes** - رفع مشکلات Mapster
6. **Commission Status Refactoring** - انتقال تبدیل Status از BFF به FrontOffice
7. **ProcessWithdrawal Fix** - رفع خطای "PayoutId invalid" در BackOffice
8. **WeekDisplayName Fix** - نمایش صحیح نام هفته به جای فرمت 1404-W40
9. **Withdrawals Page Fix** - رفع مشکل لود نشدن صفحه تأیید برداشت‌ها
10. **Network Balances Enhancement** - افزودن نام کاربر و جزئیات Carryover به صفحه balance‌ها
11. **WeekDefinitionId Mapping Fix** - رفع مشکل ارسال WeekDefinitionId=0 در FrontOffice.BFF
---
## ✨ تغییرات
### 1. 💰 SystemConstants - مقادیر ثابت ✅
**فایل**: `CMSMicroservice.Domain/Common/SystemConstants.cs`
```csharp
public static class SystemConstants
{
// Club Configuration
public const decimal ClubJoiningPercentage = 0.35m; // 35% کمیسیون پیوستن به باشگاه
public const decimal ClubActivationThreshold = 0.5m; // 50% آستانه فعال‌سازی
// Commission Configuration
public const int MaxCalculationAttempts = 3; // حداکثر تلاش محاسبه
public const int DefaultCommissionPoolDays = 7; // روزهای استخر کمیسیون
// Package Amounts
public const long GoldenPackageAmount = 56_000_000; // 56 میلیون - پکیج طلایی
public const long DayaLoanAmount = 56_000_000; // 56 میلیون - وام دایا
}
```
**Handlers آپدیت شده**:
| Handler | تغییر |
|---------|-------|
| `ProcessDayaLoanApprovalCommandHandler` | استفاده از `SystemConstants.DayaLoanAmount` |
| `ValidateGoldenPackagePurchaseQueryHandler` | استفاده از `SystemConstants.GoldenPackageAmount` |
| سایر handlers با 56_000_000 | همه به ثابت تبدیل شدند |
---
### 2. 📱 SmsTemplates - قالب‌های متمرکز پیامک ✅
**فایل جدید**: `CMSMicroservice.Domain/Common/SmsTemplates.cs`
```csharp
public static class SmsTemplates
{
private static string GetUserName(string? firstName)
=> string.IsNullOrWhiteSpace(firstName) ? "کاربر" : firstName;
public static string DayaLoanReceived(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، مبلغ {amount:N0} ریال وام دایا به کیف پول شما واریز شد. کارابازار";
public static string ClubActivated(string? firstName)
=> $"{GetUserName(firstName)} عزیز، حساب باشگاه شما فعال شد. کارابازار";
public static string PackagePurchased(string? firstName, string packageName)
=> $"{GetUserName(firstName)} عزیز، پکیج {packageName} با موفقیت خریداری شد. کارابازار";
public static string CommissionDeposited(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، مبلغ {amount:N0} ریال کمیسیون به کیف پول شما واریز شد. کارابازار";
public static string WithdrawalSuccess(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، درخواست برداشت {amount:N0} ریال با موفقیت انجام شد. کارابازار";
public static string NetworkJoined(string? firstName, string referrerName)
=> $"{GetUserName(firstName)} عزیز، به شبکه {referrerName} پیوستید. کارابازار";
public static string NewDownline(string? firstName, string newMemberName)
=> $"{GetUserName(firstName)} عزیز، {newMemberName} به زیرمجموعه شما اضافه شد. کارابازار";
public static string OtpCode(string code)
=> $"کد تأیید شما: {code}\nکارابازار";
public static string Welcome(string? firstName)
=> $"{GetUserName(firstName)} عزیز، به کارابازار خوش آمدید!";
}
```
---
### 3. 📲 ارسال SMS هنگام تأیید وام دایا ✅
**فایل**: `CMSMicroservice.Application/FinancialCQ/Commands/ProcessDayaLoanApproval/ProcessDayaLoanApprovalCommandHandler.cs`
**تغییرات**:
```csharp
public class ProcessDayaLoanApprovalCommandHandler : IRequestHandler<ProcessDayaLoanApprovalCommand, Unit>
{
private readonly IKavenegarService _smsService; // جدید
private readonly ILogger<ProcessDayaLoanApprovalCommandHandler> _logger; // جدید
// بعد از واریز موفق به کیف پول
private async Task SendDayaLoanSmsAsync(User user)
{
try
{
var message = SmsTemplates.DayaLoanReceived(
user.FirstName,
SystemConstants.DayaLoanAmount);
await _smsService.SendAsync(user.PhoneNumber, message);
_logger.LogInformation("Daya loan SMS sent to user {UserId}", user.Id);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to send Daya loan SMS to user {UserId}", user.Id);
// خطای SMS مانع عملیات اصلی نمی‌شود
}
}
}
```
---
### 4. 🖥️ BackOffice - صفحه مدیریت نسخه اپلیکیشن ✅
#### 4.1 اضافه شدن به منو
**فایل**: `BackOffice/Shared/NavMenu.razor`
```razor
@if (CanViewSettings)
{
<MudNavLink Match="NavLinkMatch.Prefix"
Href="/settings/app-versions"
Icon="@Icons.Material.Filled.PhoneAndroid">
نسخه اپلیکیشن‌ها
</MudNavLink>
}
```
**Permission**: `settings.view`
#### 4.2 دکمه افزودن نسخه جدید
**فایل**: `BackOffice/Pages/Settings/AppVersions.razor`
```razor
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Add"
OnClick="@OpenCreateDialog">
افزودن نسخه جدید
</MudButton>
```
#### 4.3 Dialog با حالت جدید/ویرایش
**فایل**: `BackOffice/Pages/Settings/Components/AppVersionEditDialog.razor`
```razor
[Parameter]
public bool IsNew { get; set; } = false;
@if (IsNew)
{
<MudSelect @bind-Value="Model.AppName"
Label="نام اپلیکیشن"
Required="true">
<MudSelectItem Value="@("KaraBazarApp")">کارابازار</MudSelectItem>
<MudSelectItem Value="@("KaraBazarAdminApp")">ادمین کارابازار</MudSelectItem>
</MudSelect>
}
else
{
<MudTextField @bind-Value="Model.AppName"
ReadOnly="true" Disabled="true" />
}
```
#### 4.4 آیکون و رنگ اپلیکیشن‌ها
```csharp
private string GetAppIcon(string appName) => appName switch
{
"KaraBazarApp" => Icons.Material.Filled.ShoppingCart,
"KaraBazarAdminApp" => Icons.Material.Filled.AdminPanelSettings,
_ => Icons.Material.Filled.PhoneAndroid
};
private Color GetAppColor(string appName) => appName switch
{
"KaraBazarApp" => Color.Primary,
"KaraBazarAdminApp" => Color.Secondary,
_ => Color.Default
};
```
---
### 5. 🔧 Mapping Fixes ✅
#### 5.1 CMS - AppVersionProfile
**فایل جدید**: `CMSMicroservice.WebApi/Common/Mappings/AppVersionProfile.cs`
```csharp
public class AppVersionProfile : IRegister
{
public void Register(TypeAdapterConfig config)
{
// Map List<AppVersionItemDto> to GetAllAppVersionsResponse
config.NewConfig<List<AppVersionItemDto>, GetAllAppVersionsResponse>()
.MapWith(src => CreateResponse(src));
// Map AppVersionItemDto to AppVersionItem (proto message)
config.NewConfig<AppVersionItemDto, AppVersionItem>()
.Map(dest => dest.Id, src => src.Id)
.Map(dest => dest.AppName, src => src.AppName)
// ... other mappings
}
private static GetAllAppVersionsResponse CreateResponse(List<AppVersionItemDto> items)
{
var response = new GetAllAppVersionsResponse();
foreach (var item in items)
{
response.Items.Add(item.Adapt<AppVersionItem>());
}
return response;
}
}
```
#### 5.2 BackOffice.BFF - CommissionProfile
**فایل**: `BackOffice.BFF.Application/Common/Mappings/CommissionProfile.cs`
```csharp
// CMS GetAllWeeklyPoolsResponse -> GetAllWeeklyPoolsResponseDto
config.NewConfig<GetAllWeeklyPoolsResponse, GetAllWeeklyPoolsResponseDto>()
.MapWith(src => new GetAllWeeklyPoolsResponseDto
{
MetaData = new MetaDataDto
{
TotalCount = (int)src.MetaData.TotalCount,
PageSize = (int)src.MetaData.PageSize,
CurrentPage = (int)src.MetaData.CurrentPage,
TotalPages = (int)src.MetaData.TotalPage
},
Models = src.Models.Select(m => new WeeklyCommissionPoolDto
{
Id = m.Id,
WeekDefinitionId = m.WeekDefinitionId,
// ... other mappings
}).ToList()
});
```
#### 5.3 BackOffice.BFF - GeneralMapping (Unit to Empty)
**فایل**: `BackOffice.BFF.WebApi/Common/Mappings/GeneralMapping.cs`
```csharp
// MediatR Unit to Google.Protobuf.Empty
config.NewConfig<MediatR.Unit, Google.Protobuf.WellKnownTypes.Empty>()
.MapWith(_ => new Google.Protobuf.WellKnownTypes.Empty());
```
---
### 6. 🔄 Commission Status Refactoring ✅
**مشکل**: تبدیل enum عددی `CommissionPayoutStatus` به متن فارسی در BFF gateway انجام می‌شد.
**راه‌حل**: انتقال منطق به FrontOffice client برای معماری بهتر.
#### 6.1 FrontOffice.BFF - Simplify Response
**فایل**: `FrontOffice.BFF.Application/.../GetMyWeeklyBalancesQueryHandler.cs`
**قبل**:
```csharp
Status = MapStatusToString(x.Status)
```
**بعد**:
```csharp
Status = x.Status // Return int directly
```
#### 6.2 FrontOffice - CommissionService
**فایل**: `FrontOffice.Main/Utilities/CommissionService.cs`
```csharp
public static string MapStatus(int status) => status switch
{
0 => "در انتظار", // Pending
1 => "پرداخت شده", // Paid
2 => "درخواست برداشت", // WithdrawRequested
3 => "برداشت شده", // Withdrawn
4 => "خطای پرداخت", // PaymentFailed
5 => "لغو شده", // Cancelled
_ => "نامشخص"
};
public static string GetStatusColor(int status) => status switch
{
0 => "warning", // Pending - زرد
1 => "success", // Paid - سبز
2 => "info", // WithdrawRequested - آبی
3 => "success", // Withdrawn - سبز
4 => "error", // PaymentFailed - قرمز
5 => "default", // Cancelled - خاکستری
_ => "default"
};
```
**Enum مرجع** (`CommissionPayoutStatus`):
```csharp
public enum CommissionPayoutStatus
{
Pending = 0,
Paid = 1,
WithdrawRequested = 2,
Withdrawn = 3,
PaymentFailed = 4,
Cancelled = 5
}
```
---
### 7. 🛠️ ProcessWithdrawal Fix ✅
**مشکل**: خطای "PayoutId invalid" هنگام تأیید/رد برداشت در BackOffice
**علت**: `PayoutId` در mapping از CMS request به BackOffice.BFF command map نمی‌شد.
**فایل**: `BackOffice.BFF.Application/Common/Mappings/CommissionProfile.cs`
**قبل**:
```csharp
config.NewConfig<ProcessWithdrawalRequest, ProcessWithdrawalCommand>();
// PayoutId ignored!
```
**بعد**:
```csharp
config.NewConfig<ProcessWithdrawalRequest, ProcessWithdrawalCommand>()
.Map(dest => dest.PayoutId, src => src.PayoutId)
.Map(dest => dest.Approve, src => src.Approve)
.Map(dest => dest.RejectionReason, src => src.RejectionReason);
```
---
### 8. 📅 WeekDisplayName Fix ✅
**مشکل**: نمایش "1404-W40" به جای "هفته چهلم" در dropdown انتخاب هفته
**علت**: استفاده از `PersianWeekNumber` به جای `DisplayName`
**فایل**: `CMS.Application/.../GetAllWeeklyPoolsQueryHandler.cs`
**قبل**:
```csharp
WeekDisplayName = x.WeekDefinition.PersianWeekNumber // "1404-W40"
```
**بعد**:
```csharp
WeekDisplayName = x.WeekDefinition.DisplayName // "هفته چهلم"
```
---
### 9. 📋 Withdrawals Page Fix ✅
**مشکل**: صفحه تأیید برداشت‌ها در BackOffice لود نمی‌شد
**علت**: mismatch بین نام propertyها در CMS proto و BackOffice.BFF DTO
**فایل**: `BackOffice.BFF.Application/.../GetWithdrawalRequestsResponseDto.cs`
**قبل**:
```csharp
public int TotalPages { get; set; }
public int TotalCount { get; set; }
```
**بعد**:
```csharp
public int TotalPage { get; set; } // Match CMS proto
public int TotalCount { get; set; }
```
**فایل**: `BackOffice.BFF.Application/.../GetWithdrawalRequestsQueryHandler.cs`
**قبل**:
```csharp
return response.Adapt<GetWithdrawalRequestsResponseDto>();
```
**بعد**:
```csharp
return new GetWithdrawalRequestsResponseDto
{
TotalCount = (int)response.MetaData.TotalCount,
TotalPage = (int)response.MetaData.TotalPage,
// ... explicit mapping
};
```
---
### 10. 👤 Network Balances Enhancement ✅
**نیاز**: نمایش نام کامل کاربر و جزئیات breakdown پای چپ/راست در صفحه balance‌های شبکه
#### 10.1 CMS Proto Update
**فایل**: `CMS/Protobufs/Protos/commission.proto`
```protobuf
message UserWeeklyBalanceModel {
// ... existing fields
string user_full_name = 13;
int64 left_leg_new_members = 14;
int64 left_leg_carryover = 15;
int64 left_leg_total = 16;
int64 right_leg_new_members = 17;
int64 right_leg_carryover = 18;
int64 right_leg_total = 19;
}
```
#### 10.2 CMS Handler Update
**فایل**: `CMS.Application/.../GetUserWeeklyBalancesQueryHandler.cs`
```csharp
var query = _dbContext.NetworkWeeklyBalances
.Include(x => x.User) // NEW: Include User
.Include(x => x.WeekDefinition)
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId);
// In projection:
UserFullName = $"{x.User.FirstName} {x.User.LastName}".Trim(),
LeftLegNewMembers = x.LeftLegNewMembers,
LeftLegCarryover = x.LeftLegCarryover,
LeftLegTotal = x.LeftLegTotal,
RightLegNewMembers = x.RightLegNewMembers,
RightLegCarryover = x.RightLegCarryover,
RightLegTotal = x.RightLegTotal,
```
#### 10.3 BackOffice UI Update
**فایل**: `BackOffice/Pages/Network/BalancesReport.razor`
```razor
@* ستون نام کاربر *@
<PropertyColumn Property="x => x.UserFullName" Title="نام کاربر" />
@* ستون پای چپ با Tooltip *@
<TemplateColumn Title="پای چپ">
<CellTemplate>
<MudTooltip Text="@($"جدید: {FormatNumber(context.Item.LeftLegNewMembers)} | انتقالی: {FormatNumber(context.Item.LeftLegCarryover)}")">
<MudText>@FormatNumber(context.Item.LeftLegTotal)</MudText>
</MudTooltip>
</CellTemplate>
</TemplateColumn>
@* ستون پای راست با Tooltip *@
<TemplateColumn Title="پای راست">
<CellTemplate>
<MudTooltip Text="@($"جدید: {FormatNumber(context.Item.RightLegNewMembers)} | انتقالی: {FormatNumber(context.Item.RightLegCarryover)}")">
<MudText>@FormatNumber(context.Item.RightLegTotal)</MudText>
</MudTooltip>
</CellTemplate>
</TemplateColumn>
```
#### 10.4 Proto Package Update
```bash
# Publish new proto package
cd CMS/Protobufs
# Update version in .csproj to 0.0.14
dotnet pack
dotnet nuget push ...
# Update BackOffice
cd BackOffice/src/BackOffice
# Update package reference in .csproj
<PackageReference Include="Foursat.BackOffice.BFF.Commission.Protobuf" Version="0.0.14" />
```
---
### 11. 🔢 WeekDefinitionId Mapping Fix ✅
**مشکل**: `WeekDefinitionId` همیشه `0` به BFF ارسال می‌شد، حتی اگر در client مقدار صحیح ست شده بود.
**علت**: در protobuf، فیلد `week_definition_id` از نوع `google.protobuf.Int64Value` است که یک wrapper type هست. در mapping مستقیم assign می‌شد بدون extract کردن `.Value`.
**فایل**: `FrontOffice.BFF/src/FrontOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs`
**Proto Definition**:
```protobuf
message GetMyWeeklyBalancesRequest {
google.protobuf.Int64Value week_definition_id = 3;
}
```
**قبل**:
```csharp
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
.Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId); // BUG: assigns Int64Value object, not the value
```
**بعد**:
```csharp
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
.Map(dest => dest.WeekDefinitionId,
src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : null);
```
**توضیح**:
- `Int64Value` یک wrapper class در protobuf هست برای nullable long
- وقتی مستقیم assign کنید، implicit conversion اتفاق نمیفته
- باید explicit از `.Value` استفاده کنید
---
## 📦 فایل‌های تغییر یافته
### CMS
| فایل | نوع تغییر |
|------|-----------|
| `Domain/Common/SystemConstants.cs` | Modified - اضافه شدن GoldenPackageAmount, DayaLoanAmount |
| `Domain/Common/SmsTemplates.cs` | **New** - قالب‌های پیامک |
| `Application/.../ProcessDayaLoanApprovalCommandHandler.cs` | Modified - اضافه شدن SMS |
| `WebApi/Common/Mappings/AppVersionProfile.cs` | **New** - Mapster profile |
| `Application/.../GetAllWeeklyPoolsQueryHandler.cs` | Modified - تغییر WeekDisplayName از PersianWeekNumber به DisplayName |
| `Application/.../GetUserWeeklyBalancesQueryHandler.cs` | Modified - اضافه شدن User include و فیلدهای جدید |
| `Application/.../GetUserWeeklyBalancesResponseDto.cs` | Modified - اضافه شدن UserFullName و breakdown fields |
| `WebApi/Common/Mappings/CommissionProfile.cs` | Modified - mapping جدید برای UserWeeklyBalanceModel |
| `Protobufs/Protos/commission.proto` | Modified - اضافه شدن فیلدهای جدید به UserWeeklyBalanceModel |
### BackOffice.BFF
| فایل | نوع تغییر |
|------|-----------|
| `Application/Common/Mappings/CommissionProfile.cs` | Modified - اضافه شدن GetAllWeeklyPools mapping + ProcessWithdrawal mapping |
| `WebApi/Common/Mappings/GeneralMapping.cs` | Modified - اضافه شدن Unit to Empty |
| `Application/.../GetWithdrawalRequestsQueryHandler.cs` | Modified - explicit mapping به جای Adapt<> |
| `Application/.../GetWithdrawalRequestsResponseDto.cs` | Modified - تطابق با CMS proto |
| `Protobufs/Protos/commission.proto` | Modified - اضافه شدن فیلدهای جدید |
### BackOffice
| فایل | نوع تغییر |
|------|-----------|
| `Shared/NavMenu.razor` | Modified - اضافه شدن لینک app-versions |
| `Pages/Settings/AppVersions.razor` | Modified - دکمه افزودن + OpenCreateDialog |
| `Pages/Settings/Components/AppVersionEditDialog.razor` | Modified - پارامتر IsNew + Select |
| `Pages/Network/BalancesReport.razor` | Modified - ستون‌های جدید با MudTooltip |
| `BackOffice.csproj` | Modified - آپدیت proto package به v0.0.14 |
### FrontOffice.BFF
| فایل | نوع تغییر |
|------|-----------|
| `WebApi/Common/Mappings/CommissionProfile.cs` | Modified - رفع WeekDefinitionId mapping (Int64Value.Value) |
| `Application/.../GetMyWeeklyBalancesQueryHandler.cs` | Modified - simplify status handling |
### FrontOffice
| فایل | نوع تغییر |
|------|-----------|
| `Utilities/CommissionService.cs` | Modified - اضافه شدن GetStatusColor و MapStatus (انتقال از BFF) |
| `Pages/Commission/WeeklyBalancePage.razor.cs` | Modified - استفاده از متدهای جدید CommissionService |
---
## ✅ Build Status
```bash
# CMS
dotnet build CMSMicroservice.WebApi/CMSMicroservice.WebApi.csproj
# Build succeeded. 0 Error(s)
# BackOffice.BFF
dotnet build BackOffice.BFF.WebApi/BackOffice.BFF.WebApi.csproj
# Build succeeded. 0 Error(s)
# BackOffice
dotnet build BackOffice/BackOffice.csproj
# Build succeeded. 0 Error(s)
# FrontOffice.BFF
dotnet build FrontOffice.BFF.WebApi/FrontOffice.BFF.WebApi.csproj
# Build succeeded. 0 Error(s)
# FrontOffice
dotnet build FrontOffice.Main/FrontOffice.Main.csproj
# Build succeeded. 0 Error(s)
```
---
## 📊 آمار Session
| متریک | مقدار |
|-------|-------|
| فایل‌های جدید | 2 |
| فایل‌های تغییر یافته | 18 |
| خطوط کد اضافه شده | ~500 |
| باگ‌های Mapping رفع شده | 5 |
| پروژه‌های تأثیرگذار | 5 (CMS, BackOffice, BackOffice.BFF, FrontOffice, FrontOffice.BFF) |
---
## 🔗 Related Changelogs
- [CHANGELOG-2025-12-26.md](CHANGELOG-2025-12-26.md) - App Version Management + ReferralCode in Tree
- [CHANGELOG-2025-12-25.md](CHANGELOG-2025-12-25.md) - درخت شبکه BackOffice
+216
View File
@@ -0,0 +1,216 @@
# 📝 Changelog - ۹ دی ۱۴۰۴ (29 December 2025)
> **Session**: Commission Data Flow Fix + UI Improvements + Terminology Cleanup (MLM-sensitive words)
---
## 🎯 خلاصه Session
این session شامل موارد زیر بود:
1. **Commission Carryover Data Flow** - رفع مشکل نمایش 0 برای carryover در صفحه weekly-balance
2. **WeekSelector Autocomplete** - افزودن انتخابگر هفته به صفحه داشبورد کمیسیون
3. **Responsive Commission Pages** - بهبود رسپانسیو صفحات با MudGrid
4. **Merge Dashboard & History Pages** - ادغام دو صفحه تکراری کمیسیون
5. **Terminology Cleanup** - جایگزینی کلمات حساس MLM با معادل‌های خنثی
---
## ✨ تغییرات
### 1. 💰 Commission Carryover Data Flow ✅
**مشکل**: صفحه `weekly-balance` مقادیر carryover را همیشه 0 نشان می‌داد در حالی که API مقادیر صحیح برمی‌گرداند.
**علت**: FrontOffice client carryover را خودش محاسبه می‌کرد به جای استفاده از مقادیر سرور.
**فایل‌های تغییر یافته**:
| فایل | تغییر |
|------|-------|
| `FrontOffice.BFF/commission.proto` | افزودن fields 11-14: carryover و new_members |
| `FrontOffice.BFF/CommissionProfile.cs` | Mapping جدید برای carryover fields |
| `FrontOffice/CommissionDtos.cs` | Properties جدید: LeftCarryover, RightCarryover, LeftNewMembers, RightNewMembers |
| `FrontOffice/CommissionService.cs` | استفاده از مقادیر واقعی سرور به جای محاسبه محلی |
**Proto Fields جدید**:
```protobuf
message WeeklyBalanceResponse {
// ... existing fields ...
int32 left_leg_carryover = 11;
int32 right_leg_carryover = 12;
int32 left_leg_new_members = 13;
int32 right_leg_new_members = 14;
}
```
---
### 2. 🎯 WeekSelector Autocomplete ✅
**نیاز**: انتخاب آسان هفته‌ها در صفحه داشبورد کمیسیون به جای dropdown ساده.
**پیاده‌سازی**:
```razor
<MudAutocomplete T="WeekSelectorItem"
Label="انتخاب هفته"
@bind-Value="_selectedWeek"
SearchFunc="SearchWeeks"
ToStringFunc="@(w => w?.DisplayName ?? "")"
Variant="Variant.Outlined"
Dense="true" />
```
**فایل**: `FrontOffice.Main/Pages/Commission/CommissionDashboardPage.razor`
---
### 3. 📱 Responsive Commission Pages ✅
**تغییرات UI**:
- استفاده از `MudGrid` با breakpoints مناسب (`xs`, `sm`, `md`)
- `MudHidden` برای نمایش/مخفی کردن المان‌ها در موبایل/دسکتاپ
- کارت‌های آماری (Summary Stats) در بالای صفحه
- جدول در دسکتاپ، کارت در موبایل
**Summary Stats Cards**:
```razor
<MudGrid Spacing="2" Class="mb-4">
<MudItem xs="6" sm="3">
<MudPaper Class="pa-3 text-center" Elevation="2">
<MudText Typo="Typo.h5" Color="Color.Primary">@TotalCommissions.ToString("N0")</MudText>
<MudText Typo="Typo.caption">کل پاداش‌ها</MudText>
</MudPaper>
</MudItem>
<!-- ... more stats ... -->
</MudGrid>
```
---
### 4. 🔀 Merge Dashboard & History Pages ✅
**قبل**: دو صفحه جداگانه با functionality تکراری
- `/commission/dashboard` - داشبورد با فیلتر
- `/commission/history` - تاریخچه با pagination
**بعد**: یک صفحه واحد با dual routing
```csharp
@attribute [Route(RouteConstants.Commission.Dashboard)]
@attribute [Route(RouteConstants.Commission.History)]
```
**فایل‌های حذف شده**:
- `CommissionHistoryPage.razor`
- `CommissionHistoryPage.razor.cs`
**فایل نهایی**: `CommissionDashboardPage.razor` با تمام قابلیت‌ها
---
### 5. 📝 Terminology Cleanup (MLM-Sensitive Words) ✅
**هدف**: جایگزینی کلمات حساس MLM با معادل‌های خنثی برای جلوگیری از حساسیت مشتریان.
#### جایگزینی‌های انجام شده:
| کلمه قبلی | کلمه جدید | توضیح |
|-----------|-----------|-------|
| کمیسیون | **پاداش** | Commission → Reward |
| شبکه‌سازی | **تیم‌سازی** | Network Building → Team Building |
| شبکه‌های فروش | **تیم‌های فروش** | Sales Networks → Sales Teams |
| مشاهده شبکه | **مشاهده تیم** | View Network → View Team |
| آمار شبکه | **آمار تیم** | Network Stats → Team Stats |
| رشد میانگین شبکه | **رشد میانگین تیم** | Network Growth → Team Growth |
#### فایل‌های تغییر یافته:
| فایل | تغییرات |
|------|---------|
| `WeeklyBalancePage.razor` | کمیسیون → پاداش |
| `CommissionDashboardPage.razor` | کمیسیون → پاداش، PageTitle |
| `MyPackages.razor` | کمیسیون → پاداش، مشاهده شبکه → مشاهده تیم |
| `Packages.razor` | کمیسیون → پاداش |
| `Index.razor` | شبکه‌سازی → تیم‌سازی، رشد میانگین شبکه → تیم |
| `About.razor` | شبکه‌سازی → تیم‌سازی، شبکه‌های فروش → تیم‌های فروش |
| `Footer.razor` | شبکه‌های فروش → تیم‌های فروش |
| `NetworkStatisticsPage.razor` | آمار شبکه → آمار تیم |
#### کلمات بدون تغییر (صحیح هستند):
| کلمه | دلیل عدم تغییر |
|------|---------------|
| شبکه‌های اجتماعی | Social Networks - مرتبط با MLM نیست |
| درخت دسته‌بندی‌ها | Category Tree - مرتبط با MLM نیست |
| درخت شبکه (BackOffice) | پنل ادمین - نیاز به صراحت دارد |
| زیرمجموعه (BackOffice) | پنل ادمین - نیاز به صراحت دارد |
---
## 📁 فایل‌های تغییر یافته
### FrontOffice.BFF:
```
src/FrontOffice.BFF.Domain/Domain.csproj # CMS proto v0.0.162
src/BackOffice.BFF.Application/.../CommissionProfile.cs # Carryover mapping
src/Protobufs/commission.proto # Fields 11-14
```
### FrontOffice Client:
```
src/FrontOffice.Main/FrontOffice.Main.csproj # Commission.Protobuf v0.0.6
src/FrontOffice.Main/Utilities/CommissionDtos.cs # New properties
src/FrontOffice.Main/Utilities/CommissionService.cs # Server values
src/FrontOffice.Main/Pages/Commission/CommissionDashboardPage.razor # Merged + Responsive
src/FrontOffice.Main/Pages/Commission/CommissionDashboardPage.razor.cs # Dual routes
src/FrontOffice.Main/Pages/Commission/WeeklyBalancePage.razor # UI + Terminology
src/FrontOffice.Main/Pages/Package/MyPackages.razor # Terminology
src/FrontOffice.Main/Pages/Store/Packages.razor # Terminology
src/FrontOffice.Main/Pages/Index.razor # Terminology
src/FrontOffice.Main/Pages/About.razor # Terminology
src/FrontOffice.Main/Pages/Network/NetworkStatisticsPage.razor # Terminology
src/FrontOffice.Main/Shared/Footer.razor # Terminology
```
### فایل‌های حذف شده:
```
src/FrontOffice.Main/Pages/Commission/CommissionHistoryPage.razor ❌
src/FrontOffice.Main/Pages/Commission/CommissionHistoryPage.razor.cs ❌
```
---
## 🧪 تست و تأیید
- ✅ صفحه weekly-balance مقادیر صحیح carryover نمایش می‌دهد
- ✅ WeekSelector در داشبورد کار می‌کند
- ✅ صفحات در موبایل responsive هستند
- ✅ هر دو route به یک صفحه می‌روند
- ✅ همه terminology ها تغییر کرده‌اند
---
## 📋 لیست کلمات MLM-حساس (مرجع)
برای آینده، این کلمات در UI مشتری باید با دقت استفاده شوند:
| کلمه حساس | جایگزین پیشنهادی | وضعیت |
|-----------|-----------------|-------|
| کمیسیون | پاداش | ✅ انجام شد |
| شبکه‌سازی | تیم‌سازی | ✅ انجام شد |
| شبکه (در context MLM) | تیم | ✅ انجام شد |
| زیرمجموعه | اعضای تیم | ⏸️ فقط BackOffice |
| درخت شبکه | نمودار سازمانی | ⏸️ فقط BackOffice |
| شاخه چپ/راست | تیم اول/دوم | ✅ قبلاً انجام شده |
| تعادل | امتیاز/جفت | ✅ قبلاً انجام شده |
| سقف | حداکثر | ✅ قبلاً انجام شده |
| Downline | اعضا | ⏸️ فقط BackOffice |
| Binary Tree | ساختار تیم | ⏸️ فقط BackOffice |
---
## 🔗 Related Files
- [`CHANGELOG-2025-12-27.md`](CHANGELOG-2025-12-27.md) - Session قبلی
- [`04-FRONTEND/FrontOffice/README.md`](04-FRONTEND/FrontOffice/README.md) - مستندات FrontOffice
- [`01-BUSINESS/network-commission-system.md`](01-BUSINESS/network-commission-system.md) - منطق تجاری کمیسیون
+264
View File
@@ -0,0 +1,264 @@
# 📝 Changelog - ۱۱ دی ۱۴۰۴ (31 December 2025)
> **Session**: Discount Shop Admin APIs Complete + BFF Layer Implementation
---
## 🎯 خلاصه Session
این session شامل موارد زیر بود:
1. **Product Image Gallery** - گالری تصاویر محصولات فروشگاه تخفیفی (CMS + BFF)
2. **GetAllDiscountOrders API** - API مدیریت سفارشات فروشگاه تخفیفی برای ادمین
3. **VAT Calculation** - محاسبه مالیات بر ارزش افزوده
4. **Sales Reports API** - گزارشات فروش روزانه/هفتگی/ماهانه با تقویم فارسی
5. **BFF WebApi Services** - سرویس‌های gRPC برای BackOffice.BFF
---
## ✨ تغییرات
### 1. 🖼️ Product Image Gallery (Phase 1) ✅
**توضیح**: پشتیبانی از گالری تصاویر برای محصولات فروشگاه تخفیفی
**فایل‌های CMS ایجاد شده**:
| فایل | توضیح |
|------|-------|
| `Domain/DiscountProduct/DiscountProductImage.cs` | Entity گالری تصویر |
| `Infrastructure/.../DiscountProductImageConfiguration.cs` | تنظیمات EF Core |
| `Application/.../AddDiscountProductImage/` | Command افزودن تصویر |
| `Application/.../UpdateDiscountProductImage/` | Command ویرایش تصویر |
| `Application/.../DeleteDiscountProductImage/` | Command حذف تصویر |
| `Application/.../ReorderDiscountProductImages/` | Command مرتب‌سازی تصاویر |
| `Application/.../GetDiscountProductImages/` | Query دریافت تصاویر |
**فایل‌های BFF Application ایجاد شده**:
| فایل | توضیح |
|------|-------|
| `DiscountProductCQ/Commands/AddDiscountProductImage/` | Command + Handler |
| `DiscountProductCQ/Commands/UpdateDiscountProductImage/` | Command + Handler |
| `DiscountProductCQ/Commands/DeleteDiscountProductImage/` | Command + Handler |
| `DiscountProductCQ/Commands/ReorderDiscountProductImages/` | Command + Handler |
| `DiscountProductCQ/Queries/GetDiscountProductImages/` | Query + Handler |
**Entity Structure**:
```csharp
public class DiscountProductImage : BaseEntity<long>
{
public long DiscountProductId { get; set; }
public string ImagePath { get; set; }
public string? ThumbnailPath { get; set; }
public string? Title { get; set; }
public string? AltText { get; set; }
public int SortOrder { get; set; }
public bool IsActive { get; set; }
}
```
---
### 2. 📋 GetAllDiscountOrders Admin API (Phase 2) ✅
**توضیح**: API برای مدیریت تمام سفارشات فروشگاه تخفیفی توسط ادمین
**فیلترهای پشتیبانی شده**:
- `UserId` - فیلتر بر اساس کاربر
- `PaymentStatus` - وضعیت پرداخت (Pending/Success/Reject)
- `DeliveryStatus` - وضعیت ارسال
- `UserMobile` - جستجو بر اساس موبایل
- `TrackingCode` - کد رهگیری
- `FromDate` / `ToDate` - بازه زمانی
- `MinAmount` / `MaxAmount` - بازه مبلغ
**فایل‌های CMS**:
- `Application/DiscountOrderCQ/Queries/GetAllDiscountOrders/`
- `WebApi/DiscountOrderService.cs` - افزودن RPC
**فایل‌های BFF**:
- `Application/DiscountOrderCQ/Queries/GetAllDiscountOrders/`
---
### 3. 💵 VAT Calculation (Phase 3) ✅
**توضیح**: سرویس محاسبه مالیات بر ارزش افزوده (۱۰٪)
**فایل‌های ایجاد شده**:
- `CMS/Application/Common/Services/VatCalculator.cs`
**متدها**:
```csharp
public class VatCalculator : IVatCalculator
{
public const decimal VatRate = 0.10m; // 10% VAT
public decimal CalculateVat(decimal amount);
public decimal CalculateTotalWithVat(decimal amount);
public decimal CalculateBaseFromTotal(decimal totalWithVat);
public (decimal baseAmount, decimal vatAmount, decimal total) CalculateBreakdown(decimal amount);
}
```
**یکپارچه‌سازی**: در `PlaceOrderCommandHandler` استفاده شده
---
### 4. 📊 Sales Reports API (Phase 4) ✅
**توضیح**: گزارشات فروش با پشتیبانی تقویم فارسی
**انواع گزارش**:
- `Summary` - خلاصه کلی
- `Daily` - روزانه
- `Weekly` - هفتگی
- `Monthly` - ماهانه
**فایل‌های CMS**:
- `Application/DiscountOrderCQ/Queries/GetDiscountSalesReport/`
**فایل‌های BFF**:
- `Application/DiscountOrderCQ/Queries/GetDiscountSalesReport/`
**Response Structure**:
```csharp
public class DiscountSalesReportDto
{
public SalesSummaryDto Summary { get; set; }
public List<PeriodSalesDto> Periods { get; set; }
}
public class SalesSummaryDto
{
public int TotalOrders { get; set; }
public int SuccessfulOrders { get; set; }
public int PendingOrders { get; set; }
public int RejectedOrders { get; set; }
public decimal TotalRevenue { get; set; }
public decimal TotalDiscountUsed { get; set; }
public decimal TotalGatewayPayments { get; set; }
public decimal TotalVat { get; set; }
public int UniqueCustomers { get; set; }
}
```
---
### 5. 🔌 BFF WebApi gRPC Services (Phase 5) ✅
**توضیح**: سرویس‌های gRPC در لایه WebApi برای expose کردن APIها
**فایل‌های ایجاد شده**:
| فایل | توضیح |
|------|-------|
| `BackOffice.BFF.WebApi/Services/DiscountProductService.cs` | سرویس محصولات تخفیفی |
| `BackOffice.BFF.WebApi/Services/DiscountOrderService.cs` | سرویس سفارشات تخفیفی |
**تغییرات Proto Projects**:
```xml
<!-- Before -->
<Protobuf GrpcServices="Client" />
<!-- After -->
<Protobuf GrpcServices="Both" />
```
**فایل‌های تغییر یافته**:
- `BackOffice.BFF.DiscountProduct.Protobuf.csproj`
- `BackOffice.BFF.DiscountOrder.Protobuf.csproj`
- `BackOffice.BFF.WebApi.csproj` - افزودن references
**DiscountProductService Endpoints**:
```csharp
// Product CRUD
CreateDiscountProduct
UpdateDiscountProduct
DeleteDiscountProduct
GetDiscountProductById
GetDiscountProducts
// Image Gallery (NEW)
AddDiscountProductImage
UpdateDiscountProductImage
DeleteDiscountProductImage
ReorderDiscountProductImages
GetDiscountProductImages
```
**DiscountOrderService Endpoints**:
```csharp
// Order Management
PlaceOrder
CompleteOrderPayment
UpdateOrderStatus
GetOrderById
GetUserOrders
// Admin APIs (NEW)
GetAllDiscountOrders
GetDiscountSalesReport
```
---
## 📁 ساختار فایل‌های جدید
```
CMS/src/
├── CMSMicroservice.Application/
│ ├── Common/Services/VatCalculator.cs
│ └── DiscountOrderCQ/Queries/
│ ├── GetAllDiscountOrders/
│ │ ├── GetAllDiscountOrdersQuery.cs
│ │ └── GetAllDiscountOrdersQueryHandler.cs
│ └── GetDiscountSalesReport/
│ ├── GetDiscountSalesReportQuery.cs
│ └── GetDiscountSalesReportQueryHandler.cs
├── CMSMicroservice.Domain/DiscountProduct/
│ └── DiscountProductImage.cs
├── CMSMicroservice.Infrastructure/.../
│ └── DiscountProductImageConfiguration.cs
└── CMSMicroservice.Protobuf/Protos/
├── discountproduct.proto (updated)
└── discountorder.proto (updated)
BackOffice.BFF/src/
├── BackOffice.BFF.Application/
│ ├── DiscountProductCQ/
│ │ ├── Commands/
│ │ │ ├── AddDiscountProductImage/
│ │ │ ├── UpdateDiscountProductImage/
│ │ │ ├── DeleteDiscountProductImage/
│ │ │ └── ReorderDiscountProductImages/
│ │ └── Queries/
│ │ └── GetDiscountProductImages/
│ └── DiscountOrderCQ/Queries/
│ ├── GetAllDiscountOrders/
│ └── GetDiscountSalesReport/
├── BackOffice.BFF.WebApi/Services/
│ ├── DiscountProductService.cs (NEW)
│ └── DiscountOrderService.cs (NEW)
└── Protobufs/
├── BackOffice.BFF.DiscountProduct.Protobuf/ (GrpcServices=Both)
└── BackOffice.BFF.DiscountOrder.Protobuf/ (GrpcServices=Both)
```
---
## ✅ Build Status
| Project | Status |
|---------|--------|
| CMS Solution | ✅ Build Succeeded |
| BackOffice.BFF Solution | ✅ Build Succeeded |
---
## 🔜 Next Steps
1. **BackOffice UI** - اتصال پنل ادمین به APIهای جدید
2. **FrontOffice.BFF** - پیاده‌سازی APIها برای فرانت‌آفیس (اگر نیاز باشد)
3. **Unit Tests** - نوشتن تست‌های واحد
+118
View File
@@ -0,0 +1,118 @@
# BackOffice Changelog
> تاریخچه تغییرات پروژه BackOffice
---
## December 20, 2025
### 🐛 Bug Fixes
#### 1. صفحه `/network/balances` - ValidationException
**مشکل**: خطای ValidationException هنگام لود صفحه
**راه‌حل**: اضافه کردن Mapster mapping در `CommissionProfile.cs`:
```csharp
config.NewConfig<GetUserWeeklyBalancesRequest, GetUserWeeklyBalancesQuery>()
.Map(dest => dest.PaginationState, src => src.PaginationState);
```
---
#### 2. صفحه `/club/members` - داده‌ها لود نمی‌شدند
**مشکل**: صفحه خالی بود و داده‌ای نمایش نمی‌داد
**راه‌حل**: ایجاد `ClubMembershipProfile.cs` در CMS و BFF با mappings کامل:
- `GetAllClubMembershipsRequest``GetAllClubMembershipsQuery`
- `GetAllClubMembershipsResponseDto``GetAllClubMembershipsResponse`
**فایل‌های جدید**:
- `CMS/WebApi/Common/Mappings/ClubMembershipProfile.cs`
- `BackOffice.BFF/WebApi/Common/Mappings/ClubMembershipProfile.cs` (بازنویسی)
---
#### 3. صفحه `/club/statistics` - Unimplemented Error
**مشکل**: خطای `Status(StatusCode="Unimplemented")`
**راه‌حل**:
1. اضافه کردن override `GetClubStatistics` در `ClubMembershipService.cs`
2. اضافه کردن mappings برای Statistics در هر دو Profile
---
### ✨ New Features
#### 4. فعال‌سازی قابلیت‌های Products
**قبل**: همه دکمه‌ها "در حال توسعه" نشان می‌دادند
**بعد**: همه قابلیت‌ها فعال شدند:
- ✅ ایجاد محصول جدید (CreateDialog)
- ✅ ویرایش محصول (UpdateDialog)
- ✅ گالری تصاویر (GalleryDialog)
- ✅ مدیریت تگ‌ها (AssignTagsDialog)
**فایل**: `ProductsMainPage.razor.cs`
---
#### 5. فیلد "تعداد موجودی" در Products
**اضافات**:
- فیلد موجودی در فرم ایجاد محصول
- فیلد موجودی در فرم ویرایش محصول
- ستون موجودی در لیست با رنگ‌بندی:
- 🔴 ناموجود (0 یا کمتر)
- 🟡 کم موجود (کمتر از 10)
- 🟢 موجود (10 یا بیشتر)
**فایل‌های تغییر یافته**:
- `CreateDialog.razor`
- `UpdateDialog.razor`
- `ProductsMainPage.razor`
- `CreateNewProductsCommand.cs` (BFF)
- `UpdateProductsCommand.cs` (BFF)
---
## December 6, 2025
### ✅ Major Milestones
- Build Errors: 60+ → 0
- MudBlazor 8 Migration Complete
- All Product Image Management APIs Implemented
- BulkEdit Module Enabled
- All Files Unexcluded
### 🔧 Technical Changes
- `IMudDialogInstance` جایگزین `MudDialogInstance`
- `MudSwitch T="bool"` اضافه شد
- `MudChip T="string"` اضافه شد
- Products از NuGet به ProjectReference تغییر کرد
---
## December 1, 2025
### ✅ Network & Commission System
- Commission Dashboard Complete
- Network Members Page Complete
- Club Members Page Complete
- Weekly Pool Management
- Withdrawal System
- Payout System
---
## November 29, 2025
### ✅ Initial Setup
- SystemConfigurations Table Created
- Base Configuration Values Added:
- `Network.MaxDepth`: 10
- `Club.DefaultMembershipDurationMonths`: 12
- `Commission.MinimumPayoutAmount`: 100000
- `System.MaintenanceMode`: false

Some files were not shown because too many files have changed in this diff Show More