Files
docs/roadmap/MAGIC-WALLET-PLAN.md
T
masoodafar-web b1dd69b31f docs: fix Magic Mode exit condition — BOTH Balance=0 AND cap reached
- Exit requires BOTH simultaneously: Balance==0 AND TotalDeposited>=100M
- If Balance=0 but cap not reached → still Magic (can charge more)
- If cap reached but Balance>0 → still Magic (can spend more)
- Added examples: partial deposit (50M/100M) stays in Magic
- Fixed handler 8.1 pseudo-code
- Updated test scenarios in PLAN (7 scenarios covering edge cases)
2026-02-19 02:32:04 +03:30

210 lines
8.9 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=50M** (سقف پر نشده) → **هنوز Magic!** نباید خارج بشه ❌
- سناریو ۴: Magic + Balance=0 + **TotalDeposited=100M** (سقف پر) → خروج ✅
- سناریو ۵: Magic + Balance=30M + TotalDeposited=100M → **هنوز Magic!** (بالانس داره) ❌
- سناریو ۶: خروج از 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
```