Files
CMS/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs
T
masoodafar-web d1ca72300d
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 8s
refactor: Remove ICurrentUserService dependency from query handlers and update user ID handling logic
2026-02-10 23:01:49 +03:30

179 lines
6.9 KiB
C#

using CMSMicroservice.Domain.Enums;
namespace CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics;
public class GetNetworkStatisticsQueryHandler : IRequestHandler<GetNetworkStatisticsQuery, GetNetworkStatisticsResponseDto>
{
private readonly IApplicationDbContext _context;
public GetNetworkStatisticsQueryHandler(
IApplicationDbContext context)
{
_context = context;
}
public async Task<GetNetworkStatisticsResponseDto> Handle(GetNetworkStatisticsQuery request, CancellationToken cancellationToken)
{
// UserId > 0 → stats for that user's network
// UserId == 0 → global stats (find root user of the network)
var userId = request.UserId;
// Get all users (needed for descendant calculation anyway)
var allUsers = await _context.Users.ToListAsync(cancellationToken);
if (userId == 0)
{
// Find root user (user with no NetworkParentId)
var rootUser = allUsers.FirstOrDefault(x => x.NetworkParentId == null || x.NetworkParentId == 0);
if (rootUser != null)
userId = rootUser.Id;
else if (allUsers.Count > 0)
userId = allUsers.First().Id;
else
throw new InvalidOperationException("No users found in the system");
}
var allDescendants = GetAllDescendants(userId, allUsers);
// Statistics for the user's network (all descendants)
var totalMembers = allDescendants.Count;
var activeMembers = allDescendants.Count(x => !x.IsDeleted);
// Get direct left and right children
var leftChild = allUsers.FirstOrDefault(x => x.NetworkParentId == userId && x.LegPosition == NetworkLeg.Left);
var rightChild = allUsers.FirstOrDefault(x => x.NetworkParentId == userId && x.LegPosition == NetworkLeg.Right);
// Count all descendants in left and right subtrees
var leftLegCount = leftChild != null ? GetAllDescendants(leftChild.Id, allUsers).Count + 1 : 0; // +1 for leftChild itself
var rightLegCount = rightChild != null ? GetAllDescendants(rightChild.Id, allUsers).Count + 1 : 0; // +1 for rightChild itself
double leftPercentage = totalMembers > 0 ? (leftLegCount / (double)totalMembers) * 100 : 0;
double rightPercentage = totalMembers > 0 ? (rightLegCount / (double)totalMembers) * 100 : 0;
// Calculate actual depth
int maxDepth = 0;
double totalDepthSum = 0;
var userDepths = new Dictionary<long, int>();
CalculateDepths(userId, allUsers, 0, userDepths, ref maxDepth);
if (allDescendants.Count > 0)
{
totalDepthSum = allDescendants.Sum(d => userDepths.ContainsKey(d.Id) ? userDepths[d.Id] : 0);
}
double averageDepth = allDescendants.Count > 0 ? totalDepthSum / allDescendants.Count : 0;
// Level distribution - calculate from depths
var levelDistribution = new List<LevelDistributionModel>();
if (allDescendants.Count > 0)
{
var levelCounts = allDescendants
.Where(d => userDepths.ContainsKey(d.Id))
.GroupBy(d => userDepths[d.Id])
.OrderBy(g => g.Key)
.Select(g => new LevelDistributionModel { Level = g.Key, Count = g.Count() })
.ToList();
levelDistribution = levelCounts;
}
// Monthly growth (last 6 months) - using descendants Created date
var sixMonthsAgo = DateTime.Now.AddMonths(-6);
var monthlyGrowthRaw = allDescendants
.Where(x => x.Created >= sixMonthsAgo)
.Select(x => new { x.Created.Year, x.Created.Month })
.ToList();
var monthlyGrowth = monthlyGrowthRaw
.GroupBy(x => new { x.Year, x.Month })
.Select(g => new MonthlyGrowthModel
{
Month = $"{g.Key.Year}-{g.Key.Month:D2}",
NewMembers = g.Count()
})
.OrderBy(x => x.Month)
.ToList();
// Top users by total descendants count
var userDescendantCounts = new Dictionary<long, int>();
foreach (var user in allDescendants)
{
var descendants = GetAllDescendants(user.Id, allUsers);
userDescendantCounts[user.Id] = descendants.Count;
}
var topUserData = allDescendants
.Where(x => x.Id != userId && userDescendantCounts[x.Id] > 0)
.Select(x => new
{
x.Id,
UserName = (x.FirstName + " " + x.LastName).Trim(),
DescendantCount = userDescendantCounts[x.Id],
LeftCount = allUsers.Count(c => c.NetworkParentId == x.Id && c.LegPosition == NetworkLeg.Left),
RightCount = allUsers.Count(c => c.NetworkParentId == x.Id && c.LegPosition == NetworkLeg.Right)
})
.OrderByDescending(x => x.DescendantCount)
.Take(10)
.ToList();
var topUserModels = topUserData.Select((x, index) => new TopNetworkUserModel
{
Rank = index + 1,
UserId = x.Id,
UserName = x.UserName,
TotalChildren = x.DescendantCount,
LeftCount = x.LeftCount,
RightCount = x.RightCount
}).ToList();
return new GetNetworkStatisticsResponseDto
{
TotalMembers = totalMembers,
ActiveMembers = activeMembers,
LeftLegCount = leftLegCount,
RightLegCount = rightLegCount,
LeftPercentage = leftPercentage,
RightPercentage = rightPercentage,
AverageDepth = averageDepth,
MaxDepth = maxDepth,
LevelDistribution = levelDistribution,
MonthlyGrowth = monthlyGrowth,
TopUsers = topUserModels
};
}
/// <summary>
/// Recursively get all descendants of a user
/// </summary>
private List<User> GetAllDescendants(long userId, List<User> allUsers)
{
var descendants = new List<User>();
var directChildren = allUsers.Where(x => x.NetworkParentId == userId).ToList();
foreach (var child in directChildren)
{
descendants.Add(child);
descendants.AddRange(GetAllDescendants(child.Id, allUsers));
}
return descendants;
}
/// <summary>
/// Calculate depth for all descendants recursively
/// </summary>
private void CalculateDepths(long userId, List<User> allUsers, int currentDepth, Dictionary<long, int> depths, ref int maxDepth)
{
var children = allUsers.Where(x => x.NetworkParentId == userId).ToList();
foreach (var child in children)
{
var childDepth = currentDepth + 1;
depths[child.Id] = childDepth;
if (childDepth > maxDepth)
maxDepth = childDepth;
CalculateDepths(child.Id, allUsers, childDepth, depths, ref maxDepth);
}
}
}