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

633 lines
21 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 - ۷ دی ۱۴۰۴ (27 December 2025)
> **Session**: بهینه‌سازی‌های Mapping + SystemConstants + SMS Templates + AppVersion UI + Commission System Fixes
---
## 🎯 خلاصه Session
این session شامل موارد زیر بود:
1. **SystemConstants** - انتقال مقادیر ثابت از hardcode به کلاس مرکزی
2. **SMS Templates** - متمرکز کردن همه قالب‌های پیامک
3. **SMS for Daya Loan** - ارسال پیامک هنگام تأیید وام دایا
4. **AppVersion UI** - تکمیل صفحه مدیریت نسخه در BackOffice
5. **Mapping Fixes** - رفع مشکلات Mapster
6. **Commission Status Refactoring** - انتقال تبدیل Status از BFF به FrontOffice
7. **ProcessWithdrawal Fix** - رفع خطای "PayoutId invalid" در BackOffice
8. **WeekDisplayName Fix** - نمایش صحیح نام هفته به جای فرمت 1404-W40
9. **Withdrawals Page Fix** - رفع مشکل لود نشدن صفحه تأیید برداشت‌ها
10. **Network Balances Enhancement** - افزودن نام کاربر و جزئیات Carryover به صفحه balance‌ها
11. **WeekDefinitionId Mapping Fix** - رفع مشکل ارسال WeekDefinitionId=0 در FrontOffice.BFF
---
## ✨ تغییرات
### 1. 💰 SystemConstants - مقادیر ثابت ✅
**فایل**: `CMSMicroservice.Domain/Common/SystemConstants.cs`
```csharp
public static class SystemConstants
{
// Club Configuration
public const decimal ClubJoiningPercentage = 0.35m; // 35% کمیسیون پیوستن به باشگاه
public const decimal ClubActivationThreshold = 0.5m; // 50% آستانه فعال‌سازی
// Commission Configuration
public const int MaxCalculationAttempts = 3; // حداکثر تلاش محاسبه
public const int DefaultCommissionPoolDays = 7; // روزهای استخر کمیسیون
// Package Amounts
public const long GoldenPackageAmount = 56_000_000; // 56 میلیون - پکیج طلایی
public const long DayaLoanAmount = 56_000_000; // 56 میلیون - وام دایا
}
```
**Handlers آپدیت شده**:
| Handler | تغییر |
|---------|-------|
| `ProcessDayaLoanApprovalCommandHandler` | استفاده از `SystemConstants.DayaLoanAmount` |
| `ValidateGoldenPackagePurchaseQueryHandler` | استفاده از `SystemConstants.GoldenPackageAmount` |
| سایر handlers با 56_000_000 | همه به ثابت تبدیل شدند |
---
### 2. 📱 SmsTemplates - قالب‌های متمرکز پیامک ✅
**فایل جدید**: `CMSMicroservice.Domain/Common/SmsTemplates.cs`
```csharp
public static class SmsTemplates
{
private static string GetUserName(string? firstName)
=> string.IsNullOrWhiteSpace(firstName) ? "کاربر" : firstName;
public static string DayaLoanReceived(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، مبلغ {amount:N0} ریال وام دایا به کیف پول شما واریز شد. کارابازار";
public static string ClubActivated(string? firstName)
=> $"{GetUserName(firstName)} عزیز، حساب باشگاه شما فعال شد. کارابازار";
public static string PackagePurchased(string? firstName, string packageName)
=> $"{GetUserName(firstName)} عزیز، پکیج {packageName} با موفقیت خریداری شد. کارابازار";
public static string CommissionDeposited(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، مبلغ {amount:N0} ریال کمیسیون به کیف پول شما واریز شد. کارابازار";
public static string WithdrawalSuccess(string? firstName, long amount)
=> $"{GetUserName(firstName)} عزیز، درخواست برداشت {amount:N0} ریال با موفقیت انجام شد. کارابازار";
public static string NetworkJoined(string? firstName, string referrerName)
=> $"{GetUserName(firstName)} عزیز، به شبکه {referrerName} پیوستید. کارابازار";
public static string NewDownline(string? firstName, string newMemberName)
=> $"{GetUserName(firstName)} عزیز، {newMemberName} به زیرمجموعه شما اضافه شد. کارابازار";
public static string OtpCode(string code)
=> $"کد تأیید شما: {code}\nکارابازار";
public static string Welcome(string? firstName)
=> $"{GetUserName(firstName)} عزیز، به کارابازار خوش آمدید!";
}
```
---
### 3. 📲 ارسال SMS هنگام تأیید وام دایا ✅
**فایل**: `CMSMicroservice.Application/FinancialCQ/Commands/ProcessDayaLoanApproval/ProcessDayaLoanApprovalCommandHandler.cs`
**تغییرات**:
```csharp
public class ProcessDayaLoanApprovalCommandHandler : IRequestHandler<ProcessDayaLoanApprovalCommand, Unit>
{
private readonly IKavenegarService _smsService; // جدید
private readonly ILogger<ProcessDayaLoanApprovalCommandHandler> _logger; // جدید
// بعد از واریز موفق به کیف پول
private async Task SendDayaLoanSmsAsync(User user)
{
try
{
var message = SmsTemplates.DayaLoanReceived(
user.FirstName,
SystemConstants.DayaLoanAmount);
await _smsService.SendAsync(user.PhoneNumber, message);
_logger.LogInformation("Daya loan SMS sent to user {UserId}", user.Id);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to send Daya loan SMS to user {UserId}", user.Id);
// خطای SMS مانع عملیات اصلی نمی‌شود
}
}
}
```
---
### 4. 🖥️ BackOffice - صفحه مدیریت نسخه اپلیکیشن ✅
#### 4.1 اضافه شدن به منو
**فایل**: `BackOffice/Shared/NavMenu.razor`
```razor
@if (CanViewSettings)
{
<MudNavLink Match="NavLinkMatch.Prefix"
Href="/settings/app-versions"
Icon="@Icons.Material.Filled.PhoneAndroid">
نسخه اپلیکیشن‌ها
</MudNavLink>
}
```
**Permission**: `settings.view`
#### 4.2 دکمه افزودن نسخه جدید
**فایل**: `BackOffice/Pages/Settings/AppVersions.razor`
```razor
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Add"
OnClick="@OpenCreateDialog">
افزودن نسخه جدید
</MudButton>
```
#### 4.3 Dialog با حالت جدید/ویرایش
**فایل**: `BackOffice/Pages/Settings/Components/AppVersionEditDialog.razor`
```razor
[Parameter]
public bool IsNew { get; set; } = false;
@if (IsNew)
{
<MudSelect @bind-Value="Model.AppName"
Label="نام اپلیکیشن"
Required="true">
<MudSelectItem Value="@("KaraBazarApp")">کارابازار</MudSelectItem>
<MudSelectItem Value="@("KaraBazarAdminApp")">ادمین کارابازار</MudSelectItem>
</MudSelect>
}
else
{
<MudTextField @bind-Value="Model.AppName"
ReadOnly="true" Disabled="true" />
}
```
#### 4.4 آیکون و رنگ اپلیکیشن‌ها
```csharp
private string GetAppIcon(string appName) => appName switch
{
"KaraBazarApp" => Icons.Material.Filled.ShoppingCart,
"KaraBazarAdminApp" => Icons.Material.Filled.AdminPanelSettings,
_ => Icons.Material.Filled.PhoneAndroid
};
private Color GetAppColor(string appName) => appName switch
{
"KaraBazarApp" => Color.Primary,
"KaraBazarAdminApp" => Color.Secondary,
_ => Color.Default
};
```
---
### 5. 🔧 Mapping Fixes ✅
#### 5.1 CMS - AppVersionProfile
**فایل جدید**: `CMSMicroservice.WebApi/Common/Mappings/AppVersionProfile.cs`
```csharp
public class AppVersionProfile : IRegister
{
public void Register(TypeAdapterConfig config)
{
// Map List<AppVersionItemDto> to GetAllAppVersionsResponse
config.NewConfig<List<AppVersionItemDto>, GetAllAppVersionsResponse>()
.MapWith(src => CreateResponse(src));
// Map AppVersionItemDto to AppVersionItem (proto message)
config.NewConfig<AppVersionItemDto, AppVersionItem>()
.Map(dest => dest.Id, src => src.Id)
.Map(dest => dest.AppName, src => src.AppName)
// ... other mappings
}
private static GetAllAppVersionsResponse CreateResponse(List<AppVersionItemDto> items)
{
var response = new GetAllAppVersionsResponse();
foreach (var item in items)
{
response.Items.Add(item.Adapt<AppVersionItem>());
}
return response;
}
}
```
#### 5.2 BackOffice.BFF - CommissionProfile
**فایل**: `BackOffice.BFF.Application/Common/Mappings/CommissionProfile.cs`
```csharp
// CMS GetAllWeeklyPoolsResponse -> GetAllWeeklyPoolsResponseDto
config.NewConfig<GetAllWeeklyPoolsResponse, GetAllWeeklyPoolsResponseDto>()
.MapWith(src => new GetAllWeeklyPoolsResponseDto
{
MetaData = new MetaDataDto
{
TotalCount = (int)src.MetaData.TotalCount,
PageSize = (int)src.MetaData.PageSize,
CurrentPage = (int)src.MetaData.CurrentPage,
TotalPages = (int)src.MetaData.TotalPage
},
Models = src.Models.Select(m => new WeeklyCommissionPoolDto
{
Id = m.Id,
WeekDefinitionId = m.WeekDefinitionId,
// ... other mappings
}).ToList()
});
```
#### 5.3 BackOffice.BFF - GeneralMapping (Unit to Empty)
**فایل**: `BackOffice.BFF.WebApi/Common/Mappings/GeneralMapping.cs`
```csharp
// MediatR Unit to Google.Protobuf.Empty
config.NewConfig<MediatR.Unit, Google.Protobuf.WellKnownTypes.Empty>()
.MapWith(_ => new Google.Protobuf.WellKnownTypes.Empty());
```
---
### 6. 🔄 Commission Status Refactoring ✅
**مشکل**: تبدیل enum عددی `CommissionPayoutStatus` به متن فارسی در BFF gateway انجام می‌شد.
**راه‌حل**: انتقال منطق به FrontOffice client برای معماری بهتر.
#### 6.1 FrontOffice.BFF - Simplify Response
**فایل**: `FrontOffice.BFF.Application/.../GetMyWeeklyBalancesQueryHandler.cs`
**قبل**:
```csharp
Status = MapStatusToString(x.Status)
```
**بعد**:
```csharp
Status = x.Status // Return int directly
```
#### 6.2 FrontOffice - CommissionService
**فایل**: `FrontOffice.Main/Utilities/CommissionService.cs`
```csharp
public static string MapStatus(int status) => status switch
{
0 => "در انتظار", // Pending
1 => "پرداخت شده", // Paid
2 => "درخواست برداشت", // WithdrawRequested
3 => "برداشت شده", // Withdrawn
4 => "خطای پرداخت", // PaymentFailed
5 => "لغو شده", // Cancelled
_ => "نامشخص"
};
public static string GetStatusColor(int status) => status switch
{
0 => "warning", // Pending - زرد
1 => "success", // Paid - سبز
2 => "info", // WithdrawRequested - آبی
3 => "success", // Withdrawn - سبز
4 => "error", // PaymentFailed - قرمز
5 => "default", // Cancelled - خاکستری
_ => "default"
};
```
**Enum مرجع** (`CommissionPayoutStatus`):
```csharp
public enum CommissionPayoutStatus
{
Pending = 0,
Paid = 1,
WithdrawRequested = 2,
Withdrawn = 3,
PaymentFailed = 4,
Cancelled = 5
}
```
---
### 7. 🛠️ ProcessWithdrawal Fix ✅
**مشکل**: خطای "PayoutId invalid" هنگام تأیید/رد برداشت در BackOffice
**علت**: `PayoutId` در mapping از CMS request به BackOffice.BFF command map نمی‌شد.
**فایل**: `BackOffice.BFF.Application/Common/Mappings/CommissionProfile.cs`
**قبل**:
```csharp
config.NewConfig<ProcessWithdrawalRequest, ProcessWithdrawalCommand>();
// PayoutId ignored!
```
**بعد**:
```csharp
config.NewConfig<ProcessWithdrawalRequest, ProcessWithdrawalCommand>()
.Map(dest => dest.PayoutId, src => src.PayoutId)
.Map(dest => dest.Approve, src => src.Approve)
.Map(dest => dest.RejectionReason, src => src.RejectionReason);
```
---
### 8. 📅 WeekDisplayName Fix ✅
**مشکل**: نمایش "1404-W40" به جای "هفته چهلم" در dropdown انتخاب هفته
**علت**: استفاده از `PersianWeekNumber` به جای `DisplayName`
**فایل**: `CMS.Application/.../GetAllWeeklyPoolsQueryHandler.cs`
**قبل**:
```csharp
WeekDisplayName = x.WeekDefinition.PersianWeekNumber // "1404-W40"
```
**بعد**:
```csharp
WeekDisplayName = x.WeekDefinition.DisplayName // "هفته چهلم"
```
---
### 9. 📋 Withdrawals Page Fix ✅
**مشکل**: صفحه تأیید برداشت‌ها در BackOffice لود نمی‌شد
**علت**: mismatch بین نام propertyها در CMS proto و BackOffice.BFF DTO
**فایل**: `BackOffice.BFF.Application/.../GetWithdrawalRequestsResponseDto.cs`
**قبل**:
```csharp
public int TotalPages { get; set; }
public int TotalCount { get; set; }
```
**بعد**:
```csharp
public int TotalPage { get; set; } // Match CMS proto
public int TotalCount { get; set; }
```
**فایل**: `BackOffice.BFF.Application/.../GetWithdrawalRequestsQueryHandler.cs`
**قبل**:
```csharp
return response.Adapt<GetWithdrawalRequestsResponseDto>();
```
**بعد**:
```csharp
return new GetWithdrawalRequestsResponseDto
{
TotalCount = (int)response.MetaData.TotalCount,
TotalPage = (int)response.MetaData.TotalPage,
// ... explicit mapping
};
```
---
### 10. 👤 Network Balances Enhancement ✅
**نیاز**: نمایش نام کامل کاربر و جزئیات breakdown پای چپ/راست در صفحه balance‌های شبکه
#### 10.1 CMS Proto Update
**فایل**: `CMS/Protobufs/Protos/commission.proto`
```protobuf
message UserWeeklyBalanceModel {
// ... existing fields
string user_full_name = 13;
int64 left_leg_new_members = 14;
int64 left_leg_carryover = 15;
int64 left_leg_total = 16;
int64 right_leg_new_members = 17;
int64 right_leg_carryover = 18;
int64 right_leg_total = 19;
}
```
#### 10.2 CMS Handler Update
**فایل**: `CMS.Application/.../GetUserWeeklyBalancesQueryHandler.cs`
```csharp
var query = _dbContext.NetworkWeeklyBalances
.Include(x => x.User) // NEW: Include User
.Include(x => x.WeekDefinition)
.Where(x => x.WeekDefinitionId == request.WeekDefinitionId);
// In projection:
UserFullName = $"{x.User.FirstName} {x.User.LastName}".Trim(),
LeftLegNewMembers = x.LeftLegNewMembers,
LeftLegCarryover = x.LeftLegCarryover,
LeftLegTotal = x.LeftLegTotal,
RightLegNewMembers = x.RightLegNewMembers,
RightLegCarryover = x.RightLegCarryover,
RightLegTotal = x.RightLegTotal,
```
#### 10.3 BackOffice UI Update
**فایل**: `BackOffice/Pages/Network/BalancesReport.razor`
```razor
@* ستون نام کاربر *@
<PropertyColumn Property="x => x.UserFullName" Title="نام کاربر" />
@* ستون پای چپ با Tooltip *@
<TemplateColumn Title="پای چپ">
<CellTemplate>
<MudTooltip Text="@($"جدید: {FormatNumber(context.Item.LeftLegNewMembers)} | انتقالی: {FormatNumber(context.Item.LeftLegCarryover)}")">
<MudText>@FormatNumber(context.Item.LeftLegTotal)</MudText>
</MudTooltip>
</CellTemplate>
</TemplateColumn>
@* ستون پای راست با Tooltip *@
<TemplateColumn Title="پای راست">
<CellTemplate>
<MudTooltip Text="@($"جدید: {FormatNumber(context.Item.RightLegNewMembers)} | انتقالی: {FormatNumber(context.Item.RightLegCarryover)}")">
<MudText>@FormatNumber(context.Item.RightLegTotal)</MudText>
</MudTooltip>
</CellTemplate>
</TemplateColumn>
```
#### 10.4 Proto Package Update
```bash
# Publish new proto package
cd CMS/Protobufs
# Update version in .csproj to 0.0.14
dotnet pack
dotnet nuget push ...
# Update BackOffice
cd BackOffice/src/BackOffice
# Update package reference in .csproj
<PackageReference Include="Foursat.BackOffice.BFF.Commission.Protobuf" Version="0.0.14" />
```
---
### 11. 🔢 WeekDefinitionId Mapping Fix ✅
**مشکل**: `WeekDefinitionId` همیشه `0` به BFF ارسال می‌شد، حتی اگر در client مقدار صحیح ست شده بود.
**علت**: در protobuf، فیلد `week_definition_id` از نوع `google.protobuf.Int64Value` است که یک wrapper type هست. در mapping مستقیم assign می‌شد بدون extract کردن `.Value`.
**فایل**: `FrontOffice.BFF/src/FrontOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs`
**Proto Definition**:
```protobuf
message GetMyWeeklyBalancesRequest {
google.protobuf.Int64Value week_definition_id = 3;
}
```
**قبل**:
```csharp
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
.Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId); // BUG: assigns Int64Value object, not the value
```
**بعد**:
```csharp
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
.Map(dest => dest.WeekDefinitionId,
src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : null);
```
**توضیح**:
- `Int64Value` یک wrapper class در protobuf هست برای nullable long
- وقتی مستقیم assign کنید، implicit conversion اتفاق نمیفته
- باید explicit از `.Value` استفاده کنید
---
## 📦 فایل‌های تغییر یافته
### CMS
| فایل | نوع تغییر |
|------|-----------|
| `Domain/Common/SystemConstants.cs` | Modified - اضافه شدن GoldenPackageAmount, DayaLoanAmount |
| `Domain/Common/SmsTemplates.cs` | **New** - قالب‌های پیامک |
| `Application/.../ProcessDayaLoanApprovalCommandHandler.cs` | Modified - اضافه شدن SMS |
| `WebApi/Common/Mappings/AppVersionProfile.cs` | **New** - Mapster profile |
| `Application/.../GetAllWeeklyPoolsQueryHandler.cs` | Modified - تغییر WeekDisplayName از PersianWeekNumber به DisplayName |
| `Application/.../GetUserWeeklyBalancesQueryHandler.cs` | Modified - اضافه شدن User include و فیلدهای جدید |
| `Application/.../GetUserWeeklyBalancesResponseDto.cs` | Modified - اضافه شدن UserFullName و breakdown fields |
| `WebApi/Common/Mappings/CommissionProfile.cs` | Modified - mapping جدید برای UserWeeklyBalanceModel |
| `Protobufs/Protos/commission.proto` | Modified - اضافه شدن فیلدهای جدید به UserWeeklyBalanceModel |
### BackOffice.BFF
| فایل | نوع تغییر |
|------|-----------|
| `Application/Common/Mappings/CommissionProfile.cs` | Modified - اضافه شدن GetAllWeeklyPools mapping + ProcessWithdrawal mapping |
| `WebApi/Common/Mappings/GeneralMapping.cs` | Modified - اضافه شدن Unit to Empty |
| `Application/.../GetWithdrawalRequestsQueryHandler.cs` | Modified - explicit mapping به جای Adapt<> |
| `Application/.../GetWithdrawalRequestsResponseDto.cs` | Modified - تطابق با CMS proto |
| `Protobufs/Protos/commission.proto` | Modified - اضافه شدن فیلدهای جدید |
### BackOffice
| فایل | نوع تغییر |
|------|-----------|
| `Shared/NavMenu.razor` | Modified - اضافه شدن لینک app-versions |
| `Pages/Settings/AppVersions.razor` | Modified - دکمه افزودن + OpenCreateDialog |
| `Pages/Settings/Components/AppVersionEditDialog.razor` | Modified - پارامتر IsNew + Select |
| `Pages/Network/BalancesReport.razor` | Modified - ستون‌های جدید با MudTooltip |
| `BackOffice.csproj` | Modified - آپدیت proto package به v0.0.14 |
### FrontOffice.BFF
| فایل | نوع تغییر |
|------|-----------|
| `WebApi/Common/Mappings/CommissionProfile.cs` | Modified - رفع WeekDefinitionId mapping (Int64Value.Value) |
| `Application/.../GetMyWeeklyBalancesQueryHandler.cs` | Modified - simplify status handling |
### FrontOffice
| فایل | نوع تغییر |
|------|-----------|
| `Utilities/CommissionService.cs` | Modified - اضافه شدن GetStatusColor و MapStatus (انتقال از BFF) |
| `Pages/Commission/WeeklyBalancePage.razor.cs` | Modified - استفاده از متدهای جدید CommissionService |
---
## ✅ Build Status
```bash
# CMS
dotnet build CMSMicroservice.WebApi/CMSMicroservice.WebApi.csproj
# Build succeeded. 0 Error(s)
# BackOffice.BFF
dotnet build BackOffice.BFF.WebApi/BackOffice.BFF.WebApi.csproj
# Build succeeded. 0 Error(s)
# BackOffice
dotnet build BackOffice/BackOffice.csproj
# Build succeeded. 0 Error(s)
# FrontOffice.BFF
dotnet build FrontOffice.BFF.WebApi/FrontOffice.BFF.WebApi.csproj
# Build succeeded. 0 Error(s)
# FrontOffice
dotnet build FrontOffice.Main/FrontOffice.Main.csproj
# Build succeeded. 0 Error(s)
```
---
## 📊 آمار Session
| متریک | مقدار |
|-------|-------|
| فایل‌های جدید | 2 |
| فایل‌های تغییر یافته | 18 |
| خطوط کد اضافه شده | ~500 |
| باگ‌های Mapping رفع شده | 5 |
| پروژه‌های تأثیرگذار | 5 (CMS, BackOffice, BackOffice.BFF, FrontOffice, FrontOffice.BFF) |
---
## 🔗 Related Changelogs
- [CHANGELOG-2025-12-26.md](CHANGELOG-2025-12-26.md) - App Version Management + ReferralCode in Tree
- [CHANGELOG-2025-12-25.md](CHANGELOG-2025-12-25.md) - درخت شبکه BackOffice