Files
docs/roadmap/MAGIC-WALLET-PLAN.md
T
masoodafar-web 2f0d43aa81 docs: clarify Magic Wallet cap is per-cycle, not lifetime
- Cap resets every time user buys a new 56M package and re-enters Magic
- MagicTotalDeposited & MagicTotalCredited reset to 0 on each new cycle
- Added multi-cycle example (cycle 1, 2, 3... ∞)
- Added section 4.3: reset behavior on re-entry
- Added test scenarios for cycle reset
- PurchaseCycleCount tracks cycles but has no limit
2026-02-19 02:17:10 +03:30

208 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🪄 پلن پیاده‌سازی کیف‌پول جادویی
> **مرجع:** [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
```