002e99f6bf
- Added PersianDateTimeService for converting Gregorian dates to Persian format in the BackOffice frontend. - Updated multiple frontend pages (Dashboard, UserPayouts, WorkerControl, UserNetworkInfo) to utilize the new Persian date service. - Enhanced GetUserNetworkPositionDto with 28+ new fields for comprehensive user network data. - Updated GetUserNetworkPositionQueryHandler to include new methods for calculating network statistics. - Modified Protobuf messages to accommodate the new fields, increasing from 14 to 42. - Refined week number calculation algorithm to ensure consistency across C# and SQL implementations. - Created new CSV and Excel files for binary plan calculations. - Ensured all changes are tested and validated for accuracy and performance.
9.3 KiB
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"
}