f5173a4def
- CHANGELOG: اضافه Phase 18 BackOffice + فعالسازی درگاه FO + تنظیمات محیطی CMS + مرج پروداکشن - PAYMENT-FINANCE: MerchantId واقعی + تنظیمات محیطی Staging/Production + Magic فاز 6 کامل - ROADMAP: بروزرسانی درصدها + Magic 100% + Payment 97% + DONE section - TECH-02: ساختار فولدر + UserAutoComplete + WalletManagement + دکمههای درگاه - BUSINESS-01: Magic Wallet فاز 1-6 کامل - TECH-04: Migration پروداکشن ExpandDiscountProductFullInformation + حذف u21 تکراری - TECH-03: جزئیات مرج پروداکشن + تنظیمات appsettings.Production.json - MAGIC-WALLET-PLAN: وضعیت کامل - INDEX: بروزرسانی تاریخ
10 KiB
10 KiB
🏆 سیستم باشگاه، کمیسیون و درخت شبکهای
منابع ادغامشده:
club-commission-system-complete.md,balance-calculation-rules.md,club-membership-contract-system.md
آخرین بروزرسانی: اسفند ۱۴۰۴ (بروزرسانی: Magic Wallet کامل + بهبود مدیریت اعضا)
۱. مفاهیم کلیدی
| مفهوم | توضیح |
|---|---|
| عضویت باشگاه | خرید پکیج طلایی (۵۶M) → فعالسازی (۲۵.۲M) → عضو فعال باشگاه |
| درخت باینری | هر کاربر حداکثر ۲ فرزند مستقیم (چپ/راست) — بدون محدودیت عمق |
| کمیسیون هفتگی | محاسبه بر اساس تعادل چپ/راست — یکشنبه ۰۰:۰۵ (Hangfire cron) |
| ۳ کیف پول | Balance (نقدی) + NetworkBalance (طلایی/کمیسیون) + DiscountBalance (اعتباری) |
| کیفپول جادویی | وقتی Balance=0 → حالت Magic فعال → شارژ ×2.5 → سقف 100M/دور |
| چرخه عضویت | ClubMembershipCycle — هر خرید پکیج = یک دور جدید (برای تاریخ کمیسیون) |
۲. ساختار درخت باینری
graph TD
ROOT["Root"] --- L["Left"]
ROOT --- R["Right"]
L --- L1["L1"] & L2["L2"]
R --- R1["R1"] & R2["R2"]
L1 --- L1a["..."] & L1b["..."]
L2 --- L2a["..."] & L2b["..."]
R1 --- R1a["..."] & R1b["..."]
R2 --- R2a["..."] & R2b["..."]
← بدون محدودیت عمق
قوانین:
- هر نود حداکثر ۲ فرزند (Binary) —
MaxDirectChildrenPerLeg = 1 - جایگذاری:
LegPosition∈ {Left=0, Right=1} (enumNetworkLeg) - مدل شبکه مستقیم روی entity
User— فیلدهایNetworkParentId,LegPosition,NetworkChildren - محاسبه کمیسیون تا عمق ۱۵ سطح (
CommissionMaxNetworkLevel = 15) — اما درخت بدون محدودیت رشد میکند
۳. فلوی عضویت و فعالسازی
flowchart TD
A["خرید پکیج طلایی — 56M"] --> B["نمایش مودال قرارداد\nغیرقابلبستهشدن"]
B --> C["مشاهده متن قرارداد\nReadContract RPC"]
C --> D["درخواست OTP\nRequestContractOtp — Kavenegar"]
D --> E["وارد کردن کد\nVerifyContractOtp"]
E --> F["امضای قرارداد\nAcceptContract"]
F --> G["شارژ ۲ کیفپول\nBalance += 56M\nDiscountBalance += 112M"]
F --> H["کسر فعالسازی\n−25.2M از Balance"]
F --> I["واریز 25.2M\nبه Pool هفتگی"]
F --> J["قرارگیری در\nدرخت باینری"]
F --> K["رفرش JWT Token\nclaims جدید"]
⚠️ در خرید با وام دایا: Balance += 56M, DiscountBalance += 112M (DayaLoanAmount × 2)
NetworkBalance شارژ نمیشود — فقط برای کمیسیون
۴. الگوریتم محاسبه کمیسیون هفتگی
۴.۱ فرمول ۴ مرحلهای
مرحله ۱: جمع فروش هر پا
SumLeft = Σ(فروشهای پای چپ در هفته جاری + CanOverLeft)
SumRight = Σ(فروشهای پای راست در هفته جاری + CanOverRight)
مرحله ۲: محاسبه تعادل
WeeklyBalance = MIN(SumLeft, SumRight)
مرحله ۳: محاسبه باقیمانده (Carryover)
CanOverLeft = SumLeft - WeeklyBalance
CanOverRight = SumRight - WeeklyBalance
مرحله ۴: سقف هفتگی
IF WeeklyBalance > 300 → WeeklyBalance = 300
IF CanOverLeft > 300 → Flush (CanOverLeft = 0)
IF CanOverRight > 300 → Flush (CanOverRight = 0)
۴.۲ مثال عددی (درخت ۵ سطحی)
هفته ۱: چپ=120, راست=80 → Balance=80, Over(L=40, R=0)
هفته ۲: چپ=90+40=130, راست=150 → Balance=130, Over(L=0, R=20)
هفته ۳: چپ=200, راست=180+20=200 → Balance=200, Over(L=0, R=0)
هفته ۴: چپ=500, راست=100 → Balance=100, Over(L=400→FLUSH=0, R=0)
۴.۳ Pool هفتگی و توزیع
flowchart LR
A["هر فعالسازی عضو\n25.2M واریز"] --> B["Pool هفتگی"]
B --> C["sp_CalculateWeeklyBalances"]
C --> D["sp_CalculateWeeklyCommissionPool"]
D --> E["توزیع بر اساس\nUserBalance / TotalBalance"]
فرمت هفته:
YYYY-Www(شمسی، شنبهپایه)
۴.۴ فیلتر کاربران Magic از کمیسیون
⚠️ کاربرانی که در حالت Magic هستند (
WalletMode = 1) از محاسبات کمیسیون هفتگی خارج میشوند.
فیلتر در ۳ نقطه:
✅ CalculateWeeklyBalancesCommandHandler.cs → WHERE wallet.WalletMode != Magic
✅ OrmCommissionCalculationStrategy.cs → فیلتر LINQ
✅ sp_CalculateWeeklyBalances.sql → NOT EXISTS (WalletMode=1)
تاریخ محاسبه:
قبل: ClubMembership.ActivatedAt (مشکل: بعد از خرید مجدد overwrite میشد)
بعد: ClubMembershipCycle.PackagePurchasedAt (هر دور تاریخ مستقل)
۵. تنظیمات سیستمی (SystemConstants)
| ثابت (SystemConstants) | مقدار | توضیح |
|---|---|---|
ClubActivationFee |
25,200,000 | هزینه فعالسازی (ریال) |
ClubMembershipGiftValue |
25,200,000 | واریز به Pool |
BasePackageAmount |
56,000,000 | قیمت پکیج طلایی (ریال) |
DayaLoanAmount |
56,000,000 | مبلغ وام دایا (ریال) |
CommissionMaxWeeklyBalancesPerLeg |
300 | سقف هفتگی هر پا |
CommissionMaxNetworkLevel |
15 | عمق محاسبه کمیسیون (نه محدودیت درخت) |
MaxDirectChildrenPerLeg |
1 | حداکثر فرزند مستقیم هر پا |
MinimumWithdrawAmount |
1,000,000 | حداقل مبلغ برداشت (ریال) |
ShopVAT |
0.1 (10%) | مالیات ارزش افزوده |
MagicWalletMultiplier |
2.5 | ضریب شارژ جادویی (واریز × 2.5) |
MagicWalletMaxDeposit |
1,000,000,000 | سقف واریز هر دور (100M تومان = 1B ریال) |
MagicWalletMaxCredit |
2,500,000,000 | سقف اعتبار هر دور (250M تومان) |
CommissionCalculationMethod |
"SP" | روش محاسبه = Stored Procedure |
۶. ۳ سناریوی خرید پکیج طلایی
| سناریو | فلو | وضعیت |
|---|---|---|
| وام دایا | درخواست وام → تأیید → Balance=56M + Discount=112M (مجموع ۱۶۸M) | ✅ پیادهشده |
| درگاه مستقیم | IPG → callback → Balance=56M + Discount=112M (مجموع ۱۶۸M) | ✅ پیادهشده |
| پرداخت دستی | کارتبهکارت → آپلود رسید → تأیید ادمین → شارژ | ⚠️ طراحیشده |
۷. یکپارچهسازی وام دایا
flowchart TD
A["Hangfire Worker\nهر ۲۰ دقیقه — */20 * * * *"] --> B["بررسی درخواستهای pending"]
B --> C["ارسال به API دایا\nMock/Real switchable"]
C --> D["دریافت نتیجه"]
D --> E["Balance += 56M"]
D --> F["DiscountBalance += 112M\nDayaLoanAmount × 2"]
مجموع شارژ: 168M — Hangfire retry:
[AutomaticRetry(Attempts = 3)]
۸. Chatika AI — اولین فیچر باشگاه
| آیتم | جزئیات |
|---|---|
| نوع | Hangfire recurring job |
| فرکانس | هر ۵ دقیقه |
| Retry | Polly — ۳ تلاش، backoff نمایی |
| فعالسازی | فقط برای اعضای فعال باشگاه |
| وضعیت | ✅ Production ready |
۹. کیفپول جادویی (Magic Wallet) ✅
وضعیت: فاز ۱ تا ۵ پیادهسازی شده — فاز ۶ باقیمانده
مرجع کامل: MAGIC-WALLET-SPEC
۹.۱ چرخه کامل
flowchart TD
A["خرید پکیج 56M\nBalance=56M, Discount=112M"] --> B["خرید از فروشگاه\nBalance کم میشود"]
B --> C{"Balance = 0?"}
C -->|خیر| B
C -->|بله| D{"عضو باشگاه فعال؟"}
D -->|خیر| E["حالت عادی باقی بمان"]
D -->|بله| F["🪄 ورود به حالت جادویی\nWalletMode = Magic"]
F --> G["شارژ از درگاه\nواریز × 2.5 = اعتبار Balance"]
G --> H{"Balance=0 AND\nTotalDeposited≥100M?"}
H -->|خیر| G
H -->|بله| I["خروج از جادویی\nWalletMode = Normal"]
I --> J["خرید مجدد پکیج\nفقط IPG — بدون دایا"]
J --> A
۹.۲ قوانین کلیدی
| قانون | مقدار |
|---|---|
| ضریب شارژ | واریز × 2.5 = اعتبار Balance |
| سقف واریز/دور | 100M تومان (1B ریال) |
| سقف اعتبار/دور | 250M تومان (2.5B ریال) |
| کمیسیون در Magic | ❌ غیرفعال |
| شرط خروج | Balance=0 و TotalDeposited≥100M (هر دو همزمان) |
| ریست سقف | هر خرید مجدد پکیج → سقف از صفر |
۹.۳ Entityهای جدید
// فیلدهای جدید UserWallet
public WalletMode WalletMode { get; set; } // Normal=0, Magic=1
public long MagicTotalDeposited { get; set; } // مجموع واریزی دور فعلی
public long MagicTotalCredited { get; set; } // مجموع اعتبار دریافتی
public DateTime? MagicActivatedAt { get; set; }
public DateTime? MagicCompletedAt { get; set; }
// Entity جدید — حل مشکل تاریخ کمیسیون
public class ClubMembershipCycle {
public long Id { get; set; }
public long ClubMembershipId { get; set; }
public int CycleNumber { get; set; } // شماره دور (1, 2, 3, ...)
public DateTime PackagePurchasedAt { get; set; } // تاریخ خرید این دور
public bool IsCurrentCycle { get; set; } // دور فعلی
}