diff --git a/src/CMSMicroservice.Application/CMSMicroservice.Application.csproj b/src/CMSMicroservice.Application/CMSMicroservice.Application.csproj
index af74491..ff3298e 100644
--- a/src/CMSMicroservice.Application/CMSMicroservice.Application.csproj
+++ b/src/CMSMicroservice.Application/CMSMicroservice.Application.csproj
@@ -14,6 +14,8 @@
+
+
diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs
index 2c7fddf..109e4f7 100644
--- a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs
+++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs
@@ -17,15 +17,18 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler _logger;
public ActivateClubMembershipCommandHandler(
IApplicationDbContext context,
ICurrentUserService currentUser,
+ IWeekDefinitionRepository weekRepository,
ILogger logger)
{
_context = context;
_currentUser = currentUser;
+ _weekRepository = weekRepository;
_logger = logger;
}
@@ -250,16 +253,16 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler 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
- /// دریافت شماره هفته جاری (مثال: "2025-W50")
+ /// دریافت شناسه تعریف هفته جاری
///
- 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;
}
}
diff --git a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommand.cs b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommand.cs
index 7ce9bfb..94a2816 100644
--- a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommand.cs
+++ b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommand.cs
@@ -8,7 +8,7 @@ public record CalculateWeeklyBalancesCommand : IRequest
///
/// شماره هفته (فرمت: YYYY-Www مثل 2025-W01)
///
- public string WeekNumber { get; init; } = string.Empty;
+ public long WeekDefinitionId { get; init; }
///
/// آیا محاسبه مجدد انجام شود؟ (پیشفرض: false)
diff --git a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs
index d586cc4..7535301 100644
--- a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs
+++ b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs
@@ -3,22 +3,33 @@ namespace CMSMicroservice.Application.CommissionCQ.Commands.CalculateWeeklyBalan
public class CalculateWeeklyBalancesCommandHandler : IRequestHandler
{
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 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 x.WeekNumber == previousWeekNumber)
- .Select(x => new
- {
- x.UserId,
- x.LeftLegRemainder,
- x.RightLegRemainder
- })
- .ToDictionaryAsync(x => x.UserId, cancellationToken);
+ var previousWeekDefinitionId = GetPreviousWeekDefinitionId(request.WeekDefinitionId);
+ Dictionary 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();
+ }
var balancesList = new List();
var calculatedAt = DateTime.Now;
@@ -85,8 +102,8 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler
- /// شماره هفته قبل را محاسبه میکند
+ /// شناسه هفته قبل را از WeekDefinitionRepository میگیرد
///
- 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);
}
///
/// شمارش اعضای جدیدی که در این هفته به یک پا اضافه شدند
/// تا maxLevel لول پایینتر شمارش میشود
///
- private async Task CountNewMembersInLeg(long userId, NetworkLeg leg, string weekNumber, int maxLevel, CancellationToken cancellationToken)
+ private async Task 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
- /// تبدیل شماره هفته به بازه تاریخی
+ /// تبدیل شماره هفته به بازه تاریخی از WeekDefinitionRepository
///
- 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);
+ // }
///
/// محاسبه مجموع تعادلهای زیرمجموعه یک کاربر تا maxLevel لول پایینتر
diff --git a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandValidator.cs b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandValidator.cs
index 5ba35ec..119adde 100644
--- a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandValidator.cs
+++ b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandValidator.cs
@@ -4,11 +4,11 @@ public class CalculateWeeklyBalancesCommandValidator : AbstractValidator x.WeekNumber)
- .NotEmpty()
+ RuleFor(x => x.WeekDefinitionId)
+ .NotNull()
+ .GreaterThan(0)
.WithMessage("شماره هفته نمیتواند خالی باشد")
- .Matches(@"^\d{4}-W\d{2}$")
- .WithMessage("فرمت شماره هفته باید YYYY-Www باشد (مثل 2025-W01)");
+;
}
public Func
public string? ActivationWeekNumber { get; set; }
+ ///
+ /// نام نمایشی هفته فعالسازی (مثل: هفته یکم)
+ ///
+ public string? ActivationWeekDisplayName { get; set; }
+
///
/// آیا کاربر در هفته هدف فعال شده است؟
///
diff --git a/src/CMSMicroservice.Domain/Entities/Commission/UserCommissionPayout.cs b/src/CMSMicroservice.Domain/Entities/Commission/UserCommissionPayout.cs
index 413ba78..12cd658 100644
--- a/src/CMSMicroservice.Domain/Entities/Commission/UserCommissionPayout.cs
+++ b/src/CMSMicroservice.Domain/Entities/Commission/UserCommissionPayout.cs
@@ -15,10 +15,16 @@ public class UserCommissionPayout : BaseAuditableEntity
///
public virtual User User { get; set; }
+
///
- /// شماره هفته
+ /// شناسه تعریف هفته (کلید خارجی به WeekDefinition)
///
- public string WeekNumber { get; set; }
+ public long WeekDefinitionId { get; set; }
+
+ ///
+ /// WeekDefinition Navigation Property
+ ///
+ public virtual WeekDefinition WeekDefinition { get; set; }
///
/// شناسه استخر هفتگی
diff --git a/src/CMSMicroservice.Domain/Entities/Commission/WeeklyCommissionPool.cs b/src/CMSMicroservice.Domain/Entities/Commission/WeeklyCommissionPool.cs
index 83ee4ce..a5ba946 100644
--- a/src/CMSMicroservice.Domain/Entities/Commission/WeeklyCommissionPool.cs
+++ b/src/CMSMicroservice.Domain/Entities/Commission/WeeklyCommissionPool.cs
@@ -5,10 +5,16 @@ namespace CMSMicroservice.Domain.Entities.Commission;
///
public class WeeklyCommissionPool : BaseAuditableEntity
{
+
///
- /// شماره هفته (مثال: "2025-W48")
+ /// شناسه تعریف هفته (کلید خارجی به WeekDefinition)
///
- public string WeekNumber { get; set; }
+ public long WeekDefinitionId { get; set; }
+
+ ///
+ /// WeekDefinition Navigation Property
+ ///
+ public virtual WeekDefinition WeekDefinition { get; set; }
///
/// مجموع مبلغ جمعشده در استخر (ریال)
diff --git a/src/CMSMicroservice.Domain/Entities/Commission/WorkerExecutionLog.cs b/src/CMSMicroservice.Domain/Entities/Commission/WorkerExecutionLog.cs
index 682ffaf..f47048d 100644
--- a/src/CMSMicroservice.Domain/Entities/Commission/WorkerExecutionLog.cs
+++ b/src/CMSMicroservice.Domain/Entities/Commission/WorkerExecutionLog.cs
@@ -12,10 +12,17 @@ public class WorkerExecutionLog : BaseAuditableEntity
///
public Guid ExecutionId { get; set; }
+
+
///
- /// شماره هفته (مثلاً 2025-W48)
+ /// شناسه تعریف هفته (کلید خارجی به WeekDefinition)
///
- public string WeekNumber { get; set; } = string.Empty;
+ public long WeekDefinitionId { get; set; }
+
+ ///
+ /// WeekDefinition Navigation Property
+ ///
+ public virtual WeekDefinition WeekDefinition { get; set; }
///
/// زمان شروع اجرا
diff --git a/src/CMSMicroservice.Domain/Entities/History/CommissionPayoutHistory.cs b/src/CMSMicroservice.Domain/Entities/History/CommissionPayoutHistory.cs
index f4dc40d..d19322b 100644
--- a/src/CMSMicroservice.Domain/Entities/History/CommissionPayoutHistory.cs
+++ b/src/CMSMicroservice.Domain/Entities/History/CommissionPayoutHistory.cs
@@ -20,10 +20,16 @@ public class CommissionPayoutHistory : BaseAuditableEntity
///
public long UserId { get; set; }
+
///
- /// شماره هفته
+ /// شناسه تعریف هفته (کلید خارجی به WeekDefinition)
///
- public string WeekNumber { get; set; }
+ public long WeekDefinitionId { get; set; }
+
+ ///
+ /// WeekDefinition Navigation Property
+ ///
+ public virtual WeekDefinition WeekDefinition { get; set; }
///
/// مبلغ قبل از تغییر
diff --git a/src/CMSMicroservice.Domain/Entities/Network/NetworkWeeklyBalance.cs b/src/CMSMicroservice.Domain/Entities/Network/NetworkWeeklyBalance.cs
index f84ccbe..c60b705 100644
--- a/src/CMSMicroservice.Domain/Entities/Network/NetworkWeeklyBalance.cs
+++ b/src/CMSMicroservice.Domain/Entities/Network/NetworkWeeklyBalance.cs
@@ -16,9 +16,14 @@ public class NetworkWeeklyBalance : BaseAuditableEntity
public virtual User User { get; set; }
///
- /// شماره هفته (مثال: "2025-W48")
+ /// شناسه تعریف هفته (کلید خارجی به WeekDefinition)
///
- public string WeekNumber { get; set; }
+ public long WeekDefinitionId { get; set; }
+
+ ///
+ /// WeekDefinition Navigation Property
+ ///
+ public virtual WeekDefinition WeekDefinition { get; set; }
///
/// تعداد اعضای جدید شاخه چپ در این هفته
diff --git a/src/CMSMicroservice.Domain/Entities/WeekDefinition.cs b/src/CMSMicroservice.Domain/Entities/WeekDefinition.cs
new file mode 100644
index 0000000..3bd7dfe
--- /dev/null
+++ b/src/CMSMicroservice.Domain/Entities/WeekDefinition.cs
@@ -0,0 +1,67 @@
+using CMSMicroservice.Domain.Entities.Commission;
+using CMSMicroservice.Domain.Entities.History;
+using CMSMicroservice.Domain.Entities.Network;
+
+namespace CMSMicroservice.Domain.Entities;
+
+///
+/// تعریف هفتهها برای نمایش در سیستم
+/// این جدول شامل اطلاعات کامل هر هفته شامل شماره نمایشی، شماره میلادی، شماره شمسی و بازه تاریخی است
+///
+public class WeekDefinition : BaseAuditableEntity
+{
+ ///
+ /// شماره ترتیبی هفته (1، 2، 3، ...)
+ /// برای مرتبسازی و محاسبات استفاده میشود
+ ///
+ public int WeekOrder { get; set; }
+
+ ///
+ /// نام نمایشی هفته (مثال: "هفته یکم"، "هفته دوم"، "هفته سوم")
+ /// این متن برای نمایش به کاربر نهایی (ادمین و مشتری) استفاده میشود
+ ///
+ public string DisplayName { get; set; } = string.Empty;
+
+ ///
+ /// شماره هفته میلادی (مثال: "2025-W01", "2025-W02")
+ /// فرمت ISO 8601
+ ///
+ public string GregorianWeekNumber { get; set; } = string.Empty;
+
+ ///
+ /// شماره هفته شمسی (مثال: "1403-W01", "1403-W02")
+ ///
+ 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; } = true;
+
+ // Navigation Properties
+ public virtual ICollection? NetworkWeeklyBalances { get; set; }
+ public virtual ICollection? WeeklyCommissionPools { get; set; }
+ public virtual ICollection? UserCommissionPayouts { get; set; }
+ public virtual ICollection? WorkerExecutionLogs { get; set; }
+ public virtual ICollection? CommissionPayoutHistories { get; set; }
+}
diff --git a/src/CMSMicroservice.Infrastructure/BackgroundJobs/WeeklyCommissionJob.cs b/src/CMSMicroservice.Infrastructure/BackgroundJobs/WeeklyCommissionJob.cs
index 58d69d9..5c12945 100644
--- a/src/CMSMicroservice.Infrastructure/BackgroundJobs/WeeklyCommissionJob.cs
+++ b/src/CMSMicroservice.Infrastructure/BackgroundJobs/WeeklyCommissionJob.cs
@@ -21,16 +21,19 @@ public class WeeklyCommissionJob
private readonly IMediator _mediator;
private readonly ILogger _logger;
private readonly IApplicationDbContext _context;
+ private readonly IWeekDefinitionRepository _weekRepository;
private readonly ResiliencePipeline _retryPipeline;
public WeeklyCommissionJob(
IMediator mediator,
ILogger logger,
- IApplicationDbContext context)
+ IApplicationDbContext context,
+ IWeekDefinitionRepository weekRepository)
{
_mediator = mediator;
_logger = logger;
_context = context;
+ _weekRepository = weekRepository;
// Polly Retry: 3 attempts, exponential backoff (5min → 10min → 20min)
_retryPipeline = new ResiliencePipelineBuilder()
@@ -57,38 +60,42 @@ public class WeeklyCommissionJob
/// Execute weekly commission calculation with retry logic
/// Called by Hangfire scheduler or manually triggered
///
- /// Week number in YYYY-Www format (e.g., 2025-W48). If null, uses previous week.
+ /// شناسه هفته. اگر null باشد، هفته قبلی محاسبه میشود
/// Cancellation token
- public async Task ExecuteAsync(string? weekNumber = null, CancellationToken cancellationToken = default)
+ public async Task ExecuteAsync(long? weekDefinitionId = null, CancellationToken cancellationToken = default)
{
var executionId = Guid.NewGuid();
var startTime = DateTime.Now;
- // Use provided week number or calculate for PREVIOUS week (completed week)
- string targetWeekNumber;
- if (!string.IsNullOrWhiteSpace(weekNumber))
+ // Use provided WeekDefinitionId or calculate for PREVIOUS week (completed week)
+ long targetWeekDefinitionId;
+ if (weekDefinitionId.HasValue && weekDefinitionId.Value > 0)
{
- targetWeekNumber = weekNumber;
- _logger.LogInformation("📅 Using manually specified week: {WeekNumber}", targetWeekNumber);
+ targetWeekDefinitionId = weekDefinitionId.Value;
+ _logger.LogInformation("📅 Using manually specified WeekDefinitionId: {WeekDefinitionId}", targetWeekDefinitionId);
}
else
{
var previousWeek = DateTime.Now.AddDays(-7);
- targetWeekNumber = GetWeekNumber(previousWeek);
- _logger.LogInformation("📅 Using previous week (auto-calculated): {WeekNumber}", targetWeekNumber);
+ var weekDef = _weekRepository.GetWeekByDate(previousWeek);
+ if (weekDef == null)
+ {
+ throw new InvalidOperationException($"هفته برای تاریخ {previousWeek:yyyy-MM-dd} تعریف نشده است");
+ }
+ targetWeekDefinitionId = weekDef.Id;
+ _logger.LogInformation("📅 Using previous week (auto-calculated): WeekDefinitionId={WeekDefinitionId}, WeekNumber={WeekNumber}",
+ targetWeekDefinitionId, weekDef.GregorianWeekNumber);
}
- var previousWeekNumber = targetWeekNumber;
-
_logger.LogInformation(
- "🚀 [{ExecutionId}] Starting weekly commission calculation for {WeekNumber}",
- executionId, previousWeekNumber);
+ "🚀 [{ExecutionId}] Starting weekly commission calculation for WeekDefinitionId={WeekDefinitionId}",
+ executionId, targetWeekDefinitionId);
// Create execution log entry
var log = new WorkerExecutionLog
{
ExecutionId = executionId,
- WeekNumber = previousWeekNumber,
+ WeekDefinitionId = targetWeekDefinitionId,
StartedAt = startTime,
Status = WorkerExecutionStatus.Running
};
@@ -100,7 +107,7 @@ public class WeeklyCommissionJob
// Execute with retry pipeline
await _retryPipeline.ExecuteAsync(async ct =>
{
- await ExecuteWeeklyCalculationAsync(executionId, previousWeekNumber, ct);
+ await ExecuteWeeklyCalculationAsync(executionId, targetWeekDefinitionId, ct);
}, cancellationToken);
// Update log on success
@@ -113,9 +120,9 @@ public class WeeklyCommissionJob
// Get counts from database
var balancesCount = await _context.NetworkWeeklyBalances
- .CountAsync(x => x.WeekNumber == previousWeekNumber, cancellationToken);
+ .CountAsync(x => x.WeekDefinitionId == targetWeekDefinitionId, cancellationToken);
var payoutsCount = await _context.UserCommissionPayouts
- .CountAsync(x => x.WeekNumber == previousWeekNumber, cancellationToken);
+ .CountAsync(x => x.WeekDefinitionId == targetWeekDefinitionId, cancellationToken);
log.ProcessedCount = balancesCount + payoutsCount;
@@ -149,21 +156,28 @@ public class WeeklyCommissionJob
private async Task ExecuteWeeklyCalculationAsync(
Guid executionId,
- string weekNumber,
+ long weekDefinitionId,
CancellationToken cancellationToken)
{
// Check idempotency: Skip if already calculated
var existingPool = await _context.WeeklyCommissionPools
- .FirstOrDefaultAsync(x => x.WeekNumber == weekNumber, cancellationToken);
+ .FirstOrDefaultAsync(x => x.WeekDefinitionId == weekDefinitionId, cancellationToken);
if (existingPool != null && existingPool.IsCalculated)
{
_logger.LogWarning(
- "⚠️ [{ExecutionId}] Week {WeekNumber} already calculated. Skipping.",
- executionId, weekNumber);
+ "⚠️ [{ExecutionId}] WeekDefinitionId={WeekDefinitionId} already calculated. Skipping.",
+ executionId, weekDefinitionId);
return;
}
+ // دریافت WeekNumber برای command ها (فعلاً هنوز از WeekNumber استفاده میکنند)
+ var GregorianWeekNumber = _weekRepository.GetGregorianWeekNumber(weekDefinitionId);
+ if (string.IsNullOrEmpty(GregorianWeekNumber))
+ {
+ throw new InvalidOperationException($"WeekDefinitionId={weekDefinitionId} یافت نشد");
+ }
+
using var transaction = new System.Transactions.TransactionScope(
System.Transactions.TransactionScopeOption.Required,
new System.Transactions.TransactionOptions
@@ -182,41 +196,14 @@ public class WeeklyCommissionJob
await _mediator.Send(new TriggerWeeklyCalculationCommand
{
- WeekNumber = weekNumber,
+ WeekDefinitionId = weekDefinitionId,
ForceRecalculate = false
}, cancellationToken);
- // await _mediator.Send(new CalculateWeeklyBalancesCommand
- // {
- // WeekNumber = weekNumber,
- // ForceRecalculate = false
- // }, cancellationToken);
- //
- // // Step 2: Calculate global commission pool
- // _logger.LogInformation(
- // "💰 [{ExecutionId}] Step 2/3: Calculating commission pool...",
- // executionId);
- //
- // await _mediator.Send(new CalculateWeeklyCommissionPoolCommand
- // {
- // WeekNumber = weekNumber,
- // ForceRecalculate = false
- // }, cancellationToken);
- //
- // // Step 3: Distribute commissions to users
- // _logger.LogInformation(
- // "💸 [{ExecutionId}] Step 3/3: Processing user payouts...",
- // executionId);
- //
- // await _mediator.Send(new ProcessUserPayoutsCommand
- // {
- // WeekNumber = weekNumber,
- // ForceReprocess = false
- // }, cancellationToken);
transaction.Complete();
_logger.LogInformation(
- "✅ [{ExecutionId}] All 2 steps completed successfully",
+ "✅ [{ExecutionId}] All steps completed successfully",
executionId);
}
catch (Exception ex)
@@ -227,24 +214,4 @@ public class WeeklyCommissionJob
throw;
}
}
-
- ///
- /// Get ISO 8601 week number (YYYY-Www format)
- ///
- private static string GetWeekNumber(DateTime date)
- {
- var calendar = System.Globalization.CultureInfo.InvariantCulture.Calendar;
- var weekNumber = calendar.GetWeekOfYear(
- date,
- System.Globalization.CalendarWeekRule.FirstDay,
- DayOfWeek.Saturday);
-
- var year = date.Year;
- if (weekNumber >= 52 && date.Month == 1)
- year--;
- else if (weekNumber == 1 && date.Month == 12)
- year++;
-
- return $"{year}-W{weekNumber:D2}";
- }
}
diff --git a/src/CMSMicroservice.Infrastructure/ConfigureServices.cs b/src/CMSMicroservice.Infrastructure/ConfigureServices.cs
index 619c7a4..065df11 100644
--- a/src/CMSMicroservice.Infrastructure/ConfigureServices.cs
+++ b/src/CMSMicroservice.Infrastructure/ConfigureServices.cs
@@ -6,6 +6,7 @@ using CMSMicroservice.Infrastructure.BackgroundJobs;
using CMSMicroservice.Infrastructure.Services.Monitoring;
using CMSMicroservice.Infrastructure.Configuration;
using CMSMicroservice.Infrastructure.Services.Payment;
+using CMSMicroservice.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Authentication.JwtBearer;
@@ -83,6 +84,10 @@ public static class ConfigureServices
services.AddScoped(p => p.GetRequiredService());
+ // Week Definition Repository - کش در حافظه برای هفتهها (Singleton برای کش، با IServiceScopeFactory برای دسترسی به DbContext)
+ services.AddSingleton();
+ services.AddSingleton(sp => sp.GetRequiredService());
+
// Background Workers - Deprecated: Using Hangfire instead
// services.AddHostedService();
services.AddScoped(); // Hangfire Job (Scoped for DI)
diff --git a/src/CMSMicroservice.Infrastructure/Data/Seeding/WeekDefinitionSeeder.cs b/src/CMSMicroservice.Infrastructure/Data/Seeding/WeekDefinitionSeeder.cs
new file mode 100644
index 0000000..b36e47f
--- /dev/null
+++ b/src/CMSMicroservice.Infrastructure/Data/Seeding/WeekDefinitionSeeder.cs
@@ -0,0 +1,255 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using CMSMicroservice.Domain.Entities;
+using CMSMicroservice.Infrastructure.Persistence;
+using System.Globalization;
+using System.Collections.Generic;
+
+namespace CMSMicroservice.Infrastructure.Data.Seeding;
+
+///
+/// Seeder برای پر کردن جدول WeekDefinitions
+/// هفته اول سیستم: 2025-W46 میلادی = 1404-W35 شمسی
+/// شروع هفته شمسی: شنبه | شروع هفته میلادی ISO: دوشنبه
+///
+public class WeekDefinitionSeeder
+{
+ private readonly ApplicationDbContext _context;
+ private readonly ILogger _logger;
+ private readonly PersianCalendar _persianCalendar;
+
+ ///
+ /// تاریخ شروع هفته اول سیستم (شنبه 17 آبان 1404 = 8 نوامبر 2025)
+ /// این شنبه در هفته 46 میلادی (ISO) و هفته 35 شمسی قرار دارد
+ ///
+ private static readonly DateTime BaseWeekStartDate = new DateTime(2025, 11, 8); // شنبه 8 نوامبر 2025
+
+ public WeekDefinitionSeeder(
+ ApplicationDbContext context,
+ ILogger logger)
+ {
+ _context = context;
+ _logger = logger;
+ _persianCalendar = new PersianCalendar();
+ }
+
+ ///
+ /// Seed هفتهها از هفته مبدأ تا یک سال بعد
+ ///
+ public async Task SeedAsync(CancellationToken cancellationToken = default)
+ {
+ _logger.LogInformation("=== WeekDefinitionSeeder: Starting ===");
+ _logger.LogInformation("Base week starts at: {BaseDate:yyyy-MM-dd} (Saturday)", BaseWeekStartDate);
+
+ try
+ {
+ var existingCount = await _context.WeekDefinitions.CountAsync(cancellationToken);
+
+ if (existingCount > 0)
+ {
+ _logger.LogInformation("WeekDefinitions already has {Count} records. Checking for missing weeks...", existingCount);
+ }
+
+ // شروع از هفته مبدأ
+ var startDate = BaseWeekStartDate;
+ // پایان تا یک سال بعد از الان
+ var endDate = GetEndOfWeek(DateTime.Today.AddYears(1));
+
+ var currentDate = startDate;
+ var addedCount = 0;
+
+ while (currentDate <= endDate)
+ {
+ var weekStart = currentDate; // شنبه
+ var weekEnd = currentDate.AddDays(6); // جمعه
+
+ // محاسبه شماره ترتیبی هفته (از 1 شروع میشود)
+ var weekOrder = CalculateWeekOrder(weekStart);
+
+ // نام نمایشی: هفته یکم، هفته دوم، ...
+ var displayName = GetPersianOrdinalWeekName(weekOrder);
+
+ // شماره هفته میلادی ISO (بر اساس دوشنبه)
+ var gregorianWeekNumber = GetISOWeekNumber(weekStart);
+
+ // شماره هفته شمسی (بر اساس شنبه)
+ var persianWeekNumber = GetPersianWeekNumber(weekStart);
+
+ // چک کنیم که این هفته قبلاً ثبت نشده باشه
+ var exists = await _context.WeekDefinitions
+ .AnyAsync(w => w.StartDate == weekStart, cancellationToken);
+
+ if (!exists)
+ {
+ var weekDefinition = new WeekDefinition
+ {
+ WeekOrder = weekOrder,
+ DisplayName = displayName,
+ GregorianWeekNumber = gregorianWeekNumber,
+ PersianWeekNumber = persianWeekNumber,
+ StartDate = weekStart,
+ EndDate = weekEnd,
+ GregorianYear = weekStart.Year,
+ PersianYear = _persianCalendar.GetYear(weekStart),
+ IsActive = true
+ };
+
+ _context.WeekDefinitions.Add(weekDefinition);
+ addedCount++;
+
+ _logger.LogDebug("Added: {DisplayName} | {GregorianWeek} | {PersianWeek} | {Start:yyyy-MM-dd} - {End:yyyy-MM-dd}",
+ displayName, gregorianWeekNumber, persianWeekNumber, weekStart, weekEnd);
+ }
+
+ // رفتن به هفته بعد (7 روز)
+ currentDate = currentDate.AddDays(7);
+ }
+
+ if (addedCount > 0)
+ {
+ await _context.SaveChangesAsync(cancellationToken);
+ _logger.LogInformation("Successfully added {Count} week definitions", addedCount);
+ }
+ else
+ {
+ _logger.LogInformation("No new week definitions needed");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error seeding WeekDefinitions");
+ throw;
+ }
+
+ _logger.LogInformation("=== WeekDefinitionSeeder: Completed ===");
+ }
+
+ ///
+ /// محاسبه شماره ترتیبی هفته بر اساس فاصله از هفته مبدأ
+ /// هفته مبدأ = 1، هفته بعد = 2، ...
+ ///
+ private static int CalculateWeekOrder(DateTime weekStartDate)
+ {
+ var daysDiff = (weekStartDate - BaseWeekStartDate).Days;
+ var weeksDiff = daysDiff / 7;
+ return weeksDiff + 1;
+ }
+
+ ///
+ /// تبدیل عدد به نام ترتیبی فارسی
+ /// 1 -> هفته یکم، 2 -> هفته دوم، ...
+ ///
+ private static string GetPersianOrdinalWeekName(int weekOrder)
+ {
+ var ordinals = new Dictionary
+ {
+ { 1, "یکم" }, { 2, "دوم" }, { 3, "سوم" }, { 4, "چهارم" }, { 5, "پنجم" },
+ { 6, "ششم" }, { 7, "هفتم" }, { 8, "هشتم" }, { 9, "نهم" }, { 10, "دهم" },
+ { 11, "یازدهم" }, { 12, "دوازدهم" }, { 13, "سیزدهم" }, { 14, "چهاردهم" }, { 15, "پانزدهم" },
+ { 16, "شانزدهم" }, { 17, "هفدهم" }, { 18, "هجدهم" }, { 19, "نوزدهم" }, { 20, "بیستم" },
+ { 21, "بیست و یکم" }, { 22, "بیست و دوم" }, { 23, "بیست و سوم" }, { 24, "بیست و چهارم" }, { 25, "بیست و پنجم" },
+ { 26, "بیست و ششم" }, { 27, "بیست و هفتم" }, { 28, "بیست و هشتم" }, { 29, "بیست و نهم" }, { 30, "سیام" },
+ { 31, "سی و یکم" }, { 32, "سی و دوم" }, { 33, "سی و سوم" }, { 34, "سی و چهارم" }, { 35, "سی و پنجم" },
+ { 36, "سی و ششم" }, { 37, "سی و هفتم" }, { 38, "سی و هشتم" }, { 39, "سی و نهم" }, { 40, "چهلم" },
+ { 41, "چهل و یکم" }, { 42, "چهل و دوم" }, { 43, "چهل و سوم" }, { 44, "چهل و چهارم" }, { 45, "چهل و پنجم" },
+ { 46, "چهل و ششم" }, { 47, "چهل و هفتم" }, { 48, "چهل و هشتم" }, { 49, "چهل و نهم" }, { 50, "پنجاهم" },
+ { 51, "پنجاه و یکم" }, { 52, "پنجاه و دوم" }, { 53, "پنجاه و سوم" }, { 54, "پنجاه و چهارم" }, { 55, "پنجاه و پنجم" },
+ { 56, "پنجاه و ششم" }, { 57, "پنجاه و هفتم" }, { 58, "پنجاه و هشتم" }, { 59, "پنجاه و نهم" }, { 60, "شصتم" }
+ };
+
+ if (ordinals.TryGetValue(weekOrder, out var ordinal))
+ {
+ return $"هفته {ordinal}";
+ }
+
+ // برای اعداد بزرگتر از 60
+ return $"هفته {weekOrder}";
+ }
+
+ ///
+ /// پیدا کردن شنبه این هفته (شروع هفته شمسی)
+ ///
+ private static DateTime GetStartOfWeek(DateTime date)
+ {
+ var diff = (7 + (date.DayOfWeek - DayOfWeek.Saturday)) % 7;
+ return date.AddDays(-diff).Date;
+ }
+
+ ///
+ /// پیدا کردن جمعه این هفته (پایان هفته شمسی)
+ ///
+ private static DateTime GetEndOfWeek(DateTime date)
+ {
+ return GetStartOfWeek(date).AddDays(6);
+ }
+
+ ///
+ /// شماره هفته میلادی ISO 8601
+ /// هفته ISO از دوشنبه شروع میشود
+ /// برای تاریخ شنبه، باید دوشنبه همان هفته را پیدا کنیم (2 روز بعد)
+ ///
+ private static string GetISOWeekNumber(DateTime saturdayDate)
+ {
+ // شنبه + 2 روز = دوشنبه همان هفته ISO
+ var mondayOfWeek = saturdayDate.AddDays(2);
+
+ // محاسبه شماره هفته ISO
+ var cal = CultureInfo.InvariantCulture.Calendar;
+ var weekNumber = cal.GetWeekOfYear(mondayOfWeek, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
+ var year = mondayOfWeek.Year;
+
+ // تصحیح سال برای هفتههای لبهای
+ if (weekNumber == 1 && mondayOfWeek.Month == 12)
+ {
+ year++;
+ }
+ else if (weekNumber >= 52 && mondayOfWeek.Month == 1)
+ {
+ year--;
+ }
+
+ return $"{year}-W{weekNumber:D2}";
+ }
+
+ ///
+ /// شماره هفته شمسی
+ /// هفته شمسی از شنبه شروع میشود
+ ///
+ private string GetPersianWeekNumber(DateTime saturdayDate)
+ {
+ var persianYear = _persianCalendar.GetYear(saturdayDate);
+ var dayOfYear = _persianCalendar.GetDayOfYear(saturdayDate);
+
+ // اول فروردین این سال
+ var firstDayOfYear = _persianCalendar.ToDateTime(persianYear, 1, 1, 0, 0, 0, 0);
+
+ // پیدا کردن اولین شنبه سال
+ var daysUntilFirstSaturday = ((int)DayOfWeek.Saturday - (int)firstDayOfYear.DayOfWeek + 7) % 7;
+
+ // اگر اول فروردین شنبه باشه، daysUntilFirstSaturday = 0
+ // در غیر این صورت روزهای قبل از اولین شنبه، هفته 0 یا هفته آخر سال قبل هستند
+
+ int weekNumber;
+ if (daysUntilFirstSaturday == 0)
+ {
+ // اول فروردین شنبه است
+ weekNumber = ((dayOfYear - 1) / 7) + 1;
+ }
+ else
+ {
+ // روزهای قبل از اولین شنبه
+ if (dayOfYear <= daysUntilFirstSaturday)
+ {
+ // این روزها متعلق به آخرین هفته سال قبل هستند
+ // اما برای سادگی، هفته 1 در نظر میگیریم
+ weekNumber = 1;
+ }
+ else
+ {
+ weekNumber = ((dayOfYear - daysUntilFirstSaturday - 1) / 7) + 1;
+ }
+ }
+
+ return $"{persianYear}-W{weekNumber:D2}";
+ }
+}
diff --git a/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs b/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs
index 1bf1274..3e5a29c 100644
--- a/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs
+++ b/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs
@@ -98,6 +98,9 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
public DbSet NetworkWeeklyBalances => Set();
public DbSet NetworkMembershipHistories => Set();
+ // Week Definitions
+ public DbSet WeekDefinitions => Set();
+
// Commission
public DbSet WeeklyCommissionPools => Set();
public DbSet UserCommissionPayouts => Set();
diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/CommissionPayoutHistoryConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/CommissionPayoutHistoryConfiguration.cs
index f72bb1f..367deb1 100644
--- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/CommissionPayoutHistoryConfiguration.cs
+++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/CommissionPayoutHistoryConfiguration.cs
@@ -18,7 +18,7 @@ public class CommissionPayoutHistoryConfiguration : IEntityTypeConfiguration entity.UserCommissionPayoutId).IsRequired();
builder.Property(entity => entity.UserId).IsRequired();
- builder.Property(entity => entity.WeekNumber).IsRequired().HasMaxLength(20);
+ builder.Property(entity => entity.WeekDefinitionId).IsRequired();
builder.Property(entity => entity.AmountBefore).IsRequired();
builder.Property(entity => entity.AmountAfter).IsRequired();
builder.Property(entity => entity.OldStatus).IsRequired();
@@ -33,6 +33,13 @@ public class CommissionPayoutHistoryConfiguration : IEntityTypeConfiguration entity.UserCommissionPayoutId)
.OnDelete(DeleteBehavior.Restrict);
+ // رابطه با WeekDefinition (Required FK)
+ builder.HasOne(entity => entity.WeekDefinition)
+ .WithMany(wd => wd.CommissionPayoutHistories)
+ .HasForeignKey(entity => entity.WeekDefinitionId)
+ .IsRequired()
+ .OnDelete(DeleteBehavior.Restrict);
+
// Index برای UserId و Created
builder.HasIndex(e => new { e.UserId, e.Created })
.HasDatabaseName("IX_CommissionPayoutHistory_UserId_Created");
@@ -41,9 +48,9 @@ public class CommissionPayoutHistoryConfiguration : IEntityTypeConfiguration e.UserCommissionPayoutId)
.HasDatabaseName("IX_CommissionPayoutHistory_PayoutId");
- // Index برای WeekNumber
- builder.HasIndex(e => e.WeekNumber)
- .HasDatabaseName("IX_CommissionPayoutHistory_WeekNumber");
+ // Index برای WeekDefinitionId
+ builder.HasIndex(e => e.WeekDefinitionId)
+ .HasDatabaseName("IX_CommissionPayoutHistory_WeekDefinitionId");
// Index برای Action
builder.HasIndex(e => e.Action)
diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/NetworkWeeklyBalanceConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/NetworkWeeklyBalanceConfiguration.cs
index 66301a5..e176f3f 100644
--- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/NetworkWeeklyBalanceConfiguration.cs
+++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/NetworkWeeklyBalanceConfiguration.cs
@@ -17,7 +17,7 @@ public class NetworkWeeklyBalanceConfiguration : IEntityTypeConfiguration entity.Id).UseIdentityColumn();
builder.Property(entity => entity.UserId).IsRequired();
- builder.Property(entity => entity.WeekNumber).IsRequired().HasMaxLength(20);
+ builder.Property(entity => entity.WeekDefinitionId).IsRequired();
builder.Property(entity => entity.LeftLegBalances).IsRequired();
builder.Property(entity => entity.RightLegBalances).IsRequired();
builder.Property(entity => entity.TotalBalances).IsRequired();
@@ -31,14 +31,20 @@ public class NetworkWeeklyBalanceConfiguration : IEntityTypeConfiguration entity.UserId)
.OnDelete(DeleteBehavior.Restrict);
- // Composite Index برای UserId و WeekNumber
- builder.HasIndex(e => new { e.UserId, e.WeekNumber })
- .IsUnique()
- .HasDatabaseName("IX_NetworkWeeklyBalance_UserId_WeekNumber");
+ // رابطه با WeekDefinition
+ builder.HasOne(entity => entity.WeekDefinition)
+ .WithMany(wd => wd.NetworkWeeklyBalances)
+ .HasForeignKey(entity => entity.WeekDefinitionId)
+ .OnDelete(DeleteBehavior.Restrict);
- // Index برای WeekNumber
- builder.HasIndex(e => e.WeekNumber)
- .HasDatabaseName("IX_NetworkWeeklyBalance_WeekNumber");
+ // Composite Index برای UserId و WeekDefinitionId
+ builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId })
+ .IsUnique()
+ .HasDatabaseName("IX_NetworkWeeklyBalance_UserId_WeekDefinitionId");
+
+ // Index برای WeekDefinitionId
+ builder.HasIndex(e => e.WeekDefinitionId)
+ .HasDatabaseName("IX_NetworkWeeklyBalance_WeekDefinitionId");
// Index برای IsExpired
builder.HasIndex(e => e.IsExpired)
diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs
index e01eaaf..f190faa 100644
--- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs
+++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs
@@ -17,7 +17,7 @@ public class UserCommissionPayoutConfiguration : IEntityTypeConfiguration entity.Id).UseIdentityColumn();
builder.Property(entity => entity.UserId).IsRequired();
- builder.Property(entity => entity.WeekNumber).IsRequired().HasMaxLength(20);
+ builder.Property(entity => entity.WeekDefinitionId).IsRequired();
builder.Property(entity => entity.WeeklyPoolId).IsRequired();
builder.Property(entity => entity.BalancesEarned).IsRequired();
builder.Property(entity => entity.ValuePerBalance).IsRequired();
@@ -43,10 +43,17 @@ public class UserCommissionPayoutConfiguration : IEntityTypeConfiguration entity.WeeklyPoolId)
.OnDelete(DeleteBehavior.Restrict);
- // Composite Index برای UserId و WeekNumber
- builder.HasIndex(e => new { e.UserId, e.WeekNumber })
+ // رابطه با WeekDefinition (Required FK)
+ builder.HasOne(entity => entity.WeekDefinition)
+ .WithMany(wd => wd.UserCommissionPayouts)
+ .HasForeignKey(entity => entity.WeekDefinitionId)
+ .IsRequired()
+ .OnDelete(DeleteBehavior.Restrict);
+
+ // Composite Index برای UserId و WeekDefinitionId
+ builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId })
.IsUnique()
- .HasDatabaseName("IX_UserCommissionPayout_UserId_WeekNumber");
+ .HasDatabaseName("IX_UserCommissionPayout_UserId_WeekDefinitionId");
// Index برای WeeklyPoolId
builder.HasIndex(e => e.WeeklyPoolId)
@@ -56,8 +63,5 @@ public class UserCommissionPayoutConfiguration : IEntityTypeConfiguration e.Status)
.HasDatabaseName("IX_UserCommissionPayout_Status");
- // Index برای WeekNumber
- builder.HasIndex(e => e.WeekNumber)
- .HasDatabaseName("IX_UserCommissionPayout_WeekNumber");
}
}
diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeekDefinitionConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeekDefinitionConfiguration.cs
new file mode 100644
index 0000000..b171ac8
--- /dev/null
+++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeekDefinitionConfiguration.cs
@@ -0,0 +1,51 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
+
+///
+/// تنظیمات جدول تعریف هفتهها
+///
+public class WeekDefinitionConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.HasQueryFilter(p => !p.IsDeleted);
+ builder.Ignore(entity => entity.DomainEvents);
+
+ builder.HasKey(entity => entity.Id);
+ builder.Property(entity => entity.Id).UseIdentityColumn();
+
+ builder.Property(entity => entity.WeekOrder).IsRequired();
+ builder.Property(entity => entity.DisplayName).IsRequired().HasMaxLength(50);
+ builder.Property(entity => entity.GregorianWeekNumber).IsRequired().HasMaxLength(20);
+ builder.Property(entity => entity.PersianWeekNumber).IsRequired().HasMaxLength(20);
+ builder.Property(entity => entity.StartDate).IsRequired();
+ builder.Property(entity => entity.EndDate).IsRequired();
+ builder.Property(entity => entity.GregorianYear).IsRequired();
+ builder.Property(entity => entity.PersianYear).IsRequired();
+ builder.Property(entity => entity.IsActive).IsRequired().HasDefaultValue(true);
+
+ // Unique Index برای GregorianWeekNumber
+ builder.HasIndex(e => e.GregorianWeekNumber)
+ .IsUnique()
+ .HasDatabaseName("IX_WeekDefinition_GregorianWeekNumber");
+
+ // Index برای PersianWeekNumber
+ builder.HasIndex(e => e.PersianWeekNumber)
+ .HasDatabaseName("IX_WeekDefinition_PersianWeekNumber");
+
+ // Index برای StartDate
+ builder.HasIndex(e => e.StartDate)
+ .HasDatabaseName("IX_WeekDefinition_StartDate");
+
+ // Index برای سالها
+ builder.HasIndex(e => e.GregorianYear)
+ .HasDatabaseName("IX_WeekDefinition_GregorianYear");
+
+ builder.HasIndex(e => e.PersianYear)
+ .HasDatabaseName("IX_WeekDefinition_PersianYear");
+
+ builder.ToTable("WeekDefinitions");
+ }
+}
diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeeklyCommissionPoolConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeeklyCommissionPoolConfiguration.cs
index 7d8217f..5a56c75 100644
--- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeeklyCommissionPoolConfiguration.cs
+++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeeklyCommissionPoolConfiguration.cs
@@ -16,17 +16,24 @@ public class WeeklyCommissionPoolConfiguration : IEntityTypeConfiguration entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
- builder.Property(entity => entity.WeekNumber).IsRequired().HasMaxLength(20);
+ builder.Property(entity => entity.WeekDefinitionId).IsRequired();
builder.Property(entity => entity.TotalPoolAmount).IsRequired();
builder.Property(entity => entity.TotalBalances).IsRequired();
builder.Property(entity => entity.ValuePerBalance).IsRequired();
builder.Property(entity => entity.IsCalculated).IsRequired();
builder.Property(entity => entity.CalculatedAt).IsRequired(false);
- // Index یونیک برای WeekNumber
- builder.HasIndex(e => e.WeekNumber)
+ // رابطه با WeekDefinition (Required FK)
+ builder.HasOne(entity => entity.WeekDefinition)
+ .WithMany(wd => wd.WeeklyCommissionPools)
+ .HasForeignKey(entity => entity.WeekDefinitionId)
+ .IsRequired()
+ .OnDelete(DeleteBehavior.Restrict);
+
+ // Index یونیک برای WeekDefinitionId
+ builder.HasIndex(e => e.WeekDefinitionId)
.IsUnique()
- .HasDatabaseName("IX_WeeklyCommissionPool_WeekNumber");
+ .HasDatabaseName("IX_WeeklyCommissionPool_WeekDefinitionId");
// Index برای IsCalculated
builder.HasIndex(e => e.IsCalculated)
diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WorkerExecutionLogConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WorkerExecutionLogConfiguration.cs
index 617ae33..483b5d9 100644
--- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WorkerExecutionLogConfiguration.cs
+++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WorkerExecutionLogConfiguration.cs
@@ -15,8 +15,7 @@ public class WorkerExecutionLogConfiguration : IEntityTypeConfiguration x.ExecutionId)
.IsRequired();
- builder.Property(x => x.WeekNumber)
- .HasMaxLength(10)
+ builder.Property(x => x.WeekDefinitionId)
.IsRequired();
builder.Property(x => x.StartedAt)
@@ -31,8 +30,15 @@ public class WorkerExecutionLogConfiguration : IEntityTypeConfiguration x.Details)
.HasColumnType("nvarchar(max)");
- // Index for querying by week
- builder.HasIndex(x => x.WeekNumber);
+ // رابطه با WeekDefinition (Required FK)
+ builder.HasOne(x => x.WeekDefinition)
+ .WithMany(wd => wd.WorkerExecutionLogs)
+ .HasForeignKey(x => x.WeekDefinitionId)
+ .IsRequired()
+ .OnDelete(DeleteBehavior.Restrict);
+
+ // Index for querying by WeekDefinitionId
+ builder.HasIndex(x => x.WeekDefinitionId);
// Index for querying by execution time
builder.HasIndex(x => x.StartedAt);
diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20251218213905_AddWeekDefinitionTable.Designer.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20251218213905_AddWeekDefinitionTable.Designer.cs
new file mode 100644
index 0000000..d30ba22
--- /dev/null
+++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20251218213905_AddWeekDefinitionTable.Designer.cs
@@ -0,0 +1,3599 @@
+//
+using System;
+using CMSMicroservice.Infrastructure.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace CMSMicroservice.Infrastructure.Persistence.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20251218213905_AddWeekDefinitionTable")]
+ partial class AddWeekDefinitionTable
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("CMS")
+ .HasAnnotation("ProductVersion", "9.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Category", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ImagePath")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ParentId")
+ .HasColumnType("bigint");
+
+ b.Property("SortOrder")
+ .HasColumnType("int");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentId");
+
+ b.ToTable("Categories", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubFeature", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SortOrder")
+ .HasColumnType("int");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IsActive", "SortOrder")
+ .HasDatabaseName("IX_ClubFeature_IsActive_SortOrder");
+
+ b.ToTable("ClubFeatures", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ActivatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("GiftValue")
+ .HasColumnType("bigint");
+
+ b.Property("InitialContribution")
+ .HasColumnType("bigint");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PurchaseMethod")
+ .HasColumnType("int");
+
+ b.Property("TotalEarned")
+ .HasColumnType("bigint");
+
+ b.Property("UserId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IsActive")
+ .HasDatabaseName("IX_ClubMembership_IsActive");
+
+ b.HasIndex("UserId")
+ .IsUnique()
+ .HasDatabaseName("IX_ClubMembership_UserId");
+
+ b.ToTable("ClubMemberships", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.UserClubFeature", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ClubFeatureId")
+ .HasColumnType("bigint");
+
+ b.Property("ClubMembershipId")
+ .HasColumnType("bigint");
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("GrantedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("IsActive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bit")
+ .HasDefaultValue(true);
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Notes")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("UserId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClubFeatureId");
+
+ b.HasIndex("ClubMembershipId")
+ .HasDatabaseName("IX_UserClubFeature_ClubMembershipId");
+
+ b.HasIndex("UserId", "ClubFeatureId")
+ .IsUnique()
+ .HasDatabaseName("IX_UserClubFeature_UserId_ClubFeatureId");
+
+ b.ToTable("UserClubFeatures", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("BalancesEarned")
+ .HasColumnType("int");
+
+ b.Property("BankReferenceId")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("BankTrackingCode")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IbanNumber")
+ .HasMaxLength(26)
+ .HasColumnType("nvarchar(26)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PaidAt")
+ .HasColumnType("datetime2");
+
+ b.Property("PaymentFailureReason")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ProcessedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("ProcessedBy")
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("RejectionReason")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("Status")
+ .HasColumnType("int");
+
+ b.Property("TotalAmount")
+ .HasColumnType("bigint");
+
+ b.Property("UserId")
+ .HasColumnType("bigint");
+
+ b.Property("ValuePerBalance")
+ .HasColumnType("bigint");
+
+ b.Property("WeekNumber")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("nvarchar(20)");
+
+ b.Property("WeeklyPoolId")
+ .HasColumnType("bigint");
+
+ b.Property("WithdrawalMethod")
+ .HasColumnType("int");
+
+ b.Property("WithdrawnAt")
+ .HasColumnType("datetime2");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Status")
+ .HasDatabaseName("IX_UserCommissionPayout_Status");
+
+ b.HasIndex("WeekNumber")
+ .HasDatabaseName("IX_UserCommissionPayout_WeekNumber");
+
+ b.HasIndex("WeeklyPoolId")
+ .HasDatabaseName("IX_UserCommissionPayout_WeeklyPoolId");
+
+ b.HasIndex("UserId", "WeekNumber")
+ .IsUnique()
+ .HasDatabaseName("IX_UserCommissionPayout_UserId_WeekNumber");
+
+ b.ToTable("UserCommissionPayouts", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CalculatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsCalculated")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TotalBalances")
+ .HasColumnType("int");
+
+ b.Property("TotalPoolAmount")
+ .HasColumnType("bigint");
+
+ b.Property("ValuePerBalance")
+ .HasColumnType("bigint");
+
+ b.Property("WeekNumber")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("nvarchar(20)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IsCalculated")
+ .HasDatabaseName("IX_WeeklyCommissionPool_IsCalculated");
+
+ b.HasIndex("WeekNumber")
+ .IsUnique()
+ .HasDatabaseName("IX_WeeklyCommissionPool_WeekNumber");
+
+ b.ToTable("WeeklyCommissionPools", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WorkerExecutionLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CompletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Details")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DurationMs")
+ .HasColumnType("bigint");
+
+ b.Property("ErrorCount")
+ .HasColumnType("int");
+
+ b.Property("ErrorMessage")
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("ErrorStackTrace")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ExecutionId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ProcessedCount")
+ .HasColumnType("int");
+
+ b.Property("StartedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("Status")
+ .HasColumnType("int");
+
+ b.Property("WeekNumber")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("nvarchar(10)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("StartedAt");
+
+ b.HasIndex("Status");
+
+ b.HasIndex("WeekNumber");
+
+ b.ToTable("WorkerExecutionLogs", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Configuration.SystemConfiguration", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DataType")
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.Property("Description")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("Key")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Scope")
+ .HasColumnType("int");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IsActive")
+ .HasDatabaseName("IX_SystemConfiguration_IsActive");
+
+ b.HasIndex("Scope", "Key")
+ .IsUnique()
+ .HasDatabaseName("IX_SystemConfiguration_Scope_Key");
+
+ b.ToTable("SystemConfigurations", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("HtmlContent")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Type")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.ToTable("Contracts", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.DayaLoanContract", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ContractNumber")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsProcessed")
+ .HasColumnType("bit");
+
+ b.Property("LastCheckDate")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("NationalCode")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ProcessedDate")
+ .HasColumnType("datetime2");
+
+ b.Property("Status")
+ .HasColumnType("int");
+
+ b.Property("TransactionId")
+ .HasColumnType("bigint");
+
+ b.Property("UserId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TransactionId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("DayaLoanContracts", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("ImagePath")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("IsActive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bit")
+ .HasDefaultValue(true);
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("ParentCategoryId")
+ .HasColumnType("bigint");
+
+ b.Property("SortOrder")
+ .HasColumnType("int");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentCategoryId");
+
+ b.ToTable("DiscountCategories", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DeliveryDescription")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("DeliveryStatus")
+ .HasColumnType("int");
+
+ b.Property("DiscountBalanceUsed")
+ .HasColumnType("bigint");
+
+ b.Property("GatewayAmountPaid")
+ .HasColumnType("bigint");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PaymentDate")
+ .HasColumnType("datetime2");
+
+ b.Property("PaymentStatus")
+ .HasColumnType("int");
+
+ b.Property("TotalAmount")
+ .HasColumnType("bigint");
+
+ b.Property("TrackingCode")
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("TransactionId")
+ .HasColumnType("bigint");
+
+ b.Property("UserAddressId")
+ .HasColumnType("bigint");
+
+ b.Property("UserId")
+ .HasColumnType("bigint");
+
+ b.Property("VatAmount")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TransactionId");
+
+ b.HasIndex("UserAddressId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("DiscountOrders", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrderDetail", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Count")
+ .HasColumnType("int");
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DiscountAmount")
+ .HasColumnType("bigint");
+
+ b.Property("DiscountOrderId")
+ .HasColumnType("bigint");
+
+ b.Property("DiscountPercentUsed")
+ .HasColumnType("int");
+
+ b.Property("FinalPrice")
+ .HasColumnType("bigint");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ProductId")
+ .HasColumnType("bigint");
+
+ b.Property("UnitPrice")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DiscountOrderId");
+
+ b.HasIndex("ProductId");
+
+ b.ToTable("DiscountOrderDetails", "CMS");
+ });
+
+ modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Created")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("FullInformation")
+ .IsRequired()
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("ImagePath")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("IsActive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bit")
+ .HasDefaultValue(true);
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("LastModified")
+ .HasColumnType("datetime2");
+
+ b.Property("LastModifiedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("MaxDiscountPercent")
+ .HasColumnType("int");
+
+ b.Property("Price")
+ .HasColumnType("bigint");
+
+ b.Property("Rate")
+ .HasColumnType("int");
+
+ b.Property("RemainingCount")
+ .HasColumnType("int");
+
+ b.Property("SaleCount")
+ .HasColumnType("int");
+
+ b.Property("ShortInfomation")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("ThumbnailPath")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property