Files
CMS/src/CMSMicroservice.Application/CommissionCQ/Commands/ProcessUserPayouts/ProcessUserPayoutsCommandHandler.cs
T
masoodafar-web 02e2f8111f
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m38s
feat: implement Kavenegar SMS service and refactor system configurations to use static constants
2025-12-26 09:04:27 +03:30

247 lines
9.2 KiB
C#

using CMSMicroservice.Domain.Common;
namespace CMSMicroservice.Application.CommissionCQ.Commands.ProcessUserPayouts;
public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayoutsCommand, int>
{
private readonly IApplicationDbContext _context;
private readonly IWeekDefinitionRepository _weekRepository;
public ProcessUserPayoutsCommandHandler(
IApplicationDbContext context,
IWeekDefinitionRepository weekRepository)
{
_context = context;
_weekRepository = weekRepository;
}
public async Task<int> Handle(ProcessUserPayoutsCommand request, CancellationToken cancellationToken)
{
var regorianWeekNumber = _weekRepository.GetGregorianWeekNumber(request.WeekDefinitionId);
if (regorianWeekNumber == null)
{
throw new InvalidOperationException($"هفته {request.WeekDefinitionId} در سیستم تعریف نشده است");
}
// بررسی وجود استخر
var pool = await _context.WeeklyCommissionPools
.FirstOrDefaultAsync(x => x.WeekDefinitionId == request.WeekDefinitionId, cancellationToken);
if (pool == null || !pool.IsCalculated)
{
throw new InvalidOperationException($"استخر کمیسیون هفته {request.WeekDefinitionId} هنوز محاسبه نشده است");
}
// بررسی پرداخت قبلی
var existingPayouts = await _context.UserCommissionPayouts
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
.ToListAsync(cancellationToken);
if (existingPayouts.Any() && !request.ForceReprocess)
{
throw new InvalidOperationException($"پرداخت‌های هفته {request.WeekDefinitionId} قبلاً انجام شده است");
}
// حذف پرداخت‌های قبلی در صورت ForceReprocess
if (existingPayouts.Any())
{
_context.UserCommissionPayouts.RemoveRange(existingPayouts);
await _context.SaveChangesAsync(cancellationToken);
}
// ⭐ خواندن MaxNetworkLevel از SystemConstants (استاتیک)
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel;
// دریافت همه تعادل‌های هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه)
var allWeeklyBalances = await _context.NetworkWeeklyBalances
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
.ToDictionaryAsync(x => x.UserId, cancellationToken);
// دریافت کاربرانی که تعادل > 0 دارند (یا زیرمجموعه‌شان دارد)
var usersWithBalances = await _context.NetworkWeeklyBalances
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId && x.TotalBalances > 0)
.Select(x => x.UserId)
.ToListAsync(cancellationToken);
// پیدا کردن تمام کاربرانی که باید کمیسیون بگیرند (شامل والدین)
var usersToProcess = new HashSet<long>(usersWithBalances);
// اضافه کردن والدین تا 15 لول بالاتر
foreach (var userId in usersWithBalances)
{
var ancestors = await GetAncestors(userId, maxNetworkLevel, cancellationToken);
foreach (var ancestorId in ancestors)
{
usersToProcess.Add(ancestorId);
}
}
var payoutsList = new List<UserCommissionPayout>();
foreach (var userId in usersToProcess)
{
// ⭐ محاسبه تعادل شخصی
var personalBalances = 0;
if (allWeeklyBalances.ContainsKey(userId))
{
personalBalances = allWeeklyBalances[userId].TotalBalances;
}
// ⭐ محاسبه مجموع تعادل‌های زیرمجموعه تا maxNetworkLevel لول
var subordinateBalances = await CalculateSubordinateBalancesAsync(
userId,
request.WeekDefinitionId,
allWeeklyBalances,
maxNetworkLevel,
cancellationToken
);
// ⭐ مجموع تعادل = شخصی + زیرمجموعه
var totalBalancesWithSubordinates = personalBalances + subordinateBalances;
// اگر مجموع تعادل صفر است، نیازی به ثبت نیست
if (totalBalancesWithSubordinates <= 0)
{
continue;
}
// محاسبه مبلغ کمیسیون
var totalAmount = (long)(totalBalancesWithSubordinates * pool.ValuePerBalance);
var payout = new UserCommissionPayout
{
UserId = userId,
WeekDefinitionId = request.WeekDefinitionId,
WeeklyPoolId = pool.Id,
BalancesEarned = totalBalancesWithSubordinates, // ⭐ شامل زیرمجموعه
ValuePerBalance = pool.ValuePerBalance,
TotalAmount = totalAmount,
Status = CommissionPayoutStatus.Pending,
PaidAt = null,
WithdrawalMethod = null,
IbanNumber = null,
WithdrawnAt = null
};
payoutsList.Add(payout);
}
await _context.UserCommissionPayouts.AddRangeAsync(payoutsList, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
// ثبت تاریخچه برای هر پرداخت
var historyList = new List<CommissionPayoutHistory>();
foreach (var payout in payoutsList)
{
var history = new CommissionPayoutHistory
{
UserCommissionPayoutId = payout.Id,
UserId = payout.UserId,
WeekDefinitionId = request.WeekDefinitionId,
AmountBefore = 0,
AmountAfter = payout.TotalAmount,
OldStatus = default(CommissionPayoutStatus),
NewStatus = CommissionPayoutStatus.Pending,
Action = CommissionPayoutAction.Created,
PerformedBy = "System",
Reason = "پردازش خودکار کمیسیون هفتگی"
};
historyList.Add(history);
}
await _context.CommissionPayoutHistories.AddRangeAsync(historyList, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
return payoutsList.Count;
}
/// <summary>
/// پیدا کردن والدین یک کاربر تا N لول بالاتر
/// </summary>
private async Task<List<long>> GetAncestors(long userId, int maxLevels, CancellationToken cancellationToken)
{
var ancestors = new List<long>();
var currentUserId = userId;
for (int level = 0; level < maxLevels; level++)
{
var user = await _context.Users
.Where(x => x.Id == currentUserId)
.Select(x => x.NetworkParentId)
.FirstOrDefaultAsync(cancellationToken);
if (user == null || !user.HasValue)
{
break;
}
ancestors.Add(user.Value);
currentUserId = user.Value;
}
return ancestors;
}
/// <summary>
/// محاسبه مجموع تعادل‌های زیرمجموعه یک کاربر تا N لول پایین‌تر
/// </summary>
private async Task<int> CalculateSubordinateBalancesAsync(
long userId,
long WeekDefinitionId,
Dictionary<long, NetworkWeeklyBalance> allBalances,
int maxLevel,
CancellationToken cancellationToken)
{
// پیدا کردن همه زیرمجموعه‌ها تا maxLevel لول
var subordinates = await GetSubordinatesRecursive(userId, 1, maxLevel, cancellationToken);
// جمع تعادل‌های آنها
var totalSubordinateBalances = 0;
foreach (var subordinateId in subordinates)
{
if (allBalances.ContainsKey(subordinateId))
{
totalSubordinateBalances += allBalances[subordinateId].TotalBalances;
}
}
return totalSubordinateBalances;
}
/// <summary>
/// پیدا کردن بازگشتی زیرمجموعه‌ها تا N لول
/// </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;
}
}