docs: add comprehensive Package Migration Guide — business impact + step-by-step deployment plan

This commit is contained in:
masoodafar-web
2026-02-27 03:00:23 +03:30
parent df1affa46e
commit ba10b6485b
+667
View File
@@ -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** مدیریت می‌شوند
- قرارداد باشگاه **فقط یک بار** (اولین خرید) امضا می‌شود
### آمار تغییرات:
| شاخص | مقدار |
|-------|-------|
| فایل‌های تغییریافته | **۱۴۱ فایل** |
| خطوط اضافه‌شده | **+۱۰,۸۳۵** |
| خطوط حذف‌شده | **−۲,۱۹۴** |
| تصمیمات بیزینسی پیاده‌شده | **۲۳ تصمیم** (Q1Q23) |
| باگ‌های فیکس‌شده | **۶ باگ بحرانی** |
| مقادیر 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 <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.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` |
---
*آخرین بروزرسانی: ۸ اسفند ۱۴۰۴ — آماده تست و استقرار*