Files
docs/archive/CHANGELOG-2025-12-25.md
T
masoodafar-web 5965b98728 update
2026-01-03 18:27:49 +03:30

16 KiB
Raw Blame History

📝 Changelog - ۵ دی ۱۴۰۴ (25 December 2025)

Session: فلگ فعال‌سازی چتیکا، اصلاح DayaLoan، بازنویسی صفحه درخت شبکه با org-chart، Stored Procedure برای GetNetworkTree


🎯 خلاصه Session

این session شامل موارد زیر بود:

  1. Chatika Enabled Flag - اضافه کردن فلگ فعال/غیرفعال برای Worker چتیکا
  2. DayaLoanCheckWorker Fix - جلوگیری از استعلام مجدد مشتریان با قرارداد
  3. BackOffice Network Tree Rewrite - بازنویسی کامل با d3-org-chart
  4. Tooltip on Hover - نمایش اطلاعات کاربر روی hover
  5. Week Filter Visual - تمایز بصری کاربران فعال شده در هفته فیلتر شده
  6. Stored Procedure - SP برای GetNetworkTree جهت بهبود performance

New Features

1. Chatika Enabled Flag

نیاز: قابلیت غیرفعال کردن موقت Worker چتیکا از طریق Config

راه‌حل: اضافه کردن فلگ Enabled در تنظیمات

تغییرات در appsettings.json:

"Chatika": {
    "Enabled": false,  // ← فلگ جدید
    "BaseUrl": "https://api.chatika.ir",
    "ApiKey": "..."
}

تغییرات در ChatikaAccountActivationJob.cs:

public async Task ExecuteAsync()
{
    // Check if Chatika integration is enabled
    var isEnabled = _configuration.GetValue<bool>("Chatika:Enabled", false);
    if (!isEnabled)
    {
        _logger.LogDebug("Chatika integration is disabled. Skipping job execution.");
        return;
    }
    
    // ... rest of the job
}

فایل‌های تغییر یافته:

  • CMSMicroservice.WebApi/appsettings.json
  • CMSMicroservice.Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs

2. DayaLoanCheckWorker - Exclude Existing Contracts

مشکل: Worker استعلام دایا برای مشتریانی که قبلاً قرارداد گرفته‌اند مجدداً کال می‌شد

راه‌حل: فیلتر کردن کاربرانی که ContractNumber دارند

کد اضافه شده:

// Get national codes of users who already have contracts
var existingContractNationalCodes = await _dbContext.DayaLoanContracts
    .Where(c => !c.IsDeleted && !string.IsNullOrEmpty(c.ContractNumber))
    .Select(c => c.NationalCode)
    .Distinct()
    .ToListAsync(stoppingToken);

// Filter out users who already have contracts
var usersToProcess = eligibleUsers
    .Where(u => !existingContractNationalCodes.Contains(u.NationalCode))
    .ToList();

_logger.LogInformation(
    "Filtered users: {TotalEligible} eligible, {WithContract} already have contracts, {ToProcess} to process",
    eligibleUsers.Count, 
    eligibleUsers.Count - usersToProcess.Count, 
    usersToProcess.Count);

فایل: CMSMicroservice.WebApi/Workers/DayaLoanCheckWorker.cs


3. 🌳 BackOffice Network Tree - Complete Rewrite with d3-org-chart

نیاز: استفاده از پلاگین org-chart به جای D3 دستی برای نمایش درخت شبکه در پنل ادمین

معماری جدید:

┌─────────────────────────────────────────────────────────────┐
│                   NetworkTreeViewer.razor                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ MudToolBar:                                          │    │
│  │ [Search] [Week Filter] [Expand] [Collapse] [Export]  │    │
│  └─────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ Navigation History (Breadcrumb)                      │    │
│  │ User 123 → User 456 → User 789                       │    │
│  └─────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ d3-org-chart Container (#admin-org-chart)            │    │
│  │                                                       │    │
│  │        ┌──────┐                                       │    │
│  │        │ Root │                                       │    │
│  │        └──┬───┘                                       │    │
│  │      ┌───┴───┐                                        │    │
│  │   ┌──┴──┐ ┌──┴──┐                                     │    │
│  │   │Left │ │Right│                                     │    │
│  │   └─────┘ └─────┘                                     │    │
│  │                                                       │    │
│  └─────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ MudDataGrid (Table View)                             │    │
│  │ Mobile | Name | Level | Position | Status | Actions  │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

فایل‌های جدید:

فایل توضیح
wwwroot/js/admin-org-chart.js Wrapper برای d3-org-chart با امکانات ادمین
wwwroot/css/admin-org-chart.css استایل کارت‌های نود با رنگ‌بندی چپ/راست
wwwroot/js/d3-org-chart3.js کتابخانه d3-org-chart (کپی از FrontOffice)
wwwroot/js/d3-flextree.min.js Dependency برای org-chart

فایل‌های تغییر یافته:

  • NetworkTreeViewer.razor - بازنویسی کامل
  • wwwroot/index.html - اضافه شدن رفرنس‌های JS/CSS

4. 💬 Tooltip on Hover

نیاز: نمایش اطلاعات کامل کاربر هنگام hover روی نود

پیاده‌سازی در admin-org-chart.js:

// Tooltip container
const tooltip = d3.select('body').append('div')
    .attr('class', 'admin-org-tooltip')
    .style('opacity', 0);

// Node hover events
.on('mouseover', function(event, d) {
    tooltip.transition().duration(200).style('opacity', .95);
    tooltip.html(`
        <div class="tooltip-header">${d.data.firstName} ${d.data.lastName}</div>
        <div class="tooltip-row"><span>📱</span> ${d.data.mobile}</div>
        <div class="tooltip-row"><span>📊</span> سطح: ${d.data.networkLevel}</div>
        <div class="tooltip-row"><span>📍</span> ${d.data.legPosition === 1 ? 'چپ' : 'راست'}</div>
        ${d.data.isClubActive ? 
            `<div class="tooltip-row active"><span>✅</span> باشگاه فعال</div>` : 
            `<div class="tooltip-row inactive"><span>❌</span> باشگاه غیرفعال</div>`
        }
        ${d.data.activationWeekDisplayName ? 
            `<div class="tooltip-row"><span>📅</span> ${d.data.activationWeekDisplayName}</div>` : ''
        }
    `)
    .style('left', (event.pageX + 15) + 'px')
    .style('top', (event.pageY - 10) + 'px');
})

استایل Tooltip:

.admin-org-tooltip {
    position: absolute;
    background: rgba(33, 33, 33, 0.95);
    color: white;
    padding: 12px 16px;
    border-radius: 8px;
    font-size: 13px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    pointer-events: none;
    z-index: 10000;
    direction: rtl;
}

5. 🎨 Week Filter Visual Distinction

نیاز: وقتی هفته فیلتر می‌شه، کاربران فعال شده در اون هفته متمایز باشن

پیاده‌سازی:

// Apply week filter styling
applyWeekFilter: function(weekId) {
    if (!this.chart || !this.chartData) return;
    
    d3.selectAll('.admin-node-card').each(function() {
        const nodeData = d3.select(this).datum();
        if (nodeData && nodeData.data) {
            if (weekId && nodeData.data.activationWeekDefinitionId !== weekId) {
                d3.select(this).classed('disabled', true);
            } else {
                d3.select(this).classed('disabled', false);
            }
        }
    });
}

استایل:

/* کاربرانی که در هفته فیلتر شده فعال نشدن */
.admin-node-card.disabled {
    opacity: 0.35;
    filter: grayscale(70%);
}

/* کاربران فعال شده در هفته هدف */
.admin-node-card:not(.disabled) {
    animation: target-glow 2s ease-in-out infinite;
}

@keyframes target-glow {
    0%, 100% { box-shadow: 0 0 5px rgba(76, 175, 80, 0.3); }
    50% { box-shadow: 0 0 20px rgba(76, 175, 80, 0.6); }
}

6. Stored Procedure for GetNetworkTree

نیاز: بهبود سرعت لود درخت شبکه + حذف محدودیت عمق

Stored Procedure (dbbkup/SP_GetNetworkTree.sql):

CREATE PROCEDURE [CMS].[GetNetworkTree]
    @RootUserId BIGINT,
    @MaxDepth INT = 100,
    @IsClubActive BIT = NULL,
    @ActivationWeekDefinitionId BIGINT = NULL
AS
BEGIN
    SET NOCOUNT ON;

    -- CTE برای پیمایش درخت باینری به صورت recursive
    ;WITH NetworkTreeCTE AS (
        -- Base case: ریشه درخت
        SELECT 
            u.Id AS UserId,
            u.Mobile,
            u.FirstName,
            u.LastName,
            u.LegPosition,
            u.NetworkParentId AS ParentId,
            0 AS NetworkLevel,
            u.Created AS UserCreated
        FROM [CMS].[Users] u
        WHERE u.Id = @RootUserId AND u.IsDeleted = 0

        UNION ALL

        -- Recursive case: فرزندان
        SELECT 
            u.Id, u.Mobile, u.FirstName, u.LastName, u.LegPosition,
            u.NetworkParentId, parent.NetworkLevel + 1, u.Created
        FROM [CMS].[Users] u
        INNER JOIN NetworkTreeCTE parent ON u.NetworkParentId = parent.UserId
        WHERE u.IsDeleted = 0 AND parent.NetworkLevel < @MaxDepth
    )
    -- Join با ClubMemberships و WeekDefinitions
    SELECT 
        t.UserId, t.Mobile, t.FirstName, t.LastName, t.LegPosition,
        t.ParentId, t.NetworkLevel,
        cm.ActivatedAt AS ClubActivatedAt,
        ISNULL(cm.IsActive, 0) AS IsClubActive,
        wd.Id AS ActivationWeekDefinitionId,
        wd.DisplayName AS ActivationWeekDisplayName,
        CASE WHEN @ActivationWeekDefinitionId IS NOT NULL 
             AND wd.Id = @ActivationWeekDefinitionId THEN 1 ELSE 0 
        END AS IsActivatedInTargetWeek,
        t.UserCreated
    FROM NetworkTreeCTE t
    LEFT JOIN [CMS].[ClubMemberships] cm ON cm.UserId = t.UserId AND cm.IsDeleted = 0
    LEFT JOIN [CMS].[WeekDefinitions] wd ON cm.ActivatedAt >= wd.StartDate 
                                         AND cm.ActivatedAt < wd.EndDate
    WHERE (@IsClubActive IS NULL OR ISNULL(cm.IsActive, 0) = @IsClubActive 
           OR t.NetworkLevel = 0)
    ORDER BY t.NetworkLevel, t.ParentId, t.LegPosition
    OPTION (MAXRECURSION 0);  -- بدون محدودیت recursion
END

تغییرات در Application Layer:

فایل جدید: NetworkTreeNodeDto.cs

public class NetworkTreeNodeDto
{
    public long UserId { get; set; }
    public string? Mobile { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public int? LegPosition { get; set; }
    public long? ParentId { get; set; }
    public int NetworkLevel { get; set; }
    public DateTime? ClubActivatedAt { get; set; }
    public bool IsClubActive { get; set; }
    public long? ActivationWeekDefinitionId { get; set; }
    public string? ActivationWeekDisplayName { get; set; }
    public bool IsActivatedInTargetWeek { get; set; }
    public DateTimeOffset UserCreated { get; set; }
}

بازنویسی GetNetworkTreeQueryHandler.cs:

  • استفاده از ADO.NET برای اجرای SP
  • تبدیل نتیجه flat به ساختار درختی
  • پشتیبانی از همه پارامترهای فیلتر

پکیج جدید:

<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.11" />

📁 فایل‌های تغییر یافته

CMS Microservice

فایل نوع تغییر توضیح
WebApi/appsettings.json Modified اضافه شدن Chatika.Enabled
Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs Modified چک فلگ Enabled
WebApi/Workers/DayaLoanCheckWorker.cs Modified فیلتر کاربران با قرارداد
Application/CMSMicroservice.Application.csproj Modified پکیج Relational
Application/Common/Interfaces/IApplicationDbContext.cs Modified اضافه شدن DatabaseFacade
Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs REWRITTEN استفاده از SP
Application/NetworkMembershipCQ/Queries/GetNetworkTree/NetworkTreeNodeDto.cs NEW DTO برای نتیجه SP

BackOffice

فایل نوع تغییر توضیح
Pages/Network/NetworkTreeViewer.razor REWRITTEN استفاده از org-chart
wwwroot/index.html Modified رفرنس‌های JS/CSS
wwwroot/js/admin-org-chart.js NEW Wrapper برای org-chart
wwwroot/css/admin-org-chart.css NEW استایل نودها
wwwroot/js/d3-org-chart3.js NEW کتابخانه org-chart
wwwroot/js/d3-flextree.min.js NEW Dependency

SQL Scripts

فایل نوع تغییر توضیح
dbbkup/SP_GetNetworkTree.sql NEW Stored Procedure

🔧 تنظیمات جدید

appsettings.json - Chatika

{
  "Chatika": {
    "Enabled": false,  // ← جدید: فعال/غیرفعال کردن Worker
    "BaseUrl": "https://api.chatika.ir",
    "ApiKey": "YOUR_API_KEY_HERE"
  }
}

🗄️ Database Changes

اجرای Stored Procedure

# اجرای اسکریپت در SQL Server
sqlcmd -S <server> -d <database> -i dbbkup/SP_GetNetworkTree.sql

Build Status

cd /home/masoud/Apps/project/FourSat/CMS/src
dotnet build
# Result: Build succeeded. 0 Error(s)

cd /home/masoud/Apps/project/FourSat/BackOffice/src
dotnet build BackOffice/BackOffice.csproj
# Result: Build succeeded

📊 آمار Session

متریک مقدار
فایل‌های جدید 6
فایل‌های تغییر یافته 8
قابلیت‌های جدید 6
خطوط کد اضافه شده ~800
Stored Procedure 1