Files
docs/CHANGELOG-2025-12-25.md
T

422 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📝 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<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` دارند
**کد اضافه شده**:
```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(`
<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**:
```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
<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
```json
{
"Chatika": {
"Enabled": false, // ← جدید: فعال/غیرفعال کردن Worker
"BaseUrl": "https://api.chatika.ir",
"ApiKey": "YOUR_API_KEY_HERE"
}
}
```
---
## 🗄️ Database Changes
### اجرای Stored Procedure
```bash
# اجرای اسکریپت در SQL Server
sqlcmd -S <server> -d <database> -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) - بهبودات قبلی