16 KiB
📝 Changelog - ۵ دی ۱۴۰۴ (25 December 2025)
Session: فلگ فعالسازی چتیکا، اصلاح DayaLoan، بازنویسی صفحه درخت شبکه با org-chart، Stored Procedure برای GetNetworkTree
🎯 خلاصه Session
این session شامل موارد زیر بود:
- Chatika Enabled Flag - اضافه کردن فلگ فعال/غیرفعال برای Worker چتیکا
- DayaLoanCheckWorker Fix - جلوگیری از استعلام مجدد مشتریان با قرارداد
- BackOffice Network Tree Rewrite - بازنویسی کامل با d3-org-chart
- Tooltip on Hover - نمایش اطلاعات کاربر روی hover
- Week Filter Visual - تمایز بصری کاربران فعال شده در هفته فیلتر شده
- 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.jsonCMSMicroservice.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 |
🔗 Related Changelogs
- CHANGELOG-2025-12-23.md - پیادهسازی Worker چتیکا
- CHANGELOG-2025-12-20.md - بهبودات قبلی