# ⚙️ معماری CMS و زیرساخت فنی > **منابع ادغام‌شده:** `CMS-README.md`, `ICURRENTUSERSERVICE-IMPLEMENTATION.md`, `FILE-MANAGEMENT-ARCHITECTURE.md`, `FRONTOFFICE-CMS-API-COMPATIBILITY.md`, `BFF-REMOVAL-PLAN.md`, `system-constants.md` > **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: فیکس ZarinPal Verify + Callback URL امنیت + appsettings.Development.json) --- ## ۱. Stack فنی | لایه | تکنولوژی | نسخه | |------|----------|------| | **Runtime** | .NET | 9.0 | | **ORM** | Entity Framework Core | 9.0 | | **Communication** | gRPC (Protobuf) | v3 | | **Pattern** | CQRS + MediatR | — | | **Database** | SQL Server (MSSQL) | 2022-CU16 | | **Job Scheduler** | Hangfire | — | | **Auth** | JWT Bearer + Identity | — | | **API Gateway** | حذف‌شده (Direct gRPC) | — | --- ## ۲. معماری لایه‌ای CMS ```mermaid flowchart TD subgraph PRES["💻 Presentation Layer"] FO["FrontOffice\nBlazor Server"] BO["BackOffice\nBlazor WASM"] end FO & BO -->|gRPC| APP subgraph APP["⚙️ Application Layer"] CMD["Commands\nMediatR IRequest"] QRY["Queries\nMediatR IRequest"] VAL["Validators\nFluentValidation"] HND["Handlers\nIRequestHandler"] end APP --> DOM subgraph DOM["📦 Domain Layer"] ENT["Entities, Enums\nValue Objects\nDomain Events"] end DOM --> INF subgraph INF["🔧 Infrastructure Layer"] EF["EF Core DbContext"] SVC["External Services"] HF["Hangfire Jobs"] FS["File Storage"] end INF --> DB[("🗄️ SQL Server\nSchema: CMS")] ``` --- ## ۳. CQRS با MediatR ### ۳.۱ ساختار فولدرها ``` CMS/src/ ├── CMSMicroservice/ │ ├── Features/ │ │ ├── Products/ │ │ │ ├── Commands/ │ │ │ │ ├── CreateProductCommand.cs │ │ │ │ └── CreateProductCommandHandler.cs │ │ │ ├── Queries/ │ │ │ │ ├── GetProductsQuery.cs │ │ │ │ └── GetProductsQueryHandler.cs │ │ │ └── Validators/ │ │ │ └── CreateProductCommandValidator.cs │ │ ├── Orders/ │ │ ├── Users/ │ │ ├── Club/ │ │ ├── Payment/ │ │ └── Blog/ │ ├── Services/ │ │ ├── gRPC/ ← gRPC service implementations │ │ ├── Background/ ← Hangfire jobs │ │ └── External/ ← ZarinPal, Kavenegar, Daya, Chatika │ ├── Infrastructure/ │ │ ├── Persistence/ ← DbContext, Migrations │ │ └── Identity/ ← JWT, Claims, ICurrentUserService │ └── Protos/ ← .proto files ``` ### ۳.۲ مثال Command ```csharp // Command public record CreateProductCommand( string Name, string Description, decimal Price, Guid CategoryId, string ImageUrl ) : IRequest; // Handler public class CreateProductCommandHandler : IRequestHandler { private readonly CMSDbContext _db; public async Task Handle( CreateProductCommand request, CancellationToken ct) { var product = new Product { /* map fields */ }; _db.Products.Add(product); // Auto-create inventory record _db.Inventories.Add(new Inventory { ProductId = product.Id }); await _db.SaveChangesAsync(ct); return product.Id; } } ``` --- ## ۴. gRPC Services ### ۴.۱ لیست سرویس‌ها | سرویس | proto | متدهای اصلی | |--------|-------|-------------| | `ProductService` | product.proto | GetProducts, GetProduct, Create, Update, Delete | | `OrderService` | order.proto | CreateOrder, GetOrders, UpdateStatus | | `UserService` | user.proto | Register, Login, GetProfile, UpdateProfile | | `ClubService` | club.proto | GetNetworkTree, GetBalance, AcceptContract | | `PaymentService` | payment.proto | CreatePayment, VerifyPayment | | `BlogService` | blog.proto | GetPosts, GetPost, Create, Update | | `InventoryService` | inventory.proto | GetInventory, UpdateStock | | `FileService` | file.proto | Upload, Download, Delete | | `SitePageService` | sitepage.proto | GetPage, SaveSettings | | `CategoryService` | category.proto | GetCategories, Create, Update | | `SystemConfigService` | config.proto | GetConfig, UpdateConfig | | `UserWalletService` | userwallet.proto | GetCustomerWallet, InitiateMagicCharge, GetMagicWalletStatus | | `UserWalletHistoryService` | userwallethistory.proto | *(renamed from UserWalletChangeLogService)* | ### ۴.۲ PaginationState (مشترک) ```protobuf message PaginationState { int32 skip = 1; int32 take = 2; } ``` **Namespace صحیح:** ```csharp using CMSMicroservice.Protobuf.Protos.PaginationState; // ⚠️ نه: CMSMicroservice.Protobuf.Protos.PublicMessages.PaginationState ``` --- ## ۵. Database ### ۵.۱ اتصال ``` Staging: Server=194.5.195.53; Database=FourSatCMS; Schema=CMS Production: Server=45.149.79.127; Database=FourSatCMS; Schema=CMS Engine: MSSQL 2022-CU16, Collation=Arabic_CI_AS ``` ### ۵.۲ جداول اصلی | جدول | توضیح | رکوردهای تقریبی | |------|--------|----------------| | Users | کاربران + فیلدهای شبکه (NetworkParentId, LegPosition) | ~5K | | Products | محصولات (+ MaxDiscountPercent) | ~200 | | Categories | دسته‌بندی‌ها | ~30 | | Orders | سفارشات | ~2K | | Inventories | موجودی | ~200 | | BlogPosts | پست‌های بلاگ | ~50 | | SitePages | صفحات سایت | ~10 | | UserClubMemberships | عضویت باشگاه | ~500 | | UserContracts | قراردادها (SignGuid, SignedPdfFile) | ~500 | | UserWallets | کیف‌پول (Balance, NetworkBalance, DiscountBalance, WalletMode) | ~5K | | ClubMembershipCycles | دوره‌های عضویت (CycleNumber, PackagePurchasedAt, IsCurrentCycle) | ~500 | | Transactions | تراکنش‌ها | ~5K | | SystemConfigurations | تنظیمات | ~30 | | ChatMessages | پیام‌های چاتیکا | ~1K | ### ۵.۳ Stored Procedures | SP | کاربرد | |----|--------| | `SP_GetNetworkTree` | بازگشتی — استخراج درخت باینری | | `sp_CalculateWeeklyBalances` | محاسبه بالانس هفتگی هر عضو | | `sp_CalculateWeeklyCommissionPool` | توزیع Pool هفتگی | ### ۵.۴ SP Auto-Deploy Worker (Q26) ```csharp // StoredProcedureDeploymentService : IHostedService // در startup: // 1. خواندن فایل‌های .sql از embedded resource // 2. مقایسه checksum با جدول __SPChecksums // 3. فقط SP‌های تغییریافته re-deploy می‌شوند ``` --- ## ۵.۵ History Tracking System (Q27) ### IHasHistory Interface ```csharp public interface IHasHistory where THistory : BaseAuditableEntity, new() { THistory CreateHistorySnapshot(string action, string? performedBy); } ``` ### HistoryTrackingSaveChangesInterceptor - **مکان:** `Infrastructure/Persistence/Interceptors/HistoryTrackingSaveChangesInterceptor.cs` - **مکانیسم:** `SaveChangesInterceptor` — قبل از `SaveChanges` اجرا می‌شود - **شناسایی:** از `ChangeTracker` entity‌هایی که `IHasHistory<>` پیاده‌سازی کردن (Modified/Added) - **Auto-fill:** فیلدهای `Old*` از `entry.OriginalValues` با naming convention (مثلاً `OldPrice` ← `OriginalValues["Price"]`) - **Entity‌های فعال:** `Package` → `PackageHistory` ### History Tables | جدول | Entity مرتبط | فیلدهای Old/New | |------|-------------|----------------| | `PackageHistories` | Package | Price, ActivationFee, MagicMultiplier, MagicMaxDeposit, MaxBalancesPerLeg, IsActive | | `ClubMembershipCycleHistories` | ClubMembershipCycle | IsCurrentCycle, MagicStartedAt, MagicCompletedAt | | `UserWalletHistories` | UserWallet | *(renamed from UserWalletChangeLogs — RenameTable migration)* | --- ## ۶. حذف BFF / Gateway ### ۶.۱ قبل vs بعد ```mermaid flowchart LR subgraph BEFORE["قبل"] F1["FrontOffice"] -->|REST| BFF1["BFF"] B1["BackOffice"] -->|REST| BFF1 BFF1 -->|gRPC| C1["CMS"] end subgraph AFTER["بعد — فعلی ✅"] F2["FrontOffice"] -->|gRPC| C2["CMS"] B2["BackOffice"] -->|gRPC| C2 end ``` > مزایا: حذف لایه واسط → کاهش latency • Type-safe از proto تا UI • کاهش ۱ سرویس در deployment ### ۶.۲ سازگاری API ``` FrontOffice Service Layer: • ProductService.cs → gRPC client wrapper • OrderService.cs → gRPC client wrapper • UserService.cs → gRPC client wrapper هر Service: • Constructor: inject GrpcChannel • Methods: wrap gRPC calls + map to DTOs • Error handling: try/catch RpcException ``` --- ## ۷. Hangfire Jobs | Job | فرکانس (cron) | کاربرد | |-----|---------|--------| | `WeeklyCommissionCalculation` | `5 0 * * 0` (یکشنبه ۰۰:۰۵) | محاسبه و توزیع کمیسیون | | `DayaLoanProcessorJob` | `*/20 * * * *` (هر ۲۰ دقیقه) | پردازش درخواست‌های وام | | `ChatikaAccountActivation` | `*/5 * * * *` (هر ۵ دقیقه) | فعال‌سازی حساب چاتیکا | --- ## ۸. پیکربندی ### ۸.۱ appsettings.json ساختار ```json { "ConnectionStrings": { "DefaultConnection": "Server=...;Database=FourSatCMS" }, "Jwt": { "Secret": "***", "Issuer": "FourSat", "ExpiryMinutes": 1440 }, "Grpc": { "CmsUrl": "https://localhost:5001" }, "Hangfire": { "DashboardPath": "/hangfire", "WorkerCount": 4 }, "Kavenegar": { "ApiKey": "***" }, "ZarinPal": { "MerchantId": "***", "UseSandbox": true }, "DayaLoan": { "UseMock": true }, "CmsBaseUrl": "https://cms.se.kbs1.ir", "FrontOfficeBaseUrl": "http://localhost:5268" } ``` > **⚠️ نکات مهم appsettings:** > - `CmsBaseUrl` — برای callback URL‌های درگاه (شارژ کیف‌پول جادویی/اعتباری) > - `FrontOfficeBaseUrl` — برای redirect بعد پرداخت (خرید پکیج/تراکنش عمومی) > - `appsettings.Development.json` — URL‌های localhost برای توسعه محلی > - همه callback URL‌ها از config خوانده می‌شوند — هیچ URL از ورودی کاربر نمی‌آید (امنیت Open Redirect)