Files
docs/archive/03-BACKEND/CMS/commission-system.md
T
masoodafar-web 5965b98728 update
2026-01-03 18:27:49 +03:30

9.3 KiB

مستندات سیستم کمیسیون (Commission System)

آخرین بروزرسانی: ۲۹ آذر ۱۴۰۴ (19 December 2025)


📋 خلاصه

سیستم کمیسیون مسئول محاسبه، ذخیره و پرداخت کمیسیون‌های کاربران بر اساس ساختار شبکه بازاریابی است.


🗄️ موجودیت‌ها (Entities)

WeekDefinition

جدول مرجع برای تعریف هفته‌های مالی:

public class WeekDefinition : BaseAuditableEntity
{
    public int WeekOrder { get; set; }        // شماره ترتیبی هفته
    public int Year { get; set; }             // سال میلادی
    public int PersianYear { get; set; }      // سال شمسی
    public DateTime StartDate { get; set; }    // تاریخ شروع
    public DateTime EndDate { get; set; }      // تاریخ پایان
    public string StartDatePersian { get; set; }  // تاریخ شروع شمسی
    public string EndDatePersian { get; set; }    // تاریخ پایان شمسی
    public bool IsActive { get; set; }         // آیا هفته جاری است
}

NetworkWeeklyBalance

تعادل هفتگی شاخه چپ و راست کاربر:

public class NetworkWeeklyBalance : BaseAuditableEntity
{
    public long UserId { get; set; }
    public long WeekDefinitionId { get; set; }  // FK به WeekDefinition
    public long LeftBalance { get; set; }       // امتیاز شاخه چپ
    public long RightBalance { get; set; }      // امتیاز شاخه راست
    
    // Navigation Properties
    public virtual User User { get; set; }
    public virtual WeekDefinition WeekDefinition { get; set; }
}

Index: (UserId, WeekDefinitionId) - Unique

WeeklyCommissionPool

استخر کمیسیون هفتگی:

public class WeeklyCommissionPool : BaseAuditableEntity
{
    public long WeekDefinitionId { get; set; }  // FK به WeekDefinition
    public long TotalPoolAmount { get; set; }   // مجموع استخر
    public long DistributedAmount { get; set; } // مقدار توزیع شده
    public int TotalBalances { get; set; }      // تعداد کل تعادل‌ها
    public long PerBalanceAmount { get; set; }  // مبلغ هر تعادل
    public bool IsFinalized { get; set; }       // آیا نهایی شده
    
    // Navigation Property
    public virtual WeekDefinition WeekDefinition { get; set; }
}

UserCommissionPayout

رکورد پرداخت کمیسیون به کاربر:

public class UserCommissionPayout : BaseAuditableEntity
{
    public long UserId { get; set; }
    public long WeekDefinitionId { get; set; }  // FK به WeekDefinition
    public int BalancesEarned { get; set; }     // تعداد تعادل‌های کسب شده
    public long Amount { get; set; }            // مبلغ کمیسیون
    public CommissionPayoutStatus Status { get; set; }  // وضعیت پرداخت
    public DateTime? PaidAt { get; set; }       // تاریخ پرداخت
    
    // Navigation Properties
    public virtual User User { get; set; }
    public virtual WeekDefinition WeekDefinition { get; set; }
}

وضعیت‌ها (Status):

  • Created - ایجاد شده
  • Paid - پرداخت به کیف پول
  • WithdrawalRequested - درخواست برداشت
  • Withdrawn - برداشت شده
  • Cancelled - لغو شده

CommissionPayoutHistory

تاریخچه تغییرات وضعیت پرداخت:

public class CommissionPayoutHistory : BaseAuditableEntity
{
    public long UserCommissionPayoutId { get; set; }
    public long WeekDefinitionId { get; set; }  // FK به WeekDefinition
    public CommissionPayoutStatus FromStatus { get; set; }
    public CommissionPayoutStatus ToStatus { get; set; }
    public string? Notes { get; set; }
    
    // Navigation Properties
    public virtual UserCommissionPayout UserCommissionPayout { get; set; }
    public virtual WeekDefinition WeekDefinition { get; set; }
}

WorkerExecutionLog

لاگ اجرای Worker های محاسبه کمیسیون:

public class WorkerExecutionLog : BaseAuditableEntity
{
    public string WorkerName { get; set; }       // نام Worker
    public long? WeekDefinitionId { get; set; }  // FK به WeekDefinition (nullable)
    public DateTime StartedAt { get; set; }       // زمان شروع
    public DateTime? CompletedAt { get; set; }    // زمان پایان
    public bool IsSuccess { get; set; }           // موفقیت
    public string? ErrorMessage { get; set; }     // پیام خطا
    public int ProcessedCount { get; set; }       // تعداد پردازش شده
    
    // Navigation Property
    public virtual WeekDefinition? WeekDefinition { get; set; }
}

🔗 روابط (Relationships)

WeekDefinition (1) ─────┬──── (*) NetworkWeeklyBalance
                        ├──── (*) WeeklyCommissionPool
                        ├──── (*) UserCommissionPayout
                        ├──── (*) CommissionPayoutHistory
                        └──── (*) WorkerExecutionLog

User (1) ───────────────┬──── (*) NetworkWeeklyBalance
                        └──── (*) UserCommissionPayout

UserCommissionPayout (1) ──── (*) CommissionPayoutHistory

📡 Proto Models

UserCommissionPayoutModel

message UserCommissionPayoutModel {
    int64 id = 1;
    int64 user_id = 2;
    string user_full_name = 3;
    int64 week_definition_id = 4;      // شناسه هفته
    int32 balances_earned = 5;          // تعداد تعادل
    int64 amount = 6;                   // مبلغ
    int32 status = 7;                   // وضعیت
    google.protobuf.Timestamp paid_at = 8;
    google.protobuf.Timestamp created = 9;
    string mobile = 10;
    string week_display_name = 11;      // نام نمایشی هفته
}

UserWeeklyBalanceModel

message UserWeeklyBalanceModel {
    int64 user_id = 1;
    int64 week_definition_id = 2;       // شناسه هفته
    int64 left_balance = 3;
    int64 right_balance = 4;
    string start_date_persian = 5;
    string end_date_persian = 6;
    int32 year = 7;
    int32 week_order = 8;
    bool is_active = 9;
    string week_display_name = 10;      // نام نمایشی هفته
}

🎯 نام‌گذاری فیلدها

قبل از مایگریشن (Legacy)

WeekNumber: "2025-01" (string)
GregorianWeekNumber: "2025-01" (string)
PersianWeekNumber: "1403-40" (string)
WeekLabel: "هفته 1 - 1403/10/01"

بعد از مایگریشن (Current)

WeekDefinitionId: 42 (long)           // FK به جدول WeekDefinition
WeekDisplayName: "هفته 1 - 1403/10/01" // ساخته شده از WeekDefinition

فرمول WeekDisplayName:

$"هفته {WeekDefinition.WeekOrder} - {WeekDefinition.StartDatePersian}"

📊 Query Examples

دریافت کمیسیون‌های کاربر

var payouts = await _context.UserCommissionPayouts
    .Include(p => p.WeekDefinition)
    .Where(p => p.UserId == userId)
    .OrderByDescending(p => p.WeekDefinition.WeekOrder)
    .Select(p => new {
        p.Id,
        p.WeekDefinitionId,
        WeekDisplayName = $"هفته {p.WeekDefinition.WeekOrder} - {p.WeekDefinition.StartDatePersian}",
        p.BalancesEarned,
        p.Amount,
        p.Status
    })
    .ToListAsync();

دریافت تعادل هفتگی

var balance = await _context.NetworkWeeklyBalances
    .Include(b => b.WeekDefinition)
    .Where(b => b.UserId == userId && b.WeekDefinitionId == weekDefinitionId)
    .Select(b => new {
        b.WeekDefinitionId,
        WeekDisplayName = $"هفته {b.WeekDefinition.WeekOrder} - {b.WeekDefinition.StartDatePersian}",
        b.LeftBalance,
        b.RightBalance,
        b.WeekDefinition.StartDatePersian,
        b.WeekDefinition.EndDatePersian
    })
    .FirstOrDefaultAsync();

⚠️ ملاحظات مایگریشن

EF Migration

# ایجاد migration
dotnet ef migrations add MigrateWeekNumberToWeekDefinitionId \
    -p CMSMicroservice.Infrastructure \
    -s CMSMicroservice.WebApi

# اجرای migration
dotnet ef database update \
    -p CMSMicroservice.Infrastructure \
    -s CMSMicroservice.WebApi

Data Migration Script

-- Step 1: Add new column
ALTER TABLE NetworkWeeklyBalances ADD WeekDefinitionId BIGINT NULL;

-- Step 2: Populate from WeekDefinitions
UPDATE nwb
SET nwb.WeekDefinitionId = wd.Id
FROM NetworkWeeklyBalances nwb
INNER JOIN WeekDefinitions wd ON 
    CONCAT(wd.Year, '-', RIGHT('0' + CAST(wd.WeekOrder AS VARCHAR), 2)) = nwb.WeekNumber;

-- Step 3: Add FK constraint
ALTER TABLE NetworkWeeklyBalances 
ADD CONSTRAINT FK_NetworkWeeklyBalances_WeekDefinitions 
FOREIGN KEY (WeekDefinitionId) REFERENCES WeekDefinitions(Id);

-- Step 4: Drop old column (after verification)
ALTER TABLE NetworkWeeklyBalances DROP COLUMN WeekNumber;

📝 تغییرات API

Request Changes

// قبل
GET /api/commission/payouts?weekNumber=2025-01

// بعد
GET /api/commission/payouts?weekDefinitionId=42

Response Changes

// قبل
{
  "weekNumber": "2025-01",
  "weekLabel": "هفته 1 - 1403/10/01"
}

// بعد
{
  "weekDefinitionId": 42,
  "weekDisplayName": "هفته 1 - 1403/10/01"
}