# 🪄 پلن پیاده‌سازی کیف‌پول جادویی > **مرجع:** [MAGIC-WALLET-SPEC](./MAGIC-WALLET-SPEC.md) > **تخمین کل:** ~۷ روز کاری > **وابستگی مستقل:** تکمیل ChargeDiscountWallet (ربطی به جادویی ندارد) --- ## فازبندی ### فاز ۱ — مدل داده و Migration (روز ۱) **هدف:** زیرساخت دیتابیس و enum‌ها ``` فایل‌های تغییری: ├── UserWallet.cs → + WalletMode, MagicTotalDeposited, MagicTotalCredited, MagicActivatedAt, MagicCompletedAt ├── WalletMode.cs → enum جدید (Normal=0, Magic=1) ├── TransactionType.cs → + MagicWalletDeposit=14, MagicWalletBonus=15 ├── SystemConstants.cs → + MagicWalletMultiplier, MagicWalletMaxDeposit, MagicWalletMaxCredit ├── UserWalletConfiguration.cs → EF config برای فیلدهای جدید └── Migration: AddMagicWalletFields → dotnet ef migrations add ``` **تست:** Migration اجرا بشه، فیلدها در DB ایجاد بشن، default‌ها درست باشن. --- ### فاز ۲ — Trigger ورود/خروج Magic (روز ۲) **هدف:** State Machine خودکار ``` فایل‌های تغییری: ├── SubmitShopBuyOrderCommandHandler.cs │ ├── بعد از کسر Balance: check ورود به Magic │ └── بعد از کسر Balance: check خروج از Magic │ ├── (Optional) Domain Event: WalletModeChangedEvent │ └── برای لاگ و نوتیفیکیشن │ └── User.cs (یا UserWallet) └── + PurchaseCycleCount (int) — تعداد دور خرید پکیج ``` **تست:** - سناریو ۱: Balance=0 بعد از خرید → WalletMode=Magic ✅ - سناریو ۲: بدون پکیج + Balance=0 → نباید Magic بشه ❌ - سناریو ۳: Magic + Balance=0 + TotalDeposited>0 → خروج ✅ - سناریو ۴: خروج از Magic → خرید مجدد پکیج → Balance=0 → Magic مجدد با **سقف ریست‌شده** ✅ - سناریو ۵: دور دوم → TotalDeposited, TotalCredited = 0 (ریست) ✅ --- ### فاز ۳ — API شارژ جادویی (روز ۳-۴) **هدف:** مسیر کامل شارژ از درگاه با ضریب ×2.5 ``` فایل‌های جدید: ├── InitiateMagicChargeCommand.cs ├── InitiateMagicChargeCommandHandler.cs ├── InitiateMagicChargeCommandValidator.cs ├── VerifyMagicChargeCommand.cs ├── VerifyMagicChargeCommandHandler.cs ├── MagicWalletController.cs → GET /api/wallet/verify-magic-charge │ ├── userwallet.proto → + InitiateMagicCharge, GetMagicWalletStatus RPCs └── UserWalletService.cs → implement new RPCs نکات مهم: ├── هر شارژ = ۲ تراکنش (Deposit + Bonus) ├── هر شارژ = ۱ WalletChangeLog (اجباری) ├── Validation: WalletMode==Magic && TotalDeposited+Amount <= Cap └── Callback: /api/wallet/verify-magic-charge → redirect FrontOffice ``` **تست:** - واریز 10M → Balance += 25M, TotalDeposited += 10M ✅ - واریز بیشتر از سقف → خطا ❌ - واریز در Normal Mode → خطا ❌ - ۲ تراکنش + ۱ لاگ ثبت شده ✅ --- ### فاز ۴ — غیرفعال‌سازی کمیسیون (روز ۴.۵) **هدف:** کاربرهای Magic از کمیسیون خارج بشن ``` فایل‌های تغییری: ├── CalculateWeeklyBalancesCommandHandler.cs │ └── فیلتر: WHERE wallet.WalletMode != Magic │ └── (یا SP اگه از Stored Procedure استفاده میکنه) └── sp_CalculateWeeklyBalances → + JOIN UserWallets WHERE WalletMode = 0 ``` **تست:** کاربر Magic در محاسبات هفتگی شرکت نکنه ✅ --- ### فاز ۵ — صفحات FrontOffice (روز ۵-۶) **هدف:** UI شارژ جادویی + نمایش وضعیت ``` فایل‌های جدید: ├── Pages/Profile/MagicWallet.razor → فرم شارژ + پروگرس‌بار سقف ├── Pages/Profile/MagicWallet.razor.cs → code-behind ├── Pages/Profile/MagicPaymentCallback.razor → نتیجه پرداخت └── Pages/Profile/MagicPaymentCallback.razor.cs فایل‌های تغییری: ├── WalletService.cs → + InitiateMagicChargeAsync, GetMagicWalletStatusAsync ├── RouteConstants.cs → + MagicWallet, MagicPaymentCallback ├── Pages/Profile/Index.razor → بنر Magic Mode ├── Pages/Profile/Wallet.razor → پروگرس سقف + لینک شارژ └── NavMenu / Sidebar → لینک شرطی به صفحه جادویی ``` **UI شارژ جادویی:** ``` ┌──────────────────────────────────────────────┐ │ 🪄 کیف‌پول جادویی │ │ │ │ وضعیت: فعال ✅ │ │ مجموع واریزی: 30M / 100M تومان │ │ ██████████░░░░░░░░░░░░░░░░░░░░ 30% │ │ مجموع اعتبار دریافتی: 75M تومان │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ مبلغ واریز: [________] تومان │ │ │ │ اعتبار دریافتی: 0 × 2.5 = 0 تومان │ │ │ │ باقیمانده سقف: 70M تومان │ │ │ │ │ │ │ │ [ 🔒 پرداخت از درگاه ] │ │ │ └──────────────────────────────────────┘ │ └──────────────────────────────────────────────┘ ``` --- ### فاز ۶ — محدودیت خرید مجدد پکیج (روز ۷) **هدف:** بعد از Magic فقط IPG مجاز باشه ``` فایل‌های تغییری: ├── CheckAndProcessDayaLoansCommandHandler.cs │ └── if PurchaseCycleCount > 0 → reject │ ├── Package Purchase UI (FrontOffice) │ └── if PurchaseCycleCount > 0 → hide Daya button │ └── ActivateClubMembershipCommandHandler.cs └── if WalletMode == Magic → "ابتدا جادویی تمام شود" ``` --- ## Checklist پیاده‌سازی - [ ] **فاز ۱:** WalletMode enum - [ ] **فاز ۱:** UserWallet entity + 5 فیلد جدید - [ ] **فاز ۱:** TransactionType + 2 مقدار - [ ] **فاز ۱:** SystemConstants + 3 ثابت - [ ] **فاز ۱:** EF Configuration - [ ] **فاز ۱:** Migration - [ ] **فاز ۲:** Trigger ورود Magic (SubmitShopBuyOrder) - [ ] **فاز ۲:** Trigger خروج Magic - [ ] **فاز ۲:** PurchaseCycleCount - [ ] **فاز ۳:** InitiateMagicChargeCommand + Handler - [ ] **فاز ۳:** VerifyMagicChargeCommand + Handler - [ ] **فاز ۳:** MagicWalletController (HTTP callback) - [ ] **فاز ۳:** gRPC Proto + Service - [ ] **فاز ۳:** ۲ تراکنش + ۱ لاگ (اجباری) - [ ] **فاز ۴:** فیلتر کمیسیون (C# یا SP) - [ ] **فاز ۵:** MagicWallet.razor - [ ] **فاز ۵:** MagicPaymentCallback.razor - [ ] **فاز ۵:** WalletService gRPC client - [ ] **فاز ۵:** Profile + Wallet page updates - [ ] **فاز ۶:** Daya restriction - [ ] **فاز ۶:** Club activation restriction --- ## وابستگی مستقل: تکمیل ChargeDiscountWallet > ⚠️ **این کار ربطی به کیف‌پول جادویی ندارد** و باید مستقل انجام شود. ``` مشکل فعلی: ├── ChargeDiscountWalletCommandHandler — CQRS handler موجوده ✅ ├── VerifyDiscountWalletChargeCommandHandler — موجوده ✅ ├── Callback URL = "/api/wallet/verify-discount-charge" — ست شده ├── HTTP Controller endpoint — ❌ وجود ندارد ├── gRPC RPC — ❌ در proto تعریف نشده └── FrontOffice page — ❌ صفحه شارژ وجود ندارد کار لازم: ├── WalletController.cs → GET /api/wallet/verify-discount-charge ├── userwallet.proto → rpc ChargeDiscountWallet ├── UserWalletService.cs → implement RPC ├── FrontOffice → صفحه شارژ DiscountBalance + callback └── تست end-to-end ```