Added 5 Commands and 4 Queries for commission calculation and payout system: Commands: - CalculateWeeklyBalances: Recursive binary tree traversal for leg balances - CalculateWeeklyCommissionPool: Calculate ValuePerBalance from total pool - ProcessUserPayouts: Distribute commission to users, create payout records - RequestWithdrawal: User requests cash/diamond withdrawal - ProcessWithdrawal: Admin approves/rejects withdrawal Queries: - GetWeeklyCommissionPool: Retrieve pool details - GetUserCommissionPayouts: List payouts with filters (status, week, user) - GetCommissionPayoutHistory: Complete audit trail - GetUserWeeklyBalances: Show leg balances and contributions Total: 35 files, ~1,100 lines of code Binary tree algorithm, state machine, withdrawal system implemented
96 lines
4.0 KiB
C#
96 lines
4.0 KiB
C#
namespace CMSMicroservice.Application.CommissionCQ.Commands.CalculateWeeklyBalances;
|
|
|
|
public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWeeklyBalancesCommand, int>
|
|
{
|
|
private readonly IApplicationDbContext _context;
|
|
|
|
public CalculateWeeklyBalancesCommandHandler(IApplicationDbContext context)
|
|
{
|
|
_context = context;
|
|
}
|
|
|
|
public async Task<int> Handle(CalculateWeeklyBalancesCommand request, CancellationToken cancellationToken)
|
|
{
|
|
// بررسی وجود محاسبه قبلی
|
|
var existingBalances = await _context.NetworkWeeklyBalances
|
|
.Where(x => x.WeekNumber == request.WeekNumber)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
if (existingBalances.Any() && !request.ForceRecalculate)
|
|
{
|
|
throw new InvalidOperationException($"تعادلهای هفته {request.WeekNumber} قبلاً محاسبه شده است. برای محاسبه مجدد از ForceRecalculate استفاده کنید");
|
|
}
|
|
|
|
// حذف محاسبات قبلی در صورت ForceRecalculate
|
|
if (existingBalances.Any())
|
|
{
|
|
_context.NetworkWeeklyBalances.RemoveRange(existingBalances);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
}
|
|
|
|
// دریافت کاربران فعال در شبکه
|
|
var usersInNetwork = await _context.Users
|
|
.Where(x => x.NetworkParentId.HasValue)
|
|
.Select(x => new { x.Id })
|
|
.ToListAsync(cancellationToken);
|
|
|
|
var balancesList = new List<NetworkWeeklyBalance>();
|
|
var calculatedAt = DateTime.UtcNow;
|
|
|
|
foreach (var user in usersInNetwork)
|
|
{
|
|
// محاسبه تعادل پای چپ (Left Leg)
|
|
var leftLegBalances = (int)await CalculateLegBalances(user.Id, NetworkLeg.Left, cancellationToken);
|
|
|
|
// محاسبه تعادل پای راست (Right Leg)
|
|
var rightLegBalances = (int)await CalculateLegBalances(user.Id, NetworkLeg.Right, cancellationToken);
|
|
|
|
// محاسبه Total Balances (کمترین مقدار دو پا)
|
|
var totalBalances = Math.Min(leftLegBalances, rightLegBalances);
|
|
|
|
// محاسبه سهم استخر (10% از Total Balances)
|
|
var weeklyPoolContribution = (long)(totalBalances * 0.10m);
|
|
|
|
var balance = new NetworkWeeklyBalance
|
|
{
|
|
UserId = user.Id,
|
|
WeekNumber = request.WeekNumber,
|
|
LeftLegBalances = leftLegBalances,
|
|
RightLegBalances = rightLegBalances,
|
|
TotalBalances = totalBalances,
|
|
WeeklyPoolContribution = weeklyPoolContribution,
|
|
CalculatedAt = calculatedAt,
|
|
IsExpired = false
|
|
};
|
|
|
|
balancesList.Add(balance);
|
|
}
|
|
|
|
await _context.NetworkWeeklyBalances.AddRangeAsync(balancesList, cancellationToken);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
return balancesList.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// محاسبه تعادل یک پا (Left یا Right) به صورت بازگشتی
|
|
/// </summary>
|
|
private async Task<long> CalculateLegBalances(long userId, NetworkLeg leg, CancellationToken cancellationToken)
|
|
{
|
|
// پیدا کردن فرزند در پای مورد نظر
|
|
var child = await _context.Users
|
|
.FirstOrDefaultAsync(x => x.NetworkParentId == userId && x.LegPosition == leg, cancellationToken);
|
|
|
|
if (child == null)
|
|
{
|
|
return 0; // اگر فرزندی نداشته باشد، تعادل صفر است
|
|
}
|
|
|
|
// محاسبه بازگشتی: مجموع تعادل فرزند چپ + راست + 1 (خود فرزند)
|
|
var childLeftLeg = await CalculateLegBalances(child.Id, NetworkLeg.Left, cancellationToken);
|
|
var childRightLeg = await CalculateLegBalances(child.Id, NetworkLeg.Right, cancellationToken);
|
|
|
|
return 1 + childLeftLeg + childRightLeg;
|
|
}
|
|
}
|