From 87d61abc5bd653cba0f009b1b56370291b408975 Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Thu, 27 Aug 2026 21:28:03 +0330 Subject: [PATCH] feat(network): add latest package leg totals to network statistics response - Introduced new properties for GoldLeftLegTotal, GoldRightLegTotal, SilverLeftLegTotal, and SilverRightLegTotal in GetNetworkStatisticsResponseDto. - Implemented LoadLatestPackageLegScoresAsync method in GetNetworkStatisticsQueryHandler to fetch the latest leg totals for the user. - Updated Protobuf definitions to include new fields for leg totals in GetMyNetworkStatisticsResponse. - Bumped Protobuf project version to reflect the addition of new features. --- .../GetNetworkStatisticsQueryHandler.cs | 75 ++++++++++++++- .../GetNetworkStatisticsResponseDto.cs | 8 ++ .../GetNetworkTreeQueryHandler.cs | 94 ------------------- .../CMSMicroservice.Protobuf.csproj | 2 +- .../Protos/networkmembership.proto | 5 + 5 files changed, 88 insertions(+), 96 deletions(-) diff --git a/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs b/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs index 903a370..b6e4828 100644 --- a/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs +++ b/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs @@ -1,4 +1,5 @@ using CMSMicroservice.Domain.Enums; +using Microsoft.EntityFrameworkCore; namespace CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics; @@ -124,6 +125,9 @@ public class GetNetworkStatisticsQueryHandler : IRequestHandler + /// آخرین Left/RightLegTotal طلایی و نقره‌ای فقط برای خود کاربر (نوار آمار شجره). + /// + private async Task<(int GoldLeft, int GoldRight, int SilverLeft, int SilverRight)> LoadLatestPackageLegScoresAsync( + long userId, + CancellationToken cancellationToken) + { + var packages = await _context.Packages + .AsNoTracking() + .Where(p => !p.IsDeleted && p.IsActive) + .Select(p => new { p.Id, p.Title, p.IsBasePackage, p.MaxBalancesPerLeg }) + .ToListAsync(cancellationToken); + + var goldPackageId = packages.FirstOrDefault(p => p.IsBasePackage)?.Id + ?? packages.FirstOrDefault(p => p.Title.Contains("طلایی") || p.Title.Contains("طلايی"))?.Id; + + var silverPackageId = packages.FirstOrDefault(p => + !p.IsBasePackage && (p.Title.Contains("نقره") || p.MaxBalancesPerLeg <= 30))?.Id + ?? packages.FirstOrDefault(p => !p.IsBasePackage)?.Id; + + var packageIds = new List(); + if (goldPackageId.HasValue) packageIds.Add(goldPackageId.Value); + if (silverPackageId.HasValue && silverPackageId != goldPackageId) + packageIds.Add(silverPackageId.Value); + + if (packageIds.Count == 0) + return (0, 0, 0, 0); + + var balances = await _context.NetworkWeeklyBalances + .AsNoTracking() + .Where(b => b.UserId == userId && packageIds.Contains(b.PackageId)) + .Select(b => new { b.PackageId, b.WeekDefinitionId, b.LeftLegTotal, b.RightLegTotal }) + .ToListAsync(cancellationToken); + + int goldLeft = 0, goldRight = 0, silverLeft = 0, silverRight = 0; + + if (goldPackageId.HasValue) + { + var gold = balances + .Where(b => b.PackageId == goldPackageId.Value) + .OrderByDescending(b => b.WeekDefinitionId) + .FirstOrDefault(); + if (gold != null) + { + goldLeft = gold.LeftLegTotal; + goldRight = gold.RightLegTotal; + } + } + + if (silverPackageId.HasValue) + { + var silver = balances + .Where(b => b.PackageId == silverPackageId.Value) + .OrderByDescending(b => b.WeekDefinitionId) + .FirstOrDefault(); + if (silver != null) + { + silverLeft = silver.LeftLegTotal; + silverRight = silver.RightLegTotal; + } + } + + return (goldLeft, goldRight, silverLeft, silverRight); + } + /// /// Recursively get all descendants of a user /// diff --git a/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsResponseDto.cs b/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsResponseDto.cs index 5b92efc..75da530 100644 --- a/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsResponseDto.cs +++ b/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsResponseDto.cs @@ -13,6 +13,14 @@ public class GetNetworkStatisticsResponseDto public List LevelDistribution { get; set; } = new(); public List MonthlyGrowth { get; set; } = new(); public List TopUsers { get; set; } = new(); + + /// آخرین LeftLegTotal پکیج طلایی برای همین کاربر + public int GoldLeftLegTotal { get; set; } + public int GoldRightLegTotal { get; set; } + public int SilverLeftLegTotal { get; set; } + public int SilverRightLegTotal { get; set; } + + public string WeakerLeg => LeftLegCount <= RightLegCount ? "Left" : "Right"; } public class LevelDistributionModel diff --git a/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs b/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs index ab1bada..a264f2b 100644 --- a/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs +++ b/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs @@ -41,11 +41,6 @@ public class GetNetworkTreeQueryHandler : IRequestHandler n.NetworkLevel == 0); return rootNode != null ? nodeDict[rootNode.UserId] : null; } - - /// - /// آخرین Left/RightLegTotal طلایی و نقره‌ای برای هر نود درخت (برنامه‌ریزی پای چپ/راست). - /// - private async Task EnrichWithPackageLegScoresAsync( - NetworkTreeDto root, - List flatNodes, - CancellationToken cancellationToken) - { - var userIds = flatNodes.Select(n => n.UserId).Distinct().ToList(); - if (userIds.Count == 0) return; - - var packages = await _context.Packages - .AsNoTracking() - .Where(p => !p.IsDeleted && p.IsActive) - .Select(p => new { p.Id, p.Title, p.IsBasePackage, p.MaxBalancesPerLeg }) - .ToListAsync(cancellationToken); - - var goldPackageId = packages.FirstOrDefault(p => p.IsBasePackage)?.Id - ?? packages.FirstOrDefault(p => p.Title.Contains("طلایی") || p.Title.Contains("طلايی"))?.Id; - - var silverPackageId = packages.FirstOrDefault(p => - !p.IsBasePackage && (p.Title.Contains("نقره") || p.MaxBalancesPerLeg <= 30))?.Id - ?? packages.FirstOrDefault(p => !p.IsBasePackage)?.Id; - - if (!goldPackageId.HasValue && !silverPackageId.HasValue) - { - _logger.LogWarning("GetNetworkTree: no gold/silver packages found for leg score enrichment"); - return; - } - - var packageIds = new List(); - if (goldPackageId.HasValue) packageIds.Add(goldPackageId.Value); - if (silverPackageId.HasValue && silverPackageId != goldPackageId) - packageIds.Add(silverPackageId.Value); - - var rawBalances = await _context.NetworkWeeklyBalances - .AsNoTracking() - .Where(b => userIds.Contains(b.UserId) && packageIds.Contains(b.PackageId)) - .Select(b => new LegScoreRow( - b.UserId, - b.PackageId, - b.WeekDefinitionId, - b.LeftLegTotal, - b.RightLegTotal)) - .ToListAsync(cancellationToken); - - var latestByUserPackage = rawBalances - .GroupBy(b => (b.UserId, b.PackageId)) - .ToDictionary( - g => g.Key, - g => g.OrderByDescending(x => x.WeekDefinitionId).First()); - - ApplyLegScores(root, goldPackageId, silverPackageId, latestByUserPackage); - } - - private static void ApplyLegScores( - NetworkTreeDto node, - long? goldPackageId, - long? silverPackageId, - Dictionary<(long UserId, long PackageId), LegScoreRow> latestByUserPackage) - { - if (goldPackageId.HasValue && - latestByUserPackage.TryGetValue((node.UserId, goldPackageId.Value), out var gold)) - { - node.GoldLeftLegTotal = gold.LeftLegTotal; - node.GoldRightLegTotal = gold.RightLegTotal; - } - - if (silverPackageId.HasValue && - latestByUserPackage.TryGetValue((node.UserId, silverPackageId.Value), out var silver)) - { - node.SilverLeftLegTotal = silver.LeftLegTotal; - node.SilverRightLegTotal = silver.RightLegTotal; - } - - if (node.LeftChild != null) - ApplyLegScores(node.LeftChild, goldPackageId, silverPackageId, latestByUserPackage); - - if (node.RightChild != null) - ApplyLegScores(node.RightChild, goldPackageId, silverPackageId, latestByUserPackage); - } - - private sealed record LegScoreRow( - long UserId, - long PackageId, - long WeekDefinitionId, - int LeftLegTotal, - int RightLegTotal); } diff --git a/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj b/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj index 2de392c..79ca2d5 100644 --- a/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj +++ b/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj @@ -3,7 +3,7 @@ net9.0 enable enable - 0.0.210 + 0.0.211 None False False diff --git a/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto b/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto index 39ee7f7..0a32e63 100644 --- a/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto +++ b/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto @@ -328,6 +328,11 @@ message GetMyNetworkStatisticsResponse string my_network_leg = 11; string my_referral_code = 12; CustomerLastMemberModel last_member = 13; + // آخرین NetworkWeeklyBalance خود کاربر — برای نوار آمار پایین شجره (D3) + int32 gold_left_leg_total = 14; + int32 gold_right_leg_total = 15; + int32 silver_left_leg_total = 16; + int32 silver_right_leg_total = 17; } message CustomerLastMemberModel