feat: add target week activation flag to network membership
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 5m28s

This commit is contained in:
masoodafar-web
2025-12-14 03:35:40 +03:30
parent cf75f14003
commit 730e5a252b
5 changed files with 68 additions and 94 deletions

View File

@@ -26,7 +26,9 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
private async Task<NetworkTreeDto> BuildTree(long userId, int maxDepth, int currentDepth, CancellationToken cancellationToken, GetNetworkTreeQuery request)
{
// دریافت کاربر با اطلاعات باشگاه مشتریان
var user = await _context.Users
.Include(u => u.ClubMembership)
.AsNoTracking()
.FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
@@ -35,23 +37,19 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
throw new NotFoundException(nameof(User), userId);
}
// دریافت اطلاعات باشگاه مشتریان
var clubMembership = await _context.ClubMemberships
.AsNoTracking()
.FirstOrDefaultAsync(x => x.UserId == userId && x.IsDeleted == false, cancellationToken);
// محاسبه شماره هفته فعال‌سازی
string? activationWeekNumber = null;
if (clubMembership?.ActivatedAt != null)
bool isActivatedInTargetWeek = false;
if (user.ClubMembership?.ActivatedAt != null)
{
var activatedAt = clubMembership.ActivatedAt.Value;
var year = activatedAt.Year;
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);
var weekNum = activatedAt < firstSaturday ? 1 : ((activatedAt - firstSaturday).Days / 7) + 1;
activationWeekNumber = $"{year}-W{weekNum:D2}";
activationWeekNumber = CalculateWeekNumber(user.ClubMembership.ActivatedAt.Value);
// بررسی آیا در هفته هدف فعال شده است
if (!string.IsNullOrEmpty(request.ActivationWeekNumber))
{
isActivatedInTargetWeek = activationWeekNumber == request.ActivationWeekNumber;
}
}
var node = new NetworkTreeDto
@@ -62,9 +60,10 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
LastName = user.LastName,
LegPosition = user.LegPosition,
CurrentDepth = currentDepth,
ClubActivatedAt = clubMembership?.ActivatedAt,
IsClubActive = clubMembership?.IsActive ?? false,
ClubActivatedAt = user.ClubMembership?.ActivatedAt,
IsClubActive = user.ClubMembership?.IsActive ?? false,
ActivationWeekNumber = activationWeekNumber,
IsActivatedInTargetWeek = isActivatedInTargetWeek,
UserCreated = user.Created
};
@@ -74,87 +73,16 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
return node;
}
// پیدا کردن فرزند چپ
var leftChildQuery = _context.Users
.AsNoTracking()
.Where(x => x.NetworkParentId == userId && x.LegPosition == NetworkLeg.Left);
// اعمال فیلتر IsClubActive
if (request.IsClubActive.HasValue)
{
var activeUserIds = await _context.ClubMemberships
.AsNoTracking()
.Where(cm => cm.IsActive == request.IsClubActive.Value && cm.IsDeleted == false)
.Select(cm => cm.UserId)
.ToListAsync(cancellationToken);
leftChildQuery = leftChildQuery.Where(u => activeUserIds.Contains(u.Id));
}
// اعمال فیلتر ActivationWeekNumber
if (!string.IsNullOrEmpty(request.ActivationWeekNumber))
{
var (startDate, endDate) = ParseWeekNumber(request.ActivationWeekNumber);
if (startDate.HasValue && endDate.HasValue)
{
var weekUserIds = await _context.ClubMemberships
.AsNoTracking()
.Where(cm => cm.IsDeleted == false
&& cm.ActivatedAt != null
&& cm.ActivatedAt >= startDate.Value
&& cm.ActivatedAt < endDate.Value)
.Select(cm => cm.UserId)
.ToListAsync(cancellationToken);
leftChildQuery = leftChildQuery.Where(u => weekUserIds.Contains(u.Id));
}
}
var leftChild = await leftChildQuery.FirstOrDefaultAsync(cancellationToken);
// پیدا کردن فرزندان (چپ و راست)
var children = await GetFilteredChildren(userId, request, cancellationToken);
var leftChild = children.FirstOrDefault(c => c.LegPosition == NetworkLeg.Left);
if (leftChild != null)
{
node.LeftChild = await BuildTree(leftChild.Id, maxDepth, currentDepth + 1, cancellationToken, request);
}
// پیدا کردن فرزند راست
var rightChildQuery = _context.Users
.AsNoTracking()
.Where(x => x.NetworkParentId == userId && x.LegPosition == NetworkLeg.Right);
// اعمال فیلتر IsClubActive
if (request.IsClubActive.HasValue)
{
var activeUserIds = await _context.ClubMemberships
.AsNoTracking()
.Where(cm => cm.IsActive == request.IsClubActive.Value && cm.IsDeleted == false)
.Select(cm => cm.UserId)
.ToListAsync(cancellationToken);
rightChildQuery = rightChildQuery.Where(u => activeUserIds.Contains(u.Id));
}
// اعمال فیلتر ActivationWeekNumber
if (!string.IsNullOrEmpty(request.ActivationWeekNumber))
{
var (startDate, endDate) = ParseWeekNumber(request.ActivationWeekNumber);
if (startDate.HasValue && endDate.HasValue)
{
var weekUserIds = await _context.ClubMemberships
.AsNoTracking()
.Where(cm => cm.IsDeleted == false
&& cm.ActivatedAt != null
&& cm.ActivatedAt >= startDate.Value
&& cm.ActivatedAt < endDate.Value)
.Select(cm => cm.UserId)
.ToListAsync(cancellationToken);
rightChildQuery = rightChildQuery.Where(u => weekUserIds.Contains(u.Id));
}
}
var rightChild = await rightChildQuery.FirstOrDefaultAsync(cancellationToken);
var rightChild = children.FirstOrDefault(c => c.LegPosition == NetworkLeg.Right);
if (rightChild != null)
{
node.RightChild = await BuildTree(rightChild.Id, maxDepth, currentDepth + 1, cancellationToken, request);
@@ -163,6 +91,45 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
return node;
}
/// <summary>
/// دریافت فرزندان با اعمال فیلترها
/// </summary>
private async Task<List<User>> GetFilteredChildren(long parentId, GetNetworkTreeQuery request, CancellationToken cancellationToken)
{
var query = _context.Users
.Include(u => u.ClubMembership)
.AsNoTracking()
.Where(x => x.NetworkParentId == parentId);
// اعمال فیلتر IsClubActive
if (request.IsClubActive.HasValue)
{
query = query.Where(u =>
u.ClubMembership != null &&
u.ClubMembership.IsDeleted == false &&
u.ClubMembership.IsActive == request.IsClubActive.Value);
}
return await query.ToListAsync(cancellationToken);
}
/// <summary>
/// محاسبه شماره هفته از تاریخ
/// </summary>
private static 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;
var firstSaturday = jan1.AddDays(daysToFirstSaturday);
var weekNum = date < firstSaturday ? 1 : ((date - firstSaturday).Days / 7) + 1;
return $"{year}-W{weekNum:D2}";
}
/// <summary>
/// تبدیل شماره هفته (مثلاً 2025-W05) به تاریخ شروع و پایان هفته
/// </summary>

View File

@@ -27,6 +27,11 @@ public class NetworkTreeDto
/// </summary>
public string? ActivationWeekNumber { get; set; }
/// <summary>
/// آیا کاربر در هفته هدف فعال شده است؟
/// </summary>
public bool IsActivatedInTargetWeek { get; set; }
/// <summary>
/// تاریخ ایجاد کاربر
/// </summary>

View File

@@ -3,7 +3,7 @@
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>0.0.154</Version>
<Version>0.0.155</Version>
<DebugType>None</DebugType>
<DebugSymbols>False</DebugSymbols>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>

View File

@@ -176,7 +176,8 @@ message NetworkTreeNodeModel
google.protobuf.Timestamp club_activated_at = 8; // تاریخ فعال‌سازی در باشگاه
bool is_club_active = 9; // فعال بودن در باشگاه
string activation_week_number = 10; // شماره هفته فعال‌سازی
google.protobuf.Timestamp user_created = 11; // تاریخ ایجاد کاربر
bool is_activated_in_target_week = 11; // آیا در هفته هدف فعال شده
google.protobuf.Timestamp user_created = 12; // تاریخ ایجاد کاربر
}
// GetHistory Query

View File

@@ -85,7 +85,8 @@ public class NetworkMembershipProfile : IRegister
NetworkLevel = node.CurrentDepth,
IsActive = true,
IsClubActive = node.IsClubActive,
ActivationWeekNumber = node.ActivationWeekNumber ?? string.Empty
ActivationWeekNumber = node.ActivationWeekNumber ?? string.Empty,
IsActivatedInTargetWeek = node.IsActivatedInTargetWeek
};
if (parentId.HasValue)