422 lines
16 KiB
Markdown
422 lines
16 KiB
Markdown
# 📝 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) - بهبودات قبلی
|