Files
docs/archive/05-TASKS/NEW-BUSINESS-REQUIREMENTS-TASKS.md
T
masoodafar-web 5965b98728 update
2026-01-03 18:27:49 +03:30

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);
        }
    }
}

تست:

  1. کاربر جدید با CreatedAt = DateTime.Now.AddDays(-15) ایجاد کنید
  2. IsActive = false, ClubMembershipId = null
  3. Worker را مجبور به اجرا کنید (یا زمان را تغییر دهید)
  4. چک کنید: user.IsDeleted = true

تخمین زمان: 4-6 ساعت


Task #2: الزامی کردن دیالوگ باشگاه مشتریان

شرح: بعد از پرداخت موفق 56 میلیون تومان، کاربر باید قرارداد باشگاه مشتریان را امضا کند. تا زمانی که امضا نکند، نمی‌تواند به سایر بخش‌های سیستم دسترسی داشته باشد و لینک معرفی خود را ببیند.

Acceptance Criteria:

  • بعد از تأیید پرداخت، Modal/Dialog باشگاه مشتریان باز شود
  • دکمه Close غیرفعال باشد (یا Modal با disableBackdropClick باز شود)
  • کاربر نتواند از دیالوگ خارج شود (ESC هم کار نکند)
  • بعد از امضای قرارداد:
    • ClubMembership record ایجاد شود
    • User.ClubMembershipId Set شود
    • 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");
}

تست:

  1. پرداخت 56M انجام دهید
  2. بعد از موفقیت، باید Dialog باز شود
  3. سعی کنید Close کنید → نشود
  4. بدون tick نزدن → دکمه غیرفعال باشد
  5. tick بزنید و امضا کنید → redirect به Dashboard
  6. لینک معرفی نمایش داده شود

تخمین زمان: 6-8 ساعت


Task #3: شرط نمایش لینک معرفی

شرح: لینک معرفی فقط باید برای کاربرانی نمایش داده شود که:

  1. پرداخت کرده‌اند (IsActive = true)
  2. عضو باشگاه مشتریان شده‌اند (ClubMembershipId != null)
  3. عضویت باشگاه فعال است (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;

تست:

  1. کاربر بدون ClubMembership → Alert نمایش داده شود
  2. کاربر با ClubMembership فعال → لینک نمایش داده شود
  3. دکمه کپی کار کند

تخمین زمان: 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; // هر دو پر است
}

تست:

  1. Parent با 2 فرزند فعال
  2. ثبت نام با کد این Parent
  3. باید 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 = "خطا در بررسی کد معرف";
    }
}

تست:

  1. Parent پر را پیدا کنید
  2. کد معرف او را در Register وارد کنید
  3. پیغام واضح نمایش داده شود

تخمین زمان: 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 حساب می‌شود که:

  1. کد نوشته شده و Build موفق
  2. Unit Test / Manual Test انجام شده
  3. Code Review شده
  4. Documentation به‌روز شده
  5. Merge به Main Branch

تهیه‌کننده: AI Assistant
تاریخ: 2025-12-08
نسخه: 1.0