feat(network): enrich network tree with package leg scores and update Protobuf definitions
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 12m47s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 12m47s
- Added logic to enrich the network tree with the latest left/right leg totals for gold and silver packages in GetNetworkTreeQueryHandler. - Introduced new properties in NetworkTreeDto for GoldLeftLegTotal, GoldRightLegTotal, SilverLeftLegTotal, and SilverRightLegTotal. - Updated Protobuf definitions to include new fields for leg scores in NetworkTreeNodeModel. - Enhanced mapping in NetworkMembershipProfile and NetworkMembershipService to accommodate new leg score properties. - Bumped Protobuf project version to reflect the addition of new features.
This commit is contained in:
+94
-1
@@ -1,5 +1,4 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.Common;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkTree;
|
namespace CMSMicroservice.Application.NetworkMembershipCQ.Queries.GetNetworkTree;
|
||||||
@@ -42,6 +41,11 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
|
|||||||
|
|
||||||
// تبدیل نتایج flat به ساختار درختی
|
// تبدیل نتایج flat به ساختار درختی
|
||||||
var tree = BuildTreeFromFlatNodes(flatNodes);
|
var tree = BuildTreeFromFlatNodes(flatNodes);
|
||||||
|
if (tree != null)
|
||||||
|
{
|
||||||
|
await EnrichWithPackageLegScoresAsync(tree, flatNodes, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -194,4 +198,93 @@ public class GetNetworkTreeQueryHandler : IRequestHandler<GetNetworkTreeQuery, N
|
|||||||
var rootNode = flatNodes.FirstOrDefault(n => n.NetworkLevel == 0);
|
var rootNode = flatNodes.FirstOrDefault(n => n.NetworkLevel == 0);
|
||||||
return rootNode != null ? nodeDict[rootNode.UserId] : null;
|
return rootNode != null ? nodeDict[rootNode.UserId] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// آخرین Left/RightLegTotal طلایی و نقرهای برای هر نود درخت (برنامهریزی پای چپ/راست).
|
||||||
|
/// </summary>
|
||||||
|
private async Task EnrichWithPackageLegScoresAsync(
|
||||||
|
NetworkTreeDto root,
|
||||||
|
List<NetworkTreeNodeDto> 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<long>();
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -60,6 +60,18 @@ public class NetworkTreeDto
|
|||||||
|
|
||||||
public long? PackageId { get; set; }
|
public long? PackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>آخرین LeftLegTotal پکیج طلایی (پایه)</summary>
|
||||||
|
public int GoldLeftLegTotal { get; set; }
|
||||||
|
|
||||||
|
/// <summary>آخرین RightLegTotal پکیج طلایی (پایه)</summary>
|
||||||
|
public int GoldRightLegTotal { get; set; }
|
||||||
|
|
||||||
|
/// <summary>آخرین LeftLegTotal پکیج نقرهای</summary>
|
||||||
|
public int SilverLeftLegTotal { get; set; }
|
||||||
|
|
||||||
|
/// <summary>آخرین RightLegTotal پکیج نقرهای</summary>
|
||||||
|
public int SilverRightLegTotal { get; set; }
|
||||||
|
|
||||||
public NetworkTreeDto? LeftChild { get; set; }
|
public NetworkTreeDto? LeftChild { get; set; }
|
||||||
public NetworkTreeDto? RightChild { get; set; }
|
public NetworkTreeDto? RightChild { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>0.0.209</Version>
|
<Version>0.0.210</Version>
|
||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<DebugSymbols>False</DebugSymbols>
|
<DebugSymbols>False</DebugSymbols>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
|
|||||||
@@ -209,6 +209,11 @@ message NetworkTreeNodeModel
|
|||||||
google.protobuf.BoolValue is_new_activation = 22; // true=اولین فعالسازی، false=خرید مجدد، null=بدون Cycle
|
google.protobuf.BoolValue is_new_activation = 22; // true=اولین فعالسازی، false=خرید مجدد، null=بدون Cycle
|
||||||
string package_name = 23; // نام پکیج موثر
|
string package_name = 23; // نام پکیج موثر
|
||||||
google.protobuf.Int64Value package_id = 24; // شناسه پکیج
|
google.protobuf.Int64Value package_id = 24; // شناسه پکیج
|
||||||
|
// آخرین NetworkWeeklyBalance: امتیاز پای چپ/راست برای پکیج طلایی و نقرهای (D3)
|
||||||
|
int32 gold_left_leg_total = 25;
|
||||||
|
int32 gold_right_leg_total = 26;
|
||||||
|
int32 silver_left_leg_total = 27;
|
||||||
|
int32 silver_right_leg_total = 28;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHistory Query
|
// GetHistory Query
|
||||||
|
|||||||
@@ -95,7 +95,11 @@ public class NetworkMembershipProfile : IRegister
|
|||||||
IsActivatedInTargetWeek = node.IsActivatedInTargetWeek,
|
IsActivatedInTargetWeek = node.IsActivatedInTargetWeek,
|
||||||
PackageName = node.PackageName ?? string.Empty,
|
PackageName = node.PackageName ?? string.Empty,
|
||||||
PackageId = node.PackageId.HasValue ? node.PackageId.Value : null,
|
PackageId = node.PackageId.HasValue ? node.PackageId.Value : null,
|
||||||
IsNewActivation = node.IsNewActivation.HasValue ? node.IsNewActivation.Value : null
|
IsNewActivation = node.IsNewActivation.HasValue ? node.IsNewActivation.Value : null,
|
||||||
|
GoldLeftLegTotal = node.GoldLeftLegTotal,
|
||||||
|
GoldRightLegTotal = node.GoldRightLegTotal,
|
||||||
|
SilverLeftLegTotal = node.SilverLeftLegTotal,
|
||||||
|
SilverRightLegTotal = node.SilverRightLegTotal
|
||||||
};
|
};
|
||||||
|
|
||||||
if (parentId.HasValue)
|
if (parentId.HasValue)
|
||||||
|
|||||||
@@ -185,6 +185,11 @@ public class NetworkMembershipService : NetworkMembershipContract.NetworkMembers
|
|||||||
node.PackageId = dto.PackageId.Value;
|
node.PackageId = dto.PackageId.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.GoldLeftLegTotal = dto.GoldLeftLegTotal;
|
||||||
|
node.GoldRightLegTotal = dto.GoldRightLegTotal;
|
||||||
|
node.SilverLeftLegTotal = dto.SilverLeftLegTotal;
|
||||||
|
node.SilverRightLegTotal = dto.SilverRightLegTotal;
|
||||||
|
|
||||||
if (dto.IsNewActivation.HasValue)
|
if (dto.IsNewActivation.HasValue)
|
||||||
{
|
{
|
||||||
node.IsNewActivation = dto.IsNewActivation.Value;
|
node.IsNewActivation = dto.IsNewActivation.Value;
|
||||||
|
|||||||
Reference in New Issue
Block a user