feat: Add monitoring alerts skeleton and enhance worker with notifications
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using CMSMicroservice.Infrastructure.Persistence;
|
||||
|
||||
namespace CMSMicroservice.Infrastructure.Data.Seeding;
|
||||
|
||||
/// <summary>
|
||||
/// Seeder for migrating existing User.ParentId to User.NetworkParentId
|
||||
/// این Seeder فقط یک بار اجرا میشود و دادههای قدیمی را به ساختار Binary Tree جدید منتقل میکند
|
||||
/// </summary>
|
||||
public class NetworkParentIdMigrationSeeder
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly ILogger<NetworkParentIdMigrationSeeder> _logger;
|
||||
|
||||
public NetworkParentIdMigrationSeeder(
|
||||
ApplicationDbContext context,
|
||||
ILogger<NetworkParentIdMigrationSeeder> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task SeedAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation("=== Starting ParentId → NetworkParentId Migration ===");
|
||||
|
||||
// Step 1: Validation - Check if migration already done
|
||||
var alreadyMigrated = await _context.Users
|
||||
.Where(u => u.ParentId != null && u.NetworkParentId != null)
|
||||
.AnyAsync(cancellationToken);
|
||||
|
||||
if (alreadyMigrated)
|
||||
{
|
||||
_logger.LogWarning("⚠️ Migration already completed! Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Find users with ParentId but no NetworkParentId
|
||||
var usersToMigrate = await _context.Users
|
||||
.Where(u => u.ParentId != null && u.NetworkParentId == null)
|
||||
.OrderBy(u => u.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (usersToMigrate.Count == 0)
|
||||
{
|
||||
_logger.LogInformation("✅ No users to migrate. All done!");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"📊 Found {usersToMigrate.Count} users to migrate");
|
||||
|
||||
// Step 3: Group by ParentId to check binary tree constraint
|
||||
var parentGroups = usersToMigrate.GroupBy(u => u.ParentId);
|
||||
|
||||
int migratedCount = 0;
|
||||
int skippedCount = 0;
|
||||
|
||||
foreach (var group in parentGroups)
|
||||
{
|
||||
var parentId = group.Key;
|
||||
var children = group.OrderBy(u => u.Id).ToList(); // ترتیب بر اساس Id
|
||||
|
||||
if (children.Count > 2)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"⚠️ Parent {ParentId} has {Count} children! Binary tree allows max 2. Taking first 2...",
|
||||
parentId, children.Count);
|
||||
|
||||
children = children.Take(2).ToList();
|
||||
skippedCount += (group.Count() - 2);
|
||||
}
|
||||
|
||||
// Assign NetworkParentId and LegPosition
|
||||
for (int i = 0; i < children.Count && i < 2; i++)
|
||||
{
|
||||
var child = children[i];
|
||||
child.NetworkParentId = parentId;
|
||||
child.LegPosition = i == 0 ? NetworkLeg.Left : NetworkLeg.Right;
|
||||
|
||||
_logger.LogDebug(
|
||||
"✅ Migrated User {UserId}: Parent={ParentId}, Leg={Leg}",
|
||||
child.Id, parentId, child.LegPosition);
|
||||
|
||||
migratedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Save changes
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"✅ Migration Completed! Migrated={Migrated}, Skipped={Skipped}",
|
||||
migratedCount, skippedCount);
|
||||
|
||||
// Step 5: Post-Migration Validation
|
||||
await ValidateMigrationAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task ValidateMigrationAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("🔍 Validating Migration...");
|
||||
|
||||
// Check 1: Orphaned nodes (NetworkParent doesn't exist)
|
||||
var orphanedUsers = await _context.Users
|
||||
.Where(u => u.NetworkParentId != null &&
|
||||
!_context.Users.Any(p => p.Id == u.NetworkParentId))
|
||||
.Select(u => new { u.Id, u.NetworkParentId })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (orphanedUsers.Any())
|
||||
{
|
||||
_logger.LogError(
|
||||
"❌ Found {Count} orphaned users (NetworkParent doesn't exist): {Ids}",
|
||||
orphanedUsers.Count,
|
||||
string.Join(", ", orphanedUsers.Select(u => u.Id)));
|
||||
}
|
||||
|
||||
// Check 2: Binary tree violation (more than 2 children per parent)
|
||||
var parentsWithTooManyChildren = await _context.Users
|
||||
.Where(u => u.NetworkParentId != null)
|
||||
.GroupBy(u => u.NetworkParentId)
|
||||
.Select(g => new { ParentId = g.Key, Count = g.Count() })
|
||||
.Where(x => x.Count > 2)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (parentsWithTooManyChildren.Any())
|
||||
{
|
||||
_logger.LogError(
|
||||
"❌ Binary tree violation! {Count} parents have more than 2 children",
|
||||
parentsWithTooManyChildren.Count);
|
||||
|
||||
foreach (var parent in parentsWithTooManyChildren)
|
||||
{
|
||||
_logger.LogError(" Parent {ParentId} has {Count} children", parent.ParentId, parent.Count);
|
||||
}
|
||||
}
|
||||
|
||||
// Check 3: Statistics
|
||||
var stats = await _context.Users
|
||||
.GroupBy(u => 1)
|
||||
.Select(g => new
|
||||
{
|
||||
TotalUsers = g.Count(),
|
||||
UsersWithNetworkParent = g.Count(u => u.NetworkParentId != null),
|
||||
LeftChildren = g.Count(u => u.LegPosition == NetworkLeg.Left),
|
||||
RightChildren = g.Count(u => u.LegPosition == NetworkLeg.Right)
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (stats != null)
|
||||
{
|
||||
_logger.LogInformation("📊 Migration Statistics:");
|
||||
_logger.LogInformation(" Total Users: {Total}", stats.TotalUsers);
|
||||
_logger.LogInformation(" Users with NetworkParent: {Count}", stats.UsersWithNetworkParent);
|
||||
_logger.LogInformation(" Left Children: {Count}", stats.LeftChildren);
|
||||
_logger.LogInformation(" Right Children: {Count}", stats.RightChildren);
|
||||
}
|
||||
|
||||
if (!orphanedUsers.Any() && !parentsWithTooManyChildren.Any())
|
||||
{
|
||||
_logger.LogInformation("✅ Validation Passed! Binary tree is intact.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("❌ Validation Failed! Please fix issues manually.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user