diff --git a/deployment/PACKAGE-MIGRATION-GUIDE.md b/deployment/PACKAGE-MIGRATION-GUIDE.md new file mode 100644 index 0000000..f6a2a82 --- /dev/null +++ b/deployment/PACKAGE-MIGRATION-GUIDE.md @@ -0,0 +1,667 @@ +# 📦 راهنمای مهاجرت سیستم پکیج‌بیس — خلاصه تغییرات و پلن استقرار + +> **وضعیت:** آماده تست و استقرار +> **تاریخ:** ۸ اسفند ۱۴۰۴ (27 Feb 2026) +> **نسخه NuGet:** v0.0.188 +> **تعداد کامیت‌ها:** ۴۳ کامیت در ۴ ریپازیتوری (+ ۴ کامیت مرتبط) +> **مدت پیاده‌سازی:** ۴ روز (۲۴–۲۷ فوریه ۲۰۲۶) +> **ریپوها:** CMS (`gitea`/`kub-stage`) · FrontOffice (`kub-stage`) · BackOffice (`kub-stage`) · totalDoc (`foursatDocs`/`main`) + +--- + +## فهرست مطالب + +1. [خلاصه اجرایی](#1-خلاصه-اجرایی) +2. [چه چیزی تغییر کرده؟ — نمای بیزینسی](#2-چه-چیزی-تغییر-کرده--نمای-بیزینسی) +3. [بخش‌های تحت تاثیر سیستم](#3-بخشهای-تحت-تاثیر-سیستم) +4. [جزئیات تغییرات هر ریپو](#4-جزئیات-تغییرات-هر-ریپو) +5. [پلن مهاجرت مرحله‌به‌مرحله](#5-پلن-مهاجرت-مرحلهبهمرحله) +6. [Rollback Plan](#6-rollback-plan) +7. [چک‌لیست تست قبل از Production](#7-چکلیست-تست-قبل-از-production) +8. [ریسک‌ها و نکات بحرانی](#8-ریسکها-و-نکات-بحرانی) + +--- + +## 1. خلاصه اجرایی + +### قبل (سیستم تک‌پکیج): +- فقط **یک پکیج پایه** (۵۶ میلیون تومان) وجود داشت +- تمام مقادیر مالی (قیمت، هزینه فعال‌سازی، ضرایب، سقف‌ها) **hardcoded** در کد بودند +- خرید مجدد پکیج **غیرممکن** بود (حتی بعد تکمیل چرخه) +- پورسانت فقط از **یک Pool واحد** محاسبه می‌شد +- همه کاربران **همه فیچرها** را دریافت می‌کردند + +### بعد (سیستم چندپکیجی): +- سیستم **N پکیج** با قیمت و ویژگی‌های متفاوت پشتیبانی می‌کند +- تمام مقادیر مالی از **دیتابیس (Package entity)** خوانده می‌شوند +- خرید مجدد بعد تکمیل چرخه Magic Wallet **فعال** شده +- هر پکیج **Commission Pool مستقل** خود را دارد +- فیچرها **per-package** هستند و با الگوریتم **DIFF** مدیریت می‌شوند +- قرارداد باشگاه **فقط یک بار** (اولین خرید) امضا می‌شود + +### آمار تغییرات: + +| شاخص | مقدار | +|-------|-------| +| فایل‌های تغییریافته | **۱۴۱ فایل** | +| خطوط اضافه‌شده | **+۱۰,۸۳۵** | +| خطوط حذف‌شده | **−۲,۱۹۴** | +| تصمیمات بیزینسی پیاده‌شده | **۲۳ تصمیم** (Q1–Q23) | +| باگ‌های فیکس‌شده | **۶ باگ بحرانی** | +| مقادیر hardcoded حذف‌شده | **۱۵+ مورد** | +| Handlerهای deprecated حذف‌شده | **۴ handler** (۱۲ فایل) | +| RPCهای deprecated حذف‌شده | **۴ RPC** + ۸ message type | + +--- + +## 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-package** — تغییر پکیج = ریست carryover (Q23) | +| 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 Logic + Commission + Proto + Cleanup | +| **FrontOffice** | ۸ | ۲۲ | +۴۷۵ | −۱۳۶ | Dynamic UI + Customer RPCs + Per-package Reports | +| **BackOffice** | ۶ | ۱۸ | +۴۹۷ | −۳۱ | Package CRUD + Feature Matrix + Per-package Reports | +| **totalDoc** | ۱۲ | ۸ | +۲,۵۳۲ | −۱۷۲ | مستندات بیزینسی + تکنیکال | + +--- + +## 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.PackageId` → `cm.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 | + +### 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 — قیمت‌گذاری داینامیک | + +### 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 | + +--- + +## 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 cms:rollback-point + └── docker tag fo:rollback-point + └── docker tag 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.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 دیتابیس + +```bash +# 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 موفق) + +```bash +# ⚠️ نکته: migration undo ممکن نیست (ستون‌های جدید اضافه شده‌اند) +# اما کد قدیمی با ستون‌های nullable مشکلی ندارد + +# Rollback فقط CMS image +kubectl set image deployment/cms cms=cms:rollback-point +``` + +### سناریو ۳: مشکل در FO/BO + +```bash +# 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-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.188 ✅ | +| R7 | JWT claims — cache invalidation | کاربران باید re-login کنند | +| R8 | Validator hardcoded 1B | فعلاً anti-abuse cap — قابل قبول | + +### ⚠️ تغییرات آینده (هنوز پیاده‌نشده — Phase بعدی) + +این موارد در BIZ spec شناسایی شده‌اند ولی **هنوز پیاده نشده‌اند**: + +| # | مورد | شدت | شرح | +|---|------|------|------| +| F1 | WalletChangeLog + PackageId | 🟡 | ۲۱ محل ساخت باید PackageId بگیرند | +| F2 | Notification + PackageId | 🟡 | ۴ notification بدون نام پکیج | +| F3 | Background Services + PackageId | 🟡 | ۳ worker بدون فیلتر پکیج | +| F4 | CSV exports + ستون پکیج | 🟡 | ۳ export بدون ستون | +| F5 | SystemConfiguration per-package | 🟡 | تنظیمات global vs per-package | +| F6 | MagicWalletChargePage hardcoded | 🟡 | ×۲.۵ و سقف در ۶ جا FO | +| F7 | Validators async per-package | 🟢 | سقف 1B → مقدار واقعی | + +> **این موارد ریسک عملیاتی ندارند** و می‌توانند در فاز بعدی انجام شوند. سیستم فعلی با fallback مناسب کار می‌کند. + +--- + +## ضمیمه: ۲۳ تصمیم بیزینسی پیاده‌شده + +| # | تصمیم | وضعیت | +|---|-------|-------| +| 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` | + +--- + +*آخرین بروزرسانی: ۸ اسفند ۱۴۰۴ — آماده تست و استقرار*