Files
docs/technical/TECH-07-SESSION-LOG-20260430.md
T
masoodafar-web 1db77b1a1b Add comprehensive database integrity audit report and fix critical bugs in commission pool charging flow
- Introduced a detailed audit report for the CMS database integrity, highlighting issues related to data entry, code bugs, and stored procedures.
- Fixed double-charge issue in the commission pool during club membership activation.
- Updated stored procedures to ensure correct pool calculations across different weeks.
- Enhanced the network tree feature to include activation type and package details.
- Improved UI for the network tree display and resolved pagination issues in the discount store.
2026-05-01 00:07:25 +03:30

13 KiB
Raw Blame History

TECH-07 — لاگ کامل Session 1404/02/10 (2026-04-30)

نوع سند: گزارش کار
تاریخ: ۱۴۰۵/۰۲/۱۰
مرتبط با: CMS · BackOffice · FrontOffice · Database


فهرست مطالب

  1. بخش اول — رفع باگ Double-Charge Pool
  2. بخش دوم — بررسی داده‌های هفته‌های ۲۲ و ۲۳
  3. بخش سوم — ویژگی Network Tree (اطلاعات هفتگی)
  4. بخش چهارم — بهبود UI نمودار درختی
  5. بخش پنجم — رفع باگ Pagination فروشگاه تخفیف
  6. خلاصه فایل‌های تغییریافته
  7. وظایف باقی‌مانده (Pending)

۱. رفع باگ Double-Charge Pool

مشکل

در جریان فعال‌سازی عضویت باشگاه، Pool کمیسیون هفتگی دوبار شارژ می‌شد:

  • بار اول: در ActivateClubMembership (از طریق VerifyPackagePurchase)
  • بار دوم: در AcceptClubMembershipContract (تأیید قرارداد توسط کاربر)

همچنین CreateManualPayment هم یک مسیر مستقل داشت که بدون Check هفته، Pool اشتباه را شارژ می‌کرد.

ریشه مشکل

تابع GetOrCreateCurrentWeeklyPool بدون در نظر گرفتن هفته واقعی PackagePurchasedAt، Pool هفته جاری را انتخاب می‌کرد.

فایل‌های اصلاح‌شده

ActivateClubMembershipCommandHandler.cs

// قبل: همیشه Pool هفته جاری را شارژ می‌کرد
// بعد: فقط یک‌بار در محل صحیح (AcceptContract) شارژ می‌شود
// حذف: شارژ Pool از داخل ActivateClubMembership (for isNewMembership scenario)

AcceptClubMembershipContractCommandHandler.cs

// اضافه: بررسی هفته قرارداد — اگر هفته PackagePurchasedAt با هفته جاری فرق دارد
// از Pool هفته مناسب استفاده می‌کند نه Pool هفته جاری

CreateManualPaymentCommandHandler.cs

// اصلاح: Cross-week fix — Pool هفته صحیح بر اساس تاریخ پرداخت دستی

sp_CalculateWeeklyCommissionPool.sql (SP در Infrastructure)

-- اصلاح: IsCurrentCycle check برای جلوگیری از Double-Count
-- هر کاربر فقط یک‌بار در محاسبه Pool شمرده می‌شود

۲. بررسی داده‌های هفته‌های ۲۲ و ۲۳

تشخیص

با اجرای diagnostic SQL روی DB، دو anomaly کشف شد:

هفته ۲۲ — Pool Ghost (PoolId=10056)

فیلد مقدار
TotalPoolAmount 2,520,000
AllCycles 0
ریشه هانیه سادات عشاقی (UserId=189) — خرید 1404/01/12 (هفته ۲۱) ولی AcceptContract در 17:02 دقیقه بعد Pool هفته ۲۲ را شارژ کرد

دلیل: CreatedAt و ModifiedAt timestamp مغایرت داشت — Pool در هفته ۲۲ ایجاد شد اما Cycle در هفته ۲۱ بود.

اصلاح دستی DB (Pending):

UPDATE CMS.WeeklyCommissionPools
SET TotalPoolAmount = 0, LastModified = GETUTCDATE()
WHERE Id = 10056 AND WeekDefinitionId = 22;

هفته ۲۳ — Pool ناقص (PoolId=10054)

فیلد مقدار
TotalPoolAmount 0
IsCalculated False
ریشه محمدصادق عسلی (UserId=190) — خرید هفته ۲۳، Cycle وجود دارد ولی Pool=0 (قبل از fix)

اصلاح دستی DB (Pending):

UPDATE CMS.WeeklyCommissionPools
SET TotalPoolAmount = 2520000, LastModified = GETUTCDATE()
WHERE Id = 10054 AND WeekDefinitionId = 23;

۳. ویژگی Network Tree (نوع فعالسازی + پکیج)

هدف

صفحه /network/tree در BackOffice باید در هر node نشان دهد:

  • آیا این کاربر در هفته انتخابی عضو جدید بوده یا تمدید کرده؟
  • نام پکیجی که خریداری کرده؟

پیاده‌سازی Full-Stack

الف) SP_GetNetworkTree (dbbkup/SP_GetNetworkTree.sql)

-- اضافه شد:
OUTER APPLY (
    SELECT TOP 1 cc.*
    FROM CMS.ClubMembershipCycles cc
    WHERE cc.ClubMembershipId = cm.Id
      AND cc.PackagePurchasedAt >= @WeekStartDate
      AND cc.PackagePurchasedAt < @WeekEndDate
      AND (@ActivationWeekDefinitionId IS NULL OR @WeekStartDate IS NOT NULL)
) AS cc_target

-- ستون‌های جدید در output:
IsActivatedInTargetWeek  -- آیا در هفته انتخابی فعال شده؟
IsNewActivation          -- 1=اولین فعالسازی (CycleNumber=1), 0=تمدید, NULL=بدون Cycle
PackageName              -- نام پکیج اون هفته
PackageId                -- شناسه پکیج

نکته: منطق هفته‌بندی از cm.ActivatedAt به Cycle.PackagePurchasedAt تغییر کرد.

Deploy SP:

SP مستقیم روی DB اجرا شد (نه EmbeddedResource Infrastructure)
اجرا شد در: /tmp/DbDiag با C# script
تأیید شد: SELECT OBJECT_ID('[CMS].[GetNetworkTree]') → موجود

ب) Application Layer

NetworkTreeNodeDto.cs — فیلدهای جدید:

bool? IsNewActivation
string? PackageName
long? PackageId

NetworkTreeDto.cs — همین فیلدها

GetNetworkTreeQueryHandler.cs:

// خواندن از DataReader:
IsNewActivation = reader.IsDBNull(reader.GetOrdinal("IsNewActivation"))
    ? null
    : reader.GetInt32(reader.GetOrdinal("IsNewActivation")) == 1,
PackageName = reader["PackageName"] as string,
PackageId = reader.IsDBNull(reader.GetOrdinal("PackageId"))
    ? null
    : reader.GetInt64(reader.GetOrdinal("PackageId"))

ج) Proto (networkmembership.proto)

// NetworkTreeNodeModel — فیلدهای جدید:
google.protobuf.BoolValue is_new_activation = 22;
string package_name = 23;
google.protobuf.Int64Value package_id = 24;

NuGet Package: Foursat.CMSMicroservice.Protobuf → از 0.0.194 به 0.0.195 bump و push شد.

د) Mapping (NetworkMembershipProfile.cs)

PackageName = node.PackageName ?? string.Empty,
PackageId = node.PackageId.HasValue ? node.PackageId.Value : null,
IsNewActivation = node.IsNewActivation.HasValue ? node.IsNewActivation.Value : null

هـ) BackOffice — NetworkTreeViewer.razor

DataGrid — دو ستون جدید:

<!-- ستون نوع فعالسازی -->
<PropertyColumn Property="x => x.IsNewActivation" Title="نوع فعالسازی">
    @if (context.Item.IsNewActivation == true)
    {
        <MudChip Color="Color.Success">🆕 عضو جدید</MudChip>
    }
    else if (context.Item.IsNewActivation == false)
    {
        <MudChip Color="Color.Secondary">🔄 خرید مجدد</MudChip>
    }
</PropertyColumn>

<!-- ستون پکیج -->
<PropertyColumn Property="x => x.PackageName" Title="پکیج" />

JS (jsNodes):

isNewActivation: n.IsNewActivation,
packageName: n.PackageName ?? ""

۴. بهبود UI نمودار درختی

مشکل اولیه

بج «🆕 جدید» با position: absolute از گوشه کارت بیرون می‌زد و با محتوای دیگر برخورد می‌کرد.

فایل‌های تغییریافته

admin-org-chart.js (wwwroot/js)

ساختار کارت بازنویسی شد:

┌─────────────────────────────┐
│ [Avatar]  نام کاربر          │
│           پکیج نقره...       │  ← inline زیر اسم
│           L13  چپ  عضو جدید  │  ← pill در meta row
├─────────────────────────────┤
│ ✓ فعال           1404/12/24 │
└─────────────────────────────┘

تغییرات:

  • activationTypeBadge (absolute positioning) → activationTypePill (inline span)
  • highlightBadge ( floating) → حذف شد
  • packageBadge به زیر اسم کاربر منتقل شد (نه footer)
  • ابعاد کارت: 160×80178×92 px

admin-org-chart.css (wwwroot/css)

/* جدید: activation pill به جای badge */
.admin-node-card .activation-pill { /* inline flex */ }
.admin-node-card .new-member-pill { background: #e8f5e9; color: #2e7d32; border: 1px solid #a5d6a7; }
.admin-node-card .renewal-pill { background: #ede7f6; color: #5e35b1; border: 1px solid #b39ddb; }

/* بهبود: پکیج روشن‌تر */
.admin-node-card .package-name-badge { background: #eceff1; color: #546e7a; border: 1px solid #b0bec5; }

۵. رفع باگ Pagination فروشگاه تخفیف

مشکل

در صفحه /discount-store/products، دکمه «نمایش محصولات بیشتر» کار نمی‌کرد — همیشه صفحه اول برمی‌گشت.

ریشه مشکل

فایل غایب: DiscountProductProfile.cs (Mapster) وجود نداشت.

جریان mapping:

GetDiscountProductsRequest (proto)
    ↓ request.Adapt<GetDiscountProductsQuery>()
GetDiscountProductsQuery

بدون profile، auto-mapping دو مشکل داشت:

  1. request.SearchQuery (string)query.SearchTerm (string?) → نامتطابق نام، NULL می‌شد
  2. request.PageNumber (int)query.PaginationQuery.PageNumberMapster نمی‌توانست به nested object مپ کندPaginationQuery = null → default: PageNumber=1 همیشه!

راه‌حل

فایل جدید: CMS/src/CMSMicroservice.WebApi/Common/Mappings/DiscountProductProfile.cs

config.NewConfig<GetDiscountProductsRequest, GetDiscountProductsQuery>()
    .Map(dest => dest.SearchTerm,
        src => string.IsNullOrEmpty(src.SearchQuery) ? null : src.SearchQuery)
    .Map(dest => dest.CategoryId,
        src => src.CategoryId != null ? src.CategoryId.Value : (long?)null)
    .Map(dest => dest.IsActive,
        src => src.IsActive != null ? src.IsActive.Value : (bool?)null)
    .Map(dest => dest.PaginationQuery, src => new PaginationState
    {
        PageNumber = src.PageNumber > 0 ? src.PageNumber : 1,
        PageSize = src.PageSize > 0 ? src.PageSize : 12
    });

همچنین GetDiscountProductsResponseDto → GetDiscountProductsResponse هم به صورت صریح مپ شد تا MetaData و Models درست انتقال یابند.


خلاصه فایل‌های تغییریافته

فایل نوع تغییر پروژه
ActivateClubMembershipCommandHandler.cs Fix — حذف Double-Charge CMS Application
AcceptClubMembershipContractCommandHandler.cs Fix — Cross-week Pool CMS Application
CreateManualPaymentCommandHandler.cs Fix — Cross-week Pool CMS Application
sp_CalculateWeeklyCommissionPool.sql Fix — IsCurrentCycle CMS Infrastructure
SP_GetNetworkTree.sql Feature — IsNewActivation, PackageName, PackageId DB/dbbkup
NetworkTreeNodeDto.cs Feature — فیلدهای جدید CMS Application
NetworkTreeDto.cs Feature — فیلدهای جدید CMS Application
GetNetworkTreeQueryHandler.cs Feature — خواندن فیلدهای جدید CMS Application
networkmembership.proto Feature — ۳ فیلد جدید در NetworkTreeNodeModel Protobuf
NetworkMembershipProfile.cs Feature — mapping فیلدهای جدید CMS WebApi
NetworkTreeViewer.razor Feature — DataGrid ستون‌های جدید BackOffice
admin-org-chart.js Feature+Fix — inline pill، پکیج زیر اسم BackOffice wwwroot
admin-org-chart.css Feature+Fix — استایل pill‌های مرتب BackOffice wwwroot
DiscountProductProfile.cs Fix — Pagination mapping صحیح CMS WebApi (جدید)

NuGet Package

پکیج نسخه قبل نسخه جدید
Foursat.CMSMicroservice.Protobuf 0.0.194 0.0.195

وظایف باقی‌مانده

ضروری — اصلاح داده‌های DB

BEGIN TRANSACTION;

-- هفته ۲۲: Pool Ghost (هانیه سادات عشاقی ← AcceptContract هفته اشتباه)
UPDATE CMS.WeeklyCommissionPools
SET TotalPoolAmount = 0, LastModified = GETUTCDATE()
WHERE Id = 10056 AND WeekDefinitionId = 22;

-- هفته ۲۳: Pool ناقص (محمدصادق عسلی ← Pool قبل از Fix ایجاد شده بود)
UPDATE CMS.WeeklyCommissionPools
SET TotalPoolAmount = 2520000, LastModified = GETUTCDATE()
WHERE Id = 10054 AND WeekDefinitionId = 23;

COMMIT;

بهبود آینده

  • SP_GetNetworkTree.sql به Infrastructure EmbeddedResource اضافه شود (auto-deploy)
  • DiscountProductDto در Application — اضافه کردن فیلد Created از DB
  • تست pagination فروشگاه پس از restart CMS