318 lines
9.1 KiB
Markdown
318 lines
9.1 KiB
Markdown
# اصلاحات سیستم کمیسیون هفتگی
|
||
|
||
## 📋 خلاصه تغییرات
|
||
|
||
سیستم کمیسیون هفتگی از **3 مرحله به 2 مرحله** سادهسازی شد:
|
||
|
||
### ❌ قبل (3 مرحله):
|
||
1. `CalculateWeeklyBalances` - محاسبه تعادلها
|
||
2. `CalculateWeeklyCommissionPool` - محاسبه استخر
|
||
3. `ProcessUserPayouts` - پردازش پرداختها (تکراری!)
|
||
|
||
### ✅ بعد (2 مرحله):
|
||
1. `CalculateWeeklyBalances` - محاسبه تعادلها تا 15 لول
|
||
2. `CalculateWeeklyCommissionPool` - محاسبه استخر + پردازش پرداختها
|
||
|
||
---
|
||
|
||
## 🔧 تغییرات جزئی
|
||
|
||
### 1️⃣ اضافه شدن فیلدها به `NetworkWeeklyBalance`
|
||
|
||
**فیلدهای جدید:**
|
||
```csharp
|
||
/// <summary>
|
||
/// مقدار فلش هر طرف (بعد از اعمال Cap)
|
||
/// </summary>
|
||
public int FlushedPerSide { get; set; }
|
||
|
||
/// <summary>
|
||
/// مجموع فلش از دو طرف (از دست رفته)
|
||
/// </summary>
|
||
public int TotalFlushed { get; set; }
|
||
```
|
||
|
||
**Migration:** `AddFlushedFieldsToNetworkWeeklyBalance`
|
||
|
||
---
|
||
|
||
### 2️⃣ اصلاح `CalculateWeeklyBalances`
|
||
|
||
**تغییرات:**
|
||
- ✅ فیلدهای `FlushedPerSide` و `TotalFlushed` ذخیره میشوند
|
||
- ✅ `WeeklyPoolContribution = 0` (دیگر در این مرحله محاسبه نمیشه)
|
||
- ✅ محدودیت 15 لول قبلاً موجود بود و درست کار میکند
|
||
|
||
**کد:**
|
||
```csharp
|
||
// محاسبه فلش
|
||
var flushedPerSide = totalBalances - cappedBalances;
|
||
var totalFlushed = flushedPerSide * 2;
|
||
|
||
// ذخیره
|
||
balance.FlushedPerSide = flushedPerSide;
|
||
balance.TotalFlushed = totalFlushed;
|
||
balance.WeeklyPoolContribution = 0; // Pool در مرحله بعد محاسبه میشه
|
||
```
|
||
|
||
---
|
||
|
||
### 3️⃣ اصلاح کامل `CalculateWeeklyCommissionPool`
|
||
|
||
**منطق جدید Pool:**
|
||
```csharp
|
||
// 1. Pool از فعالسازیهای باشگاه این هفته میاد (نه از تعادلها)
|
||
var newClubMembersCount = await _context.ClubMemberships
|
||
.Where(c => c.ActivatedAt >= startDate && c.ActivatedAt <= endDate)
|
||
.CountAsync();
|
||
|
||
var totalPoolAmount = newClubMembersCount * activationFee;
|
||
|
||
// 2. ارزش هر امتیاز
|
||
var totalBalancesInNetwork = weeklyBalances.Sum(x => x.TotalBalances);
|
||
var valuePerBalance = totalPoolAmount / totalBalancesInNetwork;
|
||
```
|
||
|
||
**افزوده شدن محاسبه تعادل زیرمجموعه:**
|
||
```csharp
|
||
// برای هر کاربر:
|
||
// 1. تعادل خودش
|
||
var directBalances = balance.TotalBalances;
|
||
|
||
// 2. تعادل زیرمجموعه (تا 15 لول)
|
||
var subordinateBalances = await CalculateSubordinateBalancesAsync(
|
||
balance.UserId,
|
||
request.WeekNumber,
|
||
maxLevels: 15
|
||
);
|
||
|
||
var totalBalancesForUser = directBalances + subordinateBalances;
|
||
```
|
||
|
||
**ایجاد UserCommissionPayout:**
|
||
```csharp
|
||
var payout = new UserCommissionPayout
|
||
{
|
||
UserId = balance.UserId,
|
||
WeekNumber = request.WeekNumber,
|
||
WeeklyPoolId = existingPool.Id,
|
||
BalancesEarned = totalBalancesForUser,
|
||
ValuePerBalance = valuePerBalance,
|
||
TotalAmount = totalBalancesForUser * valuePerBalance,
|
||
Status = CommissionPayoutStatus.Pending,
|
||
// ... subordinate fields
|
||
};
|
||
```
|
||
|
||
**ثبت تاریخچه:**
|
||
```csharp
|
||
var history = new CommissionPayoutHistory
|
||
{
|
||
UserId = payout.UserId,
|
||
PayoutId = payout.Id,
|
||
Amount = payout.TotalAmount,
|
||
Status = CommissionPayoutStatus.Pending,
|
||
ChangeReason = "محاسبه اولیه کمیسیون هفتگی"
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 4️⃣ سادهسازی `TriggerWeeklyCalculation`
|
||
|
||
**قبل:**
|
||
```csharp
|
||
// Step 1
|
||
await _mediator.Send(new CalculateWeeklyBalancesCommand { ... });
|
||
|
||
// Step 2
|
||
await _mediator.Send(new CalculateWeeklyCommissionPoolCommand { ... });
|
||
|
||
// Step 3
|
||
await _mediator.Send(new ProcessUserPayoutsCommand { ... });
|
||
```
|
||
|
||
**بعد:**
|
||
```csharp
|
||
// Step 1: محاسبه تعادلها
|
||
if (!request.SkipBalances)
|
||
{
|
||
await _mediator.Send(new CalculateWeeklyBalancesCommand { ... });
|
||
}
|
||
|
||
// Step 2: محاسبه Pool و پرداختها
|
||
if (!request.SkipPayouts)
|
||
{
|
||
await _mediator.Send(new CalculateWeeklyCommissionPoolCommand { ... });
|
||
}
|
||
```
|
||
|
||
**حذف شد:**
|
||
- ❌ `SkipPool` flag
|
||
- ❌ Step 3 کاملاً حذف شد
|
||
|
||
---
|
||
|
||
## 🎯 فرآیند نهایی
|
||
|
||
### مرحله 1: محاسبه تعادلها
|
||
```
|
||
1. برای هر کاربر در شبکه
|
||
2. تا 15 لول پایینتر شمارش کن
|
||
3. محاسبه تعادل (MIN of left/right)
|
||
4. محاسبه باقیمانده
|
||
5. محاسبه فلش
|
||
6. ذخیره در NetworkWeeklyBalance
|
||
```
|
||
|
||
### مرحله 2: محاسبه Pool و توزیع
|
||
```
|
||
1. شمارش فعالسازیهای باشگاه این هفته
|
||
2. Pool = تعداد × ActivationFee
|
||
3. ارزش هر امتیاز = Pool ÷ مجموع تعادلها
|
||
4. برای هر کاربر:
|
||
a. تعادل خودش + تعادل زیرمجموعه (تا 15 لول)
|
||
b. سهم = تعادل × ارزش
|
||
c. ثبت در UserCommissionPayout
|
||
d. ثبت تاریخچه
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 جداول درگیر
|
||
|
||
### `NetworkWeeklyBalance` (فیلدهای جدید)
|
||
```sql
|
||
ALTER TABLE [Network].[NetworkWeeklyBalances]
|
||
ADD [FlushedPerSide] INT NOT NULL DEFAULT 0,
|
||
[TotalFlushed] INT NOT NULL DEFAULT 0;
|
||
```
|
||
|
||
### `WeeklyCommissionPool`
|
||
```
|
||
- TotalPoolAmount: از فعالسازیهای باشگاه
|
||
- TotalBalances: مجموع تعادلهای شبکه
|
||
- ValuePerBalance: Pool ÷ TotalBalances
|
||
```
|
||
|
||
### `UserCommissionPayout`
|
||
```
|
||
- BalancesEarned: تعادل خودش + زیرمجموعه
|
||
- DirectBalances: فقط تعادل خودش
|
||
- SubordinateBalances: فقط زیرمجموعه
|
||
- TotalAmount: BalancesEarned × ValuePerBalance
|
||
- Status: Pending
|
||
```
|
||
|
||
### `CommissionPayoutHistory`
|
||
```
|
||
- PayoutId: شناسه UserCommissionPayout
|
||
- Status: Pending (در این مرحله)
|
||
- ChangeReason: "محاسبه اولیه کمیسیون هفتگی"
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ مزایا
|
||
|
||
1. **سادهتر**: 2 مرحله به جای 3
|
||
2. **بدون تکرار**: دیگر UserCommissionPayout دوبار ساخته نمیشه
|
||
3. **واضحتر**: Pool از کجا میاد مشخصه
|
||
4. **قابل نگهداری**: منطق مشابه یکجا هست
|
||
5. **کامل**: تاریخچه + subordinate balances همه جا هست
|
||
|
||
---
|
||
|
||
## 🔄 مراحل بعدی (اختیاری)
|
||
|
||
### مرحله 3: پرداخت واقعی (جدا از محاسبه)
|
||
|
||
میتوان یک Command جدید داشت که:
|
||
1. `UserCommissionPayout` با status=Pending رو بخونه
|
||
2. به کیف پول واریز کنه
|
||
3. Status رو به Paid تغییر بده
|
||
4. تاریخچه اضافه کنه
|
||
|
||
این مرحله **جدا از محاسبات** است و میتواند:
|
||
- دستی توسط ادمین اجرا شود
|
||
- یا به صورت خودکار بعد از تایید
|
||
|
||
---
|
||
|
||
## 📝 نکات مهم
|
||
|
||
### Pool چطور پُر میشه؟
|
||
```
|
||
1. کاربر عضو Club میشه
|
||
2. در ActivateClubMembership مبلغی کسر میشه
|
||
3. این مبلغ به Pool اضافه **نمیشه** (فقط شمارش میشه)
|
||
4. در محاسبه Pool: تعداد × ActivationFee
|
||
```
|
||
|
||
### چرا subordinate balances؟
|
||
```
|
||
در سیستم باینری، کاربر از تعادل زیرمجموعههای خود
|
||
(تا 15 لول پایینتر) هم کمیسیون میگیرد.
|
||
```
|
||
|
||
### چرا 15 لول؟
|
||
```
|
||
محدودیت عمق برای جلوگیری از بارگذاری بیش از حد
|
||
و تشویق به ایجاد شبکه متعادل
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 تست
|
||
|
||
### تست مرحله 1
|
||
```csharp
|
||
// 1. ایجاد کاربران در شبکه
|
||
// 2. فعالسازی Club برای برخی
|
||
// 3. اجرای CalculateWeeklyBalances
|
||
// 4. بررسی NetworkWeeklyBalance
|
||
// - TotalBalances
|
||
// - FlushedPerSide
|
||
// - TotalFlushed
|
||
```
|
||
|
||
### تست مرحله 2
|
||
```csharp
|
||
// 1. اجرای مرحله 1
|
||
// 2. اجرای CalculateWeeklyCommissionPool
|
||
// 3. بررسی WeeklyCommissionPool
|
||
// - TotalPoolAmount = تعداد فعالسازیها × ActivationFee
|
||
// - ValuePerBalance صحیح باشد
|
||
// 4. بررسی UserCommissionPayout
|
||
// - برای هر کاربر ایجاد شده
|
||
// - BalancesEarned شامل subordinate هم هست
|
||
// - TotalAmount = BalancesEarned × ValuePerBalance
|
||
// 5. بررسی CommissionPayoutHistory
|
||
// - برای هر پرداخت ثبت شده
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 فایلهای تغییر یافته
|
||
|
||
1. ✅ `NetworkWeeklyBalance.cs` - اضافه شدن فیلدها
|
||
2. ✅ `CalculateWeeklyBalancesCommandHandler.cs` - ذخیره فلش
|
||
3. ✅ `CalculateWeeklyCommissionPoolCommandHandler.cs` - منطق کامل جدید
|
||
4. ✅ `TriggerWeeklyCalculationCommandHandler.cs` - حذف مرحله 3
|
||
5. ✅ `TriggerWeeklyCalculationCommand.cs` - حذف SkipPool flag
|
||
6. ✅ Migration: `AddFlushedFieldsToNetworkWeeklyBalance`
|
||
|
||
---
|
||
|
||
## 🎉 نتیجه
|
||
|
||
سیستم کمیسیون هفتگی حالا:
|
||
- ✅ **سادهتر** و قابل فهمتر
|
||
- ✅ **بدون تکرار** در کد
|
||
- ✅ **Pool از منبع صحیح** (فعالسازیهای Club)
|
||
- ✅ **تعادل زیرمجموعه** محاسبه میشه
|
||
- ✅ **تاریخچه کامل** ثبت میشه
|
||
- ✅ **فلش دقیق** ذخیره میشه
|
||
|
||
آماده برای استفاده در Production! 🚀
|