# 📝 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 { private readonly IKavenegarService _smsService; // جدید private readonly ILogger _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) { نسخه اپلیکیشن‌ها } ``` **Permission**: `settings.view` #### 4.2 دکمه افزودن نسخه جدید **فایل**: `BackOffice/Pages/Settings/AppVersions.razor` ```razor افزودن نسخه جدید ``` #### 4.3 Dialog با حالت جدید/ویرایش **فایل**: `BackOffice/Pages/Settings/Components/AppVersionEditDialog.razor` ```razor [Parameter] public bool IsNew { get; set; } = false; @if (IsNew) { کارابازار ادمین کارابازار } else { } ``` #### 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 to GetAllAppVersionsResponse config.NewConfig, GetAllAppVersionsResponse>() .MapWith(src => CreateResponse(src)); // Map AppVersionItemDto to AppVersionItem (proto message) config.NewConfig() .Map(dest => dest.Id, src => src.Id) .Map(dest => dest.AppName, src => src.AppName) // ... other mappings } private static GetAllAppVersionsResponse CreateResponse(List items) { var response = new GetAllAppVersionsResponse(); foreach (var item in items) { response.Items.Add(item.Adapt()); } return response; } } ``` #### 5.2 BackOffice.BFF - CommissionProfile **فایل**: `BackOffice.BFF.Application/Common/Mappings/CommissionProfile.cs` ```csharp // CMS GetAllWeeklyPoolsResponse -> GetAllWeeklyPoolsResponseDto config.NewConfig() .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() .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(); // PayoutId ignored! ``` **بعد**: ```csharp config.NewConfig() .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(); ``` **بعد**: ```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 @* ستون نام کاربر *@ @* ستون پای چپ با Tooltip *@ @FormatNumber(context.Item.LeftLegTotal) @* ستون پای راست با Tooltip *@ @FormatNumber(context.Item.RightLegTotal) ``` #### 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 ``` --- ### 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() .Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId); // BUG: assigns Int64Value object, not the value ``` **بعد**: ```csharp config.NewConfig() .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