Files
docs/archive/SESSION-2025-12-20.md
T
masoodafar-web 5965b98728 update
2026-01-03 18:27:49 +03:30

312 lines
10 KiB
Markdown

# Session Log - December 20, 2025
## خلاصه Session
این session شامل رفع چندین باگ در صفحات BackOffice و فعال‌سازی قابلیت‌های Products بود.
---
## 1. فیکس صفحه `/network/balances`
### مشکل
```
ValidationException هنگام لود صفحه بالانس‌های هفتگی
```
### علت
Mapster mapping برای تبدیل `GetUserWeeklyBalancesRequest` به `GetUserWeeklyBalancesQuery` وجود نداشت.
### راه‌حل
اضافه کردن mapping در `CommissionProfile.cs`:
```csharp
// File: BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/CommissionProfile.cs
config.NewConfig<GetUserWeeklyBalancesRequest, GetUserWeeklyBalancesQuery>()
.Map(dest => dest.PaginationState, src => src.PaginationState);
```
---
## 2. فیکس صفحه `/club/members`
### مشکل
```
صفحه لود می‌شد ولی هیچ داده‌ای نمایش نمی‌داد
```
### علت
Mapster mappings در CMS و BFF برای `GetAllClubMemberships` وجود نداشتند.
### راه‌حل
ایجاد `ClubMembershipProfile.cs` در هر دو لایه:
**CMS/src/CMSMicroservice.WebApi/Common/Mappings/ClubMembershipProfile.cs** (NEW):
```csharp
public class ClubMembershipProfile : IRegister
{
void IRegister.Register(TypeAdapterConfig config)
{
// GetAllClubMemberships mappings
config.NewConfig<GetAllClubMembershipsRequest, GetAllClubMembershipsQuery>()
.Map(dest => dest.PaginationState, src => src.PaginationState)
.Map(dest => dest.Filter, src => src.Filter);
config.NewConfig<GetAllClubMembershipsResponseDto, GetAllClubMembershipsResponse>()
.MapWith(src => new GetAllClubMembershipsResponse
{
MetaData = src.MetaData != null ? new CMSMicroservice.Protobuf.Common.MetaData
{
PageIndex = src.MetaData.PageIndex,
TotalPages = src.MetaData.TotalPages,
TotalCount = src.MetaData.TotalCount
} : null,
Models = { src.Models?.Select(...) ?? Enumerable.Empty<...>() }
});
}
}
```
**BackOffice.BFF/src/BackOffice.BFF.WebApi/Common/Mappings/ClubMembershipProfile.cs** (REWRITTEN):
- Mapping از BFF Proto به Query
- Mapping از CMS Response به BFF Proto Response
- استفاده از alias imports برای disambiguation
---
## 3. فیکس صفحه `/club/statistics`
### مشکل
```
Status(StatusCode="Unimplemented", Detail="Method cms.ClubMembershipContract/GetClubStatistics is unimplemented")
```
### علت
متد `GetClubStatistics` در BFF Service override نشده بود.
### راه‌حل
**1. اضافه کردن override در ClubMembershipService.cs:**
```csharp
public override async Task<GetClubStatisticsResponse> GetClubStatistics(
GetClubStatisticsRequest request, ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<GetClubStatisticsRequest, GetClubStatisticsQuery, GetClubStatisticsResponse>(request, context);
}
```
**2. اضافه کردن mappings در CMS ClubMembershipProfile:**
```csharp
config.NewConfig<GetClubStatisticsRequest, GetClubStatisticsQuery>();
config.NewConfig<GetClubStatisticsResponseDto, GetClubStatisticsResponse>()
.MapWith(src => new GetClubStatisticsResponse
{
TotalMembers = src.TotalMembers,
ActiveMembers = src.ActiveMembers,
// ... سایر فیلدها
PackageDistribution = { src.PackageDistribution?.Select(...) },
MonthlyTrend = { src.MonthlyTrend?.Select(...) }
});
```
**3. اضافه کردن mappings در BFF ClubMembershipProfile:**
- Mapping از BFF Proto به Query
- Mapping از CMS Response DTO به BFF Proto Response
---
## 4. فعال‌سازی قابلیت‌های Products
### مشکل
```
همه دکمه‌های صفحه محصولات "در حال توسعه" نشان می‌دادند
```
### علت
کدهای دیالوگ‌ها comment شده بودند با TODO markers.
### راه‌حل
Uncomment کردن کدها در `ProductsMainPage.razor.cs`:
**فایل: BackOffice/src/BackOffice/Pages/Products/ProductsMainPage.razor.cs**
```csharp
// ✅ CreateNew() - فعال شد
public async Task CreateNew()
{
var dialog = await DialogService.ShowAsync<CreateDialog>("افزودن محصول",
new DialogParameters<CreateDialog> { { x => x.Model, new CreateNewProductsRequest() } },
new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });
// ...
}
// ✅ Update() - فعال شد
public async Task Update(DataModel model)
{
var parameters = new DialogParameters<UpdateDialog> { { x => x.Model, model.Adapt<UpdateProductsRequest>() } };
var dialog = await DialogService.ShowAsync<UpdateDialog>("ویرایش محصول", parameters, ...);
// ...
}
// ✅ OpenGallery() - فعال شد
public async Task OpenGallery(DataModel model)
{
var parameters = new DialogParameters<GalleryDialog>
{
{ x => x.ProductId, model.Id },
{ x => x.ProductTitle, model.Title }
};
await DialogService.ShowAsync<GalleryDialog>("گالری تصاویر", parameters, ...);
}
// ✅ OpenTagAssignment() - فعال شد
public async Task OpenTagAssignment(DataModel model)
{
var parameters = new DialogParameters<AssignTagsDialog>
{
{ x => x.ProductId, model.Id },
{ x => x.ProductTitle, model.Title }
};
await DialogService.ShowAsync<AssignTagsDialog>("مدیریت تگ‌های محصول", parameters, ...);
}
```
**Using statement uncomment شد:**
```csharp
using BackOffice.Pages.Tag.Components; // برای AssignTagsDialog
```
---
## 5. اضافه کردن فیلد "تعداد موجودی" به Products
### نیاز
نمایش و ویرایش تعداد موجودی محصول در فرم‌ها و لیست
### تغییرات
**1. فرم‌های دیالوگ (CreateDialog.razor & UpdateDialog.razor):**
```razor
<MudStack Row="true" AlignItems="AlignItems.Center">
<MudItem xs="6">
<MudNumericField T="int" HideSpinButtons="true" @bind-Value="Model.Discount"
Disabled="_isLoading" Label="تخفیف (%)" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
<MudItem xs="6">
<MudNumericField T="int" HideSpinButtons="true" @bind-Value="Model.RemainingCount"
Disabled="_isLoading" Label="تعداد موجودی" Variant="Variant.Outlined" Margin="Margin.Dense" />
</MudItem>
</MudStack>
```
**2. ستون جدید در لیست (ProductsMainPage.razor):**
```razor
<TemplateColumn Title="موجودی" CellStyle="text-wrap: nowrap;">
<CellTemplate>
@if (context.Item.RemainingCount <= 0)
{
<MudChip T="string" Color="Color.Error" Size="Size.Small">ناموجود</MudChip>
}
else if (context.Item.RemainingCount < 10)
{
<MudChip T="string" Color="Color.Warning" Size="Size.Small">@context.Item.RemainingCount</MudChip>
}
else
{
<MudChip T="string" Color="Color.Success" Size="Size.Small">@context.Item.RemainingCount</MudChip>
}
</CellTemplate>
</TemplateColumn>
```
**3. اضافه کردن فیلد به BFF Commands (فیکس مهم!):**
مشکل: فیلد `RemainingCount` در BFF Application Commands نبود و باعث می‌شد مقدار ارسال/دریافت نشه.
**CreateNewProductsCommand.cs:**
```csharp
public int Discount { get; init; }
public int Rate { get; init; }
public int RemainingCount { get; init; } // ← اضافه شد
public ImageFileModel ImageFile { get; init; }
```
**UpdateProductsCommand.cs:**
```csharp
public int Discount { get; init; }
public int Rate { get; init; }
public int RemainingCount { get; init; } // ← اضافه شد
public string ImagePath { get; init; }
```
---
## لیست کامل فایل‌های تغییر یافته
### BackOffice.BFF
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `WebApi/Common/Mappings/CommissionProfile.cs` | MODIFIED | اضافه شدن mapping برای GetUserWeeklyBalances |
| `WebApi/Common/Mappings/ClubMembershipProfile.cs` | REWRITTEN | Mappings کامل برای ClubMembership |
| `WebApi/Services/ClubMembershipService.cs` | MODIFIED | اضافه شدن GetClubStatistics override |
| `Application/ProductsCQ/Commands/CreateNewProducts/CreateNewProductsCommand.cs` | MODIFIED | اضافه شدن RemainingCount |
| `Application/ProductsCQ/Commands/UpdateProducts/UpdateProductsCommand.cs` | MODIFIED | اضافه شدن RemainingCount |
### CMS
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `WebApi/Common/Mappings/ClubMembershipProfile.cs` | NEW | Mappings برای ClubMembership |
### BackOffice UI
| فایل | نوع تغییر | توضیح |
|------|----------|-------|
| `Pages/Products/ProductsMainPage.razor.cs` | MODIFIED | فعال‌سازی CreateNew, Update, OpenGallery, OpenTagAssignment |
| `Pages/Products/ProductsMainPage.razor` | MODIFIED | اضافه شدن ستون موجودی |
| `Pages/Products/Components/CreateDialog.razor` | MODIFIED | اضافه شدن فیلد موجودی |
| `Pages/Products/Components/UpdateDialog.razor` | MODIFIED | اضافه شدن فیلد موجودی |
---
## نکات فنی مهم
### 1. Mapster با Proto Types
برای proto types که immutable هستند، باید از `MapWith` استفاده کرد:
```csharp
config.NewConfig<SourceDto, ProtoResponse>()
.MapWith(src => new ProtoResponse
{
Field1 = src.Field1,
RepeatedField = { src.List?.Select(...) ?? Enumerable.Empty<...>() }
});
```
### 2. Alias Imports برای Proto Disambiguation
وقتی دو proto با اسم یکسان داریم:
```csharp
using BffProtos = BackOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership;
using CmsProtos = CMSMicroservice.Protobuf.Protos.ClubMembership;
```
### 3. Null-Safe MetaData Mapping
```csharp
MetaData = src.MetaData != null ? new MetaData
{
PageIndex = src.MetaData.PageIndex,
TotalPages = src.MetaData.TotalPages,
TotalCount = src.MetaData.TotalCount
} : null
```
---
## Build Status پایان Session
```
BackOffice.BFF: ✅ Build succeeded (0 errors)
CMS: ✅ Build succeeded (0 errors)
BackOffice UI: ✅ Build succeeded (0 errors)
```