Files
docs/deployment/PACKAGE-MIGRATION-GUIDE.md
masoodafar-web de69bf862c docs: F1-F7 همه تکمیل — آپدیت PACKAGE-MIGRATION-GUIDE
- F1-F7 از 🟡 به  تغییر کردند
- NuGet: v0.0.188 → v0.0.189
- R8 (validator hardcoded 1B): فیکس شد
- کامیت هش‌های جدید اضافه شد
- آمار کامیت‌ها و تاریخ بروز شد
2026-02-27 09:05:50 +03:30

42 KiB
Raw Permalink Blame History

📦 راهنمای مهاجرت سیستم پکیج‌بیس — خلاصه تغییرات و پلن استقرار

وضعیت: آماده تست و استقرار — Q1-Q30 تکمیل‌شده | F1-F11 تکمیل‌شده
تاریخ: ۸ اسفند ۱۴۰۴ (27 Feb 2026) — آپدیت ۱۰ اسفند
نسخه NuGet: v0.0.189
تعداد کامیت‌ها: ۵۱+ کامیت در ۴ ریپازیتوری (۲۱ CMS + ۹ FO + ۷ BO + ۱۴+ docs)
مدت پیاده‌سازی: ۶ روز (۲۴ فوریه – ۱ مارس ۲۰۲۶)
ریپوها: CMS (gitea/kub-stage) · FrontOffice (kub-stage) · BackOffice (kub-stage) · totalDoc (foursatDocs/main)


فهرست مطالب

  1. خلاصه اجرایی
  2. چه چیزی تغییر کرده؟ — نمای بیزینسی
  3. بخش‌های تحت تاثیر سیستم
  4. جزئیات تغییرات هر ریپو
  5. پلن مهاجرت مرحله‌به‌مرحله
  6. Rollback Plan
  7. چک‌لیست تست قبل از Production
  8. ریسک‌ها و نکات بحرانی

1. خلاصه اجرایی

قبل (سیستم تک‌پکیج):

  • فقط یک پکیج پایه (۵۶ میلیون تومان) وجود داشت
  • تمام مقادیر مالی (قیمت، هزینه فعال‌سازی، ضرایب، سقف‌ها) hardcoded در کد بودند
  • خرید مجدد پکیج غیرممکن بود (حتی بعد تکمیل چرخه)
  • پورسانت فقط از یک Pool واحد محاسبه می‌شد
  • همه کاربران همه فیچرها را دریافت می‌کردند

بعد (سیستم چندپکیجی):

  • سیستم N پکیج با قیمت و ویژگی‌های متفاوت پشتیبانی می‌کند
  • تمام مقادیر مالی از دیتابیس (Package entity) خوانده می‌شوند
  • خرید مجدد بعد تکمیل چرخه Magic Wallet فعال شده
  • هر پکیج Commission Pool مستقل خود را دارد
  • فیچرها per-package هستند و با الگوریتم DIFF مدیریت می‌شوند
  • قرارداد باشگاه فقط یک بار (اولین خرید) امضا می‌شود

آمار تغییرات:

شاخص مقدار
فایل‌های تغییریافته ۲۲۷+ فایل
خطوط اضافه‌شده +۱۷,۰۰۰+
خطوط حذف‌شده −۲,۶۶۰+
تصمیمات بیزینسی پیاده‌شده ۳۰ تصمیم (Q1Q30)
باگ‌های فیکس‌شده ۶ باگ بحرانی
مقادیر hardcoded حذف‌شده ۱۵+ مورد
Handlerهای deprecated حذف‌شده ۴ handler (۱۲ فایل)
RPCهای deprecated حذف‌شده ۴ RPC + ۸ message type
فایل‌های rename شده ۳۴ فایل + ۱۱ دایرکتوری (UserWalletChangeLog → UserWalletHistory)
History Tables جدید ۳ جدول (PackageHistories, ClubMembershipCycleHistories, UserWalletHistories)

2. چه چیزی تغییر کرده؟ — نمای بیزینسی

2.1 🏪 مدل فروش پکیج

قابلیت قبل بعد
تعداد پکیج ۱ (پایه ۵۶M) N پکیج (پایه ۵۶M + نقره‌ای ۵.۶M + ...)
قیمت‌گذاری hardcoded 56_000_000 از Package.Price در دیتابیس
هزینه فعال‌سازی hardcoded 25_200_000 از Package.ActivationFee
ضریب تخفیف hardcoded × 2 از Package.DiscountMultiplier
پشتیبانی دایا فقط پکیج پایه بر اساس Package.SupportsDayaPurchase
پرداخت مستقیم همه بر اساس Package.SupportsDirectPurchase

2.2 🔄 چرخه خرید مجدد (Re-Purchase)

مرحله قبل بعد
تکمیل چرخه Magic کاربر در بن‌بست PackagePurchaseMethod = None ریست می‌شود
خرید مجدد مسدود (guard G1-G3) مجاز — بعد تکمیل چرخه Magic
قرارداد باشگاه هر بار فقط یک بار — خرید مجدد Skip (Q19)
فیچرها همه فیچرها بدون توجه به پکیج DIFF/تفاضل — فقط اختلاف اعمال می‌شود (Q20)
تاریخچه فقط ActivatedAt FirstActivationDate + LastActivationDate (Q21)

2.3 💰 پورسانت و تعادل‌ها

ویژگی قبل بعد
Commission Pool ۱ Pool واحد Pool جداگانه هر پکیج
تعادل هفتگی ۱ رکورد per user/week N رکورد per user/week/package
MaxBalancesPerLeg hardcoded 300 per-package (پایه=۳۰۰, نقره‌ای=۳۰)
MaxNetworkLevel hardcoded 15 per-package از دیتابیس
Carryover یک‌پارچه per-downline-package — بر اساس پکیج زیرمجموعه‌ها (تغییر پکیج خود کاربر تاثیری ندارد)
Stored Procedure پارامترهای ثابت پارامترهای داینامیک از Package entity
گزارش مشتری بدون تفکیک breakdown per-package
گزارش ادمین بدون فیلتر فیلتر بر اساس پکیج

2.4 🪄 کیف پول جادویی (Magic Wallet)

ویژگی قبل بعد
ضریب جادویی hardcoded × 2.5 از Package.MagicWalletMultiplier
سقف واریز hardcoded 1,000,000,000 از Package.MagicWalletMaxDeposit
سقف اعتبار hardcoded 2,500,000,000 از Package.MagicWalletMaxCredit
شرط EXIT بررسی سقف global بررسی سقف per-package

2.5 📋 فیچرهای باشگاه

ویژگی قبل بعد
تخصیص فیچر GetAllFeatureIds() — همه فیچرها از Package.PackageFeatures — per-package
خرید مجدد الگوریتم DIFF: مقایسه فیچرهای فعلی با پکیج جدید
مدیریت ادمین ماتریس checkbox پکیج × فیچر در BackOffice

3. بخش‌های تحت تاثیر سیستم

3.1 نقشه تاثیرگذاری

┌─────────────────────────────────────────────────────────────────────────┐
│                     🏗️ سیستم پکیج‌بیس — Impact Map                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─── CMS (Backend) ──────────────────────────────────────────────────┐ │
│  │                                                                     │ │
│  │  📦 Domain Layer (Entity تغییرات)                                   │ │
│  │  ├── Package.cs ← +۱۱ فیلد جدید                                    │ │
│  │  ├── PackageFeature.cs ← Entity کاملاً جدید                        │ │
│  │  ├── ClubMembership.cs ← ActivatedAt → ۴ فیلد First/Last           │ │
│  │  ├── ClubMembershipCycle.cs ← +PackageId                           │ │
│  │  ├── WeeklyCommissionPool.cs ← +PackageId                          │ │
│  │  ├── UserCommissionPayout.cs ← +PackageId                          │ │
│  │  ├── NetworkWeeklyBalance.cs ← +PackageId                          │ │
│  │  └── SystemConstants.cs ← حذف ۹ ثابت منسوخ                         │ │
│  │                                                                     │ │
│  │  ⚙️ Application Layer (Handler تغییرات)                             │ │
│  │  ├── ActivateClubMembershipCommandHandler ← فیچر DIFF + re-activate│ │
│  │  ├── AcceptClubMembershipContractCommandHandler ← فیچر DIFF         │ │
│  │  ├── VerifyPackagePurchaseCommandHandler ← حذف fallback 2.0m       │ │
│  │  ├── CustomerPurchasePackage/Verify ← Generic purchase flow         │ │
│  │  ├── ChargeMagicWalletCommandHandler ← سقف per-package             │ │
│  │  ├── VerifyMagicWalletChargeCommandHandler ← ضریب per-package      │ │
│  │  ├── UserOrderService (EXIT Magic) ← ریست + سقف per-package        │ │
│  │  ├── CreateManualPaymentCommandHandler ← ضریب از Package           │ │
│  │  └── CheckAndProcessDayaLoansCommandHandler ← حذف ID=4            │ │
│  │                                                                     │ │
│  │  🔌 Infrastructure Layer                                            │ │
│  │  ├── sp_CalculateWeeklyBalances ← @PackageId + @Max params         │ │
│  │  ├── sp_CalculateWeeklyCommissionPool ← @PackageId                 │ │
│  │  ├── WeeklyCommissionCalculationService ← Loop per-package         │ │
│  │  ├── OrmCommissionCalculationStrategy ← فیلتر PackageId            │ │
│  │  └── SpCommissionCalculationStrategy ← پارامترهای داینامیک         │ │
│  │                                                                     │ │
│  │  📡 Proto/gRPC Layer                                                │ │
│  │  ├── package.proto ← ۱۱ فیلد + PackageFeature CRUD                │ │
│  │  ├── commission.proto ← package_id/title در ۴ model + فیلتر       │ │
│  │  ├── حذف ۴ RPC deprecated (Golden/Base)                            │ │
│  │  └── حذف ۸ message type deprecated                                 │ │
│  │                                                                     │ │
│  └─────────────────────────────────────────────────────────────────────┘ │
│                                                                         │
│  ┌─── FrontOffice (مشتری) ─────────────────────────────────────────────┐│
│  │  ├── Packages.razor ← کاشی‌های داینامیک (نه hardcoded)              ││
│  │  ├── PackageDetail.razor ← فیچرها از API (نه ثابت)                  ││
│  │  ├── Checkout.razor ← پرداخت شرطی (دایا/مستقیم)                    ││
│  │  ├── MyPackages.razor ← خرید مجدد + پیشرفت Magic                   ││
│  │  ├── ActivationSection.razor ← قیمت داینامیک (نه ۵۶M hardcoded)     ││
│  │  ├── ClubMembershipContractDialog ← متن قرارداد داینامیک            ││
│  │  ├── CommissionDashboard ← فیلتر + ستون پکیج                       ││
│  │  ├── WeeklyBalancePage ← فیلتر per-package                          ││
│  │  ├── PaymentCallback ← مهاجرت به Customer* RPCs                     ││
│  │  └── حذف "پکیج طلایی" hardcoded (۵+ جا)                            ││
│  └─────────────────────────────────────────────────────────────────────┘│
│                                                                         │
│  ┌─── BackOffice (ادمین) ──────────────────────────────────────────────┐│
│  │  ├── Package CRUD ← +۱۲ فیلد جدید در Create/Update                 ││
│  │  ├── PackageFeature Matrix ← checkbox فیچرها                        ││
│  │  ├── ManualPaymentDialog ← حذف ۵۶M hardcoded + Amount editable     ││
│  │  ├── ChangeParentDialog ← جابجایی در شبکه (جدید)                   ││
│  │  ├── UserPayouts ← فیلتر + ستون پکیج                               ││
│  │  ├── BalancesReport ← فیلتر + ستون پکیج                            ││
│  │  ├── PackageSelect Component ← dropdown قابل استفاده مجدد           ││
│  │  └── حذف "پکیج طلایی" → "خرید پکیج"                               ││
│  └─────────────────────────────────────────────────────────────────────┘│
│                                                                         │
│  ┌─── Database ────────────────────────────────────────────────────────┐│
│  │  ├── Packages ← ۱۱ ستون جدید + Seed نقره‌ای                        ││
│  │  ├── PackageFeatures ← جدول جدید                                    ││
│  │  ├── ClubMemberships ← ۴ ستون First/Last + حذف ActivatedAt         ││
│  │  ├── ClubMembershipCycles ← +PackageId                              ││
│  │  ├── WeeklyCommissionPools ← +PackageId + Unique                    ││
│  │  ├── UserCommissionPayouts ← +PackageId + Unique                    ││
│  │  ├── NetworkWeeklyBalances ← +PackageId + Unique                    ││
│  │  └── EF Migration + Data Backfill                                    ││
│  └─────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────┘

3.2 خلاصه آماری per-repo

ریپو کامیت فایل اضافه حذف شرح اصلی
CMS ۲۰ ۱۷۹+ +۱۳,۵۸۶ −۲,۳۲۷ Domain + Business + Commission + Proto + History + Rename + Interceptor
FrontOffice ۸ ۲۹ +۵۵۰ −۱۳۶ Dynamic UI + Customer RPCs + Per-package Reports + UI Guidance
BackOffice ۶ ۲۴ +۵۸۰ −۳۱ Package CRUD + Feature Matrix + Per-package Reports + UI Guidance
totalDoc ۱۴ ۱۳ +۲,۷۰۰ −۱۹۴ مستندات بیزینسی + تکنیکال + Phase 9

4. جزئیات تغییرات هر ریپو

4.1 CMS — ۲۰ کامیت

فاز کامیت شرح
Phase 0 8b9c317 فیکس ۴ باگ بحرانی: DiscountBalance + UserPackagePurchase
Phase 0 fe3edd1 فیکس EXIT Magic Mode — ریست PackagePurchaseMethod + بستن چرخه
Phase 1 ae92ab8 زیرساخت Domain: Package +۱۱ فیلد، PackageFeature entity، FKهای جدید
Phase 1.5 a9cd2fd EF Migration + Seed Data + Data Backfill
Phase 2 8e5c7c5 جایگزینی همه SystemConstants با Package entity reads
Phase 3 ccb938e بازسازی لایه Package + Proto enhancement + باگ‌فیکس
Phase 4 0002a5a CRUD DTOs + Legacy fixes
Phase 5 607f791 پورسانت per-package + حذف ref طلایی
SP Fix 7176fe4 فیکس SP: cm.PackageIdcm.LastPackageId
Phase 6 d19c569 Deprecation cleanup + ConfigurationService MagicWallet
Phase 7a 469d97b Cosmetic cleanup + حذف orphan handler
Phase 7b 161f796 Embed orderId در callback URL
Phase 7c 8446e0e حذف ۴ handler deprecated (۱۴ فایل، −۱,۱۲۵ خط)
Phase 8b ce8e248 NuGet bump → 0.0.185
Phase 8d 7554d70 حذف ۴ RPC + ۸ message deprecated از Proto
Phase 8e aaaf7fc Per-package filtering در Commission queries
Phase 8f dcd1135 PackageFeature CRUD support
Audit 1ac2366 Compliance audit — Feature DIFF + حذف fallbackهای hardcoded
Phase 9a a1024a3 Q24: آستانه موجودی ≤1M ریال + Q26: SP Worker auto-deploy (IHostedService + checksum)
Phase 9b fdbb91d Q27: PackageHistory + ClubMembershipCycleHistory entities + enums + EF configs
Phase 9d 10d2ca2 Rename UserWalletChangeLog→UserWalletHistory (86 فایل) + IHasHistory + Interceptor + Migration

4.2 FrontOffice — ۸ کامیت

فاز کامیت شرح
Phase 7a b82cac4 حذف "پکیج طلایی" + PackageTitle در DTO
Phase 7b 71f391a مهاجرت به Customer* RPCs
Phase 8a 0bbc11e Checkout wire-up به Customer RPCs
Phase 8c d71d463 صفحات پکیج — فیچرهای داینامیک
Phase 8d 40882c8 NuGet bump Proto cleanup
Phase 8e a956cb9 Per-package filtering در Commission pages
Phase 8f 3bffc13 T4.2+T4.3+F3: پرداخت شرطی + خرید مجدد + PV
Audit 816dcb7 حذف ۵۶M hardcoded — قیمت‌گذاری داینامیک
Phase 9c 474d364 Q28: UI Guidance alerts (G1-G7) — ۷ صفحه MudAlert آموزشی

4.3 BackOffice — ۶ کامیت

فاز کامیت شرح
Phase 7a f1b0085 تغییر label "پکیج طلایی" → "خرید پکیج"
Phase 8b 89f5241 Package CRUD expansion — ۱۲ فیلد جدید
Phase 8d c96377a NuGet bump Proto cleanup
Phase 8e 8be98ae Per-package commission filtering + PackageSelect component
Phase 8f e020354 ChangeParentDialog + PackageFeature checkbox matrix
Audit e6cf90e ManualPaymentDialog — حذف ۵۶M + Amount editable
Phase 9c 6939780 Q28: UI Guidance alerts (G8-G13) — ۶ صفحه MudAlert

5. پلن مهاجرت مرحله‌به‌مرحله

📋 پیش‌نیازها

  • بکاپ کامل از دیتابیس Production
  • بکاپ از stateهای Kubernetes (Deployments, ConfigMaps)
  • اطمینان از دسترسی به Container Registry (تصاویر فعلی)
  • زمان‌بندی Maintenance Window (ترجیحاً شب یا آخر هفته)
  • اطلاع‌رسانی به کاربران (در صورت نیاز به downtime)

مرحله ۱ از ۶: بکاپ و آماده‌سازی محیط 🛡️

⏱️ تخمین: ۳۰ دقیقه

1.1  بکاپ کامل دیتابیس
     └── pg_dump -Fc cms_db > cms_backup_pre_package_migration.dump

1.2  بکاپ دیتابیس BO (اگر جداست)
     └── pg_dump -Fc bo_db > bo_backup_pre_package_migration.dump

1.3  ثبت وضعیت فعلی
     └── تعداد رکوردها:
         • ClubMemberships: SELECT COUNT(*) ...
         • ClubMembershipCycles: SELECT COUNT(*) ...
         • WeeklyCommissionPools: SELECT COUNT(*) ...
         • UserCommissionPayouts: SELECT COUNT(*) ...
         • NetworkWeeklyBalances: SELECT COUNT(*) ...
         • Packages: SELECT COUNT(*) ...

1.4  ذخیره نسخه فعلی Docker images
     └── docker tag <current-cms> cms:rollback-point
     └── docker tag <current-fo> fo:rollback-point
     └── docker tag <current-bo> bo:rollback-point

Checkpoint: بکاپ‌ها ذخیره شده‌اند و قابل restore هستند.


مرحله ۲ از ۶: استقرار CMS (Backend) 🏗️

⏱️ تخمین: ۴۵ دقیقه
⚠️ ترتیب بحرانی: CMS باید اول deploy شود چون FO و BO به آن وابسته‌اند.

2.1  Build CMS Docker image
     └── cd CMS/src
     └── docker build -t cms:package-based .

2.2  اجرای EF Migration
     └── این migration شامل:
         • ۱۱ ستون جدید به جدول Packages
         • جدول جدید PackageFeatures  
         • ستون PackageId به ۵ جدول (ClubMemberships, Cycles, Pools, Payouts, Balances)
         • ۴ ستون First/Last به ClubMemberships
         • Unique Indexها

     ⚠️ Migration خودکار اجرا می‌شود در startup اگر EF auto-migration فعال باشد.
     ✅ اگر دستی: dotnet ef database update

2.3  Data Backfill — مقداردهی پکیج پایه
     └── اسکریپت SQL:
     ┌──────────────────────────────────────────────────────────┐
     │  -- مشخص کردن ID پکیج پایه                               │
     │  DO $$                                                    │
     │  DECLARE base_pkg_id BIGINT;                              │
     │  BEGIN                                                    │
     │      SELECT "Id" INTO base_pkg_id                         │
     │      FROM "CMS"."Packages"                                │
     │      WHERE "IsBasePackage" = true LIMIT 1;                │
     │                                                           │
     │      -- ClubMemberships                                   │
     │      UPDATE "CMS"."ClubMemberships"                       │
     │      SET "FirstActivationDate" = "ActivatedAt",           │
     │          "LastActivationDate" = "ActivatedAt",            │
     │          "FirstPackageId" = base_pkg_id,                  │
     │          "LastPackageId" = base_pkg_id                    │
     │      WHERE "FirstActivationDate" IS NULL;                 │
     │                                                           │
     │      -- ClubMembershipCycles                              │
     │      UPDATE "CMS"."ClubMembershipCycles"                  │
     │      SET "PackageId" = base_pkg_id                        │
     │      WHERE "PackageId" IS NULL;                           │
     │                                                           │
     │      -- WeeklyCommissionPools                             │
     │      UPDATE "CMS"."WeeklyCommissionPools"                 │
     │      SET "PackageId" = base_pkg_id                        │
     │      WHERE "PackageId" IS NULL;                           │
     │                                                           │
     │      -- UserCommissionPayouts                             │
     │      UPDATE "CMS"."UserCommissionPayouts"                 │
     │      SET "PackageId" = base_pkg_id                        │
     │      WHERE "PackageId" IS NULL;                           │
     │                                                           │
     │      -- NetworkWeeklyBalances                             │
     │      UPDATE "CMS"."NetworkWeeklyBalances"                 │
     │      SET "PackageId" = base_pkg_id                        │
     │      WHERE "PackageId" IS NULL;                           │
     │                                                           │
     │      RAISE NOTICE 'Migration done: PackageId=%',          │
     │          base_pkg_id;                                     │
     │  END $$;                                                  │
     └──────────────────────────────────────────────────────────┘

2.4  Verification — بررسی migration
     ┌──────────────────────────────────────────────────────────┐
     │  SELECT 'ClubMemberships' AS tbl, COUNT(*)               │
     │  FROM "CMS"."ClubMemberships"                            │
     │  WHERE "LastPackageId" IS NULL                            │
     │  UNION ALL                                                │
     │  SELECT 'Cycles', COUNT(*)                                │
     │  FROM "CMS"."ClubMembershipCycles"                       │
     │  WHERE "PackageId" IS NULL                                │
     │  UNION ALL                                                │
     │  SELECT 'Pools', COUNT(*)                                 │
     │  FROM "CMS"."WeeklyCommissionPools"                      │
     │  WHERE "PackageId" IS NULL                                │
     │  UNION ALL                                                │
     │  SELECT 'Payouts', COUNT(*)                               │
     │  FROM "CMS"."UserCommissionPayouts"                      │
     │  WHERE "PackageId" IS NULL                                │
     │  UNION ALL                                                │
     │  SELECT 'Balances', COUNT(*)                              │
     │  FROM "CMS"."NetworkWeeklyBalances"                      │
     │  WHERE "PackageId" IS NULL;                               │
     │                                                           │
     │  -- ✅ همه باید 0 باشند!                                  │
     └──────────────────────────────────────────────────────────┘

2.5  Seed پکیج نقره‌ای (اگر توسط EF Seed انجام نشده)
     └── INSERT پکیج نقره‌ای + PackageFeatures

2.5b اجرای Migration دوم: Q27_HistoryTables_And_RenameWalletHistory
     └── این migration شامل:
         • RenameTable: UserWalletChangeLogs → UserWalletHistories (حفظ داده‌ها!)
         • RenameIndex × 2 + sp_rename PK + FK × 2
         • CreateTable: PackageHistories (فیلدهای Old*/New*)
         • CreateTable: ClubMembershipCycleHistories (فیلدهای Old*/New*)
     ⚠️ داده‌های قبلی UserWalletChangeLogs حفظ می‌شوند (RenameTable نه DropTable)

2.6  Deploy CMS به Kubernetes
     └── kubectl set image deployment/cms cms=cms:package-based
     └── kubectl rollout status deployment/cms

2.7  Health Check
     └── curl http://cms-service/health
     └── بررسی لاگ‌ها: kubectl logs deployment/cms --tail=100

Checkpoint: CMS جدید بالا آمده، migration اجرا شده، همه رکوردها PackageId دارند.


مرحله ۳ از ۶: استقرار FrontOffice 🖥️

⏱️ تخمین: ۲۰ دقیقه
پیش‌نیاز: CMS باید بالا و سالم باشد

3.1  Build FrontOffice Docker image
     └── cd FrontOffice/src
     └── docker build -t fo:package-based .

3.2  Deploy به Kubernetes
     └── kubectl set image deployment/frontoffice fo=fo:package-based
     └── kubectl rollout status deployment/frontoffice

3.3  Smoke Test
     └── ✅ صفحه پکیج‌ها باز می‌شود (کاشی‌های داینامیک)
     └── ✅ جزئیات پکیج — فیچرها نمایش داده می‌شود
     └── ✅ صفحه پاداش‌ها — فیلتر پکیج کار می‌کند
     └── ✅ صفحه تعادل‌ها — per-package نمایش داده می‌شود
     └── ✅ متن قرارداد — مبلغ داینامیک (نه ۵۶M hardcoded)

Checkpoint: FrontOffice جدید بالا آمده و صفحات اصلی کار می‌کنند.


مرحله ۴ از ۶: استقرار BackOffice 🛠️

⏱️ تخمین: ۲۰ دقیقه
پیش‌نیاز: CMS باید بالا و سالم باشد

4.1  Build BackOffice Docker image
     └── cd BackOffice/src
     └── docker build -t bo:package-based .

4.2  Deploy به Kubernetes
     └── kubectl set image deployment/backoffice bo=bo:package-based
     └── kubectl rollout status deployment/backoffice

4.3  Smoke Test
     └── ✅ CRUD پکیج — ۱۲ فیلد جدید نمایش داده می‌شود
     └── ✅ ماتریس فیچر — checkboxها load می‌شوند
     └── ✅ گزارش تعادل‌ها — فیلتر پکیج کار می‌کند
     └── ✅ گزارش پرداخت‌ها — ستون پکیج نمایش داده می‌شود
     └── ✅ ManualPayment — مبلغ editable (نه ۵۶M disabled)

Checkpoint: BackOffice جدید بالا آمده و CRUD + گزارشات کار می‌کنند.


مرحله ۵ از ۶: بررسی پورسانت (بحرانی!) 💰

⏱️ تخمین: ۳۰ دقیقه
⚠️ پورسانت = پول واقعی — دقت مضاعف لازم است

5.1  بررسی SP پارامترها
     └── محاسبه پورسانت هفته تستی (staging)
     └── بررسی: هر پکیج Pool جداگانه دارد
     └── بررسی: MaxBalancesPerLeg صحیح (پایه=۳۰۰, نقره‌ای=۳۰)
     └── بررسی: MaxNetworkLevel صحیح

5.2  مقایسه نتایج
     └── اجرای محاسبه در staging
     └── مقایسه Pool مبلغ با محاسبه دستی
     └── ✅ تفاوت < ۱% قابل قبول

5.3  بررسی carryover
     └── ✅ carryover فقط per-package
     └── ✅ تغییر پکیج → ریست carryover

Checkpoint: محاسبات پورسانت per-package صحیح هستند.


مرحله ۶ از ۶: تنظیمات نهایی و بررسی سلامت

⏱️ تخمین: ۱۵ دقیقه

6.1  بررسی PackageFeatures seed شده‌اند
     └── SELECT * FROM "CMS"."PackageFeatures";
     └── پکیج پایه: همه فیچرها ✅
     └── پکیج نقره‌ای: فیچرهای تعیین‌شده ✅

6.2  بررسی JWT Claims (اختیاری)
     └── لاگین یک کاربر تست → decode JWT
     └── ✅ PackageId وجود دارد
     └── ✅ CanRepurchase صحیح

6.3  غیرفعال کردن Maintenance Mode (اگر فعال بود)

6.4  مانیتورینگ ۲۴ ساعته
     └── بررسی لاگ خطاها
     └── بررسی response timeها
     └── بررسی پرداخت‌های جدید

مهاجرت تکمیل شد!


6. Rollback Plan

سناریو ۱: مشکل در Migration دیتابیس

# Restore از بکاپ
pg_restore -d cms_db cms_backup_pre_package_migration.dump

# Rollback CMS image
kubectl set image deployment/cms cms=cms:rollback-point

سناریو ۲: مشکل در CMS (بعد Migration موفق)

# ⚠️ نکته: migration undo ممکن نیست (ستون‌های جدید اضافه شده‌اند)
# اما کد قدیمی با ستون‌های nullable مشکلی ندارد

# Rollback فقط CMS image
kubectl set image deployment/cms cms=cms:rollback-point

سناریو ۳: مشکل در FO/BO

# FO و BO مستقل از هم هستند — هرکدام جداگانه rollback
kubectl set image deployment/frontoffice fo=fo:rollback-point
kubectl set image deployment/backoffice bo=bo:rollback-point

نکته مهم Rollback:

  • ستون‌های جدید nullable هستند → کد قدیمی بدون مشکل کار می‌کند
  • جدول PackageFeatures جدید است → کد قدیمی آن را ignore می‌کند
  • فقط Data Backfill غیرقابل‌برگشت است (ولی ضرری ندارد — فقط NULL → مقدار)

7. چک‌لیست تست قبل از Production

🛒 خرید و فعال‌سازی

# تست روش نتیجه مورد انتظار
1 خرید پکیج نقره‌ای (ZarinPal) از FO → پکیج‌ها → نقره‌ای → پرداخت Balance = ۵.۶M, Discount = ۱۱.۲M
2 خرید پکیج پایه (ZarinPal) از FO → پکیج‌ها → پایه → پرداخت Balance = ۵۶M, Discount = ۱۱۲M
3 خرید پکیج پایه (Daya Loan) از FO → پکیج‌ها → پایه → دایا Balance = ۵۶M + loan created
4 پرداخت دستی (BO) از BO → ManualPayment → مبلغ دلخواه Amount editable, not hardcoded
5 فعال‌سازی با نقره‌ای فعال‌سازی باشگاه بعد خرید نقره‌ای فقط فیچرهای نقره‌ای فعال (نه همه)
6 فعال‌سازی با پایه فعال‌سازی باشگاه بعد خرید پایه همه فیچرها فعال

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

# تست نتیجه مورد انتظار
7 تکمیل چرخه Magic → ریست PackagePurchaseMethod = None
8 خرید مجدد همان پکیج بدون قرارداد مجدد، فقط شارژ wallet
9 خرید مجدد پکیج متفاوت (پایه → نقره‌ای) DIFF اجرا: فیچرهای اضافی غیرفعال

💰 پورسانت per-package

# تست نتیجه مورد انتظار
10 Pool جداگانه هر پکیج WeeklyCommissionPool با PackageId متفاوت
11 MaxBalancesPerLeg متفاوت پایه=۳۰۰, نقره‌ای=۳۰
12 Carryover per-downline-package تغییر پکیج خود کاربر → carryover حفظ (بر اساس زیرمجموعه‌ها)
13 SP پارامترها از Package بدون hardcoded ۳۰۰/۱۵

📊 گزارشات per-package

# تست نتیجه مورد انتظار
14 FO — فیلتر dropdown پکیج فیلتر عملکرد صحیح
15 FO — breakdown پاداش per-package مبالغ صحیح به تفکیک
16 BO — فیلتر پکیج در تعادل‌ها فیلتر عملکرد صحیح
17 BO — ستون پکیج در پرداخت‌ها نام پکیج نمایش داده می‌شود

📋 UI / قرارداد

# تست نتیجه مورد انتظار
18 متن قرارداد — مبلغ داینامیک مبلغ و نام پکیج صحیح (نه ۵۶M hardcoded)
19 ActivationSection — قیمت از API خوانده می‌شود
20 BO — ManualPayment editable مبلغ قابل ویرایش با validation
21 BO — Package CRUD ۱۲ فیلد همه فیلدهای جدید ذخیره/بارگذاری
22 BO — Feature Matrix checkboxها sync با DB

8. ریسک‌ها و نکات بحرانی

🔴 ریسک‌های بحرانی

# ریسک احتمال تاثیر کاهش‌دهنده
R1 Migration دیتابیس — PackageId اشتباه کم فاجعه Verification query (مرحله 2.4) + بکاپ
R2 SP تغییریافته → محاسبات مالی اشتباه متوسط فاجعه تست staging + مقایسه دستی
R3 Magic Wallet EXIT — سقف global به‌جای per-package متوسط بالا بررسی MW1-MW3 در CMS handlers
R4 قرارداد حقوقی — مبلغ اشتباه کم حقوقی متن قرارداد داینامیک فیکس شده

🟡 ریسک‌های متوسط

# ریسک کاهش‌دهنده
R5 Proto breaking change Field numberها backward compatible (فقط اضافه)
R6 NuGet version mismatch بین repos همه روی v0.0.189
R7 JWT claims — cache invalidation کاربران باید re-login کنند
R8 Validator hardcoded 1B فیکس شد — SystemConstants.WalletMaxSafeAmount (10B) حصار ایمنی

⚠️ تغییرات آینده (هنوز پیاده‌نشده — Phase بعدی)

این موارد در BIZ spec شناسایی شده‌اند ولی هنوز پیاده نشده‌اند:

# مورد شدت شرح
F1 WalletChangeLog + PackageId انجام‌شده CMS:e5bc3a9 — PackageId در UserWalletHistory
F2 Notification + PackageId انجام‌شده CMS:61b7e4f — SmsTemplates+IUserNotificationService+UserNotificationService با packageName
F3 Background Services + PackageId بررسی‌شده بدون تغییر — هر ۳ worker از قبل per-package صحیح کار می‌کنند
F4 CSV exports + ستون پکیج انجام‌شده CMS:61b7e4f BO:92c9922 — proto+handler+CSV برای ManualPayments/WithdrawalRequests
F5 SystemConfiguration per-package بررسی‌شده بدون تغییر — مقادیر per-package قبلاً به Package entity منتقل شده‌اند
F6 MagicWalletChargePage hardcoded انجام‌شده CMS:61b7e4f FO:ecc4f44 — magic_multiplier+magic_max_credit از API، داشبورد "شارژ چند‌برابری"
F7 Validators async per-package انجام‌شده CMS:61b7e4f FO:ecc4f44 — SystemConstants.WalletMaxSafeAmount (10B) حصار ایمنی، سقف واقعی per-package در هندلر
F8 آستانه موجودی ورود به Magic (Q24) انجام‌شده CMS:a1024a3Balance <= 1_000_000
F9 SP Worker — مدیریت خودکار SP (Q26) انجام‌شده CMS:a1024a3StoredProcedureDeploymentService
F10 History Tables — یکسان‌سازی + خودکار (Q27) انجام‌شده CMS:fdbb91d+10d2ca2 — IHasHistory + Interceptor + RenameTable migration
F11 UI Guidance — آموزش و هشدار (Q28) انجام‌شده FO:474d364 BO:6939780 — ۱۳ صفحه MudAlert

F1-F11 همه پیاده‌سازی شدند.


ضمیمه: ۳۰ تصمیم بیزینسی (Q1–Q30)

پیاده‌شده (Q1Q23):

# تصمیم وضعیت
Q1 باگ DiscountBalance → فیکس 8b9c317
Q2 ادغام ۳ مسیر پرداخت → Generic ccb938e + 8446e0e
Q3 پکیج نقره‌ای + پایه — داینامیک ae92ab8 + a9cd2fd
Q4 ActivationFee یک فیلد (حذف GiftValue) ae92ab8
Q5 DiscountMultiplier داینامیک 8e5c7c5
Q6 Migration کاربران فعلی → پکیج پایه a9cd2fd
Q7 خرید N بار بعد تکمیل چرخه fe3edd1 + 8e5c7c5
Q8 Commission Pool جدا per-package 607f791
Q9 MagicWallet Multiplier داینامیک 8e5c7c5
Q10 دایا = پکیج پایه (نه طلایی) ccb938e
Q11 فیچرها داینامیک per-package dcd1135
Q12 MaxBalancesPerLeg per-package 607f791
Q13 MaxNetworkLevel per-package 607f791
Q14 MagicWalletMaxDeposit per-package ae92ab8
Q15 MagicWalletMaxCredit per-package ae92ab8
Q16 NetworkWeeklyBalance + PackageId ae92ab8
Q17 گزارش FO breakdown per-package a956cb9
Q18 گزارش BO فیلتر per-package 8be98ae
Q19 قرارداد فقط یک بار 1ac2366
Q20 فیچر DIFF/تفاضل 1ac2366
Q21 First/Last ActivationDate ae92ab8
Q22 تشخیص هفته از LastActivationDate 607f791
Q23 Carryover strictly per-package 607f791

تصمیمات v6 (Q24Q30) — تکمیل‌شده:

# تصمیم وضعیت کامیت
Q24 آستانه موجودی ≤ ۱,۰۰۰,۰۰۰ ریال (ورود Magic + خرید مجدد) CMS:a1024a3
Q25 DayaLoans فقط پکیج پایه — تایید (بدون تغییر کد) تایید
Q26 SP Worker — auto-deploy با checksum (IHostedService) CMS:a1024a3
Q27 History Tables — PackageHistory + CycleHistory + IHasHistory + Interceptor + Rename UserWalletChangeLog→UserWalletHistory CMS:fdbb91d+10d2ca2
Q28 UI Guidance — ۱۳ صفحه MudAlert آموزشی/هشداری در FO/BO FO:474d364 BO:6939780
Q29 شرط EXIT Magic — تایید: آخرین پکیج فعال (بدون تغییر کد) تایید
Q30 Carryover — تایید: توضیح مستند شد (بدون تغییر کد) تایید

آخرین بروزرسانی: ۱۰ اسفند ۱۴۰۴ — v7: F1-F7 همه تکمیل‌شده | ۵۱+ کامیت (۲۱ CMS + ۹ FO + ۷ BO + ۱۴+ docs) | NuGet v0.0.189 | Notifications+PackageName, CSV ستون پکیج, Dynamic MagicWallet, SystemConstants validators