421a651975
- All 14 totalDoc files updated with Magic Wallet additions - MAGIC-WALLET-PLAN.md: Phase 1-6 checklist fully marked complete - Business docs: Magic Wallet section, commission filter, new entities - Payment docs: VAT 9%→10%, TransactionType 14+15, ZarinPal 4th usage - Technical docs: UserWallet fields, ClubMembershipCycle, gRPC RPCs - Overview docs: Magic flowchart, ER diagram, changelog, glossary, roadmap
229 lines
10 KiB
Markdown
229 lines
10 KiB
Markdown
# 🪄 پلن پیادهسازی کیفپول جادویی
|
||
|
||
> **مرجع:** [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 برای فیلدهای جدید
|
||
├── ClubMembershipCycle.cs → 🆕 entity جدید (حل مشکل تاریخ کمیسیون)
|
||
├── ClubMembershipCycleConfiguration.cs → EF config
|
||
├── Migration: AddMagicWalletFields → dotnet ef migrations add
|
||
└── Migration: AddClubMembershipCycle → dotnet ef migrations add + data seed
|
||
```
|
||
|
||
**تست:** Migration اجرا بشه، فیلدها در DB ایجاد بشن، defaultها درست باشن. هر ClubMembership موجود یه رکورد Cycle=1 داشته باشه.
|
||
|
||
---
|
||
|
||
### فاز ۲ — Trigger ورود/خروج Magic (روز ۲)
|
||
|
||
**هدف:** State Machine خودکار
|
||
|
||
```
|
||
فایلهای تغییری:
|
||
├── SubmitShopBuyOrderCommandHandler.cs
|
||
│ ├── بعد از کسر Balance: check ورود به Magic
|
||
│ └── بعد از کسر Balance: check خروج از Magic
|
||
│
|
||
├── ActivateClubMembershipCommandHandler.cs
|
||
│ ├── ActivatedAt فقط بار اول ست بشه (دیگه overwrite نشه)
|
||
│ └── هر بار یک ClubMembershipCycle جدید اضافه بشه
|
||
│
|
||
├── (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 → خطا ❌
|
||
- ۲ تراکنش + ۱ لاگ ثبت شده ✅
|
||
|
||
---
|
||
|
||
### فاز ۴ — غیرفعالسازی کمیسیون + تاریخ Cycle (روز ۴.۵)
|
||
|
||
**هدف:** کاربرهای Magic از کمیسیون خارج بشن + تاریخ کمیسیون از Cycle بخونه
|
||
|
||
```
|
||
فایلهای تغییری:
|
||
├── CalculateWeeklyBalancesCommandHandler.cs
|
||
│ ├── فیلتر: WHERE wallet.WalletMode != Magic
|
||
│ └── تاریخ: ActivatedAt → ClubMembershipCycle.PackagePurchasedAt
|
||
│
|
||
├── sp_CalculateWeeklyBalances.sql
|
||
│ ├── + JOIN UserWallets WHERE WalletMode = 0
|
||
│ └── WHERE cm.ActivatedAt → cc.PackagePurchasedAt (AND cc.IsCurrentCycle = 1)
|
||
│
|
||
└── WeekRepository (اگه date range query داره)
|
||
└── آپدیت query
|
||
```
|
||
|
||
**تست:**
|
||
- کاربر Magic در محاسبات هفتگی شرکت نکنه ✅
|
||
- کاربر دور ۲ (پکیج مجدد): با تاریخ PackagePurchasedAt جدید امتیاز بگیره ✅
|
||
- تاریخ اصلی ActivatedAt تغییر نکرده باشه ✅
|
||
|
||
---
|
||
|
||
### فاز ۵ — صفحات 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 پیادهسازی
|
||
|
||
- [x] **فاز ۱:** WalletMode enum
|
||
- [x] **فاز ۱:** UserWallet entity + 5 فیلد جدید
|
||
- [x] **فاز ۱:** ClubMembershipCycle entity (جدید)
|
||
- [x] **فاز ۱:** TransactionType + 2 مقدار
|
||
- [x] **فاز ۱:** SystemConstants + 3 ثابت
|
||
- [x] **فاز ۱:** EF Configuration (UserWallet + ClubMembershipCycle)
|
||
- [ ] **فاز ۱:** Migration: AddMagicWalletFields
|
||
- [ ] **فاز ۱:** Migration: AddClubMembershipCycle + data seed
|
||
- [x] **فاز ۲:** Trigger ورود Magic (SubmitShopBuyOrder)
|
||
- [x] **فاز ۲:** Trigger خروج Magic
|
||
- [x] **فاز ۲:** ActivateClubMembership → ActivatedAt نگهداشته بشه + Cycle جدید
|
||
- [x] **فاز ۲:** PurchaseCycleCount
|
||
- [x] **فاز ۳:** InitiateMagicChargeCommand + Handler
|
||
- [x] **فاز ۳:** VerifyMagicChargeCommand + Handler
|
||
- [x] **فاز ۳:** MagicWalletController (HTTP callback)
|
||
- [x] **فاز ۳:** gRPC Proto + Service
|
||
- [x] **فاز ۳:** ۲ تراکنش + ۱ لاگ (اجباری)
|
||
- [x] **فاز ۴:** فیلتر کمیسیون Magic (C# handler + SP)
|
||
- [x] **فاز ۴:** تاریخ کمیسیون: ActivatedAt → Cycle.PackagePurchasedAt (C# + SP)
|
||
- [x] **فاز ۵:** MagicWallet.razor
|
||
- [x] **فاز ۵:** MagicPaymentCallback — نتیجه پرداخت از طریق ?payment= query param در همان MagicWallet.razor هندل میشه
|
||
- [x] **فاز ۵:** WalletService gRPC client
|
||
- [x] **فاز ۵:** Profile + Wallet page updates
|
||
- [x] **فاز ۶:** Daya restriction (CheckAndProcessDayaLoansCommandHandler + FO Purchase UI)
|
||
- [x] **فاز ۶:** Club activation restriction (ActivateClubMembershipCommandHandler + WalletMode guard)
|
||
|
||
---
|
||
|
||
## وابستگی مستقل: تکمیل 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
|
||
```
|