Files
docs/roadmap/PACKAGE-TRANSFORMATION-TASKS.md
T
masoodafar-web 39590d2cbe docs: Phase 10 — DataMigration + EF Staging + PackagePurchaseDialog + UI Fixes
Updated 8 docs:
- CHANGELOG: Phase 10a-d (DataMigration tool, EF staging migrations, PackagePurchaseDialog, 4 UI fixes)
- PAYMENT-FINANCE: Rial→Toman conversion chain documented, PackagePurchaseDialog status
- TECH-02: Added PackagePurchaseDialog to folder structure + status table
- TECH-04: DataMigration tool features (smart retry, FK handling, fallback tables), EF staging
- PACKAGE-TASKS: Phase 10 graph, NuGet v0.0.189, T4.1+T4.2 marked 
- BIZ-PACKAGE: v6→v7, commits updated, T4.1+T4.2 marked 
- INDEX: Updated last-update + R3 description
- ROADMAP: Progress bars updated, DONE section + NOW section refreshed
2026-02-27 20:56:00 +03:30

40 KiB
Raw Blame History

🔄 نقشه‌راه تحول پکیج‌بیس — تسک‌های گام‌به‌گام

وضعیت: در حال اجرا — فاز ۰-۱۰ (تکمیل کد + استقرار staging) | NuGet v0.0.189 | تست باقی‌مانده
تاریخ: ۱۴۰۴/۱۲/۰۸
پیش‌نیاز: BIZ-PACKAGE-BASED-SYSTEM.md v6 (تکمیل Q24-Q30 + History + Rename)
هدف: شکستن ۴۸+ تغییر به تسک‌های اتمیک با ترتیب اجرا و وابستگی‌ها
کامیت‌ها:
CMS: 8b9c317fe3edd1ae92ab8a9cd2fd8e5c7c5ccb938e0002a5a607f7917176fe4d19c569469d97b161f7968446e0ece8e2487554d70aaaf7fcdcd1135a1024a3fdbb91d10d2ca2
FrontOffice: b82cac471f391a0bbc11ed71d46340882c8a956cb93bffc13474d364
BackOffice: f1b008589f5241c96377a8be98aee0203546939780

⚠️ تغییرات v3: پورسانت per-package، carryover مجزا، SP parameters داینامیک، گزارش‌دهی FO/BO per-package


📊 نمای کلی

مرحله ۰: فیکس باگ فوری (۱ روز)                           ✅ `8b9c317` + `fe3edd1`
    └─→ مرحله ۱: زیرساخت Domain + DB (۴ روز)         ✅ `ae92ab8`
        └─→ مرحله ۱.۵: Migration + Seed                    ✅ `a9cd2fd`
            ├─→ مرحله ۲: منطق کسب‌وکار (۴ روز)           ✅ `8e5c7c5`
            │       └─→ مرحله ۳: بازسازی لایه Package       ✅ `ccb938e`
            │               └─→ مرحله ۴: CRUD + Legacy Fixes   ✅ `0002a5a`
            │                       └─→ مرحله UI (۵ روز)  ✅
            └─→ مرحله ۳: پورسانت (۴ روز)               ✅ `607f791`+`7176fe4`
                    └─→ مرحله ۶: Deprecation cleanup    ✅ `d19c569`
                            └─→ مرحله ۷: Migration + Cleanup  ✅
                                ├─→ 7a: Cosmetic cleanup      ✅ CMS:`469d97b` FO:`b82cac4` BO:`f1b0085`
                                ├─→ 7b: FO RPC migration      ✅ CMS:`161f796` FO:`71f391a`
                                └─→ 7c: Delete deprecated      ✅ CMS:`8446e0e`
                                        └─→ مرحله ۸: FO/BO Completion
                                            ├─→ 8a: Checkout wire-up    ✅ FO:`0bbc11e`
                                            ├─→ 8b: BO CRUD expansion  ✅ CMS:`ce8e248` BO:`89f5241`
                                            ├─→ 8c: FO Package pages   ✅ FO:`d71d463`
                                            └─→ 8d: Proto cleanup      ✅ CMS:`7554d70` FO:`40882c8` BO:`c96377a`
                                                └─→ 8e: Per-package reports ✅ CMS:`aaaf7fc` FO:`a956cb9` BO:`8be98ae`
                                                    └─→ 8f: UI completion   ✅ CMS:`dcd1135` FO:`3bffc13` BO:`e020354`
                                                            └─→ مرحله ۹: Q24-Q30 + History + Rename
                                                                ├─→ 9a: Q24+Q26 (threshold+SP) ✅ CMS:`a1024a3`
                                                                ├─→ 9b: Q27 History entities  ✅ CMS:`fdbb91d`
                                                                ├─→ 9c: Q28 UI Guidance       ✅ FO:`474d364` BO:`6939780`
                                                                └─→ 9d: Rename+Interceptor+Mig ✅ CMS:`10d2ca2`
                                                                    └─→ مرحله ۱۰: استقرار + DataMigration + UI
                                                                        ├─→ 10a: DataMigration Tool   ✅ Local: `0e8c6fd`→`31cc464`
                                                                        ├─→ 10b: EF Staging Migrations ✅
                                                                        ├─→ 10c: PackagePurchaseDialog ✅ FO:`a3681a8`
                                                                        └─→ 10d: UI Fixes (Rial/Toman+لیبل+HTML) ✅ FO:`3c1a8ff`
                                                                            └─→ مرحله ۵: تست + نهایی  ⬜

مسیر بحرانی: ۰→۱→۱.۵→۲→۳→۴→UI→۹→۱۰→۵ = ~۲۲ روز | انجام‌شده: ۰→10d (~۲۰ روز)

مرحله ۰ — فیکس باگ‌های فوری

تکمیل‌شده | کامیت: 8b9c317 + fe3edd1

وضعیت باگ‌ها (بررسی اولیه لازم)

# باگ Handler شرح فیکس
B1 DiscountBalance شارژ نمی‌شود VerifyGoldenPackagePurchaseCommandHandler اضافه DiscountBalance += Amount × 2 + WalletChangeLog
B2 UserPackagePurchase ساخته نمی‌شود VerifyGoldenPackagePurchaseCommandHandler ساخت record بعد verify موفق
B3 UserPackagePurchase ساخته نمی‌شود VerifyPackagePurchaseCommandHandler ساخت record بعد verify موفق
B4 UserPackagePurchase ساخته نمی‌شود VerifyBasePackagePaymentCommandHandler ساخت record بعد verify موفق

دستور کار B1:

1. باز کردن VerifyGoldenPackagePurchaseCommandHandler.cs
2. پیدا کردن جایی که Balance شارژ می‌شود
3. اضافه کردن:
   wallet.DiscountBalance += command.Amount * 2;
   // + ساخت WalletChangeLog برای DiscountBalance
4. تست: verify → چک DiscountBalance در DB

دستور کار B2-B4 (الگوی مشترک):

1. بعد از verify موفق و شارژ wallet:
   var purchase = new UserPackagePurchase
   {
       UserId = userId,
       PackageId = packageId,         // فعلاً BasePackageId = 4
       PurchaseDate = DateTime.UtcNow,
       Amount = amount,
       PurchaseMethod = purchaseMethod, // ZarinPal, BFF, etc.
       TransactionId = transactionId,
       IsVerified = true
   };
   _context.UserPackagePurchases.Add(purchase);
2. تست: verify → چک UserPackagePurchases table

مرحله ۱ — زیرساخت (Domain + DB)

تکمیل‌شده | کامیت: ae92ab8 (Phase 1) + a9cd2fd (Phase 1.5 Migration)

T1.1 — بروزرسانی Package Entity (۱۱ فیلد جدید — v3)

فایل: CMS/src/CMSMicroservice.Domain/Entities/Package.cs

+ public int SortOrder { get; set; }
+ public bool IsActive { get; set; } = true;
+ public bool IsBasePackage { get; set; }
+ public bool SupportsDayaPurchase { get; set; }
+ public bool SupportsDirectPurchase { get; set; } = true;
+ public long ActivationFee { get; set; }
+ public decimal DiscountMultiplier { get; set; } = 2.0m;
+ public decimal MagicWalletMultiplier { get; set; } = 2.5m;
+ // === v3: تنظیمات پورسانت per-package ===
+ public int MaxBalancesPerLeg { get; set; } = 300;         // نقره‌ای=۳۰
+ public int MaxNetworkLevel { get; set; } = 15;
+ // === v3: سقف کیف‌پول جادویی per-package ===
+ public long MagicWalletMaxDeposit { get; set; } = 1_000_000_000;
+ public long MagicWalletMaxCredit { get; set; } = 2_500_000_000;
+
+ public virtual ICollection<PackageFeature> PackageFeatures { get; set; }

EF Config: PackageConfiguration.cs

  • حداکثر یک IsBasePackage = true (Index filter)
  • Precision for decimal fields

T1.2 — ایجاد PackageFeature Entity

فایل جدید: CMS/src/CMSMicroservice.Domain/Entities/PackageFeature.cs

public class PackageFeature : BaseAuditableEntity
{
    public long PackageId { get; set; }
    public virtual Package Package { get; set; }
    public long ClubFeatureId { get; set; }
    public virtual ClubFeature ClubFeature { get; set; }
    public bool IsIncluded { get; set; } = true;
}

T1.3-T1.6 — اضافه PackageId به entityها

Entity فیلد Required? توضیح v3?
ClubMembership long? PackageId nullable (بعد migration → required) آخرین پکیج
ClubMembershipCycle long PackageId required پکیج این چرخه
WeeklyCommissionPool long PackageId required + Unique(WeekDefId, PkgId) Pool هر پکیج
UserCommissionPayout long? PackageId nullable + Unique(UserId, WeekId, PkgId) ردیابی 🔄
NetworkWeeklyBalance long PackageId required + Unique(UserId, WeekId, PkgId) تعادل per-package 🆕

T1.7 — حذف SystemConstants (v3: ۷ ثابت)

فایل: CMS/src/CMSMicroservice.Domain/Common/SystemConstants.cs

- public const long BasePackageAmount = 56_000_000;
- public const long DayaLoanAmount = 56_000_000;
- public const long ClubActivationFee = 25_200_000;
- public const long ClubMembershipGiftValue = 25_200_000;
- public const decimal MagicWalletMultiplier = 2.5m;
- // === v3: انتقال به Package entity ===
- public const int CommissionMaxWeeklyBalancesPerLeg = 300;
- public const int CommissionMaxNetworkLevel = 15;

⚠️ قبل از حذف: grep تمام مصرف‌کننده‌ها → جایگزین با Package.Property
⚠️ v3: CommissionMaxWeeklyBalancesPerLeg و CommissionMaxNetworkLevel هم باید per-package شوند

T1.8 — Database Migration

dotnet ef migrations add AddPackageBasedSystem

شامل:

  • ستون‌های جدید Package
  • جدول PackageFeatures
  • FKها در 4 entity
  • Unique constraint

T1.9 — Data Migration Script

-- 1. بروزرسانی پکیج فعلی (ID=4 → اضافه فیلدهای جدید)
UPDATE "CMS"."Packages" SET
    "SortOrder" = 2,
    "IsActive" = true,
    "IsBasePackage" = true,
    "SupportsDayaPurchase" = true,
    "SupportsDirectPurchase" = true,
    "ActivationFee" = 25200000,
    "DiscountMultiplier" = 2.0,
    "MagicWalletMultiplier" = 2.5,
    -- v3: تنظیمات پورسانت
    "MaxBalancesPerLeg" = 300,
    "MaxNetworkLevel" = 15,
    "MagicWalletMaxDeposit" = 1000000000,
    "MagicWalletMaxCredit" = 2500000000
WHERE "Id" = 4;

-- 2. Link existing data to base package
UPDATE "CMS"."ClubMemberships" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."ClubMembershipCycles" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."WeeklyCommissionPools" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."UserCommissionPayouts" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
-- v3: NetworkWeeklyBalance هم PackageId می‌گیره
UPDATE "CMS"."NetworkWeeklyBalances" SET "PackageId" = 4 WHERE "PackageId" IS NULL;

-- 3. Seed Silver package (شامل فیلدهای v3)
INSERT INTO "CMS"."Packages" (..., "MaxBalancesPerLeg", "MaxNetworkLevel",
    "MagicWalletMaxDeposit", "MagicWalletMaxCredit", ...)
VALUES ('پکیج نقره‌ای', 5600000, ..., 30, 15, 100000000, 250000000, ...);

T1.10 — بروزرسانی Protoها

Proto File تغیر v3?
package.proto فیلدهای جدید Package message (۱۱ فیلد) 🔄
clubmembership.proto package_id در request/response
commission.proto package_id + package_title در ۴ message 🆕
commission.proto Message جدید: CustomerCommissionPackageSummary 🆕
commission.proto فیلتر package_id در Requestها 🆕

T1.11 — اضافه PackageId به NetworkWeeklyBalance (🆕 v3)

فایل: CMS/src/CMSMicroservice.Domain/Entities/NetworkWeeklyBalance.cs

+ public long PackageId { get; set; }
+ public virtual Package Package { get; set; }

EF Config: اضافه Unique Index:

builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId, e.PackageId }).IsUnique();
builder.HasOne(e => e.Package).WithMany().HasForeignKey(e => e.PackageId);

⚠️ تاثیر حجم: رکوردهای تعادل ×N (تعداد پکیج). مثلاً ۱۰۰۰ کاربر × ۲ پکیج = ۲۰۰۰ رکورد هفتگی

T1.12 — Data Migration: NetworkWeeklyBalance (🆕 v3)

-- رکوردهای موجود → پکیج پایه
UPDATE "CMS"."NetworkWeeklyBalances" 
SET "PackageId" = (SELECT "Id" FROM "CMS"."Packages" WHERE "IsBasePackage" = true LIMIT 1)
WHERE "PackageId" IS NULL;

مرحله ۲ — منطق کسب‌وکار

تکمیل‌شده | کامیت: 8e5c7c5 (Phase 2) + ccb938e (Phase 3) + 0002a5a (Phase 4)

T2.1 — Generic Verify Handler

هدف: ادغام VerifyGolden + VerifyBase + VerifyGeneric → یک handler

الگوریتم:

1. دریافت TransactionId از request
2. خواندن Transaction → PackageId → Package entity
3. verify با درگاه (ZarinPal/BFF/...)
4. اگر موفق:
   a. wallet.Balance += Package.Price
   b. wallet.DiscountBalance += Package.Price × Package.DiscountMultiplier
   c. ساخت WalletChangeLog (Balance)
   d. ساخت WalletChangeLog (DiscountBalance)
   e. ساخت UserPackagePurchase record
   f. اگر اولین خرید: JoinNetwork
   g. بروزرسانی ClubMembershipCycle.PackageId
5. return success + receipt

T2.2 — Generic Purchase Handler

هدف: ادغام PurchaseGolden + PurchasePackage + InitiateBase → یک handler

تغییرات:

  • حذف فیلتر Title.Contains("طلایی")
  • حذف BasePackageId = 4
  • خواندن Package entity از DB بر اساس request.PackageId
  • Gateway URL + Amount از Package.Price

T2.3-T2.4 — ActivateClubMembership بهبود

تغییرات:

- var features = await GetAllFeatureIds();        // همه فیچرها
+ var features = await GetPackageFeatures(packageId);  // فیچرهای پکیج

- membership.PackageAmount = SystemConstants.BasePackageAmount;
+ membership.PackageAmount = package.Price;

- var activationFee = SystemConstants.ClubActivationFee;
+ var activationFee = package.ActivationFee;

T2.5-T2.6 — Re-Purchase Logic

EXIT Magic Mode — تغییرات:

  wallet.WalletMode = WalletMode.Normal;
  wallet.MagicCompletedAt = DateTime.UtcNow;
  cycle.MagicCompletedAt = DateTime.UtcNow;
+ user.PackagePurchaseMethod = PackagePurchaseMethod.None;
+ membership.IsActive = false;
+ cycle.IsCurrentCycle = false;

Guard تغییرات:

- if (user.PackagePurchaseMethod != PackagePurchaseMethod.None)
-     throw new RpcException("قبلاً پکیج خریداری شده");
+ if (user.PackagePurchaseMethod != PackagePurchaseMethod.None 
+     && !HasCompletedMagicCycle(membership))
+     throw new RpcException("چرخه جاری هنوز تکمیل نشده");

T2.7 — JWT Claims جدید

  claims.Add("HasPurchasedPackage", "true");
+ claims.Add("CanRepurchase", HasCompletedMagicCycle(membership).ToString());
+ claims.Add("PackageId", membership.PackageId?.ToString() ?? "");
+ claims.Add("PackageTitle", package?.Title ?? "");

مرحله ۳ — محاسبه پورسانت (موازی با مرحله ۲)

تکمیل‌شده | کامیت: 607f791 + 7176fe4 | ⏱️ ۴ روز | ریسک: بحرانی (مالی)

T3.1-T3.2 — SPs + PackageId + پارامترهای داینامیک (🔄 v3)

-- sp_CalculateWeeklyBalances — v3: حذف hardcode
ALTER PROCEDURE sp_CalculateWeeklyBalances
    @WeekDefinitionId BIGINT,
    @PackageId BIGINT,
    @MaxBalancesPerLeg INT,          -- v3: از Package entity (نه ۳۰۰ hardcode!)
    @MaxNetworkLevel INT             -- v3: از Package entity (نه ۱۵ hardcode!)
AS
BEGIN
    -- فیلتر: فقط کاربرانی که این پکیج را دارند
    -- carryover: فقط رکوردهای PackageId = @PackageId
    -- cap: از @MaxBalancesPerLeg (نه ۳۰۰)
    -- depth: CTE تا @MaxNetworkLevel (نه ۱۵)
    INSERT INTO "CMS"."NetworkWeeklyBalances" ("PackageId", ...)
    SELECT @PackageId, ...
    FROM "CMS"."UserWallets" w
    INNER JOIN "CMS"."ClubMemberships" m ON m."UserId" = w."UserId"
    WHERE m."PackageId" = @PackageId
      AND m."IsActive" = true;
END;

T3.3 — Loop Service (🔄 v3: ارسال تنظیمات پکیج)

// WeeklyCommissionCalculationService.cs
var activePackages = await _context.Packages
    .Where(p => p.IsActive && !p.IsDeleted)
    .ToListAsync();

foreach (var package in activePackages)
{
    _logger.LogInformation(
        "Calculating commission for package {Id}: {Title} " +
        "(MaxBalances={Max}, MaxLevel={Level})", 
        package.Id, package.Title, 
        package.MaxBalancesPerLeg, package.MaxNetworkLevel);
    
    // v3: پاس دادن تنظیمات پکیج
    await strategy.CalculateWeeklyBalancesAsync(
        weekId, package.Id, 
        package.MaxBalancesPerLeg, package.MaxNetworkLevel);
    
    await strategy.CalculateWeeklyPoolAsync(weekId, package.Id);
}

T3.4 — OrmCommissionCalculationStrategy (🔄 v3)

تغییرات:

- var maxBalances = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; // 300
- var maxLevel = SystemConstants.CommissionMaxNetworkLevel;           // 15
+ // پارامتر از بیرون — per-package
+ int maxBalances = maxBalancesPerLeg;  // e.g., نقره‌ای=30, پایه=300
+ int maxLevel = maxNetworkLevel;

- // فیلتر کاربران
+ // فیلتر کاربران بر اساس پکیج
+ .Where(m => m.PackageId == packageId && m.IsActive)

- // carryover
+ // carryover: فقط رکوردهای همان PackageId
+ .Where(b => b.PackageId == packageId && b.WeekDefinitionId == prevWeekId)

T3.5 — SpCommissionCalculationStrategy (🆕 v3)

// قبل: فقط WeekDefinitionId
await connection.ExecuteAsync("CMS.sp_CalculateWeeklyBalances",
    new { WeekDefinitionId = weekId, ForceRecalculate = true });

// بعد (v3): پکیج + تنظیمات داینامیک
await connection.ExecuteAsync("CMS.sp_CalculateWeeklyBalances",
    new { 
        WeekDefinitionId = weekId, 
        PackageId = package.Id,
        MaxBalancesPerLeg = package.MaxBalancesPerLeg,
        MaxNetworkLevel = package.MaxNetworkLevel,
        ForceRecalculate = true 
    });

T3.6 — Carryover per-package (🆕 v3)

⚠️ بحرانی: week-shifting باید فقط رکوردهای همان PackageId را shift کند

هفته ۱۰ → هفته ۱۱:
    علی: carryover_پایه = {Left: surplus, Right: surplus}    ← جداگانه
    علی: carryover_نقره‌ای = {Left: 0, Right: 0}             ← جداگانه
    
    ✖ اشتباه: قاطی کردن carryover پایه و نقره‌ای!
    ✔ صحیح: هر PackageId فقط carryover خودش را می‌بینه

⚠️ نکته بحرانی

پورسانت = پول واقعی. هر تغییر در SPs باید:

  1. ابتدا در staging با داده واقعی تست شود
  2. نتایج قبل و بعد مقایسه شوند
  3. Rollback plan آماده باشد
  4. در production ابتدا read-only اجرا شود (بدون commit)

مرحله ۷ — Migration + Cleanup (سه‌گانه)

تکمیل‌شده | ⏱️ ۱ روز | ریسک: پایین

فاز 7a — Cosmetic Cleanup

کامیت: CMS 469d97b | FO b82cac4 | BO f1b0085

CMS:

  • حذف orphaned PurchasePackage handler (۳ فایل، بدون caller)
  • فیکس doc-comments: طلاییپکیج در ۶ فایل (enums, entities, handlers)

FrontOffice:

  • حذف hardcoded پکیج طلایی از MyPackages.razor و Packages.razor
  • اضافه PackageTitle property به UserPackageStatusDto record

BackOffice:

  • تغییر label پکیج طلاییخرید پکیج در UserNetworkInfo.razor

فاز 7b — FrontOffice RPC Migration

کامیت: CMS 161f796 | FO 71f391a

CMS:

  • CustomerPurchasePackage: embed orderId در callback URL قبل از ارسال به درگاه
    • $"{request.CallbackUrl}{separator}orderId={purchase.Id}"

FrontOffice:

  • Profile/Index.razor.cs: مهاجرت InitiateBasePackagePaymentAsyncCustomerPurchasePackageAsync
  • Profile/PaymentCallback.razor: مهاجرت VerifyBasePackagePaymentAsyncCustomerVerifyPackagePurchaseAsync
  • پارامترهای جدید: PackageId, CallbackUrl, PurchaseMethod, OrderId, Authority, Status

فاز 7c — Delete Deprecated Handlers

کامیت: CMS 8446e0e (14 فایل، 1125 حذف)

حذف ۴ handler CQRS (۱۲ فایل):

  • PurchaseGoldenPackage/ (Command, Handler, Validator)
  • VerifyGoldenPackagePurchase/ (Command, Handler, Validator)
  • InitiateBasePackagePayment/ (Command, Handler, Validator)
  • VerifyBasePackagePayment/ (Command, Handler, Validator)

Cleanup:

  • PackageService.cs: حذف ۴ gRPC override method (proto RPCs حالا auto-throw Unimplemented)
  • PackageProfile.cs: حذف ۶ Mapster mapping block + ۴ using directive
  • Build: 0 Error

مرحله ۸ — FrontOffice Checkout + NuGet

🔄 در حال اجرا | فاز 8a تکمیل

فاز 8a — Checkout Wire-up

کامیت: FO 0bbc11e

Checkout.razor.cs:

  • حذف dead code: ProcessPayment() از flow قدیمی TransactionsContract + UserOrderContract استفاده می‌کرد
  • Rewrite با CustomerPurchasePackageAsync (مثل Profile/Index.razor.cs)
  • Callback URL → /profile/payment-callback (از صفحه verify موجود استفاده مجدد)
  • حذف DI بلااستفاده: UserOrderContract, TransactionContract
  • حذف usings: Transactions, UserOrder, WellKnownTypes

Profile/Index.razor.cs (cosmetic):

  • Rename basePackageselectedPackage, tempCallbackUrlcallbackUrl

فاز 8b — BackOffice Package CRUD Expansion

کامیت: CMS ce8e248 | BO 89f5241 (7 فایل، +138/-23)

NuGet Rebuild:

  • Proto version bump: 0.0.1840.0.185
  • Pack و deploy به local feed (/nupkg)
  • BackOffice NuGet.config: اضافه local feed source

CreateDialog.razor (۱۲ فیلد جدید):

  • SortOrder — MudNumericField ترتیب نمایش
  • ActivationFee — MudNumericField هزینه فعال‌سازی
  • DiscountMultiplier — MudNumericField ضریب تخفیف
  • MagicWalletMultiplier — MudNumericField ضریب کیف پول جادویی
  • MagicWalletMaxDeposit — MudNumericField سقف واریز جادویی
  • MagicWalletMaxCredit — MudNumericField سقف اعتبار جادویی
  • MaxBalancesPerLeg — MudNumericField حداکثر تعادل هر پا
  • MaxNetworkLevel — MudNumericField حداکثر سطح شبکه
  • IsActive — MudCheckBox فعال/غیرفعال
  • IsBasePackage — MudCheckBox پکیج پایه
  • SupportsDirectPurchase — MudCheckBox پرداخت مستقیم
  • SupportsDayaPurchase — MudCheckBox اعتبار دایا

UpdateDialog.razor: همان ۱۲ فیلد

PackageMainPage Grid (۴ ستون جدید):

  • Price — فرمت‌شده با N0
  • SortOrder — ترتیب
  • IsActive — MudChip فعال/غیرفعال
  • IsBasePackage — MudChip پایه/عادی

سایر:

  • Dialog size: MaxWidth.SmallMaxWidth.Medium
  • CreateNew defaults: IsActive=true, DiscountMultiplier=2.0, MagicWalletMultiplier=2.5, ...
  • فیکس HasPurchasedGoldenPackageHasPurchasedPackage در UserNetworkInfo.razor

فاز 8c — FrontOffice Package Pages

کامیت: FO d71d463 (5 فایل، +109/-56)

NuGet: 0.0.1820.0.185 + local feed source

PackageDetail.razor.cs:

  • مهاجرت GetPackageAsync (admin RPC) → GetCustomerPackageDetailsAsync (customer RPC)
  • Features: از hardcoded ثابت → از PackageFeature API داینامیک
  • Specifications: از hardcoded → از PackageFeature.IsHighlighted API
  • حذف ۵ hardcoded feature string + ۴ hardcoded specification

PackageService.cs:

  • PackageDto: اضافه ۸ فیلد جدید (ActivationFee, DiscountMultiplier, MagicWalletMultiplier, etc.)
  • GetAllPackagesAsync: مپ فیلدهای جدید از CustomerPackageModel
  • GetUserPackageStatusAsync: از stub → اتصال واقعی به GetUserPackageStatusAsync RPC

Packages.razor:

  • Un-exclude از build (حذف <Content Remove> + <Compile Remove>)
  • جایگزینی ۳ feature bullet hardcoded → dynamic features:
    • SupportsDirectPurchase → پرداخت مستقیم
    • SupportsDayaPurchase → پرداخت با اعتبار دایا
    • DiscountMultiplier → ضریب تخفیف: X.Xx
    • MagicWalletMultiplier → کیف پول جادویی: X.Xx
    • IsBasePackage → پکیج پایه

فاز 8d — Proto Cleanup

کامیت: CMS 7554d70 (2 فایل، -103) | FO 40882c8 | BO c96377a

حذف ۴ deprecated RPC:

  • PurchaseGoldenPackage — جایگزین: CustomerPurchasePackage
  • VerifyGoldenPackagePurchase — جایگزین: CustomerVerifyPackagePurchase
  • InitiateBasePackagePayment — جایگزین: CustomerPurchasePackage
  • VerifyBasePackagePayment — جایگزین: CustomerVerifyPackagePurchase

حذف ۸ deprecated message type:

  • PurchaseGoldenPackageRequest / PurchaseGoldenPackageResponse
  • VerifyGoldenPackagePurchaseRequest / VerifyGoldenPackagePurchaseResponse
  • InitiateBasePackagePaymentRequest / InitiateBasePackagePaymentResponse
  • VerifyBasePackagePaymentRequest / VerifyBasePackagePaymentResponse

حفظ شده: GetUserPackageStatus RPC + messages (هنوز در استفاده)

NuGet: 0.0.1850.0.186 (همه ریپوها)

فاز 8e — Per-Package Commission Reports

کامیت: CMS aaaf7fc | FO a956cb9 | BO 8be98ae

Proto (commission.proto):

  • اضافه package_id فیلتر به ۴ request message: GetUserCommissionPayoutsRequest, GetUserWeeklyBalancesRequest, GetMyCommissionPayoutsRequest, GetMyWeeklyBalancesRequest
  • اضافه package_id + package_title به ۴ response model: UserCommissionPayoutModel, UserWeeklyBalanceModel, CustomerCommissionPayoutModel, CustomerWeeklyBalanceModel

CMS (12 فایل):

  • ۴ Query record: اضافه public long? PackageId { get; init; }
  • ۴ Handler: اضافه .Include(x => x.Package) + فیلتر Where(x => x.PackageId == request.PackageId.Value) + map PackageId/PackageTitle
  • ۳ Response DTO: اضافه PackageId + PackageTitle
  • CommissionProfile.cs: تنظیم mapping‌های Mapster برای admin + customer

BackOffice (6 فایل):

  • کامپوننت جدید PackageSelect.razor/.cs: dropdown قابل استفاده مجدد با بارگذاری پکیج‌ها از PackageContract
  • UserPayouts.razor/.cs: فیلتر PackageSelect + ستون پکیج با MudChip
  • BalancesReport.razor: فیلتر PackageSelect + ستون پکیج با MudChip + mapping PackageTitle

FrontOffice (7 فایل):

  • CommissionDtos.cs: اضافه PackageId + PackageTitle به CommissionPayoutDto و WeeklyBalanceDto
  • CommissionService.cs: اضافه پارامتر packageId به GetMyCommissionPayoutsAsync و GetMyWeeklyBalanceAsync
  • CommissionDashboardPage.razor/.cs: فیلتر dropdown پکیج + ستون «پکیج» با MudChip (دسکتاپ + موبایل)
  • WeeklyBalancePage.razor/.cs: فیلتر MudSelect پکیج + نمایش MudChip پکیج در بخش اطلاعات هفته

NuGet: 0.0.1860.0.187 (همه ریپوها)

فاز 8f — UI Completion (T4.2 + T4.3 + T4.13 + F2 + F3)

کامیت: CMS dcd1135 | FO 3bffc13 | BO e020354

CMS (T4.13 — PackageFeature CRUD):

  • Proto: اضافه repeated int64 feature_ids به ۴ message (Create/Update Request, Get/GetAll Response)
  • CreateNewPackageCommand/Handler: sync FeatureIds → ساخت PackageFeature records
  • UpdatePackageCommand/Handler: sync FeatureIds → حذف قبلی‌ها + ساخت جدید
  • GetPackage/GetAllPackageByFilter: اضافه .Include(x => x.PackageFeatures) + map FeatureIds

BackOffice (F2 + T4.13):

  • F2: کامپوننت جدید ChangeParentDialog.razor/.cs — مودال جابجایی در شبکه با NewParentId, NewLeg, Reason
  • F2: دکمه «تغییر والد» در UserNetworkInfo.razor
  • T4.13: checkbox matrix فیچرها در CreateDialog و UpdateDialog — بارگذاری از ConfigurationContractClient

FrontOffice (T4.2 + T4.3 + F3):

  • T4.2: پرداخت شرطی در Checkout.razor بر اساس SupportsDirectPurchase/SupportsDayaPurchase
  • T4.3: منطق خرید مجدد در MyPackages.razor — بارگذاری MagicWalletStatus + CTA شرطی + progress bar
  • F3: نمایش PV سفارش در Store/OrderDetail.razorCalculateOrderPVAsync + جدول PV هر محصول

NuGet: 0.0.1870.0.188 (همه ریپوها)


مرحله ۴ — UI (FrontOffice + BackOffice)

⏱️ ۵ روز (v3: +۲) | وابستگی: مرحله ۲ + ۳ | ریسک: متوسط

T4.1 — کاشی‌های پکیج داینامیک

فایل: FrontOffice/src/.../Pages/Package/Packages.razor

@* قبل: hardcoded *@
@* بعد: *@
@foreach (var package in _packages.OrderBy(p => p.SortOrder))
{
    <PackageCard Package="@package" 
                 OnPurchase="StartPurchase"
                 ShowFeatures="true"
                 ShowPV="true" />
}

T4.2 — مودال پرداخت شرطی FO:3bffc13

پیاده‌سازی:

  • Checkout.razor.cs: اضافه PackageService injection، بارگذاری پکیج‌ها با GetAllPackagesAsync()
  • Checkout.razor: دکمه‌های پرداخت شرطی بر اساس SupportsDirectPurchase و SupportsDayaPurchase
  • اضافه DayaLoanPayment() method + alert برای عدم وجود روش پرداخت
  • Pack record: اضافه SupportsDirectPurchase و SupportsDayaPurchase

T4.3 — MyPackages + Re-Purchase FO:3bffc13

پیاده‌سازی:

  • MyPackages.razor.cs: بارگذاری MagicWalletStatus از WalletService.GetMagicWalletStatusAsync()
  • فرمول خرید مجدد: WalletMode == 0 && PurchaseCycleCount >= 1 && MagicRemainingDeposit == 0
  • MyPackages.razor: CTA شرطی «🎉 چرخه جادویی تکمیل شد!» + دکمه «خرید پکیج جدید»
  • بخش پیشرفت کیف پول جادویی: مبلغ واریزی، باقی‌مانده، اعتبار دریافتی + progress bar

T4.8 — FrontOffice: CommissionDashboard per-package (🆕 v3) FO:a956cb9

پیاده‌سازی:

  • CommissionDtos.cs: اضافه PackageId + PackageTitle به CommissionPayoutDto و WeeklyBalanceDto
  • CommissionService.cs: اضافه پارامتر packageId به GetMyCommissionPayoutsAsync و GetMyWeeklyBalanceAsync
  • CommissionDashboardPage.razor: اضافه dropdown فیلتر پکیج + ستون «پکیج» با MudChip + نمایش پکیج در card موبایل
  • CommissionDashboardPage.razor.cs: inject PackageService، فیلد _filterPackageId، بارگذاری لیست پکیج‌ها

T4.9 — FrontOffice: WeeklyBalance per-package (🆕 v3) FO:a956cb9

پیاده‌سازی:

  • WeeklyBalancePage.razor: اضافه MudSelect فیلتر پکیج کنار WeekSelector + نمایش MudChip پکیج در بخش اطلاعات هفته
  • WeeklyBalancePage.razor.cs: inject PackageService، فیلد _filterPackageId، ارسال به CommissionService.GetMyWeeklyBalanceAsync

T4.10-T4.12 — BackOffice: گزارش‌های پورسانت per-package (🆕 v3) BO:8be98ae

پیاده‌سازی:

  • کامپوننت جدید PackageSelect.razor/.cs: dropdown قابل استفاده مجدد با بارگذاری پکیج‌ها از PackageContract
  • UserPayouts.razor/.cs: فیلتر PackageSelect + ستون پکیج با MudChip
  • BalancesReport.razor: فیلتر PackageSelect + ستون پکیج با MudChip + mapping PackageTitle

T4.13 — BackOffice: Package CRUD + Quick Access فیچرها (🆕 v3) CMS:dcd1135 BO:e020354

CMS پیاده‌سازی:

  • Proto: اضافه repeated int64 feature_ids به ۴ message (Create/Update Request, Get/GetAll Response)
  • CreateNewPackageCommand/Handler: اضافه FeatureIds + ساخت PackageFeature records
  • UpdatePackageCommand/Handler: اضافه FeatureIds + sync (حذف قبلی‌ها + ساخت جدید)
  • GetPackageQueryHandler: اضافه .Include(x => x.PackageFeatures) + map FeatureIds
  • GetAllPackageByFilterQueryHandler: اضافه .Include(x => x.PackageFeatures) قبل از PaginatedListAsync
  • NuGet: 0.0.1870.0.188

BO پیاده‌سازی:

  • CreateDialog.razor/.cs: بارگذاری ClubFeatures از ConfigurationContractClient + checkbox matrix
  • UpdateDialog.razor/.cs: همان pattern + pre-populate از Model.FeatureIds
  • Mapster: Adapt<UpdatePackageRequest>() خودکار FeatureIds را map می‌کند

مرحله ۹ — Q24-Q30 Business Decisions + History Infrastructure

تکمیل‌شده | وابستگی: مرحله ۸ | کامیت‌ها: CMS:a1024a3fdbb91d10d2ca2 FO:474d364 BO:6939780

9a: Q24 آستانه موجودی + Q26 SP Worker (CMS:a1024a3)

Q24 — آستانه موجودی:

  • شرط ورود به Magic و خرید مجدد از Balance == 0 به Balance <= 1_000_000 ریال تغییر کرد
  • چون قیمت محصولات متفاوته، Balance دقیقاً صفر نمی‌شه
  • فایل‌ها: UserOrderService.cs (شرط EXIT Magic + Re-purchase guard)

Q26 — SP Worker:

  • StoredProcedureDeploymentService (IHostedService) — در startup فایل‌های .sql از embedded resource خوانده می‌شوند
  • مقایسه checksum با جدول __SPChecksums — فقط SP‌های تغییریافته re-deploy می‌شوند
  • فایل‌ها: StoredProcedureDeploymentService.cs, embedded .sql resources

9b: Q27 History Tables Entities (CMS:fdbb91d)

Entity‌های جدید:

  • PackageHistory: فیلدهای Old*/New* برای Price, ActivationFee, MagicMultiplier, MagicMaxDeposit, MaxBalancesPerLeg, IsActive + Action + PerformedBy + Reason
  • ClubMembershipCycleHistory: فیلدهای Old*/New* برای IsCurrentCycle, MagicStartedAt, MagicCompletedAt + Action + UserId + CycleNumber

Enums جدید:

  • PackageAction: Created, Updated, Activated, Deactivated, PriceChanged, FeaturesChanged
  • ClubMembershipCycleAction: Created, MagicStarted, MagicCompleted, Closed, AdminModified

زیرساخت:

  • EF Configurations (indexes, maxLength, precision)
  • DbSets در IApplicationDbContext و ApplicationDbContext
  • Navigation Properties: Package.Histories, ClubMembershipCycle.Histories

9c: Q28 UI Guidance (FO:474d364 BO:6939780)

FrontOffice — ۷ صفحه با MudAlert آموزشی:

  • G1: Packages.razor — توضیح سیستم پکیج‌بیس
  • G2: Checkout — هشدار شارژ کیف‌پول اعتباری
  • G3: MyPackages — توضیح وضعیت پکیج‌ها
  • G4: MagicWallet — هشدار شرایط خروج + سقف شارژ
  • G5: CommissionDashboard — توضیح per-package
  • G6: ClubMembership — آموزش چرخه عضویت
  • G7: ActivationSection — هشدار هزینه فعال‌سازی

BackOffice — ۶ صفحه با MudAlert:

  • G8: PackageCRUD — هشدار ثبت تغییرات در History
  • G9: ClubFeatures — توضیح ارتباط فیچر-پکیج
  • G10: ManualPayments — هشدار مبلغ بر اساس پکیج
  • G11: Commission Dashboard — توضیح Pool per-package
  • G12: UserPayouts — توضیح فیلتر پکیج
  • G13: ClubMembers — اطلاعات چرخه عضویت

9d: Rename + History Interceptor + EF Migration (CMS:10d2ca2)

Rename (86 فایل):

  • UserWalletChangeLogUserWalletHistory در 54+ فایل (entities, configs, DTOs, commands, queries, protos, services)
  • 34 فایل rename شده + 11 دایرکتوری rename شده
  • Proto: userwalletchangelog.protouserwallethistory.proto

History Interceptor:

  • IHasHistory<T> generic interface در Domain/Common — متد CreateHistorySnapshot(action, performedBy)
  • HistoryTrackingSaveChangesInterceptor در Infrastructure/Persistence/Interceptors — reflection-based
    • شناسایی entity‌های IHasHistory<> از ChangeTracker
    • فراخوانی CreateHistorySnapshot برای Modified/Added
    • Auto-fill فیلدهای Old* از OriginalValues با naming convention
  • Package implements IHasHistory<PackageHistory> — اولین entity

EF Migration (Q27_HistoryTables_And_RenameWalletHistory):

  • ⚠️ EF Core اتوماتیک DropTable + CreateTable تولید کرد → دستی اصلاح شد به RenameTable (حفظ داده‌ها)
  • RenameTable + RenameIndex × 2 + sp_rename برای PK و FK‌ها
  • CreateTable برای ClubMembershipCycleHistories و PackageHistories (جداول جدید)
  • Down method: reverse rename + drop new tables

مرحله ۵ — تست و استقرار

⏱️ ۳ روز (v3: +۱) | وابستگی: مرحله ۴

Checklist تست

خرید + فعال‌سازی:

  • خرید پکیج نقره‌ای (ZarinPal)
  • خرید پکیج پایه (ZarinPal)
  • خرید پکیج پایه (Daya Loan)
  • خرید پکیج پایه (Manual Payment)
  • فعالسازی باشگاه با پکیج نقره‌ای → فیچرهای محدود
  • فعالسازی باشگاه با پکیج پایه → همه فیچرها

چرخه Magic + خرید مجدد:

  • تکمیل چرخه Magic → ریست وضعیت
  • خرید مجدد بعد تکمیل چرخه (همان پکیج)
  • خرید مجدد با پکیج متفاوت (پایه → نقره‌ای)

پورسانت per-package (v3):

  • Commission Pool جداگانه هر پکیج
  • تعادل per-package: MaxBalancesPerLeg متفاوت (پایه=۳۰۰, نقره‌ای=۳۰)
  • Carryover مجزا: shift فقط رکوردهای همان PackageId
  • SP پارامترها صحیح: @MaxBalancesPerLeg و @MaxNetworkLevel از Package
  • NetworkWeeklyBalance رکوردها: ۲ پکیج = ۲× رکورد

گزارش per-package (v3):

  • FO: مشتری کارت‌های خلاصه per-package را می‌بیند
  • FO: مجموع پاداش = جمع همه پکیج‌ها
  • BO: فیلتر dropdown پکیج کار می‌کند
  • BO: CSV export شامل ستون پکیج

Migration + سایر:

  • Data Migration — PackageId در رکوردهای قبلی (شامل NetworkWeeklyBalance)
  • JWT claims جدید (CanRepurchase, PackageId)
  • UI: کاشی‌های داینامیک FrontOffice
  • UI: ماتریس فیچر + Quick Access BackOffice
  • Rollback: بدون data loss

📅 تقویم پیشنهادی (v3)

هفته روز تسک
هفته ۱ روز ۱ مرحله ۰: فیکس ۴ باگ
روز ۲-۳ مرحله ۱: Package entity (۱۱ فیلد) + PackageFeature
روز ۴-۵ مرحله ۱: FKها + NetworkWeeklyBalance + Migration
هفته ۲ روز ۶-۷ مرحله ۲: Generic handlers + re-purchase
روز ۶-۸ مرحله ۳: SP params + carryover per-package (موازی)
روز ۸-۱۰ مرحله ۲: Guards + JWT + Manual
هفته ۳ روز ۱۱-۱۲ مرحله ۴: FrontOffice UI + گزارش per-package
روز ۱۳-۱۴ مرحله ۴: BackOffice UI + گزارش per-package
روز ۱۵-۱۷ مرحله ۵: تست + deploy

🔗 ارجاعات

مستند محتوا
BIZ-PACKAGE-BASED-SYSTEM.md طراحی فنی — ۴۸+ تغییر (v3) + باگ‌ها
PACKAGE-TRANSFORMATION-UX.md تاثیر UX بر فرانت‌ها
FEATURE-BACKLOG.md بکلاگ ۱۲ RPC آماده
GRPC-SERVICES-AUDIT.md آدیت ۳۴۲ RPC

آخرین بروزرسانی: ۱۴۰۴/۱۲/۰۸ — فاز ۰-۹d تکمیل (۳۰ کامیت: ۲۰ CMS + ۸ FO + ۶ BO) | NuGet v0.0.188 | باقی‌مانده: تست + deploy