Files
docs/SESSION-2025-12-20.md
T

10 KiB

Session Log - December 20, 2025

خلاصه Session

این session شامل رفع چندین باگ در صفحات BackOffice و فعال‌سازی قابلیت‌های Products بود.


1. فیکس صفحه /network/balances

مشکل

ValidationException هنگام لود صفحه بالانس‌های هفتگی

علت

Mapster mapping برای تبدیل GetUserWeeklyBalancesRequest به GetUserWeeklyBalancesQuery وجود نداشت.

راه‌حل

اضافه کردن mapping در CommissionProfile.cs:

// 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):

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:

public override async Task<GetClubStatisticsResponse> GetClubStatistics(
    GetClubStatisticsRequest request, ServerCallContext context)
{
    return await _dispatchRequestToCQRS.Handle<GetClubStatisticsRequest, GetClubStatisticsQuery, GetClubStatisticsResponse>(request, context);
}

2. اضافه کردن mappings در CMS ClubMembershipProfile:

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

// ✅ 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 شد:

using BackOffice.Pages.Tag.Components; // برای AssignTagsDialog

5. اضافه کردن فیلد "تعداد موجودی" به Products

نیاز

نمایش و ویرایش تعداد موجودی محصول در فرم‌ها و لیست

تغییرات

1. فرم‌های دیالوگ (CreateDialog.razor & UpdateDialog.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):

<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:

public int Discount { get; init; }
public int Rate { get; init; }
public int RemainingCount { get; init; }  // ← اضافه شد
public ImageFileModel ImageFile { get; init; }

UpdateProductsCommand.cs:

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 استفاده کرد:

config.NewConfig<SourceDto, ProtoResponse>()
    .MapWith(src => new ProtoResponse
    {
        Field1 = src.Field1,
        RepeatedField = { src.List?.Select(...) ?? Enumerable.Empty<...>() }
    });

2. Alias Imports برای Proto Disambiguation

وقتی دو proto با اسم یکسان داریم:

using BffProtos = BackOffice.BFF.ClubMembership.Protobuf.Protos.ClubMembership;
using CmsProtos = CMSMicroservice.Protobuf.Protos.ClubMembership;

3. Null-Safe MetaData Mapping

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)