# 🔄 Migration Guide: ParentId → NetworkParentId ## 📋 Overview در سیستم قدیمی، کاربران با استفاده از `User.ParentId` به هم متصل می‌شدند (Parent-Child relationship). سیستم جدید **Network-Club-Commission** از یک **Binary Tree** استفاده می‌کند که نیاز به: - `User.NetworkParentId` (شناسه پدر در شبکه باینری) - `User.LegPosition` (Left یا Right) برای اجرای صحیح Worker و محاسبات، **باید** تمام کاربران قدیمی Migrate شوند. --- ## ⚠️ Critical Issues ### مشکل 1: Binary Tree Constraint - هر Parent فقط می‌تواند **2 فرزند** داشته باشد (Left & Right) - اگر کاربری در سیستم قدیمی بیشتر از 2 فرزند دارد، Migration فقط **2 فرزند اول** را می‌گیرد ### مشکل 2: Orphaned Nodes - اگر `ParentId` اشاره به یک کاربر نامعتبر (حذف شده) باشد، آن User **Orphaned** است - Orphaned nodes در Binary Tree نادیده گرفته می‌شوند --- ## 🚀 Migration Methods ### روش 1: Automatic (Seeder - توصیه می‌شود) Migration به صورت خودکار در `Program.cs` در حالت **Development** اجرا می‌شود: ```csharp // در Program.cs var migrationSeeder = new NetworkParentIdMigrationSeeder(dbContext, logger); await migrationSeeder.SeedAsync(); ``` **مزایا:** - ✅ Idempotent (می‌توان چندین بار اجرا کرد، فقط یکبار تاثیر می‌گذارد) - ✅ Validation اتوماتیک - ✅ Logging کامل **کجا اجرا می‌شود؟** - فقط در **Development** environment - هر بار که پروژه Run شود --- ### روش 2: Manual (Command) اگر نیاز به اجرای دستی دارید: ```csharp // درخواست از طریق MediatR var result = await _mediator.Send(new MigrateNetworkParentIdCommand()); if (result.Success) { Console.WriteLine($"Migrated: {result.MigratedCount}"); Console.WriteLine($"Skipped: {result.SkippedCount}"); } else { Console.WriteLine($"Error: {result.Message}"); } ``` --- ### روش 3: SQL Script برای Production یا اجرای مستقیم روی Database: ```bash # فایل: CMSMicroservice.Infrastructure/Migrations/Scripts/20250601_MigrateParentIdToNetworkParentId.sql ``` **نکته مهم:** قبل از اجرا، **حتماً** بررسی کنید که آیا کاربری بیش از 2 فرزند دارد: ```sql SELECT ParentId, COUNT(*) as ChildCount, STRING_AGG(CAST(Id AS VARCHAR), ', ') as ChildIds FROM Users WHERE ParentId IS NOT NULL GROUP BY ParentId HAVING COUNT(*) > 2; ``` --- ## 📊 Validation After Migration ### 1. بررسی تعداد کاربران Migrate شده ```csharp 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(); ``` ### 2. بررسی Orphaned Nodes ```sql SELECT Id, NetworkParentId FROM Users WHERE NetworkParentId IS NOT NULL AND NetworkParentId NOT IN (SELECT Id FROM Users); ``` ### 3. بررسی Binary Tree Violation ```sql SELECT NetworkParentId, COUNT(*) as ChildCount FROM Users WHERE NetworkParentId IS NOT NULL GROUP BY NetworkParentId HAVING COUNT(*) > 2; ``` --- ## ⚙️ Algorithm Details ### مراحل Migration: 1. **Find Users**: یافتن کاربران با `ParentId != NULL` و `NetworkParentId == NULL` 2. **Group by Parent**: گروه‌بندی بر اساس ParentId 3. **Check Constraint**: اگر Parent بیش از 2 فرزند دارد، فقط 2 تا اول را بگیر 4. **Assign Values**: ```csharp child.NetworkParentId = parentId; child.LegPosition = (i == 0) ? NetworkLeg.Left : NetworkLeg.Right; ``` 5. **Save & Validate**: ذخیره و اعتبارسنجی Binary Tree --- ## 🐛 Troubleshooting ### مشکل: Parent has more than 2 children **راه حل:** تصمیم دستی بگیرید که کدام 2 فرزند را نگه دارید: ```sql -- بررسی کنید که کدام Parent مشکل دارد SELECT ParentId, COUNT(*) as ChildCount FROM Users WHERE ParentId = 123 GROUP BY ParentId; -- لیست فرزندان را ببینید SELECT Id, FullName, CreatedAt FROM Users WHERE ParentId = 123 ORDER BY CreatedAt; -- دستی NetworkParentId را برای 2 فرزند انتخابی Set کنید UPDATE Users SET NetworkParentId = 123, LegPosition = 0 -- Left WHERE Id = 456; UPDATE Users SET NetworkParentId = 123, LegPosition = 1 -- Right WHERE Id = 789; ``` --- ### مشکل: Orphaned Nodes (Parent doesn't exist) **راه حل:** ParentId را NULL کنید یا به یک Parent معتبر متصل کنید: ```sql -- گزینه 1: NULL کردن (Root شدن) UPDATE Users SET ParentId = NULL, NetworkParentId = NULL WHERE ParentId = 999; -- 999 وجود ندارد -- گزینه 2: اتصال به Parent دیگر UPDATE Users SET ParentId = 1, NetworkParentId = 1 WHERE ParentId = 999; ``` --- ## ✅ Checklist Before Production - [ ] Migration در Development اجرا شده؟ - [ ] Validation Errors بررسی شد؟ - [ ] Orphaned Nodes رفع شدند؟ - [ ] Binary Tree Violations رفع شدند؟ - [ ] Backup از Database گرفته شده؟ - [ ] Migration Script برای Production آماده است؟ - [ ] Testing کامل انجام شده؟ --- ## 🔗 Related Files - **Seeder**: `CMSMicroservice.Infrastructure/Data/Seeding/NetworkParentIdMigrationSeeder.cs` - **Command**: `CMSMicroservice.Application/UserCQ/Commands/MigrateNetworkParentId/` - **SQL Script**: `CMSMicroservice.Infrastructure/Migrations/Scripts/20250601_MigrateParentIdToNetworkParentId.sql` - **Entity**: `CMSMicroservice.Domain/Entities/User.cs` (خطوط 16, 45, 49) --- ## 📞 Support اگر مشکل خاصی با Migration پیدا کردید: 1. Log های Seeder را بررسی کنید 2. ValidationErrors را چک کنید 3. SQL Script را به صورت دستی اجرا کنید