Files
docs/technical/TECH-01-CMS-ARCHITECTURE.md
T
masoodafar-web e3850f9dd8 docs: فاز ۱۱ — فیکس‌های پرداخت ZarinPal + تصحیح تومان/ریال + امنیت Callback URL
- CHANGELOG: Phase 11 (11a-11f) — ZarinPal verify fix, تومان/ریال مدل, صفحه موفقیت, حذف ×۱۰ دوبار, callback URL امنیت
- BUSINESS-02: تصحیح مدل ارزی (DB=تومان نه ریال), ZarinPal verify fix, جدول callback URL امنیت
- TECH-01: اضافه CmsBaseUrl/FrontOfficeBaseUrl به appsettings, توضیح امنیت Open Redirect
- TECH-02: اضافه PaymentCallback.razor, وضعیت‌های جدید
- ROADMAP: بروزرسانی Payment 97→99%, اضافه فاز ۱۱ به DONE list
2026-02-27 22:33:35 +03:30

10 KiB
Raw Blame History

⚙️ معماری 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

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

// Command
public record CreateProductCommand(
    string Name, string Description, decimal Price,
    Guid CategoryId, string ImageUrl
) : IRequest<Guid>;

// Handler
public class CreateProductCommandHandler 
    : IRequestHandler<CreateProductCommand, Guid>
{
    private readonly CMSDbContext _db;
    
    public async Task<Guid> 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 (مشترک)

message PaginationState {
    int32 skip = 1;
    int32 take = 2;
}

Namespace صحیح:

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)

// StoredProcedureDeploymentService : IHostedService
// در startup:
// 1. خواندن فایل‌های .sql از embedded resource
// 2. مقایسه checksum با جدول __SPChecksums
// 3. فقط SP‌های تغییریافته re-deploy می‌شوند

۵.۵ History Tracking System (Q27)

IHasHistory Interface

public interface IHasHistory<THistory> 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 (مثلاً OldPriceOriginalValues["Price"])
  • Entity‌های فعال: PackagePackageHistory

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 بعد

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 ساختار

{
    "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)