# 📦 راهنمای مهاجرت سیستم پکیج‌بیس — خلاصه تغییرات و پلن استقرار > **وضعیت:** آماده تست و استقرار — **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. [خلاصه اجرایی](#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–Q30) | | باگ‌های فیکس‌شده | **۶ باگ بحرانی** | | مقادیر 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.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 | | **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 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.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 دیتابیس ```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-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:`a1024a3` — `Balance <= 1_000_000` | | ~~F9~~ | ~~SP Worker — مدیریت خودکار SP (Q26)~~ | ✅ انجام‌شده | CMS:`a1024a3` — `StoredProcedureDeploymentService` | | ~~F10~~ | ~~History Tables — یکسان‌سازی + خودکار (Q27)~~ | ✅ انجام‌شده | CMS:`fdbb91d`+`10d2ca2` — IHasHistory + Interceptor + RenameTable migration | | ~~F11~~ | ~~UI Guidance — آموزش و هشدار (Q28)~~ | ✅ انجام‌شده | FO:`474d364` BO:`6939780` — ۱۳ صفحه MudAlert | > ✅ **F1-F11 همه پیاده‌سازی شدند.** --- ## ضمیمه: ۳۰ تصمیم بیزینسی (Q1–Q30) ### پیاده‌شده (Q1–Q23): | # | تصمیم | وضعیت | |---|-------|-------| | 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 (Q24–Q30) — ✅ تکمیل‌شده: | # | تصمیم | وضعیت | کامیت | |---|-------|-------|-------| | 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*