- Added PersianDateTimeService for converting Gregorian dates to Persian format in the BackOffice frontend. - Updated multiple frontend pages (Dashboard, UserPayouts, WorkerControl, UserNetworkInfo) to utilize the new Persian date service. - Enhanced GetUserNetworkPositionDto with 28+ new fields for comprehensive user network data. - Updated GetUserNetworkPositionQueryHandler to include new methods for calculating network statistics. - Modified Protobuf messages to accommodate the new fields, increasing from 14 to 42. - Refined week number calculation algorithm to ensure consistency across C# and SQL implementations. - Created new CSV and Excel files for binary plan calculations. - Ensured all changes are tested and validated for accuracy and performance.
20 KiB
📋 Task List - توضیحات جدید بیزینس 2025-12-08
تاریخ ایجاد: 2025-12-08
آخرین بهروزرسانی: 2025-12-09
منبع: تحلیل توضیحات شفاهی جدید بیزینس
وضعیت: ✅ Task #0 Complete, بقیه آماده برای اجرا
✅ Completed Tasks
Task #0: اصلاح محاسبات تعادل و فلش ✅
شرح: کد محاسبه تعادل با توضیحات دقیق بیزینس تطبیق داده شد.
انجام شده:
- ✅ ترتیب محاسبات اصلاح شد (تعادل → باقیمانده → سقف → فلش)
- ✅ فلش از هر دو طرف محاسبه میشود
- ✅ باقیمانده جداگانه ذخیره میشود (چپ و راست)
- ✅ Documentation بهروزرسانی شد
- ✅ مثالهای 5 لول عمقی اضافه شد
فایلهای تغییر یافته:
CMS/src/.../CalculateWeeklyBalancesCommandHandler.cs (اصلاح شد)
totalDoc/01-BUSINESS/balance-calculation-rules.md (بهروزرسانی شد)
totalDoc/01-BUSINESS/balance-calculation-examples-5-levels.md (جدید)
تاریخ اتمام: 2025-12-09
🔥 Priority 1: Critical Tasks
Task #1: پیادهسازی Worker حذف خودکار کاربران غیرفعال
شرح: کاربرانی که تا 2 هفته بعد از ثبت نام هیچکدام از موارد زیر را انجام ندادند باید به صورت خودکار حذف شوند:
- وام دایا نگرفتند
- پرداخت مستقیم 56 میلیون نکردند
Acceptance Criteria:
- Worker روزانه یک بار اجرا شود (مثلاً ساعت 3 صبح)
- کاربرانی با
CreatedAt < Now - 14 daysوIsActive = falseوClubMembershipId = nullشناسایی شوند - کاربر به صورت Soft Delete حذف شود (یا Hard Delete بر اساس تصمیم)
- جایگاه شبکه (Network Position) آزاد شود
- معرف (Parent) بتواند دوباره کاربر جدید جذب کند
- Log کامل عملیات حذف ثبت شود
فایلهای نیاز به ایجاد/تغییر:
CMS/src/CMSMicroservice.WebApi/BackgroundWorkers/
└── DeleteInactiveUsersJob.cs (جدید)
CMS/src/CMSMicroservice.Application/UserCQ/Commands/
└── DeleteInactiveUser/
├── DeleteInactiveUserCommand.cs (جدید)
└── DeleteInactiveUserCommandHandler.cs (جدید)
CMS/src/CMSMicroservice.WebApi/Program.cs
└── services.AddHostedService<DeleteInactiveUsersJob>();
کد پیشنهادی:
public class DeleteInactiveUsersJob : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<DeleteInactiveUsersJob> _logger;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// محاسبه زمان اجرا (3 صبح)
var now = DateTime.Now;
var next3AM = now.Date.AddDays(1).AddHours(3);
var delay = next3AM - now;
await Task.Delay(delay, stoppingToken);
using var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<IApplicationDbContext>();
var twoWeeksAgo = DateTime.Now.AddDays(-14);
var inactiveUsers = await context.Users
.Where(u => u.Created < twoWeeksAgo
&& u.ClubMembershipId == null
&& !u.IsActive)
.ToListAsync(stoppingToken);
_logger.LogInformation($"🧹 حذف {inactiveUsers.Count} کاربر غیرفعال بیش از 2 هفته");
foreach (var user in inactiveUsers)
{
// حذف کاربر
user.IsDeleted = true; // Soft Delete
user.DeletedAt = DateTime.Now;
// آزادسازی جایگاه شبکه
// (NetworkParentId را null نکنید چون تاریخچه نیاز دارد)
_logger.LogWarning($"❌ حذف کاربر: {user.Id} - {user.UserName}");
}
await context.SaveChangesAsync(stoppingToken);
}
}
}
تست:
- کاربر جدید با
CreatedAt = DateTime.Now.AddDays(-15)ایجاد کنید IsActive = false,ClubMembershipId = null- Worker را مجبور به اجرا کنید (یا زمان را تغییر دهید)
- چک کنید:
user.IsDeleted = true
تخمین زمان: 4-6 ساعت
Task #2: الزامی کردن دیالوگ باشگاه مشتریان
شرح: بعد از پرداخت موفق 56 میلیون تومان، کاربر باید قرارداد باشگاه مشتریان را امضا کند. تا زمانی که امضا نکند، نمیتواند به سایر بخشهای سیستم دسترسی داشته باشد و لینک معرفی خود را ببیند.
Acceptance Criteria:
- بعد از تأیید پرداخت، Modal/Dialog باشگاه مشتریان باز شود
- دکمه Close غیرفعال باشد (یا Modal با
disableBackdropClickباز شود) - کاربر نتواند از دیالوگ خارج شود (ESC هم کار نکند)
- بعد از امضای قرارداد:
ClubMembershiprecord ایجاد شودUser.ClubMembershipIdSet شود- 25 میلیون تومان به
WeeklyCommissionPoolاضافه شود
- بعد از امضا، redirect به Dashboard
- در Dashboard لینک معرفی نمایش داده شود
فایلهای نیاز به تغییر:
FrontOffice/src/FrontOffice.Main/Pages/
└── Payment/PaymentSuccess.razor
└── Payment/PaymentSuccess.razor.cs
FrontOffice/src/FrontOffice.Main/Components/
└── ClubMembershipDialog.razor (جدید یا اصلاح)
CMS/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/
└── CreateClubMembership/
├── CreateClubMembershipCommand.cs
└── CreateClubMembershipCommandHandler.cs
کد پیشنهادی (Frontend):
@* PaymentSuccess.razor *@
@if (_showClubDialog)
{
<MudDialog @bind-IsVisible="_showClubDialog"
Options="@(new DialogOptions {
DisableBackdropClick = true,
CloseButton = false
})">
<DialogContent>
<h3>عضویت در باشگاه مشتریان</h3>
<p>برای ادامه، لطفاً قرارداد باشگاه مشتریان را مطالعه و امضا کنید.</p>
<MudPaper Class="pa-4 my-4" Elevation="2">
<p>متن قرارداد...</p>
</MudPaper>
<MudCheckBox @bind-Checked="_agreedToTerms">
متن قرارداد را مطالعه کردم و با آن موافقم
</MudCheckBox>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Disabled="!_agreedToTerms"
OnClick="SignContract">
امضای قرارداد
</MudButton>
</DialogActions>
</MudDialog>
}
// PaymentSuccess.razor.cs
private bool _showClubDialog = false;
private bool _agreedToTerms = false;
protected override async Task OnInitializedAsync()
{
// بعد از تأیید پرداخت
if (PaymentConfirmed && !User.ClubMembershipId.HasValue)
{
_showClubDialog = true;
}
}
private async Task SignContract()
{
var request = new CreateClubMembershipRequest
{
UserId = User.Id,
InitialContribution = 25000000
};
await ClubMembershipContract.CreateClubMembershipAsync(request);
_showClubDialog = false;
NavigationManager.NavigateTo("/dashboard");
}
تست:
- پرداخت 56M انجام دهید
- بعد از موفقیت، باید Dialog باز شود
- سعی کنید Close کنید → نشود
- بدون tick نزدن → دکمه غیرفعال باشد
- tick بزنید و امضا کنید → redirect به Dashboard
- لینک معرفی نمایش داده شود
تخمین زمان: 6-8 ساعت
Task #3: شرط نمایش لینک معرفی
شرح: لینک معرفی فقط باید برای کاربرانی نمایش داده شود که:
- پرداخت کردهاند (
IsActive = true) - عضو باشگاه مشتریان شدهاند (
ClubMembershipId != null) - عضویت باشگاه فعال است (
ClubMembership.IsActive = true)
Acceptance Criteria:
- در صفحه Dashboard یا Profile، شرط بالا چک شود
- اگر شرایط برقرار نیست:
- پیغام: "برای دریافت لینک معرفی، ابتدا عضو باشگاه مشتریان شوید"
- دکمه "عضویت در باشگاه" (در صورت عدم عضویت)
- اگر شرایط برقرار است:
- لینک معرفی نمایش داده شود
- دکمه کپی
- QR Code (اختیاری)
فایلهای نیاز به تغییر:
FrontOffice/src/FrontOffice.Main/Pages/
└── Dashboard/Dashboard.razor
└── Dashboard/Dashboard.razor.cs
یا
FrontOffice/src/FrontOffice.Main/Pages/
└── Profile/MyProfile.razor
کد پیشنهادی:
@if (CanShowReferralLink)
{
<MudCard Class="my-4">
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.h6">🔗 لینک معرفی شما</MudText>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent>
<MudTextField @bind-Value="_referralLink"
ReadOnly="true"
Adornment="Adornment.End"
AdornmentIcon="@Icons.Material.Filled.ContentCopy"
OnAdornmentClick="CopyLink"/>
</MudCardContent>
</MudCard>
}
else
{
<MudAlert Severity="Severity.Warning" Class="my-4">
برای دریافت لینک معرفی، ابتدا عضو باشگاه مشتریان شوید.
@if (!User.ClubMembershipId.HasValue)
{
<MudButton Color="Color.Primary"
Variant="Variant.Filled"
Class="mt-2"
OnClick="OpenClubDialog">
عضویت در باشگاه
</MudButton>
}
</MudAlert>
}
private bool CanShowReferralLink =>
User.IsActive
&& User.ClubMembershipId.HasValue
&& User.ClubMembership?.IsActive == true;
تست:
- کاربر بدون
ClubMembership→ Alert نمایش داده شود - کاربر با
ClubMembershipفعال → لینک نمایش داده شود - دکمه کپی کار کند
تخمین زمان: 3-4 ساعت
⚠️ Priority 2: Medium Tasks
Task #4: Validation دقیقتر محدودیت 2 فرزند فعال
شرح: در هنگام ثبت نام با کد معرف، باید بررسی شود که آیا Parent حداکثر 2 فرزند فعال دارد یا نه (نه فقط 2 فرزند).
Acceptance Criteria:
- Validation در
CreateUserCommandHandlerیاNetworkPlacementService - شمارش فرزندان با شرط:
u.NetworkParentId == parentId && u.IsActive && u.ClubMembershipId != null - اگر
activeChildCount >= 2:- Exception: "این کاربر تعداد زیرمجموعههاش پر شده و شما نمیتونید جزو زیرمجموعه این آدم بشید"
- یا Auto-Placement (بر اساس تصمیم)
فایلهای نیاز به تغییر:
CMS/src/CMSMicroservice.Application/Services/
└── NetworkPlacementService.cs
CMS/src/CMSMicroservice.Application/UserCQ/Commands/CreateUser/
└── CreateUserCommandHandler.cs
└── CreateUserCommandValidator.cs
کد پیشنهادی:
public async Task<NetworkLeg?> CalculateLegPositionAsync(long parentId, CancellationToken cancellationToken)
{
var activeChildrenCount = await _context.Users
.CountAsync(u => u.NetworkParentId == parentId
&& u.IsActive
&& u.ClubMembershipId != null,
cancellationToken);
if (activeChildrenCount >= 2)
{
throw new InvalidOperationException(
"این کاربر تعداد زیرمجموعههاش پر شده و شما نمیتونید جزو زیرمجموعه این آدم بشید");
}
// بررسی Left و Right
var hasLeft = await _context.Users
.AnyAsync(u => u.NetworkParentId == parentId
&& u.LegPosition == NetworkLeg.Left
&& u.IsActive
&& u.ClubMembershipId != null,
cancellationToken);
if (!hasLeft) return NetworkLeg.Left;
var hasRight = await _context.Users
.AnyAsync(u => u.NetworkParentId == parentId
&& u.LegPosition == NetworkLeg.Right
&& u.IsActive
&& u.ClubMembershipId != null,
cancellationToken);
if (!hasRight) return NetworkLeg.Right;
return null; // هر دو پر است
}
تست:
- Parent با 2 فرزند فعال
- ثبت نام با کد این Parent
- باید Exception بیاید
تخمین زمان: 3-4 ساعت
Task #5: بهبود پیغام خطای کد معرف پر
شرح: در صفحه ثبت نام، اگر کاربر کد معرفی وارد کند که ظرفیتش پر است، باید پیغام خطای واضح و فارسی نمایش داده شود.
Acceptance Criteria:
- در Frontend، بعد از وارد کردن کد معرف، validation شود
- اگر کد پر بود، پیغام:
"این کد معرف ظرفیتش پر شده، لطفا از کد معرف دیگری استفاده کنید"
- Snackbar یا Alert با Severity.Warning
- فیلد کد معرف هایلایت شود (قرمز)
فایلهای نیاز به تغییر:
FrontOffice/src/FrontOffice.Main/Pages/
└── Register.razor
└── Register.razor.cs
کد پیشنهادی:
private async Task ValidateReferralCode()
{
if (string.IsNullOrWhiteSpace(_referralCode))
return;
try
{
var request = new ValidateReferralCodeRequest
{
ReferralCode = _referralCode
};
var response = await UserContract.ValidateReferralCodeAsync(request);
if (!response.IsValid)
{
_referralCodeError = "کد معرف نامعتبر است";
}
else if (response.IsFull)
{
_referralCodeError = "این کد معرف ظرفیتش پر شده، لطفا از کد معرف دیگری استفاده کنید";
Snackbar.Add(_referralCodeError, Severity.Warning);
}
}
catch (Exception ex)
{
_referralCodeError = "خطا در بررسی کد معرف";
}
}
تست:
- Parent پر را پیدا کنید
- کد معرف او را در Register وارد کنید
- پیغام واضح نمایش داده شود
تخمین زمان: 2-3 ساعت
📝 Priority 3: Documentation Tasks
Task #6: بهروزرسانی مستندات
شرح: با توجه به توضیحات جدید، داکیومنتهای زیر باید Update شوند.
فایلهای نیاز به تغییر:
1. totalDoc/01-BUSINESS/network-commission-system.md
# اضافه کردن بخش جدید:
## ۱۰. حذف خودکار کاربران غیرفعال
کاربرانی که تا 2 هفته بعد از ثبت نام:
- وام دایا نگرفتهاند
- پرداخت مستقیم 56 میلیون نکردهاند
به صورت خودکار حذف میشوند.
**Worker**: `DeleteInactiveUsersJob`
**زمان اجرا**: روزانه ساعت 3 صبح
**منطق**: `CreatedAt < Now - 14 days && !IsActive && ClubMembershipId == null`
---
## ۱۱. شرایط نمایش لینک معرفی
لینک معرفی فقط برای کاربرانی نمایش داده میشود که:
1. پرداخت کردهاند (IsActive = true)
2. عضو باشگاه مشتریان شدهاند (ClubMembershipId != null)
3. عضویت باشگاه فعال است (ClubMembership.IsActive = true)
**تا زمانی که این شرایط برقرار نباشد، کاربر نمیتواند لینک معرفی خود را ببیند.**
---
## ۱۲. الزامی بودن دیالوگ باشگاه مشتریان
بعد از پرداخت موفق 56 میلیون تومان، کاربر **باید** قرارداد باشگاه مشتریان را امضا کند.
**فرآیند**:
1. پرداخت موفق
2. Dialog باشگاه مشتریان باز میشود
3. کاربر نمیتواند Dialog را ببندد
4. باید قرارداد را بخواند و امضا کند
5. بعد از امضا → redirect به Dashboard
6. لینک معرفی نمایش داده میشود
2. totalDoc/01-BUSINESS/binary-tree-guide.md
# اصلاح بخش Validation:
### محدودیت 2 فرزند **فعال**
هر Parent فقط میتواند **2 فرزند فعال** داشته باشد.
**تعریف فعال**:
- IsActive = true
- ClubMembershipId != null
- عضویت باشگاه فعال است
**نکته مهم**: کاربرانی که ثبت نام کردهاند اما هنوز فعال نشدهاند، در شمارش 2 فرزند محسوب نمیشوند.
3. totalDoc/03-BACKEND/CMS/implementation-status.md
# افزودن به بخش Background Workers:
### ✅ DeleteInactiveUsersWorker (NEW - 2025-12-08)
**وضعیت**: 🔴 نیاز به پیادهسازی
**شرح**: حذف خودکار کاربران غیرفعال بعد از 2 هفته
**منطق**:
- روزانه ساعت 3 صبح اجرا میشود
- کاربرانی که `CreatedAt < Now - 14 days`
- و `IsActive = false`
- و `ClubMembershipId = null`
- به صورت Soft Delete حذف میشوند
**فایل**: `CMS/BackgroundWorkers/DeleteInactiveUsersJob.cs`
**Dependencies**:
- IApplicationDbContext
- ILogger
4. totalDoc/05-TASKS/BACKLOG.md
# اضافه کردن این 5 Task به Backlog
## 🔥 Critical
- [ ] Task #1: پیادهسازی DeleteInactiveUsersWorker (6h)
- [ ] Task #2: الزامی کردن دیالوگ باشگاه (8h)
- [ ] Task #3: شرط نمایش لینک معرفی (4h)
## ⚠️ Medium
- [ ] Task #4: Validation 2 فرزند فعال (4h)
- [ ] Task #5: پیغام خطای کد معرف پر (3h)
## 📝 Low
- [ ] Task #6: Update Documentation (2h)
**زمان کل**: 27 ساعت (~4 روز کاری)
تخمین زمان: 2-3 ساعت
📊 خلاصه Task ها
| # | عنوان | Priority | زمان | وضعیت |
|---|---|---|---|---|
| 1 | DeleteInactiveUsersWorker | 🔥 Critical | 6h | ⬜ Todo |
| 2 | الزامی دیالوگ باشگاه | 🔥 Critical | 8h | ⬜ Todo |
| 3 | شرط لینک معرفی | 🔥 Critical | 4h | ⬜ Todo |
| 4 | Validation 2 فرزند فعال | ⚠️ Medium | 4h | ⬜ Todo |
| 5 | پیغام کد معرف پر | ⚠️ Medium | 3h | ⬜ Todo |
| 6 | Update Documentation | 📝 Low | 3h | ⬜ Todo |
مجموع زمان: 28 ساعت (~4 روز کاری)
🎯 پلان اجرا (پیشنهادی)
روز 1 (8 ساعت):
- Task #1: DeleteInactiveUsersWorker (6h)
- شروع Task #2 (2h)
روز 2 (8 ساعت):
- ادامه Task #2: Dialog الزامی (6h)
- شروع Task #3 (2h)
روز 3 (8 ساعت):
- ادامه Task #3: شرط لینک (2h)
- Task #4: Validation (4h)
- شروع Task #5 (2h)
روز 4 (4 ساعت):
- ادامه Task #5 (1h)
- Task #6: Documentation (3h)
✅ Definition of Done
هر Task زمانی Complete حساب میشود که:
- ✅ کد نوشته شده و Build موفق
- ✅ Unit Test / Manual Test انجام شده
- ✅ Code Review شده
- ✅ Documentation بهروز شده
- ✅ Merge به Main Branch
تهیهکننده: AI Assistant
تاریخ: 2025-12-08
نسخه: 1.0