312 lines
10 KiB
Markdown
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)
|
|
```
|