8.0 KiB
8.0 KiB
🌳 Binary Tree Network Registration Guide
📋 Overview
از این پس، هر کاربر جدید که در سیستم ثبت میشود، همزمان در دو ساختار قرار میگیرد:
- Old System:
User.ParentId(برای Backward Compatibility) - New Binary Tree System:
User.NetworkParentId+User.LegPosition(Left/Right)
این تغییر تضمین میکند که:
- ✅ کاربران جدید بلافاصله در محاسبات Commission شرکت میکنند
- ✅ نیازی به Migration اضافی نیست
- ✅ Binary Tree Constraint رعایت میشود (حداکثر 2 فرزند)
🔧 Changes in Registration Flow
قبل از تغییر:
var entity = request.Adapt<User>();
entity.ReferralCode = UtilExtensions.Generate(digits: 10);
await _context.Users.AddAsync(entity, cancellationToken);
مشکل: فقط ParentId Set میشد، NetworkParentId و LegPosition خالی میماند.
بعد از تغییر:
var entity = request.Adapt<User>();
entity.ReferralCode = UtilExtensions.Generate(digits: 10);
// === محاسبه موقعیت در Binary Tree ===
if (request.ParentId.HasValue)
{
var legPosition = await _networkPlacementService.CalculateLegPositionAsync(
request.ParentId.Value, cancellationToken);
if (legPosition.HasValue)
{
entity.NetworkParentId = request.ParentId.Value;
entity.LegPosition = legPosition.Value; // Left یا Right
}
else
{
// Parent پر است! Auto-Placement یا Error
var availableParent = await _networkPlacementService.FindAvailableParentAsync(
request.ParentId.Value, cancellationToken);
// ... Set کردن NetworkParentId و LegPosition با Parent جدید
}
}
await _context.Users.AddAsync(entity, cancellationToken);
مزایا:
- ✅
NetworkParentIdوLegPositionبه صورت خودکار محاسبه میشود - ✅ Binary Tree Constraint چک میشود
- ✅ اگر Parent پر باشد، Auto-Placement انجام میشود
📐 Binary Tree Logic
قوانین:
- هر Parent فقط 2 فرزند میتواند داشته باشد (Left & Right)
- فرزند اول:
LegPosition = Left - فرزند دوم:
LegPosition = Right - اگر Parent پر باشد، سیستم به صورت BFS دنبال Parent خالی میگردد
مثال:
User1 (Root)
/ \
User2 (L) User3 (R)
/ \
User4(L) User5(R)
- User2 → Parent=User1, Leg=Left
- User3 → Parent=User1, Leg=Right
- User4 → Parent=User2, Leg=Left
- User5 → Parent=User2, Leg=Right
اگر کاربر جدید با ParentId=User1 بیاید:
- User1 پر است! (دو فرزند دارد)
- سیستم به User2 میرود (BFS)
- User2 هم پر است!
- به User3 میرود → User3 خالی است
- کاربر جدید → Parent=User3, Leg=Left
🛠️ NetworkPlacementService API
1. CalculateLegPositionAsync
محاسبه موقعیت (Left/Right) برای کاربر جدید زیر یک Parent مشخص.
var legPosition = await _networkPlacementService.CalculateLegPositionAsync(parentId);
Return Values:
NetworkLeg.Left: اگر Parent فرزند چپ نداردNetworkLeg.Right: اگر Parent فرزند راست نداردnull: اگر Parent پر است (دو فرزند دارد)
2. CanAcceptChildAsync
بررسی اینکه آیا Parent میتواند فرزند جدید بپذیرد.
bool canAccept = await _networkPlacementService.CanAcceptChildAsync(parentId);
Return Values:
true: اگر Parent کمتر از 2 فرزند داردfalse: اگر Parent پر است
3. FindAvailableParentAsync (Auto-Placement)
پیدا کردن اولین Parent خالی در Binary Tree با استفاده از BFS.
long? availableParentId = await _networkPlacementService.FindAvailableParentAsync(rootParentId);
Use Case:
- زمانی که Parent مورد نظر پر است
- سیستم به صورت خودکار Parent جایگزین پیدا میکند
- از BFS استفاده میکند (Level-by-Level)
Return Values:
long: شناسه Parent مناسبnull: اگر هیچ Parent خالی پیدا نشد (تمام Binary Tree پر است!)
⚠️ Error Handling
Scenario 1: Parent پر است و Auto-Placement موفق
// Parent اصلی پر است
// سیستم Parent جدید پیدا میکند
_logger.LogWarning("Parent {ParentId} is full. Auto-placing under {NewParentId}");
نتیجه: کاربر با موفقیت در جای دیگری قرار میگیرد.
Scenario 2: کل Binary Tree پر است
throw new InvalidOperationException(
$"شبکه Parent با شناسه {parentId} پر است و نمیتواند کاربر جدید بپذیرد.");
نتیجه: Exception پرتاب میشود، ثبت کاربر انجام نمیشود.
راه حل:
- افزایش سطح Binary Tree
- یا تخصیص دستی Parent
Scenario 3: Parent وجود ندارد
var parentExists = await _context.Users.AnyAsync(u => u.Id == parentId);
if (!parentExists)
{
return null; // Parent نامعتبر
}
نتیجه: null برگردانده میشود، Exception پرتاب میشود.
📊 Logging & Monitoring
سیستم Log های زیر را مینویسد:
Success:
User 123 placed in Binary Tree: Parent=45, Leg=Left
Warning (Auto-Placement):
Parent 45 has no available leg! Finding alternative parent...
User 123 auto-placed under alternative Parent=67, Leg=Right
Error (Binary Tree Full):
No available parent found in network for ParentId=45
🧪 Testing Scenarios
Test 1: کاربر اول (Root)
var command = new CreateNewUserCommand { Mobile = "09121234567" }; // No ParentId
// Result: ParentId=null, NetworkParentId=null, LegPosition=null
Test 2: فرزند اول
var command = new CreateNewUserCommand { Mobile = "09121234568", ParentId = 1 };
// Result: ParentId=1, NetworkParentId=1, LegPosition=Left
Test 3: فرزند دوم
var command = new CreateNewUserCommand { Mobile = "09121234569", ParentId = 1 };
// Result: ParentId=1, NetworkParentId=1, LegPosition=Right
Test 4: فرزند سوم (Parent پر است)
var command = new CreateNewUserCommand { Mobile = "09121234570", ParentId = 1 };
// Result: Auto-Placement → ParentId=1, NetworkParentId=2 (یا 3), LegPosition=Left
🔗 Related Files
- Service Interface:
CMSMicroservice.Application/Common/Interfaces/INetworkPlacementService.cs - Service Implementation:
CMSMicroservice.Infrastructure/Services/NetworkPlacementService.cs - Handler:
CMSMicroservice.Application/UserCQ/Commands/CreateNewUser/CreateNewUserCommandHandler.cs - DI Registration:
CMSMicroservice.Infrastructure/ConfigureServices.cs(خط 23)
✅ Checklist
INetworkPlacementServiceاضافه شدNetworkPlacementServiceپیادهسازی شد- DI Container تنظیم شد
CreateNewUserCommandHandlerاصلاح شد- Unit Tests نوشته شود
- Integration Tests انجام شود
- Manual Testing با Postman/gRPC Client
🚀 Next Steps
- Test کردن: ثبت چند کاربر با Parent مشابه و بررسی LegPosition
- Load Testing: بررسی Performance با 10,000 کاربر
- Edge Cases: تست Binary Tree Full scenario
- Documentation: Update کردن API Docs
📞 Support
اگر مشکلی پیش آمد:
- Log های
NetworkPlacementServiceرا بررسی کنید - چک کنید که DI به درستی تنظیم شده باشد
- از
CanAcceptChildAsyncبرای Pre-Validation استفاده کنید