feat: refactor week number handling to use WeekDefinitionId across multiple entities and queries
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.11" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.4.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.10" />
|
||||
<PackageReference Include="DateTimeConverterCL" Version="1.0.0" />
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CMSMicroservice.Domain\CMSMicroservice.Domain.csproj" />
|
||||
|
||||
+18
-13
@@ -17,15 +17,18 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ICurrentUserService _currentUser;
|
||||
private readonly IWeekDefinitionRepository _weekRepository;
|
||||
private readonly ILogger<ActivateClubMembershipCommandHandler> _logger;
|
||||
|
||||
public ActivateClubMembershipCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
ICurrentUserService currentUser,
|
||||
IWeekDefinitionRepository weekRepository,
|
||||
ILogger<ActivateClubMembershipCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_currentUser = currentUser;
|
||||
_weekRepository = weekRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -250,16 +253,16 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// ⭐ 8. اضافه کردن مبلغ به Pool هفته جاری
|
||||
var currentWeekNumber = GetCurrentWeekNumber();
|
||||
var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
|
||||
var weeklyPool = await _context.WeeklyCommissionPools
|
||||
.FirstOrDefaultAsync(p => p.WeekNumber == currentWeekNumber, cancellationToken);
|
||||
.FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken);
|
||||
|
||||
if (weeklyPool == null)
|
||||
{
|
||||
// ایجاد Pool جدید برای این هفته
|
||||
weeklyPool = new WeeklyCommissionPool
|
||||
{
|
||||
WeekNumber = currentWeekNumber,
|
||||
WeekDefinitionId = currentWeekDefinitionId,
|
||||
TotalPoolAmount = activationFeeValue, // مبلغ هدیه به Pool اضافه میشه
|
||||
TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه
|
||||
ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه
|
||||
@@ -270,8 +273,8 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
||||
await _context.WeeklyCommissionPools.AddAsync(weeklyPool, cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Created new WeeklyCommissionPool for {WeekNumber} with initial amount: {Amount}",
|
||||
currentWeekNumber,
|
||||
"Created new WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId} with initial amount: {Amount}",
|
||||
currentWeekDefinitionId,
|
||||
giftValue
|
||||
);
|
||||
}
|
||||
@@ -282,9 +285,9 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
||||
_context.WeeklyCommissionPools.Update(weeklyPool);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Added {Amount} to existing WeeklyCommissionPool for {WeekNumber}. New total: {NewTotal}",
|
||||
"Added {Amount} to existing WeeklyCommissionPool for WeekDefinitionId={WeekDefinitionId}. New total: {NewTotal}",
|
||||
activationFeeValue,
|
||||
currentWeekNumber,
|
||||
currentWeekDefinitionId,
|
||||
weeklyPool.TotalPoolAmount
|
||||
);
|
||||
}
|
||||
@@ -346,13 +349,15 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت شماره هفته جاری (مثال: "2025-W50")
|
||||
/// دریافت شناسه تعریف هفته جاری
|
||||
/// </summary>
|
||||
private string GetCurrentWeekNumber()
|
||||
private long GetCurrentWeekDefinitionId()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var calendar = CultureInfo.CurrentCulture.Calendar;
|
||||
var weekOfYear = calendar.GetWeekOfYear(now, CalendarWeekRule.FirstDay, DayOfWeek.Saturday);
|
||||
return $"{now.Year}-W{weekOfYear:D2}";
|
||||
var week = _weekRepository.GetCurrentWeek();
|
||||
if (week == null)
|
||||
{
|
||||
throw new InvalidOperationException("هفته جاری در سیستم تعریف نشده است");
|
||||
}
|
||||
return week.Id;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ public record CalculateWeeklyBalancesCommand : IRequest<int>
|
||||
/// <summary>
|
||||
/// شماره هفته (فرمت: YYYY-Www مثل 2025-W01)
|
||||
/// </summary>
|
||||
public string WeekNumber { get; init; } = string.Empty;
|
||||
public long WeekDefinitionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// آیا محاسبه مجدد انجام شود؟ (پیشفرض: false)
|
||||
|
||||
+67
-52
@@ -3,22 +3,33 @@ namespace CMSMicroservice.Application.CommissionCQ.Commands.CalculateWeeklyBalan
|
||||
public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWeeklyBalancesCommand, int>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekRepository;
|
||||
|
||||
public CalculateWeeklyBalancesCommandHandler(IApplicationDbContext context)
|
||||
public CalculateWeeklyBalancesCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekRepository = weekRepository;
|
||||
}
|
||||
|
||||
public async Task<int> Handle(CalculateWeeklyBalancesCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
var regorianWeekNumber = _weekRepository.GetGregorianWeekNumber(request.WeekDefinitionId);
|
||||
if (regorianWeekNumber == null)
|
||||
{
|
||||
throw new InvalidOperationException($"هفته {request.WeekDefinitionId} در سیستم تعریف نشده است");
|
||||
}
|
||||
|
||||
// بررسی وجود محاسبه قبلی
|
||||
var existingBalances = await _context.NetworkWeeklyBalances
|
||||
.Where(x => x.WeekNumber == request.WeekNumber)
|
||||
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (existingBalances.Any() && !request.ForceRecalculate)
|
||||
{
|
||||
throw new InvalidOperationException($"تعادلهای هفته {request.WeekNumber} قبلاً محاسبه شده است. برای محاسبه مجدد از ForceRecalculate استفاده کنید");
|
||||
throw new InvalidOperationException($"تعادلهای هفته {request.WeekDefinitionId} قبلاً محاسبه شده است. برای محاسبه مجدد از ForceRecalculate استفاده کنید");
|
||||
}
|
||||
|
||||
// حذف محاسبات قبلی در صورت ForceRecalculate
|
||||
@@ -43,16 +54,22 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// دریافت باقیماندههای هفته قبل
|
||||
var previousWeekNumber = GetPreviousWeekNumber(request.WeekNumber);
|
||||
var previousWeekCarryovers = await _context.NetworkWeeklyBalances
|
||||
.Where(x => x.WeekNumber == previousWeekNumber)
|
||||
.Select(x => new
|
||||
{
|
||||
x.UserId,
|
||||
x.LeftLegRemainder,
|
||||
x.RightLegRemainder
|
||||
})
|
||||
.ToDictionaryAsync(x => x.UserId, cancellationToken);
|
||||
var previousWeekDefinitionId = GetPreviousWeekDefinitionId(request.WeekDefinitionId);
|
||||
Dictionary<long, (int LeftLegRemainder, int RightLegRemainder)> previousWeekCarryovers;
|
||||
|
||||
if (previousWeekDefinitionId.HasValue)
|
||||
{
|
||||
previousWeekCarryovers = await _context.NetworkWeeklyBalances
|
||||
.Where(x => x.WeekDefinitionId == previousWeekDefinitionId.Value)
|
||||
.ToDictionaryAsync(
|
||||
x => x.UserId,
|
||||
x => (x.LeftLegRemainder, x.RightLegRemainder),
|
||||
cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
previousWeekCarryovers = new Dictionary<long, (int, int)>();
|
||||
}
|
||||
|
||||
var balancesList = new List<NetworkWeeklyBalance>();
|
||||
var calculatedAt = DateTime.Now;
|
||||
@@ -85,8 +102,8 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
||||
}
|
||||
|
||||
// محاسبه تعداد اعضای جدید در این هفته (تا maxNetworkLevel لول پایینتر)
|
||||
var leftNewMembers = await CountNewMembersInLeg(user.Id, NetworkLeg.Left, request.WeekNumber, maxNetworkLevel, cancellationToken);
|
||||
var rightNewMembers = await CountNewMembersInLeg(user.Id, NetworkLeg.Right, request.WeekNumber, maxNetworkLevel, cancellationToken);
|
||||
var leftNewMembers = await CountNewMembersInLeg(user.Id, NetworkLeg.Left, request.WeekDefinitionId, maxNetworkLevel, cancellationToken);
|
||||
var rightNewMembers = await CountNewMembersInLeg(user.Id, NetworkLeg.Right, request.WeekDefinitionId, maxNetworkLevel, cancellationToken);
|
||||
|
||||
// محاسبه مجموع هر پا (جدید + باقیمانده)
|
||||
var leftTotal = leftNewMembers + leftCarryover;
|
||||
@@ -124,7 +141,7 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
||||
var balance = new NetworkWeeklyBalance
|
||||
{
|
||||
UserId = user.Id,
|
||||
WeekNumber = request.WeekNumber,
|
||||
WeekDefinitionId = request.WeekDefinitionId,
|
||||
|
||||
// اطلاعات جدید
|
||||
LeftLegNewMembers = leftNewMembers,
|
||||
@@ -189,36 +206,30 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// شماره هفته قبل را محاسبه میکند
|
||||
/// شناسه هفته قبل را از WeekDefinitionRepository میگیرد
|
||||
/// </summary>
|
||||
private string GetPreviousWeekNumber(string currentWeekNumber)
|
||||
private long? GetPreviousWeekDefinitionId(long currentWeekDefinitionId)
|
||||
{
|
||||
// مثال: "2025-W48" -> "2025-W47"
|
||||
var parts = currentWeekNumber.Split('-');
|
||||
var year = int.Parse(parts[0]);
|
||||
var week = int.Parse(parts[1].Replace("W", ""));
|
||||
var currentWeekNumber = _weekRepository.GetGregorianWeekNumber(currentWeekDefinitionId);
|
||||
if (currentWeekNumber == null) return null;
|
||||
|
||||
week--;
|
||||
if (week < 1)
|
||||
{
|
||||
year--;
|
||||
week = 52; // یا 53 بسته به سال
|
||||
}
|
||||
var previousWeekNumber = _weekRepository.GetPreviousWeekNumber(currentWeekNumber);
|
||||
if (previousWeekNumber == null) return null;
|
||||
|
||||
return $"{year}-W{week:D2}";
|
||||
return _weekRepository.GetWeekDefinitionId(previousWeekNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// شمارش اعضای جدیدی که در این هفته به یک پا اضافه شدند
|
||||
/// تا maxLevel لول پایینتر شمارش میشود
|
||||
/// </summary>
|
||||
private async Task<int> CountNewMembersInLeg(long userId, NetworkLeg leg, string weekNumber, int maxLevel, CancellationToken cancellationToken)
|
||||
private async Task<int> CountNewMembersInLeg(long userId, NetworkLeg leg, long WeekDefinitionId, int maxLevel, CancellationToken cancellationToken)
|
||||
{
|
||||
// تبدیل WeekNumber به بازه تاریخی
|
||||
var (startDate, endDate) = GetWeekDateRange(weekNumber);
|
||||
var startDateEndDate =_weekRepository.GetWeekDateRange(WeekDefinitionId);
|
||||
|
||||
// شمارش تمام اعضای زیرمجموعه که در این هفته فعال شدند (تا maxLevel لول)
|
||||
var count = await CountNewMembersRecursive(userId, leg, startDate, endDate, 0, maxLevel, cancellationToken);
|
||||
var count = await CountNewMembersRecursive(userId, leg, startDateEndDate.Value.startDate, startDateEndDate.Value.endDate, 0, maxLevel, cancellationToken);
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -270,27 +281,31 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// تبدیل شماره هفته به بازه تاریخی
|
||||
/// تبدیل شماره هفته به بازه تاریخی از WeekDefinitionRepository
|
||||
/// </summary>
|
||||
private (DateTime startDate, DateTime endDate) GetWeekDateRange(string weekNumber)
|
||||
{
|
||||
// مثال: "2025-W48"
|
||||
var parts = weekNumber.Split('-');
|
||||
var year = int.Parse(parts[0]);
|
||||
var week = int.Parse(parts[1].Replace("W", ""));
|
||||
|
||||
// محاسبه اولین شنبه سال
|
||||
var jan1 = new DateTime(year, 1, 1);
|
||||
var jan1DayOfWeek = (int)jan1.DayOfWeek;
|
||||
// اگر 1 ژانویه شنبه باشد: offset=0، اگر یکشنبه: offset=6، دوشنبه: offset=5، ...
|
||||
var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
|
||||
var firstSaturday = jan1.AddDays(daysToFirstSaturday);
|
||||
|
||||
var weekStart = firstSaturday.AddDays((week - 1) * 7);
|
||||
var weekEnd = weekStart.AddDays(6).AddHours(23).AddMinutes(59).AddSeconds(59);
|
||||
|
||||
return (weekStart, weekEnd);
|
||||
}
|
||||
// private (DateTime startDate, DateTime endDate) GetWeekDateRange(string weekNumber)
|
||||
// {
|
||||
// var dateRange = _weekRepository.GetWeekDateRange(weekNumber);
|
||||
// if (dateRange.HasValue)
|
||||
// {
|
||||
// return dateRange.Value;
|
||||
// }
|
||||
//
|
||||
// // Fallback: محاسبه دستی اگر در کش نبود
|
||||
// var parts = weekNumber.Split('-');
|
||||
// var year = int.Parse(parts[0]);
|
||||
// var week = int.Parse(parts[1].Replace("W", ""));
|
||||
//
|
||||
// var jan1 = new DateTime(year, 1, 1);
|
||||
// var jan1DayOfWeek = (int)jan1.DayOfWeek;
|
||||
// var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
|
||||
// var firstSaturday = jan1.AddDays(daysToFirstSaturday);
|
||||
//
|
||||
// var weekStart = firstSaturday.AddDays((week - 1) * 7);
|
||||
// var weekEnd = weekStart.AddDays(6).AddHours(23).AddMinutes(59).AddSeconds(59);
|
||||
//
|
||||
// return (weekStart, weekEnd);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// محاسبه مجموع تعادلهای زیرمجموعه یک کاربر تا maxLevel لول پایینتر
|
||||
|
||||
+4
-4
@@ -4,11 +4,11 @@ public class CalculateWeeklyBalancesCommandValidator : AbstractValidator<Calcula
|
||||
{
|
||||
public CalculateWeeklyBalancesCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.WeekNumber)
|
||||
.NotEmpty()
|
||||
RuleFor(x => x.WeekDefinitionId)
|
||||
.NotNull()
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شماره هفته نمیتواند خالی باشد")
|
||||
.Matches(@"^\d{4}-W\d{2}$")
|
||||
.WithMessage("فرمت شماره هفته باید YYYY-Www باشد (مثل 2025-W01)");
|
||||
;
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
|
||||
+2
-2
@@ -8,10 +8,10 @@ public record CalculateWeeklyCommissionPoolCommand : IRequest<long>
|
||||
/// <summary>
|
||||
/// شماره هفته (فرمت: YYYY-Www)
|
||||
/// </summary>
|
||||
public string WeekNumber { get; init; } = string.Empty;
|
||||
public long WeekDefinitionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// آیا محاسبه مجدد انجام شود؟
|
||||
/// </summary>
|
||||
public bool ForceRecalculate { get; init; }
|
||||
public bool ForceRecalculate { get; init; }
|
||||
}
|
||||
|
||||
+20
-9
@@ -3,39 +3,50 @@ namespace CMSMicroservice.Application.CommissionCQ.Commands.CalculateWeeklyCommi
|
||||
public class CalculateWeeklyCommissionPoolCommandHandler : IRequestHandler<CalculateWeeklyCommissionPoolCommand, long>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekRepository;
|
||||
|
||||
public CalculateWeeklyCommissionPoolCommandHandler(IApplicationDbContext context)
|
||||
public CalculateWeeklyCommissionPoolCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekRepository = weekRepository;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(CalculateWeeklyCommissionPoolCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// تبدیل WeekNumber به WeekDefinitionId
|
||||
var weekDefinitionId = _weekRepository.GetGregorianWeekNumber(request.WeekDefinitionId);
|
||||
if (weekDefinitionId == null)
|
||||
{
|
||||
throw new InvalidOperationException($"هفته {request.WeekDefinitionId} در سیستم تعریف نشده است");
|
||||
}
|
||||
|
||||
// بررسی وجود استخر
|
||||
var existingPool = await _context.WeeklyCommissionPools
|
||||
.FirstOrDefaultAsync(x => x.WeekNumber == request.WeekNumber, cancellationToken);
|
||||
.FirstOrDefaultAsync(x => x.WeekDefinitionId == request.WeekDefinitionId, cancellationToken);
|
||||
|
||||
if (existingPool == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Pool هفته {request.WeekNumber} وجود ندارد. " +
|
||||
$"Pool هفته {request.WeekDefinitionId} وجود ندارد. " +
|
||||
"Pool باید در هنگام فعالسازی باشگاه مشتریان ایجاد شده باشد"
|
||||
);
|
||||
}
|
||||
|
||||
if (existingPool.IsCalculated && !request.ForceRecalculate)
|
||||
{
|
||||
throw new InvalidOperationException($"استخر کمیسیون هفته {request.WeekNumber} قبلاً محاسبه شده است");
|
||||
throw new InvalidOperationException($"استخر کمیسیون هفته {request.WeekDefinitionId} قبلاً محاسبه شده است");
|
||||
}
|
||||
|
||||
// بررسی وجود تعادلهای هفتگی
|
||||
var weeklyBalances = await _context.NetworkWeeklyBalances
|
||||
.Where(x => x.WeekNumber == request.WeekNumber)
|
||||
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (!weeklyBalances.Any())
|
||||
{
|
||||
throw new InvalidOperationException($"تعادلهای هفته {request.WeekNumber} هنوز محاسبه نشده است. ابتدا CalculateWeeklyBalances را اجرا کنید");
|
||||
throw new InvalidOperationException($"تعادلهای هفته {request.WeekDefinitionId} هنوز محاسبه نشده است. ابتدا CalculateWeeklyBalances را اجرا کنید");
|
||||
}
|
||||
|
||||
// ⭐ Pool از قبل پُر شده (توسط ActivateClubMembership)
|
||||
@@ -66,7 +77,7 @@ public class CalculateWeeklyCommissionPoolCommandHandler : IRequestHandler<Calcu
|
||||
if (request.ForceRecalculate)
|
||||
{
|
||||
var oldPayouts = await _context.UserCommissionPayouts
|
||||
.Where(p => p.WeekNumber == request.WeekNumber)
|
||||
.Where(p => p.WeekDefinitionId == request.WeekDefinitionId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (oldPayouts.Any())
|
||||
@@ -110,7 +121,7 @@ public class CalculateWeeklyCommissionPoolCommandHandler : IRequestHandler<Calcu
|
||||
var payout = new UserCommissionPayout
|
||||
{
|
||||
UserId = balance.UserId,
|
||||
WeekNumber = request.WeekNumber,
|
||||
WeekDefinitionId = request.WeekDefinitionId,
|
||||
WeeklyPoolId = existingPool.Id,
|
||||
BalancesEarned = userBalance,
|
||||
ValuePerBalance = valuePerBalance,
|
||||
@@ -138,7 +149,7 @@ public class CalculateWeeklyCommissionPoolCommandHandler : IRequestHandler<Calcu
|
||||
{
|
||||
UserCommissionPayoutId = payout.Id,
|
||||
UserId = payout.UserId,
|
||||
WeekNumber = request.WeekNumber,
|
||||
WeekDefinitionId = request.WeekDefinitionId,
|
||||
AmountBefore = 0,
|
||||
AmountAfter = payout.TotalAmount,
|
||||
OldStatus = default(CommissionPayoutStatus),
|
||||
|
||||
+4
-5
@@ -4,11 +4,10 @@ public class CalculateWeeklyCommissionPoolCommandValidator : AbstractValidator<C
|
||||
{
|
||||
public CalculateWeeklyCommissionPoolCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.WeekNumber)
|
||||
.NotEmpty()
|
||||
.WithMessage("شماره هفته نمیتواند خالی باشد")
|
||||
.Matches(@"^\d{4}-W\d{2}$")
|
||||
.WithMessage("فرمت شماره هفته باید YYYY-Www باشد");
|
||||
RuleFor(x => x.WeekDefinitionId)
|
||||
.NotNull()
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شماره هفته نمیتواند خالی باشد");
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ public record ProcessUserPayoutsCommand : IRequest<int>
|
||||
/// <summary>
|
||||
/// شماره هفته
|
||||
/// </summary>
|
||||
public string WeekNumber { get; init; } = string.Empty;
|
||||
public long WeekDefinitionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// آیا پرداخت مجدد انجام شود؟
|
||||
|
||||
+21
-11
@@ -3,31 +3,41 @@ namespace CMSMicroservice.Application.CommissionCQ.Commands.ProcessUserPayouts;
|
||||
public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayoutsCommand, int>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekRepository;
|
||||
|
||||
public ProcessUserPayoutsCommandHandler(IApplicationDbContext context)
|
||||
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.WeekNumber == request.WeekNumber, cancellationToken);
|
||||
.FirstOrDefaultAsync(x => x.WeekDefinitionId == request.WeekDefinitionId, cancellationToken);
|
||||
|
||||
if (pool == null || !pool.IsCalculated)
|
||||
{
|
||||
throw new InvalidOperationException($"استخر کمیسیون هفته {request.WeekNumber} هنوز محاسبه نشده است");
|
||||
throw new InvalidOperationException($"استخر کمیسیون هفته {request.WeekDefinitionId} هنوز محاسبه نشده است");
|
||||
}
|
||||
|
||||
// بررسی پرداخت قبلی
|
||||
var existingPayouts = await _context.UserCommissionPayouts
|
||||
.Where(x => x.WeekNumber == request.WeekNumber)
|
||||
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (existingPayouts.Any() && !request.ForceReprocess)
|
||||
{
|
||||
throw new InvalidOperationException($"پرداختهای هفته {request.WeekNumber} قبلاً انجام شده است");
|
||||
throw new InvalidOperationException($"پرداختهای هفته {request.WeekDefinitionId} قبلاً انجام شده است");
|
||||
}
|
||||
|
||||
// حذف پرداختهای قبلی در صورت ForceReprocess
|
||||
@@ -46,12 +56,12 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
|
||||
|
||||
// دریافت همه تعادلهای هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه)
|
||||
var allWeeklyBalances = await _context.NetworkWeeklyBalances
|
||||
.Where(x => x.WeekNumber == request.WeekNumber)
|
||||
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
|
||||
.ToDictionaryAsync(x => x.UserId, cancellationToken);
|
||||
|
||||
// دریافت کاربرانی که تعادل > 0 دارند (یا زیرمجموعهشان دارد)
|
||||
var usersWithBalances = await _context.NetworkWeeklyBalances
|
||||
.Where(x => x.WeekNumber == request.WeekNumber && x.TotalBalances > 0)
|
||||
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId && x.TotalBalances > 0)
|
||||
.Select(x => x.UserId)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
@@ -82,7 +92,7 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
|
||||
// ⭐ محاسبه مجموع تعادلهای زیرمجموعه تا maxNetworkLevel لول
|
||||
var subordinateBalances = await CalculateSubordinateBalancesAsync(
|
||||
userId,
|
||||
request.WeekNumber,
|
||||
request.WeekDefinitionId,
|
||||
allWeeklyBalances,
|
||||
maxNetworkLevel,
|
||||
cancellationToken
|
||||
@@ -103,7 +113,7 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
|
||||
var payout = new UserCommissionPayout
|
||||
{
|
||||
UserId = userId,
|
||||
WeekNumber = request.WeekNumber,
|
||||
WeekDefinitionId = request.WeekDefinitionId,
|
||||
WeeklyPoolId = pool.Id,
|
||||
BalancesEarned = totalBalancesWithSubordinates, // ⭐ شامل زیرمجموعه
|
||||
ValuePerBalance = pool.ValuePerBalance,
|
||||
@@ -129,7 +139,7 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
|
||||
{
|
||||
UserCommissionPayoutId = payout.Id,
|
||||
UserId = payout.UserId,
|
||||
WeekNumber = request.WeekNumber,
|
||||
WeekDefinitionId = request.WeekDefinitionId,
|
||||
AmountBefore = 0,
|
||||
AmountAfter = payout.TotalAmount,
|
||||
OldStatus = default(CommissionPayoutStatus),
|
||||
@@ -180,7 +190,7 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
|
||||
/// </summary>
|
||||
private async Task<int> CalculateSubordinateBalancesAsync(
|
||||
long userId,
|
||||
string weekNumber,
|
||||
long WeekDefinitionId,
|
||||
Dictionary<long, NetworkWeeklyBalance> allBalances,
|
||||
int maxLevel,
|
||||
CancellationToken cancellationToken)
|
||||
|
||||
+4
-5
@@ -4,11 +4,10 @@ public class ProcessUserPayoutsCommandValidator : AbstractValidator<ProcessUserP
|
||||
{
|
||||
public ProcessUserPayoutsCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.WeekNumber)
|
||||
.NotEmpty()
|
||||
.WithMessage("شماره هفته نمیتواند خالی باشد")
|
||||
.Matches(@"^\d{4}-W\d{2}$")
|
||||
.WithMessage("فرمت شماره هفته باید YYYY-Www باشد");
|
||||
RuleFor(x => x.WeekDefinitionId)
|
||||
.NotNull()
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شماره هفته نمیتواند خالی باشد");
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
|
||||
+3
-3
@@ -73,7 +73,7 @@ public class ProcessWithdrawalCommandHandler : IRequestHandler<ProcessWithdrawal
|
||||
UserId = payout.UserId,
|
||||
Iban = payout.IbanNumber ?? throw new InvalidOperationException("شماره شبا یافت نشد"),
|
||||
AccountHolderName = $"{payout.User.FirstName} {payout.User.LastName}",
|
||||
Description = $"برداشت کمیسیون هفته {payout.WeekNumber}",
|
||||
Description = $"برداشت کمیسیون هفته {payout.WeekDefinitionId}",
|
||||
InternalRefId = $"PAYOUT-{payout.Id}"
|
||||
};
|
||||
|
||||
@@ -120,7 +120,7 @@ public class ProcessWithdrawalCommandHandler : IRequestHandler<ProcessWithdrawal
|
||||
{
|
||||
UserCommissionPayoutId = payout.Id,
|
||||
UserId = payout.UserId,
|
||||
WeekNumber = payout.WeekNumber,
|
||||
WeekDefinitionId = payout.WeekDefinitionId,
|
||||
AmountBefore = payout.TotalAmount,
|
||||
AmountAfter = payout.TotalAmount,
|
||||
OldStatus = oldStatus,
|
||||
@@ -147,7 +147,7 @@ public class ProcessWithdrawalCommandHandler : IRequestHandler<ProcessWithdrawal
|
||||
{
|
||||
UserCommissionPayoutId = payout.Id,
|
||||
UserId = payout.UserId,
|
||||
WeekNumber = payout.WeekNumber,
|
||||
WeekDefinitionId = payout.WeekDefinitionId,
|
||||
AmountBefore = payout.TotalAmount,
|
||||
AmountAfter = payout.TotalAmount,
|
||||
OldStatus = oldStatus,
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class RequestWithdrawalCommandHandler : IRequestHandler<RequestWithdrawal
|
||||
{
|
||||
UserCommissionPayoutId = payout.Id,
|
||||
UserId = payout.UserId,
|
||||
WeekNumber = payout.WeekNumber,
|
||||
WeekDefinitionId = payout.WeekDefinitionId,
|
||||
AmountBefore = payout.TotalAmount,
|
||||
AmountAfter = payout.TotalAmount,
|
||||
OldStatus = oldStatus,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ public record TriggerWeeklyCalculationCommand : IRequest<TriggerWeeklyCalculatio
|
||||
/// <summary>
|
||||
/// شماره هفته (فرمت: "YYYY-Www")
|
||||
/// </summary>
|
||||
public string WeekNumber { get; init; } = string.Empty;
|
||||
public long WeekDefinitionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// اگر true باشد، محاسبات قبلی را حذف و دوباره محاسبه میکند
|
||||
|
||||
+5
-4
@@ -26,8 +26,9 @@ public class TriggerWeeklyCalculationCommandHandler : IRequestHandler<TriggerWee
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Validate week number format
|
||||
if (string.IsNullOrWhiteSpace(request.WeekNumber))
|
||||
if (request.WeekDefinitionId<=0)
|
||||
{
|
||||
return new TriggerWeeklyCalculationResponseDto
|
||||
{
|
||||
@@ -45,7 +46,7 @@ public class TriggerWeeklyCalculationCommandHandler : IRequestHandler<TriggerWee
|
||||
{
|
||||
await _mediator.Send(new CalculateWeeklyBalancesCommand
|
||||
{
|
||||
WeekNumber = request.WeekNumber,
|
||||
WeekDefinitionId = request.WeekDefinitionId,
|
||||
ForceRecalculate = request.ForceRecalculate
|
||||
}, cancellationToken);
|
||||
steps.Add("محاسبه امتیازات هفتگی");
|
||||
@@ -56,7 +57,7 @@ public class TriggerWeeklyCalculationCommandHandler : IRequestHandler<TriggerWee
|
||||
{
|
||||
await _mediator.Send(new CalculateWeeklyCommissionPoolCommand
|
||||
{
|
||||
WeekNumber = request.WeekNumber,
|
||||
WeekDefinitionId = request.WeekDefinitionId,
|
||||
ForceRecalculate = request.ForceRecalculate
|
||||
}, cancellationToken);
|
||||
steps.Add("محاسبه استخر و پرداخت کاربران");
|
||||
@@ -65,7 +66,7 @@ public class TriggerWeeklyCalculationCommandHandler : IRequestHandler<TriggerWee
|
||||
return new TriggerWeeklyCalculationResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = $"محاسبات هفته {request.WeekNumber} با موفقیت انجام شد. مراحل: {string.Join(", ", steps)}",
|
||||
Message = $"محاسبات هفته {request.WeekDefinitionId} با موفقیت انجام شد. مراحل: {string.Join(", ", steps)}",
|
||||
ExecutionId = executionId,
|
||||
StartedAt = startedAt
|
||||
};
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using CMSMicroservice.Application.CommissionCQ.Commands.ProcessWithdrawal;
|
||||
|
||||
namespace CMSMicroservice.Application.CommissionCQ.Commands.TriggerWeeklyCalculation;
|
||||
|
||||
public class TriggerWeeklyCalculationCommandValidator : AbstractValidator<TriggerWeeklyCalculationCommand>
|
||||
{
|
||||
public TriggerWeeklyCalculationCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.WeekDefinitionId)
|
||||
.NotNull()
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شماره هفته نمیتواند خالی باشد");
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
{
|
||||
var result = await ValidateAsync(
|
||||
ValidationContext<TriggerWeeklyCalculationCommand>.CreateWithOptions(
|
||||
(TriggerWeeklyCalculationCommand)model,
|
||||
x => x.IncludeProperties(propertyName)));
|
||||
|
||||
if (result.IsValid)
|
||||
return Array.Empty<string>();
|
||||
|
||||
return result.Errors.Select(e => e.ErrorMessage);
|
||||
};
|
||||
}
|
||||
+2
-2
@@ -8,12 +8,12 @@ public record GetAllWeeklyPoolsQuery : IRequest<GetAllWeeklyPoolsResponseDto>
|
||||
/// <summary>
|
||||
/// از هفته (فیلتر اختیاری)
|
||||
/// </summary>
|
||||
public string? FromWeek { get; init; }
|
||||
public int? FromWeekOrder { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// تا هفته (فیلتر اختیاری)
|
||||
/// </summary>
|
||||
public string? ToWeek { get; init; }
|
||||
public int? ToWeekOrder { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// فقط Pool های محاسبه شده
|
||||
|
||||
+28
-8
@@ -3,25 +3,31 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetAllWeeklyPools;
|
||||
public class GetAllWeeklyPoolsQueryHandler : IRequestHandler<GetAllWeeklyPoolsQuery, GetAllWeeklyPoolsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetAllWeeklyPoolsQueryHandler(IApplicationDbContext context)
|
||||
public GetAllWeeklyPoolsQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<GetAllWeeklyPoolsResponseDto> Handle(GetAllWeeklyPoolsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.WeeklyCommissionPools.AsNoTracking();
|
||||
var query = _context.WeeklyCommissionPools
|
||||
.Include(i=> i.WeekDefinition)
|
||||
.AsNoTracking();
|
||||
|
||||
// Apply filters
|
||||
if (!string.IsNullOrWhiteSpace(request.FromWeek))
|
||||
if (request.FromWeekOrder!=null)
|
||||
{
|
||||
query = query.Where(x => string.Compare(x.WeekNumber, request.FromWeek) >= 0);
|
||||
query = query.Where(x => x.WeekDefinition.WeekOrder>=request.FromWeekOrder );
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.ToWeek))
|
||||
if (request.ToWeekOrder!=null)
|
||||
{
|
||||
query = query.Where(x => string.Compare(x.WeekNumber, request.ToWeek) <= 0);
|
||||
query = query.Where(x =>x.WeekDefinition.WeekOrder<= request.ToWeekOrder);
|
||||
}
|
||||
|
||||
if (request.OnlyCalculated.HasValue && request.OnlyCalculated.Value)
|
||||
@@ -30,7 +36,7 @@ public class GetAllWeeklyPoolsQueryHandler : IRequestHandler<GetAllWeeklyPoolsQu
|
||||
}
|
||||
|
||||
// Order by week number descending (newest first)
|
||||
query = query.OrderByDescending(x => x.WeekNumber);
|
||||
query = query.OrderByDescending(x => x.WeekDefinitionId);
|
||||
|
||||
// Count total
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
@@ -42,7 +48,8 @@ public class GetAllWeeklyPoolsQueryHandler : IRequestHandler<GetAllWeeklyPoolsQu
|
||||
.Select(x => new WeeklyCommissionPoolDto
|
||||
{
|
||||
Id = x.Id,
|
||||
WeekNumber = x.WeekNumber,
|
||||
WeekDefinitionId = x.WeekDefinitionId,
|
||||
WeekDisplayName = x.WeekDefinition.PersianWeekNumber,
|
||||
TotalPoolAmount = x.TotalPoolAmount,
|
||||
TotalBalances = x.TotalBalances,
|
||||
ValuePerBalance = x.ValuePerBalance,
|
||||
@@ -52,6 +59,19 @@ public class GetAllWeeklyPoolsQueryHandler : IRequestHandler<GetAllWeeklyPoolsQu
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// Populate WeekDisplayName from cache
|
||||
// foreach (var pool in pools)
|
||||
// {
|
||||
// // WeeklyCommissionPoolDto is a record, need to create new instance with display name
|
||||
// // Since records are immutable, we can't modify them directly
|
||||
// }
|
||||
|
||||
// // Create new list with WeekDisplayName populated
|
||||
// var poolsWithDisplayName = pools.Select(p => p with
|
||||
// {
|
||||
// WeekDisplayName = _weekDefinitionRepository.GetDisplayNameByGregorianWeekNumber(p.WeekNumber)
|
||||
// }).ToList();
|
||||
|
||||
return new GetAllWeeklyPoolsResponseDto
|
||||
{
|
||||
MetaData = new MetaDataDto
|
||||
|
||||
+2
-1
@@ -9,7 +9,8 @@ public record GetAllWeeklyPoolsResponseDto
|
||||
public record WeeklyCommissionPoolDto
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public string WeekNumber { get; init; } = string.Empty;
|
||||
public long WeekDefinitionId { get; init; }
|
||||
public string WeekDisplayName { get; init; } = string.Empty;
|
||||
public long TotalPoolAmount { get; init; }
|
||||
public int TotalBalances { get; init; }
|
||||
public long ValuePerBalance { get; init; }
|
||||
|
||||
+97
-78
@@ -2,29 +2,36 @@ using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Globalization;
|
||||
using DateTimeConverterCL;
|
||||
|
||||
namespace CMSMicroservice.Application.CommissionCQ.Queries.GetAvailableWeeks;
|
||||
|
||||
public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQuery, GetAvailableWeeksResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetAvailableWeeksQueryHandler(IApplicationDbContext context)
|
||||
public GetAvailableWeeksQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<GetAvailableWeeksResponseDto> Handle(
|
||||
GetAvailableWeeksQuery request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var currentDate = DateTime.Now;
|
||||
var currentWeekNumber = GetWeekNumber(currentDate);
|
||||
var currentWeekDef = _weekDefinitionRepository.GetCurrentWeek();
|
||||
// var currentWeekNumber = currentWeekDef != null
|
||||
// ? $"{currentWeekDef.GregorianYear}-W{currentWeekDef.GregorianWeekNumber:D2}"
|
||||
// : _weekDefinitionRepository.CalculateGregorianWeekNumber(DateTime.Now);
|
||||
|
||||
// دریافت هفتههای محاسبه شده از دیتابیس
|
||||
var calculatedPools = await _context.WeeklyCommissionPools
|
||||
.Where(p => p.IsCalculated)
|
||||
.OrderByDescending(p => p.WeekNumber)
|
||||
.OrderByDescending(p => p.WeekDefinitionId)
|
||||
.Take(request.PastWeeksCount)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
@@ -32,7 +39,7 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
var executionLogs = await _context.WorkerExecutionLogs
|
||||
.Where(log => log.Status == WorkerExecutionStatus.Success ||
|
||||
log.Status == WorkerExecutionStatus.Failed)
|
||||
.GroupBy(log => log.WeekNumber)
|
||||
.GroupBy(log => log.WeekDefinitionId)
|
||||
.Select(g => new
|
||||
{
|
||||
WeekNumber = g.Key,
|
||||
@@ -43,15 +50,16 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
var allWeeks = new List<WeekInfoDto>();
|
||||
|
||||
// هفته جاری
|
||||
var currentWeekInfo = CreateWeekInfo(currentDate, currentWeekNumber, calculatedPools, executionLogs);
|
||||
var currentDate = DateTime.Now;
|
||||
var currentWeekInfo = CreateWeekInfo(currentDate, currentWeekDef.Id, calculatedPools, executionLogs);
|
||||
|
||||
// هفتههای گذشته (12 هفته)
|
||||
var pastWeeks = new List<WeekInfoDto>();
|
||||
for (int i = 1; i <= request.PastWeeksCount; i++)
|
||||
{
|
||||
var pastDate = currentDate.AddDays(-7 * i);
|
||||
var weekNumber = GetWeekNumber(pastDate);
|
||||
pastWeeks.Add(CreateWeekInfo(pastDate, weekNumber, calculatedPools, executionLogs));
|
||||
var weekNumber = GetWeekNumberUsingRepository(pastDate);
|
||||
pastWeeks.Add(CreateWeekInfo(pastDate, currentWeekDef.Id, calculatedPools, executionLogs));
|
||||
}
|
||||
|
||||
// هفتههای آینده (4 هفته)
|
||||
@@ -59,8 +67,8 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
for (int i = 1; i <= request.FutureWeeksCount; i++)
|
||||
{
|
||||
var futureDate = currentDate.AddDays(7 * i);
|
||||
var weekNumber = GetWeekNumber(futureDate);
|
||||
futureWeeks.Add(CreateWeekInfo(futureDate, weekNumber, calculatedPools, executionLogs));
|
||||
var weekNumber = GetWeekNumberUsingRepository(futureDate);
|
||||
futureWeeks.Add(CreateWeekInfo(futureDate, currentWeekDef.Id, calculatedPools, executionLogs));
|
||||
}
|
||||
|
||||
// تفکیک به calculated و pending
|
||||
@@ -76,26 +84,54 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
};
|
||||
}
|
||||
|
||||
private string GetWeekNumberUsingRepository(DateTime date)
|
||||
{
|
||||
var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
|
||||
if (weekDef != null)
|
||||
{
|
||||
return $"{weekDef.GregorianYear}-W{weekDef.GregorianWeekNumber:D2}";
|
||||
}
|
||||
|
||||
// Fallback to manual calculation if not in cache
|
||||
return _weekDefinitionRepository.CalculateGregorianWeekNumber(date);
|
||||
}
|
||||
|
||||
private WeekInfoDto CreateWeekInfo(
|
||||
DateTime date,
|
||||
string weekNumber,
|
||||
long weekDefinitionId,
|
||||
List<Domain.Entities.Commission.WeeklyCommissionPool> calculatedPools,
|
||||
Dictionary<string, Domain.Entities.Commission.WorkerExecutionLog> executionLogs)
|
||||
Dictionary<long, Domain.Entities.Commission.WorkerExecutionLog> executionLogs)
|
||||
{
|
||||
// Get the actual WeekDefinition for this date
|
||||
var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
|
||||
var actualWeekDefId = weekDef?.Id ?? weekDefinitionId;
|
||||
|
||||
var (startDate, endDate) = GetWeekRange(date);
|
||||
var pool = calculatedPools.FirstOrDefault(p => p.WeekNumber == weekNumber);
|
||||
var log = executionLogs.GetValueOrDefault(weekNumber);
|
||||
var pool = calculatedPools.FirstOrDefault(p => p.WeekDefinitionId == actualWeekDefId);
|
||||
var log = executionLogs.GetValueOrDefault(actualWeekDefId);
|
||||
|
||||
var isCalculated = pool != null && pool.IsCalculated;
|
||||
//
|
||||
// // تبدیل تاریخ به شمسی برای نمایش
|
||||
// var persianCalendar = new PersianCalendar();
|
||||
// var startDatePersian = $"{persianCalendar.GetYear(startDate):D4}/{persianCalendar.GetMonth(startDate):D2}/{persianCalendar.GetDayOfMonth(startDate):D2}";
|
||||
// var endDatePersian = $"{persianCalendar.GetYear(endDate):D4}/{persianCalendar.GetMonth(endDate):D2}/{persianCalendar.GetDayOfMonth(endDate):D2}";
|
||||
|
||||
// تبدیل تاریخ به شمسی برای نمایش
|
||||
var persianCalendar = new PersianCalendar();
|
||||
var startDatePersian = $"{persianCalendar.GetYear(startDate):D4}/{persianCalendar.GetMonth(startDate):D2}/{persianCalendar.GetDayOfMonth(startDate):D2}";
|
||||
var endDatePersian = $"{persianCalendar.GetYear(endDate):D4}/{persianCalendar.GetMonth(endDate):D2}/{persianCalendar.GetDayOfMonth(endDate):D2}";
|
||||
// استفاده از DisplayName از WeekDefinition
|
||||
var displayName = weekDef?.DisplayName ?? GetDisplayName(startDate);
|
||||
string displayText;
|
||||
|
||||
// تبدیل weekNumber به شمسی فقط برای نمایش
|
||||
var persianWeekNumber = ConvertWeekNumberToPersian(weekNumber, startDate);
|
||||
var displayText = $"{persianWeekNumber} ({startDatePersian} - {endDatePersian})";
|
||||
if (!string.IsNullOrEmpty(displayName))
|
||||
{
|
||||
// اگر DisplayName وجود داشت، از آن استفاده کن
|
||||
displayText = $"{displayName} ({startDate.MiladiToJalali()} - {endDate.MiladiToJalali()})";
|
||||
}
|
||||
else
|
||||
{
|
||||
// تبدیل weekNumber به شمسی فقط برای نمایش (fallback)
|
||||
var persianWeekNumber = _weekDefinitionRepository.GetWeekById(actualWeekDefId);
|
||||
displayText = $"{persianWeekNumber?.PersianWeekNumber} ({startDate.MiladiToJalali()} - {endDate.MiladiToJalali()})";
|
||||
}
|
||||
|
||||
if (isCalculated)
|
||||
{
|
||||
@@ -104,7 +140,8 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
|
||||
return new WeekInfoDto
|
||||
{
|
||||
WeekNumber = weekNumber, // میلادی (برای API)
|
||||
WeekDefinitionId = actualWeekDefId,
|
||||
DisplayName = weekDef?.DisplayName ?? _weekDefinitionRepository.GetWeekById(actualWeekDefId)?.DisplayName ?? "", // میلادی (برای API)
|
||||
StartDate = startDate,
|
||||
EndDate = endDate,
|
||||
IsCalculated = isCalculated,
|
||||
@@ -116,40 +153,16 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetWeekNumber(DateTime date)
|
||||
private (DateTime startDate, DateTime endDate) GetWeekRange(DateTime date)
|
||||
{
|
||||
var year = date.Year;
|
||||
|
||||
// پیدا کردن اولین شنبه سال
|
||||
var jan1 = new DateTime(year, 1, 1);
|
||||
var jan1DayOfWeek = (int)jan1.DayOfWeek; // 0=Sunday, 1=Monday, ..., 6=Saturday
|
||||
|
||||
// محاسبه تعداد روزهایی که باید اضافه کنیم تا به اولین شنبه برسیم
|
||||
var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
|
||||
var firstSaturday = jan1.AddDays(daysToFirstSaturday);
|
||||
|
||||
// پیدا کردن شنبه شروع هفته جاری
|
||||
var currentDayOfWeek = (int)date.DayOfWeek; // 0=Sun, 1=Mon, ..., 6=Sat
|
||||
var daysToCurrentSaturday = currentDayOfWeek == 6 ? 0 : (currentDayOfWeek + 1) % 7;
|
||||
var weekStartSaturday = date.Date.AddDays(-daysToCurrentSaturday);
|
||||
|
||||
// محاسبه شماره هفته
|
||||
int weekNum;
|
||||
if (weekStartSaturday < firstSaturday)
|
||||
// First try to get from repository
|
||||
var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
|
||||
if (weekDef != null)
|
||||
{
|
||||
weekNum = 1; // هفته اول سال
|
||||
}
|
||||
else
|
||||
{
|
||||
var daysSinceFirstSaturday = (weekStartSaturday - firstSaturday).Days;
|
||||
weekNum = (daysSinceFirstSaturday / 7) + 1;
|
||||
return (weekDef.StartDate, weekDef.EndDate);
|
||||
}
|
||||
|
||||
return $"{year}-W{weekNum:D2}";
|
||||
}
|
||||
|
||||
private static (DateTime startDate, DateTime endDate) GetWeekRange(DateTime date)
|
||||
{
|
||||
// Fallback: manual calculation
|
||||
var dayOfWeek = (int)date.DayOfWeek;
|
||||
// محاسبه تعداد روزهایی که باید عقب برویم تا به شنبه برسیم
|
||||
// شنبه = 6, یکشنبه = 0, دوشنبه = 1, ..., جمعه = 5
|
||||
@@ -161,32 +174,38 @@ public class GetAvailableWeeksQueryHandler : IRequestHandler<GetAvailableWeeksQu
|
||||
return (startDate, endDate);
|
||||
}
|
||||
|
||||
private static string ConvertWeekNumberToPersian(string gregorianWeekNumber, DateTime weekStartDate)
|
||||
private string GetDisplayName(DateTime date)
|
||||
{
|
||||
// ورودی: "2025-W48"
|
||||
var persianCalendar = new PersianCalendar();
|
||||
var persianYear = persianCalendar.GetYear(weekStartDate);
|
||||
|
||||
// محاسبه شماره هفته شمسی
|
||||
var jan1Persian = persianCalendar.ToDateTime(persianYear, 1, 1, 0, 0, 0, 0);
|
||||
|
||||
// پیدا کردن اولین شنبه سال شمسی
|
||||
var jan1DayOfWeek = (int)jan1Persian.DayOfWeek;
|
||||
var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
|
||||
var firstSaturday = jan1Persian.AddDays(daysToFirstSaturday);
|
||||
|
||||
// محاسبه شماره هفته
|
||||
int weekNum;
|
||||
if (weekStartDate < firstSaturday)
|
||||
{
|
||||
weekNum = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var daysSinceFirstSaturday = (weekStartDate - firstSaturday).Days;
|
||||
weekNum = (daysSinceFirstSaturday / 7) + 1;
|
||||
}
|
||||
|
||||
return $"{persianYear}-W{weekNum:D2}";
|
||||
var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
|
||||
return weekDef?.DisplayName ?? "";
|
||||
}
|
||||
|
||||
// private static string ConvertWeekNumberToPersian(string gregorianWeekNumber, DateTime weekStartDate)
|
||||
// {
|
||||
// // ورودی: "2025-W48"
|
||||
// var persianCalendar = new PersianCalendar();
|
||||
// var persianYear = persianCalendar.GetYear(weekStartDate);
|
||||
//
|
||||
// // محاسبه شماره هفته شمسی
|
||||
// var jan1Persian = persianCalendar.ToDateTime(persianYear, 1, 1, 0, 0, 0, 0);
|
||||
//
|
||||
// // پیدا کردن اولین شنبه سال شمسی
|
||||
// var jan1DayOfWeek = (int)jan1Persian.DayOfWeek;
|
||||
// var daysToFirstSaturday = jan1DayOfWeek == 6 ? 0 : (6 - jan1DayOfWeek + 7) % 7;
|
||||
// var firstSaturday = jan1Persian.AddDays(daysToFirstSaturday);
|
||||
//
|
||||
// // محاسبه شماره هفته
|
||||
// int weekNum;
|
||||
// if (weekStartDate < firstSaturday)
|
||||
// {
|
||||
// weekNum = 1;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var daysSinceFirstSaturday = (weekStartDate - firstSaturday).Days;
|
||||
// weekNum = (daysSinceFirstSaturday / 7) + 1;
|
||||
// }
|
||||
//
|
||||
// return $"{persianYear}-W{weekNum:D2}";
|
||||
// }
|
||||
}
|
||||
|
||||
+4
-1
@@ -17,8 +17,11 @@ public class GetAvailableWeeksResponseDto
|
||||
|
||||
public class WeekInfoDto
|
||||
{
|
||||
/// <summary>شناسه یکتای هفته</summary>
|
||||
public required long WeekDefinitionId { get; init; }
|
||||
|
||||
/// <summary>شماره هفته (YYYY-Www)</summary>
|
||||
public required string WeekNumber { get; init; }
|
||||
public required string DisplayName { get; init; }
|
||||
|
||||
/// <summary>تاریخ شروع هفته</summary>
|
||||
public required DateTime StartDate { get; init; }
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ public record GetCommissionPayoutHistoryQuery : IRequest<GetCommissionPayoutHist
|
||||
/// <summary>
|
||||
/// شماره هفته (اختیاری)
|
||||
/// </summary>
|
||||
public string? WeekNumber { get; init; }
|
||||
public long? WeekDefinitionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// مرتبسازی
|
||||
|
||||
+16
-4
@@ -3,15 +3,20 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetCommissionPayoutHi
|
||||
public class GetCommissionPayoutHistoryQueryHandler : IRequestHandler<GetCommissionPayoutHistoryQuery, GetCommissionPayoutHistoryResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetCommissionPayoutHistoryQueryHandler(IApplicationDbContext context)
|
||||
public GetCommissionPayoutHistoryQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<GetCommissionPayoutHistoryResponseDto> Handle(GetCommissionPayoutHistoryQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.CommissionPayoutHistories
|
||||
.Include(i=>i.WeekDefinition)
|
||||
.AsNoTracking()
|
||||
.AsQueryable();
|
||||
|
||||
@@ -26,9 +31,9 @@ public class GetCommissionPayoutHistoryQueryHandler : IRequestHandler<GetCommiss
|
||||
query = query.Where(x => x.UserId == request.UserId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.WeekNumber))
|
||||
if (request.WeekDefinitionId!=null)
|
||||
{
|
||||
query = query.Where(x => x.WeekNumber == request.WeekNumber);
|
||||
query = query.Where(x => x.WeekDefinitionId == request.WeekDefinitionId);
|
||||
}
|
||||
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||
@@ -42,7 +47,8 @@ public class GetCommissionPayoutHistoryQueryHandler : IRequestHandler<GetCommiss
|
||||
Id = x.Id,
|
||||
UserCommissionPayoutId = x.UserCommissionPayoutId,
|
||||
UserId = x.UserId,
|
||||
WeekNumber = x.WeekNumber,
|
||||
WeekDefinitionId = x.WeekDefinitionId,
|
||||
WeekDisplayName=x.WeekDefinition!.DisplayName,
|
||||
AmountBefore = x.AmountBefore,
|
||||
AmountAfter = x.AmountAfter,
|
||||
OldStatus = x.OldStatus,
|
||||
@@ -54,6 +60,12 @@ public class GetCommissionPayoutHistoryQueryHandler : IRequestHandler<GetCommiss
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// // Populate WeekDisplayName from cache
|
||||
// foreach (var model in models)
|
||||
// {
|
||||
// model.WeekDisplayName = _weekDefinitionRepository.GetDisplayNameByGregorianWeekNumber(model.WeekNumber);
|
||||
// }
|
||||
|
||||
return new GetCommissionPayoutHistoryResponseDto
|
||||
{
|
||||
MetaData = meta,
|
||||
|
||||
+4
-5
@@ -13,11 +13,10 @@ public class GetCommissionPayoutHistoryQueryValidator : AbstractValidator<GetCom
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شناسه کاربر معتبر نیست")
|
||||
.When(x => x.UserId.HasValue);
|
||||
|
||||
RuleFor(x => x.WeekNumber)
|
||||
.Matches(@"^\d{4}-W\d{2}$")
|
||||
.WithMessage("فرمت شماره هفته باید YYYY-Www باشد")
|
||||
.When(x => !string.IsNullOrEmpty(x.WeekNumber));
|
||||
RuleFor(x => x.WeekDefinitionId)
|
||||
.NotNull()
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شماره هفته نمیتواند خالی باشد");
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@ public class GetCommissionPayoutHistoryResponseModel
|
||||
public long Id { get; set; }
|
||||
public long UserCommissionPayoutId { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public string WeekNumber { get; set; } = string.Empty;
|
||||
public long WeekDefinitionId { get; set; }
|
||||
public string WeekDisplayName { get; set; } = string.Empty;
|
||||
public long AmountBefore { get; set; }
|
||||
public long AmountAfter { get; set; }
|
||||
public CommissionPayoutStatus? OldStatus { get; set; }
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ public record GetUserCommissionPayoutsQuery : IRequest<GetUserCommissionPayoutsR
|
||||
/// <summary>
|
||||
/// شماره هفته (اختیاری)
|
||||
/// </summary>
|
||||
public string? WeekNumber { get; init; }
|
||||
public long? WeekDefinitionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// مرتبسازی
|
||||
|
||||
+16
-4
@@ -3,15 +3,20 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetUserCommissionPayo
|
||||
public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommissionPayoutsQuery, GetUserCommissionPayoutsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetUserCommissionPayoutsQueryHandler(IApplicationDbContext context)
|
||||
public GetUserCommissionPayoutsQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<GetUserCommissionPayoutsResponseDto> Handle(GetUserCommissionPayoutsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.UserCommissionPayouts
|
||||
.Include(x => x.WeekDefinition)
|
||||
.Include(x => x.User)
|
||||
.AsNoTracking()
|
||||
.AsQueryable();
|
||||
@@ -27,9 +32,9 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
||||
query = query.Where(x => x.Status == request.Status.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.WeekNumber))
|
||||
if (request.WeekDefinitionId!=null)
|
||||
{
|
||||
query = query.Where(x => x.WeekNumber == request.WeekNumber);
|
||||
query = query.Where(x => x.WeekDefinitionId == request.WeekDefinitionId);
|
||||
}
|
||||
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||
@@ -44,7 +49,8 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
||||
UserId = x.UserId,
|
||||
FirstName = x.User.FirstName,
|
||||
LastName = x.User.LastName,
|
||||
WeekNumber = x.WeekNumber,
|
||||
WeekDefinitionId = x.WeekDefinitionId,
|
||||
WeekDisplayName = x.WeekDefinition != null ? x.WeekDefinition.DisplayName : "",
|
||||
WeeklyPoolId = x.WeeklyPoolId,
|
||||
BalancesEarned = x.BalancesEarned,
|
||||
ValuePerBalance = x.ValuePerBalance,
|
||||
@@ -58,6 +64,12 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// // Populate WeekDisplayName from cache
|
||||
// foreach (var model in models)
|
||||
// {
|
||||
// model.WeekDisplayName = _weekDefinitionRepository.GetDisplayNameByGregorianWeekNumber(model.WeekNumber);
|
||||
// }
|
||||
|
||||
return new GetUserCommissionPayoutsResponseDto
|
||||
{
|
||||
MetaData = meta,
|
||||
|
||||
+4
-4
@@ -14,10 +14,10 @@ public class GetUserCommissionPayoutsQueryValidator : AbstractValidator<GetUserC
|
||||
.WithMessage("وضعیت معتبر نیست")
|
||||
.When(x => x.Status.HasValue);
|
||||
|
||||
RuleFor(x => x.WeekNumber)
|
||||
.Matches(@"^\d{4}-W\d{2}$")
|
||||
.WithMessage("فرمت شماره هفته باید YYYY-Www باشد")
|
||||
.When(x => !string.IsNullOrEmpty(x.WeekNumber));
|
||||
RuleFor(x => x.WeekDefinitionId)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شماره هفته نمیتواند صفر باشد")
|
||||
.When(x => x.WeekDefinitionId.HasValue);
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@ public class GetUserCommissionPayoutsResponseModel
|
||||
public long UserId { get; set; }
|
||||
public string FirstName { get; set; } = string.Empty;
|
||||
public string LastName { get; set; } = string.Empty;
|
||||
public string WeekNumber { get; set; } = string.Empty;
|
||||
public long WeekDefinitionId { get; set; }
|
||||
public string WeekDisplayName { get; set; } = string.Empty;
|
||||
public long WeeklyPoolId { get; set; }
|
||||
public long BalancesEarned { get; set; }
|
||||
public decimal ValuePerBalance { get; set; }
|
||||
|
||||
+3
-2
@@ -11,9 +11,10 @@ public record GetUserWeeklyBalancesQuery : IRequest<GetUserWeeklyBalancesRespons
|
||||
public long? UserId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// شماره هفته (اختیاری)
|
||||
/// شناسه تعریف هفته (اختیاری) - روش ترجیحی
|
||||
/// </summary>
|
||||
public string? WeekNumber { get; init; }
|
||||
public long? WeekDefinitionId { get; init; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// فقط موارد Expired نشده؟
|
||||
|
||||
+14
-7
@@ -3,15 +3,20 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetUserWeeklyBalances
|
||||
public class GetUserWeeklyBalancesQueryHandler : IRequestHandler<GetUserWeeklyBalancesQuery, GetUserWeeklyBalancesResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetUserWeeklyBalancesQueryHandler(IApplicationDbContext context)
|
||||
public GetUserWeeklyBalancesQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<GetUserWeeklyBalancesResponseDto> Handle(GetUserWeeklyBalancesQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.NetworkWeeklyBalances
|
||||
.Include(x => x.WeekDefinition)
|
||||
.AsNoTracking()
|
||||
.AsQueryable();
|
||||
|
||||
@@ -21,19 +26,20 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler<GetUserWeeklyBa
|
||||
query = query.Where(x => x.UserId == request.UserId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.WeekNumber))
|
||||
// فیلتر بر اساس WeekDefinitionId (روش ترجیحی)
|
||||
if (request.WeekDefinitionId.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.WeekNumber == request.WeekNumber);
|
||||
query = query.Where(x => x.WeekDefinitionId == request.WeekDefinitionId.Value);
|
||||
}
|
||||
|
||||
|
||||
if (request.OnlyActive.HasValue && request.OnlyActive.Value)
|
||||
{
|
||||
query = query.Where(x => !x.IsExpired);
|
||||
}
|
||||
|
||||
// نمیتوانیم "-WeekNumber" استفاده کنیم چون WeekNumber یک string است
|
||||
// از Created برای مرتبسازی استفاده میکنیم (جدیدترین هفتهها اول)
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||
// مرتبسازی بر اساس WeekDefinitionId (نزولی = جدیدترین اول)
|
||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "-WeekDefinitionId");
|
||||
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
|
||||
@@ -43,7 +49,8 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler<GetUserWeeklyBa
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
WeekNumber = x.WeekNumber,
|
||||
WeekDefinitionId = x.WeekDefinitionId,
|
||||
WeekDisplayName = x.WeekDefinition != null ? x.WeekDefinition.DisplayName : "",
|
||||
LeftLegBalances = x.LeftLegBalances,
|
||||
RightLegBalances = x.RightLegBalances,
|
||||
TotalBalances = x.TotalBalances,
|
||||
|
||||
+4
-4
@@ -9,10 +9,10 @@ public class GetUserWeeklyBalancesQueryValidator : AbstractValidator<GetUserWeek
|
||||
.WithMessage("شناسه کاربر معتبر نیست")
|
||||
.When(x => x.UserId.HasValue);
|
||||
|
||||
RuleFor(x => x.WeekNumber)
|
||||
.Matches(@"^\d{4}-W\d{2}$")
|
||||
.WithMessage("فرمت شماره هفته باید YYYY-Www باشد")
|
||||
.When(x => !string.IsNullOrEmpty(x.WeekNumber));
|
||||
RuleFor(x => x.WeekDefinitionId)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شماره هفته نمیتواند صفر باشد")
|
||||
.When(x => x.WeekDefinitionId.HasValue);
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
|
||||
+3
-1
@@ -10,7 +10,9 @@ public class GetUserWeeklyBalancesResponseModel
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public string WeekNumber { get; set; } = string.Empty;
|
||||
public long WeekDefinitionId { get; set; }
|
||||
|
||||
public string WeekDisplayName { get; set; } = string.Empty;
|
||||
public int LeftLegBalances { get; set; }
|
||||
public int RightLegBalances { get; set; }
|
||||
public int TotalBalances { get; set; }
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
|
||||
namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWeekDefinitions;
|
||||
|
||||
/// <summary>
|
||||
/// Query برای دریافت لیست هفتهها از کش (برای dropdown)
|
||||
/// </summary>
|
||||
public record GetWeekDefinitionsQuery : IRequest<GetWeekDefinitionsResponseDto>
|
||||
{
|
||||
//موقعیت صفحه بندی
|
||||
public PaginationState? PaginationState { get; init; }
|
||||
|
||||
//مرتب سازی بر اساس
|
||||
public string? SortBy { get; init; }
|
||||
|
||||
//فیلتر
|
||||
public GetWeekDefinitionsFilter? Filter { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// فیلترهای جستجوی هفتهها
|
||||
/// </summary>
|
||||
public class GetWeekDefinitionsFilter
|
||||
{
|
||||
//جستجوی متنی روی DisplayName (مثل "هفته یک")
|
||||
public string? SearchText { get; set; }
|
||||
|
||||
//شماره ترتیب هفته
|
||||
public int? WeekOrder { get; set; }
|
||||
|
||||
//شماره هفته میلادی (2025-W46)
|
||||
public string? GregorianWeekNumber { get; set; }
|
||||
|
||||
//شماره هفته شمسی (1404-W35)
|
||||
public string? PersianWeekNumber { get; set; }
|
||||
|
||||
//سال میلادی
|
||||
public int? GregorianYear { get; set; }
|
||||
|
||||
//سال شمسی
|
||||
public int? PersianYear { get; set; }
|
||||
|
||||
//فقط هفتههای فعال
|
||||
public bool? IsActive { get; set; }
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
|
||||
namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWeekDefinitions;
|
||||
|
||||
/// <summary>
|
||||
/// Handler برای دریافت لیست هفتهها از کش
|
||||
/// </summary>
|
||||
public class GetWeekDefinitionsQueryHandler : IRequestHandler<GetWeekDefinitionsQuery, GetWeekDefinitionsResponseDto>
|
||||
{
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetWeekDefinitionsQueryHandler(IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public Task<GetWeekDefinitionsResponseDto> Handle(
|
||||
GetWeekDefinitionsQuery request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// گرفتن همه هفتهها از کش
|
||||
var allWeeks = _weekDefinitionRepository.GetAllWeeks();
|
||||
|
||||
// اعمال فیلترها
|
||||
var filteredWeeks = allWeeks.AsEnumerable();
|
||||
|
||||
if (request.Filter != null)
|
||||
{
|
||||
var filter = request.Filter;
|
||||
|
||||
// جستجوی متنی روی DisplayName
|
||||
if (!string.IsNullOrWhiteSpace(filter.SearchText))
|
||||
{
|
||||
filteredWeeks = filteredWeeks.Where(w =>
|
||||
w.DisplayName.Contains(filter.SearchText, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
// فیلتر بر اساس WeekOrder
|
||||
if (filter.WeekOrder.HasValue)
|
||||
{
|
||||
filteredWeeks = filteredWeeks.Where(w => w.WeekOrder == filter.WeekOrder.Value);
|
||||
}
|
||||
|
||||
// فیلتر بر اساس GregorianWeekNumber
|
||||
if (!string.IsNullOrWhiteSpace(filter.GregorianWeekNumber))
|
||||
{
|
||||
filteredWeeks = filteredWeeks.Where(w =>
|
||||
w.GregorianWeekNumber == filter.GregorianWeekNumber);
|
||||
}
|
||||
|
||||
// فیلتر بر اساس PersianWeekNumber
|
||||
if (!string.IsNullOrWhiteSpace(filter.PersianWeekNumber))
|
||||
{
|
||||
filteredWeeks = filteredWeeks.Where(w =>
|
||||
w.PersianWeekNumber == filter.PersianWeekNumber);
|
||||
}
|
||||
|
||||
// فیلتر بر اساس سال میلادی
|
||||
if (filter.GregorianYear.HasValue)
|
||||
{
|
||||
filteredWeeks = filteredWeeks.Where(w => w.GregorianYear == filter.GregorianYear.Value);
|
||||
}
|
||||
|
||||
// فیلتر بر اساس سال شمسی
|
||||
if (filter.PersianYear.HasValue)
|
||||
{
|
||||
filteredWeeks = filteredWeeks.Where(w => w.PersianYear == filter.PersianYear.Value);
|
||||
}
|
||||
|
||||
// فیلتر بر اساس فعال بودن
|
||||
if (filter.IsActive.HasValue)
|
||||
{
|
||||
filteredWeeks = filteredWeeks.Where(w => w.IsActive == filter.IsActive.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// مرتبسازی
|
||||
filteredWeeks = request.SortBy?.ToLower() switch
|
||||
{
|
||||
"weekorder_desc" => filteredWeeks.OrderByDescending(w => w.WeekOrder),
|
||||
"displayname" => filteredWeeks.OrderBy(w => w.DisplayName),
|
||||
"displayname_desc" => filteredWeeks.OrderByDescending(w => w.DisplayName),
|
||||
"startdate" => filteredWeeks.OrderBy(w => w.StartDate),
|
||||
"startdate_desc" => filteredWeeks.OrderByDescending(w => w.StartDate),
|
||||
_ => filteredWeeks.OrderBy(w => w.WeekOrder) // پیشفرض: weekorder
|
||||
};
|
||||
|
||||
// تعداد کل قبل از صفحهبندی
|
||||
var weeksList = filteredWeeks.ToList();
|
||||
var totalCount = weeksList.Count;
|
||||
|
||||
// صفحهبندی
|
||||
if (request.PaginationState != null)
|
||||
{
|
||||
var pageNumber = request.PaginationState.PageNumber > 0 ? request.PaginationState.PageNumber : 1;
|
||||
var pageSize = request.PaginationState.PageSize > 0 ? request.PaginationState.PageSize : 20;
|
||||
|
||||
weeksList = weeksList
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// گرفتن هفته جاری برای مشخص کردن IsCurrentWeek
|
||||
var currentWeek = _weekDefinitionRepository.GetCurrentWeek();
|
||||
|
||||
// تبدیل به DTO
|
||||
var weekDtos = weeksList.Select(w => new WeekDefinitionItemDto
|
||||
{
|
||||
WeekOrder = w.WeekOrder,
|
||||
DisplayName = w.DisplayName,
|
||||
GregorianWeekNumber = w.GregorianWeekNumber,
|
||||
PersianWeekNumber = w.PersianWeekNumber,
|
||||
StartDate = w.StartDate,
|
||||
EndDate = w.EndDate,
|
||||
GregorianYear = w.GregorianYear,
|
||||
PersianYear = w.PersianYear,
|
||||
IsActive = w.IsActive,
|
||||
IsCurrentWeek = currentWeek != null && w.WeekOrder == currentWeek.WeekOrder
|
||||
}).ToList();
|
||||
|
||||
var response = new GetWeekDefinitionsResponseDto
|
||||
{
|
||||
Data = weekDtos,
|
||||
TotalCount = totalCount
|
||||
};
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWeekDefinitions;
|
||||
|
||||
/// <summary>
|
||||
/// DTO برای پاسخ لیست هفتهها
|
||||
/// </summary>
|
||||
public class GetWeekDefinitionsResponseDto
|
||||
{
|
||||
//لیست هفتهها
|
||||
public List<WeekDefinitionItemDto> Data { get; set; } = new();
|
||||
|
||||
//تعداد کل
|
||||
public int TotalCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// آیتم هفته برای dropdown
|
||||
/// </summary>
|
||||
public class WeekDefinitionItemDto
|
||||
{
|
||||
//شماره ترتیب هفته (1, 2, 3, ...)
|
||||
public int WeekOrder { get; set; }
|
||||
|
||||
//نام نمایشی هفته (هفته یکم، هفته دوم، ...)
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
|
||||
//شماره هفته میلادی (2025-W46)
|
||||
public string GregorianWeekNumber { get; set; } = string.Empty;
|
||||
|
||||
//شماره هفته شمسی (1404-W35)
|
||||
public string PersianWeekNumber { get; set; } = string.Empty;
|
||||
|
||||
//تاریخ شروع هفته
|
||||
public DateTime StartDate { get; set; }
|
||||
|
||||
//تاریخ پایان هفته
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
//سال میلادی
|
||||
public int GregorianYear { get; set; }
|
||||
|
||||
//سال شمسی
|
||||
public int PersianYear { get; set; }
|
||||
|
||||
//فعال بودن
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
//آیا هفته جاری است؟
|
||||
public bool IsCurrentWeek { get; set; }
|
||||
}
|
||||
+1
-1
@@ -8,5 +8,5 @@ public record GetWeeklyCommissionPoolQuery : IRequest<WeeklyCommissionPoolDto?>
|
||||
/// <summary>
|
||||
/// شماره هفته
|
||||
/// </summary>
|
||||
public string WeekNumber { get; init; } = string.Empty;
|
||||
public long WeekDefinitionId { get; init; }
|
||||
}
|
||||
|
||||
+16
-5
@@ -3,21 +3,27 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWeeklyCommissionPo
|
||||
public class GetWeeklyCommissionPoolQueryHandler : IRequestHandler<GetWeeklyCommissionPoolQuery, WeeklyCommissionPoolDto?>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetWeeklyCommissionPoolQueryHandler(IApplicationDbContext context)
|
||||
public GetWeeklyCommissionPoolQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<WeeklyCommissionPoolDto?> Handle(GetWeeklyCommissionPoolQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var pool = await _context.WeeklyCommissionPools
|
||||
var pool = await _context.WeeklyCommissionPools
|
||||
.Include(x => x.WeekDefinition)
|
||||
.AsNoTracking()
|
||||
.Where(x => x.WeekNumber == request.WeekNumber)
|
||||
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId)
|
||||
.Select(x => new WeeklyCommissionPoolDto
|
||||
{
|
||||
Id = x.Id,
|
||||
WeekNumber = x.WeekNumber,
|
||||
WeekDefinitionId = x.WeekDefinitionId,
|
||||
WeekDisplayName=x.WeekDefinition.DisplayName,
|
||||
TotalPoolAmount = x.TotalPoolAmount,
|
||||
TotalBalances = x.TotalBalances,
|
||||
ValuePerBalance = x.ValuePerBalance,
|
||||
@@ -27,6 +33,11 @@ public class GetWeeklyCommissionPoolQueryHandler : IRequestHandler<GetWeeklyComm
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
return pool??new WeeklyCommissionPoolDto();
|
||||
// if (pool != null)
|
||||
// {
|
||||
// pool.WeekDisplayName = _weekDefinitionRepository.GetDisplayNameByGregorianWeekNumber(pool.WeekNumber);
|
||||
// }
|
||||
|
||||
return pool ?? new WeeklyCommissionPoolDto();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -4,11 +4,9 @@ public class GetWeeklyCommissionPoolQueryValidator : AbstractValidator<GetWeekly
|
||||
{
|
||||
public GetWeeklyCommissionPoolQueryValidator()
|
||||
{
|
||||
RuleFor(x => x.WeekNumber)
|
||||
.NotEmpty()
|
||||
.WithMessage("شماره هفته نمیتواند خالی باشد")
|
||||
.Matches(@"^\d{4}-W\d{2}$")
|
||||
.WithMessage("فرمت شماره هفته باید YYYY-Www باشد");
|
||||
RuleFor(x => x.WeekDefinitionId)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("شماره هفته نمیتواند صفر باشد");
|
||||
}
|
||||
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWeeklyCommissionPo
|
||||
public class WeeklyCommissionPoolDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string WeekNumber { get; set; } = string.Empty;
|
||||
public long WeekDefinitionId { get; set; }
|
||||
public string WeekDisplayName { get; set; } = string.Empty;
|
||||
public long TotalPoolAmount { get; set; }
|
||||
public long TotalBalances { get; set; }
|
||||
public decimal ValuePerBalance { get; set; }
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ public class GetWithdrawalRequestsQuery : IRequest<GetWithdrawalRequestsResponse
|
||||
{
|
||||
public int? Status { get; set; } // CommissionPayoutStatus enum
|
||||
public long? UserId { get; set; }
|
||||
public string? WeekNumber { get; set; }
|
||||
public long? WeekDefinitionId { get; init; }
|
||||
public string? IbanNumber { get; set; }
|
||||
public PaginationState? PaginationState { get; set; }
|
||||
public string? SortBy { get; set; }
|
||||
|
||||
+10
-4
@@ -3,10 +3,14 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWithdrawalRequests
|
||||
public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRequestsQuery, GetWithdrawalRequestsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetWithdrawalRequestsQueryHandler(IApplicationDbContext context)
|
||||
public GetWithdrawalRequestsQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<GetWithdrawalRequestsResponseDto> Handle(GetWithdrawalRequestsQuery request, CancellationToken cancellationToken)
|
||||
@@ -14,6 +18,7 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRe
|
||||
var query = _context.UserCommissionPayouts
|
||||
.AsNoTracking()
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.WeekDefinition)
|
||||
.Where(x => x.WithdrawalMethod != null) // Only requests with withdrawal method
|
||||
.AsQueryable();
|
||||
|
||||
@@ -28,9 +33,9 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRe
|
||||
query = query.Where(x => x.UserId == request.UserId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(request.WeekNumber))
|
||||
if (request.WeekDefinitionId!= null && request.WeekDefinitionId >0)
|
||||
{
|
||||
query = query.Where(x => x.WeekNumber == request.WeekNumber);
|
||||
query = query.Where(x => x.WeekDefinitionId == request.WeekDefinitionId);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.IbanNumber))
|
||||
@@ -51,7 +56,8 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRe
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
UserName = x.User != null ? (x.User.FirstName + " " + x.User.LastName).Trim() : x.User?.Mobile ?? "N/A",
|
||||
WeekNumber = x.WeekNumber,
|
||||
WeekDefinitionId = x.WeekDefinitionId,
|
||||
WeekDisplayName =x.WeekDefinition.DisplayName,
|
||||
Amount = x.TotalAmount,
|
||||
Status = (int)x.Status,
|
||||
WithdrawalMethod = x.WithdrawalMethod.HasValue ? (int)x.WithdrawalMethod.Value : 0,
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@ public class WithdrawalRequestModel
|
||||
public long Id { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
public string WeekNumber { get; set; } = string.Empty;
|
||||
public long WeekDefinitionId { get; set; }
|
||||
public string WeekDisplayName { get; set; } = string.Empty;
|
||||
public long Amount { get; set; }
|
||||
public int Status { get; set; } // CommissionPayoutStatus enum
|
||||
public int? WithdrawalMethod { get; set; }
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWorkerExecutionLog
|
||||
|
||||
public record GetWorkerExecutionLogsQuery : IRequest<GetWorkerExecutionLogsResponseDto>
|
||||
{
|
||||
public string? WeekNumber { get; init; }
|
||||
public long? WeekDefinitionId { get; init; }
|
||||
public string? ExecutionId { get; init; }
|
||||
public bool? SuccessOnly { get; init; }
|
||||
public bool? FailedOnly { get; init; }
|
||||
|
||||
+18
-5
@@ -6,10 +6,14 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWorkerExecutionLog
|
||||
public class GetWorkerExecutionLogsQueryHandler : IRequestHandler<GetWorkerExecutionLogsQuery, GetWorkerExecutionLogsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetWorkerExecutionLogsQueryHandler(IApplicationDbContext context)
|
||||
public GetWorkerExecutionLogsQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<GetWorkerExecutionLogsResponseDto> Handle(
|
||||
@@ -17,12 +21,14 @@ public class GetWorkerExecutionLogsQueryHandler : IRequestHandler<GetWorkerExecu
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// Query from database
|
||||
var query = _context.WorkerExecutionLogs.AsQueryable();
|
||||
var query = _context.WorkerExecutionLogs
|
||||
.Include(i=> i.WeekDefinition)
|
||||
.AsQueryable();
|
||||
|
||||
// Apply filters
|
||||
if (!string.IsNullOrEmpty(request.WeekNumber))
|
||||
if (request.WeekDefinitionId!=null && request.WeekDefinitionId > 0)
|
||||
{
|
||||
query = query.Where(x => x.WeekNumber == request.WeekNumber);
|
||||
query = query.Where(x => x.WeekDefinitionId == request.WeekDefinitionId);
|
||||
}
|
||||
|
||||
if (request.SuccessOnly == true)
|
||||
@@ -49,7 +55,8 @@ public class GetWorkerExecutionLogsQueryHandler : IRequestHandler<GetWorkerExecu
|
||||
.Select(x => new WorkerExecutionLogModel
|
||||
{
|
||||
ExecutionId = x.ExecutionId.ToString(),
|
||||
WeekNumber = x.WeekNumber,
|
||||
WeekDefinitionId = x.WeekDefinitionId,
|
||||
WeekDisplayName = x.WeekDefinition.DisplayName,
|
||||
Step = "Full", // We only have full execution now
|
||||
Success = x.Status == Domain.Entities.Commission.WorkerExecutionStatus.Success ||
|
||||
x.Status == Domain.Entities.Commission.WorkerExecutionStatus.SuccessWithWarnings,
|
||||
@@ -62,6 +69,12 @@ public class GetWorkerExecutionLogsQueryHandler : IRequestHandler<GetWorkerExecu
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// // Populate WeekDisplayName from cache
|
||||
// foreach (var log in logs)
|
||||
// {
|
||||
// log.WeekDisplayName = _weekDefinitionRepository.GetDisplayNameByGregorianWeekNumber(log.WeekNumber);
|
||||
// }
|
||||
|
||||
return new GetWorkerExecutionLogsResponseDto
|
||||
{
|
||||
MetaData = new MetaData
|
||||
|
||||
+2
-1
@@ -11,7 +11,8 @@ public class GetWorkerExecutionLogsResponseDto
|
||||
public class WorkerExecutionLogModel
|
||||
{
|
||||
public string ExecutionId { get; set; } = string.Empty;
|
||||
public string WeekNumber { get; set; } = string.Empty;
|
||||
public long WeekDefinitionId { get; set; }
|
||||
public string WeekDisplayName { get; set; } = string.Empty;
|
||||
public string Step { get; set; } = string.Empty; // "Balances" | "Pool" | "Payouts" | "Full"
|
||||
public bool Success { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
+12
-2
@@ -5,10 +5,14 @@ namespace CMSMicroservice.Application.CommissionCQ.Queries.GetWorkerStatus;
|
||||
public class GetWorkerStatusQueryHandler : IRequestHandler<GetWorkerStatusQuery, GetWorkerStatusResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetWorkerStatusQueryHandler(IApplicationDbContext context)
|
||||
public GetWorkerStatusQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<GetWorkerStatusResponseDto> Handle(
|
||||
@@ -19,13 +23,19 @@ public class GetWorkerStatusQueryHandler : IRequestHandler<GetWorkerStatusQuery,
|
||||
// فعلاً mock data برمیگرداند
|
||||
|
||||
await Task.CompletedTask;
|
||||
|
||||
var currentWeek = _weekDefinitionRepository.GetCurrentWeek();
|
||||
var currentWeekNumber = currentWeek != null
|
||||
? $"{currentWeek.GregorianYear}-W{currentWeek.GregorianWeekNumber:D2}"
|
||||
: null;
|
||||
|
||||
return new GetWorkerStatusResponseDto
|
||||
{
|
||||
IsRunning = false,
|
||||
IsEnabled = true,
|
||||
CurrentExecutionId = null,
|
||||
CurrentWeekNumber = null,
|
||||
CurrentWeekNumber = currentWeekNumber,
|
||||
CurrentWeekDisplayName = currentWeek?.DisplayName,
|
||||
CurrentStep = "Idle",
|
||||
LastRunAt = DateTime.Now.AddHours(-24),
|
||||
NextScheduledRun = DateTime.Now.AddDays(7),
|
||||
|
||||
+1
@@ -6,6 +6,7 @@ public class GetWorkerStatusResponseDto
|
||||
public bool IsEnabled { get; set; }
|
||||
public string? CurrentExecutionId { get; set; }
|
||||
public string? CurrentWeekNumber { get; set; }
|
||||
public string? CurrentWeekDisplayName { get; set; }
|
||||
public string? CurrentStep { get; set; } // "Balances" | "Pool" | "Payouts" | "Idle"
|
||||
public DateTime? LastRunAt { get; set; }
|
||||
public DateTime? NextScheduledRun { get; set; }
|
||||
|
||||
@@ -40,6 +40,7 @@ public interface IApplicationDbContext
|
||||
DbSet<UserClubFeature> UserClubFeatures { get; }
|
||||
DbSet<NetworkWeeklyBalance> NetworkWeeklyBalances { get; }
|
||||
DbSet<NetworkMembershipHistory> NetworkMembershipHistories { get; }
|
||||
DbSet<WeekDefinition> WeekDefinitions { get; }
|
||||
DbSet<WeeklyCommissionPool> WeeklyCommissionPools { get; }
|
||||
DbSet<UserCommissionPayout> UserCommissionPayouts { get; }
|
||||
DbSet<CommissionPayoutHistory> CommissionPayoutHistories { get; }
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
|
||||
namespace CMSMicroservice.Application.Common.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// اینترفیس ریپازیتوری هفتهها با کش در حافظه
|
||||
/// این سرویس موقع استارت برنامه از دیتابیس لود میشود و در حافظه نگهداری میشود
|
||||
/// </summary>
|
||||
public interface IWeekDefinitionRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// گرفتن هفته جاری
|
||||
/// </summary>
|
||||
WeekDefinition? GetCurrentWeek();
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته جاری (async برای سازگاری)
|
||||
/// </summary>
|
||||
Task<WeekDefinition?> GetCurrentWeekAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته بعد
|
||||
/// </summary>
|
||||
WeekDefinition? GetNextWeek();
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته بعد از هفته مشخص
|
||||
/// </summary>
|
||||
WeekDefinition? GetNextWeek(WeekDefinition currentWeek);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته قبل
|
||||
/// </summary>
|
||||
WeekDefinition? GetPreviousWeek();
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته قبل از هفته مشخص
|
||||
/// </summary>
|
||||
WeekDefinition? GetPreviousWeek(WeekDefinition currentWeek);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته بر اساس تاریخ میلادی
|
||||
/// </summary>
|
||||
WeekDefinition? GetWeekByDate(DateTime date);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته بر اساس شماره هفته میلادی (مثال: "2025-W46")
|
||||
/// </summary>
|
||||
WeekDefinition? GetWeekByGregorianWeekNumber(string gregorianWeekNumber);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته بر اساس شماره هفته شمسی (مثال: "1404-W35")
|
||||
/// </summary>
|
||||
WeekDefinition? GetWeekByPersianWeekNumber(string persianWeekNumber);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفته بر اساس شماره ترتیبی
|
||||
/// </summary>
|
||||
WeekDefinition? GetWeekByOrder(int weekOrder);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن لیست همه هفتهها
|
||||
/// </summary>
|
||||
IReadOnlyList<WeekDefinition> GetAllWeeks();
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفتههای یک سال میلادی خاص
|
||||
/// </summary>
|
||||
IReadOnlyList<WeekDefinition> GetWeeksByGregorianYear(int year);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن هفتههای یک سال شمسی خاص
|
||||
/// </summary>
|
||||
IReadOnlyList<WeekDefinition> GetWeeksByPersianYear(int year);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن شماره هفته میلادی جاری (مثال: "2025-W51")
|
||||
/// </summary>
|
||||
string GetCurrentGregorianWeekNumber();
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن شماره هفته شمسی جاری (مثال: "1404-W39")
|
||||
/// </summary>
|
||||
string GetCurrentPersianWeekNumber();
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن نام نمایشی هفته جاری (مثال: "هفته ششم")
|
||||
/// </summary>
|
||||
string GetCurrentWeekDisplayName();
|
||||
|
||||
/// <summary>
|
||||
/// آیا تاریخ مشخص در هفته جاری است؟
|
||||
/// </summary>
|
||||
bool IsDateInCurrentWeek(DateTime date);
|
||||
|
||||
/// <summary>
|
||||
/// محاسبه شماره هفته میلادی برای یک تاریخ (حتی اگر در کش نباشد)
|
||||
/// </summary>
|
||||
string CalculateGregorianWeekNumber(DateTime date);
|
||||
|
||||
/// <summary>
|
||||
/// محاسبه شماره هفته شمسی برای یک تاریخ (حتی اگر در کش نباشد)
|
||||
/// </summary>
|
||||
string CalculatePersianWeekNumber(DateTime date);
|
||||
|
||||
/// <summary>
|
||||
/// بارگذاری مجدد کش از دیتابیس
|
||||
/// </summary>
|
||||
Task ReloadCacheAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن بازه تاریخی یک هفته بر اساس شماره هفته میلادی
|
||||
/// </summary>
|
||||
(DateTime startDate, DateTime endDate)? GetWeekDateRange(string gregorianWeekNumber);
|
||||
|
||||
(DateTime startDate, DateTime endDate)? GetWeekDateRange(long WeekDefinitionId);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن شماره هفته قبل از هفته مشخص
|
||||
/// </summary>
|
||||
string? GetPreviousWeekNumber(string gregorianWeekNumber);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن شماره هفته بعد از هفته مشخص
|
||||
/// </summary>
|
||||
string? GetNextWeekNumber(string gregorianWeekNumber);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن DisplayName یک هفته بر اساس شماره هفته میلادی (مثل "هفته یکم")
|
||||
/// </summary>
|
||||
string GetDisplayNameByGregorianWeekNumber(string gregorianWeekNumber);
|
||||
|
||||
/// <summary>
|
||||
/// جستجوی هفتهها بر اساس DisplayName (برای dropdown)
|
||||
/// اگر فیلتر خالی باشد همه هفتهها برگردانده میشود
|
||||
/// </summary>
|
||||
IEnumerable<WeekDefinition> SearchWeeksByDisplayName(string? filter = null);
|
||||
|
||||
/// <summary>
|
||||
/// گرفتن WeekDefinitionId از شماره هفته میلادی
|
||||
/// </summary>
|
||||
/// <param name="gregorianWeekNumber">شماره هفته میلادی (مثال: "2025-W46")</param>
|
||||
/// <returns>شناسه WeekDefinition یا null اگر پیدا نشد</returns>
|
||||
long? GetWeekDefinitionId(string gregorianWeekNumber);
|
||||
|
||||
WeekDefinition? GetWeekById(long weekDefinitionId);
|
||||
/// <summary>
|
||||
/// گرفتن شماره هفته میلادی از WeekDefinitionId
|
||||
/// </summary>
|
||||
/// <param name="weekDefinitionId">شناسه WeekDefinition</param>
|
||||
/// <returns>شماره هفته میلادی یا null اگر پیدا نشد</returns>
|
||||
string? GetGregorianWeekNumber(long weekDefinitionId);
|
||||
|
||||
/// <summary>
|
||||
/// آیا کش لود شده؟
|
||||
/// </summary>
|
||||
bool IsCacheLoaded { get; }
|
||||
|
||||
/// <summary>
|
||||
/// تعداد هفتههای موجود در کش
|
||||
/// </summary>
|
||||
int CachedWeeksCount { get; }
|
||||
}
|
||||
+18
-56
@@ -3,10 +3,14 @@ namespace CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkTree
|
||||
public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, NetworkTreeDto?>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IWeekDefinitionRepository _weekDefinitionRepository;
|
||||
|
||||
public GetNetworkTreeQueryHandler(IApplicationDbContext context)
|
||||
public GetNetworkTreeQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
IWeekDefinitionRepository weekDefinitionRepository)
|
||||
{
|
||||
_context = context;
|
||||
_weekDefinitionRepository = weekDefinitionRepository;
|
||||
}
|
||||
|
||||
public async Task<NetworkTreeDto?> Handle(GetNetworkTreeQuery request, CancellationToken cancellationToken)
|
||||
@@ -39,11 +43,13 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
|
||||
|
||||
// محاسبه شماره هفته فعالسازی
|
||||
string? activationWeekNumber = null;
|
||||
string? activationWeekDisplayName = null;
|
||||
bool isActivatedInTargetWeek = false;
|
||||
|
||||
if (user.ClubMembership?.ActivatedAt != null)
|
||||
{
|
||||
activationWeekNumber = CalculateWeekNumber(user.ClubMembership.ActivatedAt.Value);
|
||||
activationWeekDisplayName = _weekDefinitionRepository.GetDisplayNameByGregorianWeekNumber(activationWeekNumber);
|
||||
|
||||
// بررسی آیا در هفته هدف فعال شده است
|
||||
if (!string.IsNullOrEmpty(request.ActivationWeekNumber))
|
||||
@@ -63,6 +69,7 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
|
||||
ClubActivatedAt = user.ClubMembership?.ActivatedAt,
|
||||
IsClubActive = user.ClubMembership?.IsActive ?? false,
|
||||
ActivationWeekNumber = activationWeekNumber,
|
||||
ActivationWeekDisplayName = activationWeekDisplayName,
|
||||
IsActivatedInTargetWeek = isActivatedInTargetWeek,
|
||||
UserCreated = user.Created
|
||||
};
|
||||
@@ -116,64 +123,19 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
|
||||
/// <summary>
|
||||
/// محاسبه شماره هفته از تاریخ
|
||||
/// </summary>
|
||||
private static string CalculateWeekNumber(DateTime date)
|
||||
private string CalculateWeekNumber(DateTime date)
|
||||
{
|
||||
var year = date.Year;
|
||||
var jan1 = new DateTime(year, 1, 1);
|
||||
var daysToFirstSaturday = (7 - (int)jan1.DayOfWeek + 7) % 7;
|
||||
if (jan1.DayOfWeek == DayOfWeek.Saturday)
|
||||
daysToFirstSaturday = 0;
|
||||
// First try to get from repository cache
|
||||
var weekDef = _weekDefinitionRepository.GetWeekByDate(date);
|
||||
if (weekDef != null)
|
||||
{
|
||||
return $"{weekDef.GregorianYear}-W{weekDef.GregorianWeekNumber:D2}";
|
||||
}
|
||||
|
||||
var firstSaturday = jan1.AddDays(daysToFirstSaturday);
|
||||
var weekNum = date < firstSaturday ? 1 : ((date - firstSaturday).Days / 7) + 1;
|
||||
|
||||
return $"{year}-W{weekNum:D2}";
|
||||
// Fallback: use repository's calculation method
|
||||
return _weekDefinitionRepository.CalculateGregorianWeekNumber(date);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// تبدیل شماره هفته (مثلاً 2025-W05) به تاریخ شروع و پایان هفته
|
||||
/// </summary>
|
||||
private static (DateTime? StartDate, DateTime? EndDate) ParseWeekNumber(string weekNumber)
|
||||
{
|
||||
try
|
||||
{
|
||||
// فرمت: YYYY-W##
|
||||
var parts = weekNumber.Split('-');
|
||||
if (parts.Length != 2 || !parts[1].StartsWith("W"))
|
||||
return (null, null);
|
||||
|
||||
if (!int.TryParse(parts[0], out var year))
|
||||
return (null, null);
|
||||
|
||||
if (!int.TryParse(parts[1].Substring(1), out var weekNum))
|
||||
return (null, null);
|
||||
|
||||
// محاسبه اولین شنبه سال
|
||||
var jan1 = new DateTime(year, 1, 1);
|
||||
var daysToFirstSaturday = (7 - (int)jan1.DayOfWeek + 7) % 7;
|
||||
if (jan1.DayOfWeek == DayOfWeek.Saturday)
|
||||
daysToFirstSaturday = 0;
|
||||
|
||||
var firstSaturday = jan1.AddDays(daysToFirstSaturday);
|
||||
|
||||
// محاسبه تاریخ شروع هفته مورد نظر
|
||||
DateTime startDate;
|
||||
if (weekNum == 1)
|
||||
{
|
||||
startDate = firstSaturday;
|
||||
}
|
||||
else
|
||||
{
|
||||
startDate = firstSaturday.AddDays((weekNum - 1) * 7);
|
||||
}
|
||||
|
||||
var endDate = startDate.AddDays(7);
|
||||
|
||||
return (startDate, endDate);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
@@ -27,6 +27,11 @@ public class NetworkTreeDto
|
||||
/// </summary>
|
||||
public string? ActivationWeekNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// نام نمایشی هفته فعالسازی (مثل: هفته یکم)
|
||||
/// </summary>
|
||||
public string? ActivationWeekDisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// آیا کاربر در هفته هدف فعال شده است؟
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user