117 lines
4.1 KiB
C#
117 lines
4.1 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using CMSMicroservice.Application.Common.Interfaces;
|
|
using CMSMicroservice.Domain.Enums;
|
|
using System.Collections.Generic;
|
|
|
|
namespace CMSMicroservice.Infrastructure.Services;
|
|
|
|
/// <summary>
|
|
/// پیادهسازی سرویس محاسبه موقعیت در Binary Tree
|
|
/// </summary>
|
|
public class NetworkPlacementService : INetworkPlacementService
|
|
{
|
|
private readonly IApplicationDbContext _context;
|
|
private readonly ILogger<NetworkPlacementService> _logger;
|
|
|
|
public NetworkPlacementService(
|
|
IApplicationDbContext context,
|
|
ILogger<NetworkPlacementService> logger)
|
|
{
|
|
_context = context;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<NetworkLeg?> CalculateLegPositionAsync(long parentId, CancellationToken cancellationToken = default)
|
|
{
|
|
// بررسی وجود Parent
|
|
var parentExists = await _context.Users.AnyAsync(u => u.Id == parentId, cancellationToken);
|
|
if (!parentExists)
|
|
{
|
|
_logger.LogWarning("Parent {ParentId} does not exist", parentId);
|
|
return null;
|
|
}
|
|
|
|
// شمارش فرزندان فعلی
|
|
var children = await _context.Users
|
|
.Where(u => u.NetworkParentId == parentId)
|
|
.Select(u => new { u.LegPosition })
|
|
.ToListAsync(cancellationToken);
|
|
|
|
if (children.Count >= 2)
|
|
{
|
|
_logger.LogWarning("Parent {ParentId} already has 2 children. Binary Tree is full!", parentId);
|
|
return null; // Binary Tree پر است
|
|
}
|
|
|
|
// بررسی کدام Leg خالی است
|
|
var hasLeft = children.Any(c => c.LegPosition == NetworkLeg.Left);
|
|
var hasRight = children.Any(c => c.LegPosition == NetworkLeg.Right);
|
|
|
|
if (!hasLeft)
|
|
{
|
|
_logger.LogDebug("Parent {ParentId}: Left leg is available", parentId);
|
|
return NetworkLeg.Left;
|
|
}
|
|
|
|
if (!hasRight)
|
|
{
|
|
_logger.LogDebug("Parent {ParentId}: Right leg is available", parentId);
|
|
return NetworkLeg.Right;
|
|
}
|
|
|
|
// نباید به اینجا برسیم (چون Count < 2 بود)
|
|
_logger.LogError("Unexpected state: Parent {ParentId} has {Count} children but no available leg",
|
|
parentId, children.Count);
|
|
return null;
|
|
}
|
|
|
|
public async Task<bool> CanAcceptChildAsync(long parentId, CancellationToken cancellationToken = default)
|
|
{
|
|
var childCount = await _context.Users
|
|
.CountAsync(u => u.NetworkParentId == parentId, cancellationToken);
|
|
|
|
return childCount < 2;
|
|
}
|
|
|
|
public async Task<long?> FindAvailableParentAsync(long rootParentId, CancellationToken cancellationToken = default)
|
|
{
|
|
// BFS (Breadth-First Search) برای پیدا کردن اولین Parent با جای خالی
|
|
var queue = new Queue<long>();
|
|
queue.Enqueue(rootParentId);
|
|
var visited = new HashSet<long>();
|
|
|
|
while (queue.Count > 0)
|
|
{
|
|
var currentParentId = queue.Dequeue();
|
|
|
|
if (visited.Contains(currentParentId))
|
|
continue;
|
|
|
|
visited.Add(currentParentId);
|
|
|
|
// بررسی کنید که آیا این Parent میتواند فرزند بپذیرد
|
|
var canAccept = await CanAcceptChildAsync(currentParentId, cancellationToken);
|
|
if (canAccept)
|
|
{
|
|
_logger.LogInformation("Found available parent: {ParentId}", currentParentId);
|
|
return currentParentId;
|
|
}
|
|
|
|
// اضافه کردن فرزندان به صف برای جستجو
|
|
var children = await _context.Users
|
|
.Where(u => u.NetworkParentId == currentParentId)
|
|
.Select(u => u.Id)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
foreach (var childId in children)
|
|
{
|
|
queue.Enqueue(childId);
|
|
}
|
|
}
|
|
|
|
_logger.LogWarning("No available parent found in network starting from {RootParentId}", rootParentId);
|
|
return null; // هیچ Parent خالی پیدا نشد
|
|
}
|
|
}
|