12 KiB
12 KiB
✅ اصلاح محاسبه کمیسیون هفتگی - تحلیل و پیادهسازی
تاریخ شروع: ۱۴ آذر ۱۴۰۴ (2025-12-04)
تاریخ تکمیل: ۱۴ آذر ۱۴۰۴
وضعیت: ✅ تکمیل شد
اولویت: 🔴 بحرانی - تأثیر مستقیم بر بیزینس
📊 خلاصه مشکلات
مشکل ۱: محدودیت لول (Max Network Level) پیادهسازی نشده
- مشکل: شمارش اعضا بدون محدودیت عمق انجام میشود
- انتظار: فقط تا ۱۵ لول پایینتر باید شمارش شود
- راهحل: اضافه کردن پارامتر
maxLevelبه متد بازگشتی و خواندن از Config
مشکل ۲: تعادل شخص vs تعادل شبکه (بحرانی)
- مشکل: کمیسیون بر اساس تعادل شخصی محاسبه میشود (نه مجموع زیرمجموعه)
- انتظار: کمیسیون = (تعادل شخص + تعادل زیرمجموعه تا ۱۵ لول) × ارزش هر تعادل
- راهحل: محاسبه تعادلهای زیرمجموعه در ProcessUserPayouts
🎯 قانون صحیح کمیسیون (بیزینس)
فرمول محاسبه کمیسیون هفتگی:
1️⃣ محاسبه تعادل هر شخص:
- تعادل_شخص = MIN(چپ، راست)
- سقف هر دست = 300
- حداکثر تعادل شخصی = 300
2️⃣ محاسبه کل تعادلهای شبکه:
- کل_تعادل_شبکه = SUM(تعادل_شخصی همه اعضا)
3️⃣ محاسبه صندوق:
- صندوق_هفتگی = SUM(سهم_استخر همه اعضا)
- سهم_استخر هر عضو = تعداد_زیرمجموعه_جدید × هزینه_فعالسازی × ۲۰%
4️⃣ ارزش هر تعادل:
- ارزش_هر_تعادل = صندوق_هفتگی ÷ کل_تعادل_شبکه
5️⃣ کمیسیون هر شخص:
- مجموع_تعادل = تعادل_شخص + SUM(تعادل_زیرمجموعه تا 15 لول)
- کمیسیون = مجموع_تعادل × ارزش_هر_تعادل
مثال عملی:
شبکه:
User A
├─ Left: User B (تعادل: 5)
│ ├─ Left: User D (تعادل: 2)
│ └─ Right: User E (تعادل: 1)
└─ Right: User C (تعادل: 3)
└─ Left: User F (تعادل: 1)
فرض: تعادل شخصی User A = 10
محاسبه مجموع تعادل User A (تا 15 لول):
= 10 + 5 + 2 + 1 + 3 + 1 = 22 تعادل
اگر ارزش هر تعادل = 1,000,000 ریال:
کمیسیون User A = 22 × 1,000,000 = 22,000,000 ریال
🔍 تحلیل کد فعلی
فایلهای تأثیرپذیر:
| # | فایل | وضعیت فعلی | نیاز به تغییر |
|---|---|---|---|
| 1 | ApplicationDbContextInitialiser.cs |
ندارد MaxNetworkLevel |
✅ اضافه Config |
| 2 | CalculateWeeklyBalancesCommandHandler.cs |
بدون محدودیت لول | ✅ اضافه maxLevel |
| 3 | ProcessUserPayoutsCommandHandler.cs |
فقط تعادل شخص | ✅ جمع زیرمجموعه |
| 4 | NetworkWeeklyBalance.cs |
Entity | ⚪ نیاز ندارد |
| 5 | UserCommissionPayout.cs |
Entity | 🟡 شاید فیلد جدید |
کد فعلی ProcessUserPayoutsCommandHandler:
// ❌ مشکل: فقط تعادل شخصی
foreach (var balance in weeklyBalances)
{
var totalAmount = (long)(balance.TotalBalances * pool.ValuePerBalance);
// ...
}
کد صحیح باید باشد:
// ✅ صحیح: تعادل شخصی + زیرمجموعه تا 15 لول
foreach (var balance in weeklyBalances)
{
// محاسبه مجموع تعادلهای زیرمجموعه
var subordinateBalances = await CalculateSubordinateBalances(
balance.UserId,
request.WeekNumber,
maxNetworkLevel, // از Config
cancellationToken
);
var totalBalancesWithSubordinates = balance.TotalBalances + subordinateBalances;
var totalAmount = (long)(totalBalancesWithSubordinates * pool.ValuePerBalance);
// ...
}
📋 تسکهای اجرایی
فاز ۱: Configuration (نیم روز)
تسک ۱.۱: اضافه کردن MaxNetworkLevel به Seed Data
// ApplicationDbContextInitialiser.cs
new SystemConfiguration
{
Key = "Commission.MaxNetworkLevel",
Value = "15",
Description = "حداکثر عمق شبکه برای محاسبه کمیسیون (تعداد لول)",
Scope = ConfigurationScope.Commission,
IsActive = true
}
تسک ۱.۲: Migration (در صورت نیاز)
- اگر دیتابیس موجود دارید، یک SQL Script یا Migration
فاز ۲: اصلاح CalculateWeeklyBalances (نیم روز)
تسک ۲.۱: خواندن MaxNetworkLevel از Config
// در Handle method
var maxNetworkLevel = int.Parse(configs.GetValueOrDefault("Commission.MaxNetworkLevel", "15"));
تسک ۲.۲: اضافه کردن محدودیت لول به متد بازگشتی
private async Task<int> CountNewMembersRecursive(
long userId,
NetworkLeg leg,
DateTime startDate,
DateTime endDate,
int currentLevel, // ← جدید
int maxLevel, // ← جدید
CancellationToken cancellationToken)
{
// ⬅️ محدودیت عمق
if (currentLevel >= maxLevel)
return 0;
var child = await _context.Users
.FirstOrDefaultAsync(x => x.NetworkParentId == userId && x.LegPosition == leg, cancellationToken);
if (child == null)
return 0;
// ... محاسبه count ...
// ⬅️ افزایش سطح
var childLeft = await CountNewMembersRecursive(child.Id, NetworkLeg.Left, startDate, endDate, currentLevel + 1, maxLevel, cancellationToken);
var childRight = await CountNewMembersRecursive(child.Id, NetworkLeg.Right, startDate, endDate, currentLevel + 1, maxLevel, cancellationToken);
return count + childLeft + childRight;
}
فاز ۳: اصلاح ProcessUserPayouts (۱ روز)
تسک ۳.۱: اضافه کردن متد محاسبه تعادل زیرمجموعه
/// <summary>
/// محاسبه مجموع تعادلهای زیرمجموعه یک کاربر تا N لول
/// </summary>
private async Task<int> CalculateSubordinateBalancesAsync(
long userId,
string weekNumber,
int maxLevel,
CancellationToken cancellationToken)
{
var totalSubordinateBalances = 0;
// پیدا کردن همه زیرمجموعهها تا maxLevel
var subordinates = await GetSubordinatesRecursive(userId, 1, maxLevel, cancellationToken);
// جمع تعادلهای آنها
foreach (var subordinateId in subordinates)
{
var balance = await _context.NetworkWeeklyBalances
.Where(x => x.UserId == subordinateId && x.WeekNumber == weekNumber)
.Select(x => x.TotalBalances)
.FirstOrDefaultAsync(cancellationToken);
totalSubordinateBalances += balance;
}
return totalSubordinateBalances;
}
/// <summary>
/// پیدا کردن بازگشتی زیرمجموعهها
/// </summary>
private async Task<List<long>> GetSubordinatesRecursive(
long userId,
int currentLevel,
int maxLevel,
CancellationToken cancellationToken)
{
if (currentLevel > maxLevel)
return new List<long>();
var result = new List<long>();
// پیدا کردن فرزندان مستقیم
var children = await _context.Users
.Where(x => x.NetworkParentId == userId)
.Select(x => x.Id)
.ToListAsync(cancellationToken);
result.AddRange(children);
// بازگشت برای هر فرزند
foreach (var childId in children)
{
var grandChildren = await GetSubordinatesRecursive(childId, currentLevel + 1, maxLevel, cancellationToken);
result.AddRange(grandChildren);
}
return result;
}
تسک ۳.۲: اصلاح Handle method
public async Task<int> Handle(ProcessUserPayoutsCommand request, CancellationToken cancellationToken)
{
// ... کدهای موجود ...
// خواندن MaxNetworkLevel از Config
var maxNetworkLevel = await _context.SystemConfigurations
.Where(x => x.Key == "Commission.MaxNetworkLevel" && x.IsActive)
.Select(x => x.Value)
.FirstOrDefaultAsync(cancellationToken);
var maxLevel = int.Parse(maxNetworkLevel ?? "15");
foreach (var balance in weeklyBalances)
{
// ✅ محاسبه تعادل شخص + زیرمجموعه
var subordinateBalances = await CalculateSubordinateBalancesAsync(
balance.UserId,
request.WeekNumber,
maxLevel,
cancellationToken
);
var totalBalancesWithSubordinates = balance.TotalBalances + subordinateBalances;
var totalAmount = (long)(totalBalancesWithSubordinates * pool.ValuePerBalance);
var payout = new UserCommissionPayout
{
UserId = balance.UserId,
WeekNumber = request.WeekNumber,
WeeklyPoolId = pool.Id,
BalancesEarned = totalBalancesWithSubordinates, // ← شامل زیرمجموعه
ValuePerBalance = pool.ValuePerBalance,
TotalAmount = totalAmount,
// ...
};
// ...
}
}
تسک ۳.۳ (اختیاری): اضافه کردن فیلد به Entity
// UserCommissionPayout.cs
/// <summary>
/// تعادل شخصی (بدون زیرمجموعه)
/// </summary>
public int PersonalBalances { get; set; }
/// <summary>
/// تعادل زیرمجموعهها
/// </summary>
public int SubordinateBalances { get; set; }
/// <summary>
/// مجموع (PersonalBalances + SubordinateBalances)
/// </summary>
public int BalancesEarned { get; set; } // ← قبلاً هم بود
فاز ۴: تست و Build (نیم روز)
تسک ۴.۱: Build و رفع خطاها
cd CMS/src && dotnet build
تسک ۴.۲: تست با سناریوهای مختلف
- کاربر بدون زیرمجموعه
- کاربر با ۵ لول زیرمجموعه
- کاربر با ۲۰ لول (باید ۱۵ تا بشمارد)
- کاربر با سقف ۳۰۰ در هر دست
⏱️ زمانبندی
| فاز | تسک | زمان | مجموع |
|---|---|---|---|
| ۱ | Config + Seed | 0.5 روز | 0.5 روز |
| ۲ | اصلاح CalculateWeeklyBalances | 0.5 روز | 1 روز |
| ۳ | اصلاح ProcessUserPayouts | 1 روز | 2 روز |
| ۴ | تست و Build | 0.5 روز | 2.5 روز |
مجموع: ۲.۵ روز کاری
⚠️ نکات مهم
- تغییرات Breaking نیست: ساختار Entity تغییر نمیکند (فقط مقادیر)
- Backward Compatible: فیلد
BalancesEarnedقبلاً هم بود - Idempotent: با
ForceRecalculateمیتوان دوباره حساب کرد - Performance: متد بازگشتی ممکن است کند باشد - بهینهسازی در فاز بعد
- Migration: فقط اگر فیلد جدید به Entity اضافه شود
✅ وضعیت پیادهسازی - تکمیل شده
تاریخ تکمیل: ۱۴ آذر ۱۴۰۴
| فاز | شرح | وضعیت |
|---|---|---|
| 1 | Config: Commission.MaxNetworkLevel = 15 |
✅ تکمیل |
| 2 | CalculateWeeklyBalances: محدودیت ۱۵ لول | ✅ تکمیل |
| 3 | ProcessUserPayouts: جمع تعادل زیرمجموعه | ✅ تکمیل |
| 4 | Build Test: 0 Errors | ✅ تکمیل |
تغییرات انجام شده:
Seed Data:
- ✅
Commission.MaxNetworkLevel = 15
CalculateWeeklyBalancesCommandHandler:
- ✅ خواندن
maxNetworkLevelاز Config - ✅ پارامتر
maxLevelبهCountNewMembersInLeg - ✅ پارامتر
currentLevelوmaxLevelبهCountNewMembersRecursive - ✅ شرط توقف در عمق ۱۵
ProcessUserPayoutsCommandHandler:
- ✅ متد جدید
SumSubordinateBalancesAsync - ✅ متد کمکی
GetChildUserIdAsync - ✅ محاسبه
subordinateBalancesبرای هر کاربر - ✅ کمیسیون = (شخص + زیرمجموعه) × ارزش هر تعادل
🎉 نتیجه نهایی
✅ Build Succeeded - 0 Errors
✅ همه فازها تکمیل شدند
✅ منطق کمیسیون اصلاح شد