# 📝 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`**: ```json "Chatika": { "Enabled": false, // ← فلگ جدید "BaseUrl": "https://api.chatika.ir", "ApiKey": "..." } ``` **تغییرات در `ChatikaAccountActivationJob.cs`**: ```csharp public async Task ExecuteAsync() { // Check if Chatika integration is enabled var isEnabled = _configuration.GetValue("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` دارند **کد اضافه شده**: ```csharp // 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`**: ```javascript // 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(`
${d.data.firstName} ${d.data.lastName}
📱 ${d.data.mobile}
📊 سطح: ${d.data.networkLevel}
📍 ${d.data.legPosition === 1 ? 'چپ' : 'راست'}
${d.data.isClubActive ? `
باشگاه فعال
` : `
باشگاه غیرفعال
` } ${d.data.activationWeekDisplayName ? `
📅 ${d.data.activationWeekDisplayName}
` : '' } `) .style('left', (event.pageX + 15) + 'px') .style('top', (event.pageY - 10) + 'px'); }) ``` **استایل Tooltip**: ```css .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 ✅ **نیاز**: وقتی هفته فیلتر می‌شه، کاربران فعال شده در اون هفته متمایز باشن **پیاده‌سازی**: ```javascript // 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); } } }); } ``` **استایل**: ```css /* کاربرانی که در هفته فیلتر شده فعال نشدن */ .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`): ```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` ```csharp 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 به ساختار درختی - پشتیبانی از همه پارامترهای فیلتر **پکیج جدید**: ```xml ``` --- ## 📁 فایل‌های تغییر یافته ### 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 ```json { "Chatika": { "Enabled": false, // ← جدید: فعال/غیرفعال کردن Worker "BaseUrl": "https://api.chatika.ir", "ApiKey": "YOUR_API_KEY_HERE" } } ``` --- ## 🗄️ Database Changes ### اجرای Stored Procedure ```bash # اجرای اسکریپت در SQL Server sqlcmd -S -d -i dbbkup/SP_GetNetworkTree.sql ``` --- ## ✅ Build Status ```bash 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 | --- ## 🔗 Related Changelogs - [CHANGELOG-2025-12-23.md](CHANGELOG-2025-12-23.md) - پیاده‌سازی Worker چتیکا - [CHANGELOG-2025-12-20.md](CHANGELOG-2025-12-20.md) - بهبودات قبلی