- 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.
20 KiB
محاسبات پلن باینری (Binary Plan Calculations)
مستندات فرمولهای محاسبه کمیسیون باینری
این سند فرمولهای محاسباتی سیستم کمیسیون باینری را که از فایل اکسل استخراج شده، توضیح میدهد.
متغیرها و تعاریف
ورودیهای هفته قبل (Last Week Remainders)
| نام فارسی | نماد | توضیحات |
|---|---|---|
| باقیمانده هفته قبل چپ | LL (Last Left) |
باقیماندهای که از هفته قبل در پای چپ باقی مانده |
| باقیمانده هفته قبل راست | LR (Last Right) |
باقیماندهای که از هفته قبل در پای راست باقی مانده |
مثال از اکسل:
LL = 200(میلیون ریال)LR = 0
ورودیهای هفته جدید (New Week Values)
| نام فارسی | نماد | توضیحات |
|---|---|---|
| هفته جدید چپ | NL (New Left) |
مجموع فروش/شارژ پای چپ در هفته جاری |
| هفته جدید راست | NR (New Right) |
مجموع فروش/شارژ پای راست در هفته جاری |
مثال از اکسل:
NL = 400(میلیون ریال)NR = 500(میلیون ریال)
پارامتر سیستم (System Parameter)
| نام فارسی | نماد | توضیحات |
|---|---|---|
| ماکسیمم تعادل | MX (Maximum Balance) |
حداکثر مقداری که در یک هفته میتواند به عنوان تعادل (کمیسیون) محاسبه شود |
مثال از اکسل:
MX = 300(میلیون ریال)
نکته مهم: این مقدار معمولاً بر اساس سطح کاربر یا پکیج خریداری شده تعیین میشود.
فرمولهای محاسباتی
1️⃣ محاسبه مجموع پا چپ (Sum Left Total)
SLT = LL + NL
توضیح:
SLT(Sum Left Total) = مجموع کل پای چپ- باقیمانده هفته قبل + فروش هفته جدید
مثال:
SLT = 200 + 400 = 600
2️⃣ محاسبه مجموع پا راست (Sum Right Total)
SRT = LR + NR
توضیح:
SRT(Sum Right Total) = مجموع کل پای راست- باقیمانده هفته قبل + فروش هفته جدید
مثال:
SRT = 0 + 500 = 500
3️⃣ محاسبه کمترین کل (Minimum Total)
MinT = MIN(SLT, SRT)
توضیح:
MinT= کوچکترین مقدار بین دو پا- این مقدار نشاندهنده حداکثر تعادل بالقوه است
مثال:
MinT = MIN(600, 500) = 500
4️⃣ محاسبه باقیمانده هفته بعد چپ (Remainder Next Week Left)
RNWL = SLT - MinT
توضیح:
RNWL(Remainder Next Week Left) = باقیماندهای که به هفته بعد منتقل میشود- مازاد پای چپ که برای تعادل استفاده نشد
مثال:
RNWL = 600 - 500 = 100
5️⃣ محاسبه باقیمانده هفته بعد راست (Remainder Next Week Right)
RNWR = SRT - MinT
توضیح:
RNWR(Remainder Next Week Right) = باقیماندهای که به هفته بعد منتقل میشود- مازاد پای راست که برای تعادل استفاده نشد
مثال:
RNWR = 500 - 500 = 0
نکته: یکی از دو باقیمانده همیشه صفر است (چون MinT کوچکترین است).
6️⃣ محاسبه فلش چپ (Flush Left)
FL = SLT - MX - RNWL
توضیح:
FL(Flush Left) = مقداری که از ماکسیمم هم بیشتر بود و باید دور ریخته شود- این مقدار نشاندهنده سرریز (overflow) است که نمیتواند به هفته بعد منتقل شود
مثال:
FL = 600 - 300 - 100 = 200
معنی: از 600 میلیون پای چپ:
- 300 به عنوان کمیسیون استفاده شد (تا حد MX)
- 100 به هفته بعد منتقل شد
- 200 فلش شد (از دست رفت) ❌
7️⃣ محاسبه فلش راست (Flush Right)
FR = SRT - MX - RNWR
توضیح:
FR(Flush Right) = مقداری که از پای راست دور ریخته میشود
مثال:
FR = 500 - 300 - 0 = 200
معنی: از 500 میلیون پای راست:
- 300 به عنوان کمیسیون استفاده شد
- 0 به هفته بعد منتقل شد
- 200 فلش شد (از دست رفت) ❌
8️⃣ محاسبه کل تعادل (Total Balance / Commission)
TB = IF(MinT > MX, MX, MinT)
یا به زبان سادهتر:
TB = MIN(MinT, MX)
توضیح:
TB(Total Balance) = مقدار واقعی کمیسیونی که به کاربر تعلق میگیرد- نمیتواند از ماکسیمم تعادل (
MX) بیشتر شود
مثال:
TB = MIN(500, 300) = 300
معنی: هرچند تعادل واقعی 500 بود، اما به دلیل محدودیت MX، فقط 300 به عنوان کمیسیون پرداخت میشود.
خلاصه جریان محاسبات
┌─────────────────────────────────────────────────────────────┐
│ ورودیها │
├─────────────────────────────────────────────────────────────┤
│ LL = 200 باقیمانده هفته قبل چپ │
│ LR = 0 باقیمانده هفته قبل راست │
│ NL = 400 هفته جدید چپ │
│ NR = 500 هفته جدید راست │
│ MX = 300 ماکسیمم تعادل │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ گام 1: محاسبه مجموع دو پا │
├─────────────────────────────────────────────────────────────┤
│ SLT = LL + NL = 200 + 400 = 600 │
│ SRT = LR + NR = 0 + 500 = 500 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ گام 2: محاسبه کمترین کل │
├─────────────────────────────────────────────────────────────┤
│ MinT = MIN(SLT, SRT) = MIN(600, 500) = 500 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ گام 3: محاسبه کمیسیون واقعی (با اعمال Cap) │
├─────────────────────────────────────────────────────────────┤
│ TB = MIN(MinT, MX) = MIN(500, 300) = 300 ✅ کمیسیون │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ گام 4: محاسبه باقیمانده هفته بعد │
├─────────────────────────────────────────────────────────────┤
│ RNWL = SLT - MinT = 600 - 500 = 100 → هفته بعد │
│ RNWR = SRT - MinT = 500 - 500 = 0 → هفته بعد │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ گام 5: محاسبه فلش (از دست رفته) │
├─────────────────────────────────────────────────────────────┤
│ FL = SLT - MX - RNWL = 600 - 300 - 100 = 200 ❌ فلش │
│ FR = SRT - MX - RNWR = 500 - 300 - 0 = 200 ❌ فلش │
└─────────────────────────────────────────────────────────────┘
تحلیل نتایج
📊 خروجیهای نهایی
| مقدار | توضیح | وضعیت |
|---|---|---|
| TB = 300 | کمیسیون پرداختی این هفته | ✅ پرداخت میشود |
| RNWL = 100 | باقیمانده پای چپ برای هفته بعد | ⏭️ منتقل میشود |
| RNWR = 0 | باقیمانده پای راست برای هفته بعد | ⏭️ منتقل میشود |
| FL = 200 | فلش پای چپ | ❌ از دست میرود |
| FR = 200 | فلش پای راست | ❌ از دست میرود |
🔍 تفسیر کسبوکار
کمیسیون محاسبه شده
کمیسیون = 300 میلیون ریال
- به دلیل محدودیت
MX = 300، از تعادل بالقوه 500، فقط 300 قابل برداشت است - این یک مکانیزم کنترل هزینه است
باقیمانده به هفته بعد
هفته بعد LL = 100 (از پای چپ)
هفته بعد LR = 0 (از پای راست)
- 100 میلیون از پای چپ به هفته بعد منتقل میشود
- این باقیمانده در محاسبات هفته آینده دوباره استفاده خواهد شد
فلش (Flush) - نکته مهم ⚠️
فلش کل = 400 میلیون ریال (200 چپ + 200 راست)
چرا فلش رخ میدهد؟
- مجموع دو پا = 1100 میلیون (600 + 500)
- کمیسیون محاسبه شده = 300 میلیون
- باقیمانده منتقل شده = 100 میلیون
- فلش = 1100 - 300 - 100 = 700 میلیون ❌
توضیح:
- فلش نشاندهنده مقداری است که به دلیل عدم تعادل و محدودیت Cap از دست میرود
- این یک ضرر برای کاربر است که میتواند با متعادل کردن دو پا کاهش یابد
پیادهسازی در C#
کلاس مدل
public class BinaryPlanCalculationInput
{
// ورودیهای هفته قبل
public decimal LastLeftRemainder { get; set; } // LL
public decimal LastRightRemainder { get; set; } // LR
// ورودیهای هفته جاری
public decimal NewLeftVolume { get; set; } // NL
public decimal NewRightVolume { get; set; } // NR
// تنظیمات سیستم
public decimal MaximumBalance { get; set; } // MX
}
public class BinaryPlanCalculationResult
{
// محاسبات واسط
public decimal SumLeftTotal { get; set; } // SLT
public decimal SumRightTotal { get; set; } // SRT
public decimal MinimumTotal { get; set; } // MinT
// باقیماندهها
public decimal RemainderNextWeekLeft { get; set; } // RNWL
public decimal RemainderNextWeekRight { get; set; } // RNWR
// فلش
public decimal FlushLeft { get; set; } // FL
public decimal FlushRight { get; set; } // FR
// نتیجه نهایی
public decimal TotalBalance { get; set; } // TB - کمیسیون واقعی
public decimal TotalFlush { get; set; } // مجموع فلش
}
متد محاسبه
public static BinaryPlanCalculationResult Calculate(BinaryPlanCalculationInput input)
{
var result = new BinaryPlanCalculationResult();
// گام 1: محاسبه مجموع دو پا
result.SumLeftTotal = input.LastLeftRemainder + input.NewLeftVolume;
result.SumRightTotal = input.LastRightRemainder + input.NewRightVolume;
// گام 2: محاسبه کمترین کل
result.MinimumTotal = Math.Min(result.SumLeftTotal, result.SumRightTotal);
// گام 3: محاسبه کمیسیون واقعی (با اعمال Cap)
result.TotalBalance = Math.Min(result.MinimumTotal, input.MaximumBalance);
// گام 4: محاسبه باقیمانده هفته بعد
result.RemainderNextWeekLeft = result.SumLeftTotal - result.MinimumTotal;
result.RemainderNextWeekRight = result.SumRightTotal - result.MinimumTotal;
// گام 5: محاسبه فلش
result.FlushLeft = result.SumLeftTotal - input.MaximumBalance - result.RemainderNextWeekLeft;
result.FlushRight = result.SumRightTotal - input.MaximumBalance - result.RemainderNextWeekRight;
// محاسبه مجموع فلش
result.TotalFlush = result.FlushLeft + result.FlushRight;
// اطمینان از عدم منفی شدن فلش
result.FlushLeft = Math.Max(0, result.FlushLeft);
result.FlushRight = Math.Max(0, result.FlushRight);
result.TotalFlush = Math.Max(0, result.TotalFlush);
return result;
}
مثال استفاده
var input = new BinaryPlanCalculationInput
{
LastLeftRemainder = 200_000_000, // 200 میلیون
LastRightRemainder = 0,
NewLeftVolume = 400_000_000, // 400 میلیون
NewRightVolume = 500_000_000, // 500 میلیون
MaximumBalance = 300_000_000 // 300 میلیون
};
var result = Calculate(input);
Console.WriteLine($"کمیسیون قابل پرداخت: {result.TotalBalance:N0} ریال");
// Output: کمیسیون قابل پرداخت: 300,000,000 ریال
Console.WriteLine($"باقیمانده چپ هفته بعد: {result.RemainderNextWeekLeft:N0} ریال");
// Output: باقیمانده چپ هفته بعد: 100,000,000 ریال
Console.WriteLine($"باقیمانده راست هفته بعد: {result.RemainderNextWeekRight:N0} ریال");
// Output: باقیمانده راست هفته بعد: 0 ریال
Console.WriteLine($"فلش کل: {result.TotalFlush:N0} ریال");
// Output: فلش کل: 400,000,000 ریال
نکات مهم برای پیادهسازی
1️⃣ ذخیره باقیماندهها
// باید در دیتابیس ذخیره شود
await SaveWeeklyRemainders(userId, weekId, new WeeklyRemainders
{
LeftRemainder = result.RemainderNextWeekLeft,
RightRemainder = result.RemainderNextWeekRight
});
2️⃣ لاگ فلش برای تحلیل
if (result.TotalFlush > 0)
{
await LogFlush(userId, weekId, new FlushLog
{
FlushLeft = result.FlushLeft,
FlushRight = result.FlushRight,
Reason = "Cap limitation and imbalance"
});
}
3️⃣ تعیین MaximumBalance
// بر اساس سطح کاربر
decimal GetMaximumBalance(User user)
{
return user.MembershipLevel switch
{
MembershipLevel.Bronze => 100_000_000,
MembershipLevel.Silver => 300_000_000,
MembershipLevel.Gold => 500_000_000,
MembershipLevel.Platinum => 1_000_000_000,
_ => 50_000_000
};
}
4️⃣ واحد پول
// همه مقادیر باید در واحد ریال ذخیره شوند
// برای نمایش میتوان به میلیون یا تومان تبدیل کرد
decimal DisplayInMillions(decimal rials) => rials / 1_000_000;
decimal DisplayInTomans(decimal rials) => rials / 10;
سناریوهای مختلف
سناریو 1: تعادل کامل
LL = 0, LR = 0, NL = 300, NR = 300, MX = 500
→ TB = 300, RNWL = 0, RNWR = 0, FL = 0, FR = 0
نتیجه: کمیسیون کامل بدون فلش ✅
سناریو 2: یک پا خیلی بیشتر
LL = 0, LR = 0, NL = 1000, NR = 100, MX = 500
→ TB = 100, RNWL = 900, RNWR = 0, FL = 400, FR = 0
نتیجه: کمیسیون کم + فلش زیاد ❌
سناریو 3: باقیمانده قبلی موثر
LL = 400, LR = 0, NL = 100, NR = 400, MX = 300
→ SLT = 500, SRT = 400
→ TB = 300, RNWL = 100, RNWR = 0, FL = 100, FR = 100
نتیجه: باقیمانده قبلی در محاسبه کمیسیون موثر است ✅
تفاوت با کد فعلی
در کد فعلی (CalculateWeeklyBalancesCommandHandler.cs):
// 1. ابتدا Cap اعمال میشود
var cappedLeft = Math.Min(leftLegTotal, maxBalance);
var cappedRight = Math.Min(rightLegTotal, maxBalance);
// 2. سپس تعادل محاسبه میشود
var balance = Math.Min(cappedLeft, cappedRight);
// 3. باقیماندهها محاسبه میشوند
var leftRemainder = leftLegTotal - balance;
var rightRemainder = rightLegTotal - balance;
در فرمول اکسل:
// 1. ابتدا تعادل کامل محاسبه میشود
var minTotal = Math.Min(leftLegTotal, rightLegTotal);
// 2. سپس Cap اعمال میشود
var balance = Math.Min(minTotal, maxBalance);
// 3. باقیماندهها بر اساس minTotal محاسبه میشوند
var leftRemainder = leftLegTotal - minTotal;
var rightRemainder = rightLegTotal - minTotal;
// 4. فلش محاسبه میشود
var flushLeft = leftLegTotal - maxBalance - leftRemainder;
var flushRight = rightLegTotal - maxBalance - rightRemainder;
تفاوت کلیدی:
- کد فعلی Cap را ابتدا اعمال میکند (میتواند باقیماندههای بیشتری ایجاد کند)
- فرمول اکسل ابتدا تعادل را محاسبه میکند، سپس Cap اعمال میشود (فلش دقیقتر محاسبه میشود)
نتیجهگیری
این فرمولها نشان میدهند که:
- ✅ تعادل اهمیت دارد - هرچه دو پا متعادلتر باشند، فلش کمتر است
- ✅ Cap محدودیت ایجاد میکند - حتی با تعادل کامل، بیش از MX کمیسیون داده نمیشود
- ✅ باقیماندهها منتقل میشوند - برای هفته بعد ذخیره میشوند
- ❌ فلش ضرر است - مقداری که به دلیل عدم تعادل یا Cap از دست میرود
توصیه: برای افزایش کمیسیون، کاربران باید:
- دو پای خود را متعادل نگه دارند
- سطح عضویت خود را ارتقا دهند (برای افزایش MX)
- از باقیماندهها در هفتههای بعد استفاده کنند