Compare commits
3 Commits
f5173a4def
...
c78850f86e
| Author | SHA1 | Date | |
|---|---|---|---|
| c78850f86e | |||
| 0115142faf | |||
| 52e6e1530c |
@@ -0,0 +1,554 @@
|
|||||||
|
# 📦 سیستم مبتنی بر پکیج (Package-Based System)
|
||||||
|
|
||||||
|
> **وضعیت:** تحلیل و بررسی — منتظر تایید
|
||||||
|
> **تاریخ:** اسفند ۱۴۰۴
|
||||||
|
> **تاثیرگذاری:** زیاد — بخشهای متعدد سیستم تحت تاثیر قرار میگیرد
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۱. خلاصه فیچر
|
||||||
|
|
||||||
|
**وضعیت فعلی:** سیستم فقط یک پکیج پایه (۵۶ میلیون تومان) دارد و همه چیز حول آن میچرخد.
|
||||||
|
|
||||||
|
**وضعیت هدف:** سیستم چندین پکیج با قیمتها و ویژگیهای متفاوت پشتیبانی میکند. هر پکیج روشهای پرداخت، محاسبه پورسانت، شارژ کیف پول و فیچرهای مختص خود را دارد.
|
||||||
|
|
||||||
|
```
|
||||||
|
مثال پکیجها:
|
||||||
|
┌──────────────┬──────────────┬──────────────┬──────────────┐
|
||||||
|
│ 🥈 نقرهای │ 🥇 طلایی │ 💎 الماسی │ ⭐ ویژه │
|
||||||
|
│ ۵.۶M تومان │ ۵۶M تومان │ ؟؟ تومان │ ؟؟ تومان │
|
||||||
|
│ │ (پکیج پایه) │ │ │
|
||||||
|
│ فقط مستقیم │ دایا+مستقیم │ فقط مستقیم │ فقط مستقیم │
|
||||||
|
│ فیچر محدود │ همه فیچرها │ همه فیچرها │ همه+اختصاصی │
|
||||||
|
└──────────────┴──────────────┴──────────────┴──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۲. وضعیت فعلی سیستم (AS-IS)
|
||||||
|
|
||||||
|
### ۲.۱ فلوی فعلی فعالسازی
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A["کاربر وارد سیستم میشود"] --> B{"روش پرداخت"}
|
||||||
|
B -->|"خرید الماس دایا"| C["DayaLoan — ۵۶M"]
|
||||||
|
B -->|"پرداخت مستقیم"| D["درگاه بانکی — ۵۶M"]
|
||||||
|
C --> E["بررسی موفقیت پرداخت"]
|
||||||
|
D --> E
|
||||||
|
E --> F["شارژ کیف پول"]
|
||||||
|
F --> G["مدال قرارداد باشگاه مشتریان"]
|
||||||
|
G --> H["تایید OTP + امضا"]
|
||||||
|
H --> I["فعالسازی عضویت باشگاه"]
|
||||||
|
I --> J["اختصاص فیچرها"]
|
||||||
|
I --> K["ایجاد Cycle"]
|
||||||
|
I --> L["اضافه به Commission Pool"]
|
||||||
|
J --> M["✅ کاربر فعال — لینک معرف"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۲.۲ جریان پول فعلی
|
||||||
|
|
||||||
|
```
|
||||||
|
کاربر ۵۶M پرداخت میکند
|
||||||
|
│
|
||||||
|
├── Balance (کیف پول عادی) += ۵۶,۰۰۰,۰۰۰ ریال
|
||||||
|
├── DiscountBalance (اعتباری) += ۱۱۲,۰۰۰,۰۰۰ ریال (×۲)
|
||||||
|
│
|
||||||
|
└── Club Activation:
|
||||||
|
├── CommissionPool += ۲۵,۲۰۰,۰۰۰ ریال (ClubActivationFee)
|
||||||
|
└── GiftValue = ۲۵,۲۰۰,۰۰۰ ریال (اطلاعرسانی)
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۲.۳ مقادیر Hardcoded فعلی (`SystemConstants.cs`)
|
||||||
|
|
||||||
|
| ثابت | مقدار | کاربرد |
|
||||||
|
|------|-------|--------|
|
||||||
|
| `BasePackageAmount` | ۵۶,۰۰۰,۰۰۰ | قیمت پکیج |
|
||||||
|
| `DayaLoanAmount` | ۵۶,۰۰۰,۰۰۰ | مبلغ وام دایا |
|
||||||
|
| `ClubActivationFee` | ۲۵,۲۰۰,۰۰۰ | سهم هفتگی Commission Pool |
|
||||||
|
| `ClubMembershipGiftValue` | ۲۵,۲۰۰,۰۰۰ | ارزش هدیه حق عضویت |
|
||||||
|
| `MagicWalletMultiplier` | ×۲.۵ | ضریب کیف پول جادویی |
|
||||||
|
|
||||||
|
### ۲.۴ مشکلات فعلی
|
||||||
|
|
||||||
|
| # | مشکل | فایل |
|
||||||
|
|---|------|------|
|
||||||
|
| ۱ | پکیج ID=4 **hardcoded** در `InitiateBasePackagePaymentCommandHandler` | Application/Commands |
|
||||||
|
| ۲ | مبلغ ۵۶M **hardcoded** در `SystemConstants` و چندین handler | Domain/Common |
|
||||||
|
| ۳ | فیچرها **همه یکجا** assign میشن (۴ فیچر ثابت: چتیکا، بیمه، تریپ، لرن) | ActivateClubMembershipHandler |
|
||||||
|
| ۴ | Commission Pool فقط با `ClubActivationFee` ثابت پر میشه | ActivateClubMembershipHandler |
|
||||||
|
| ۵ | `DiscountBalance = Amount × 2` — ضریب hardcoded | VerifyPayment handlers |
|
||||||
|
| ۶ | فرانتاند فقط یک مسیر خرید نشون میده | FrontOffice pages |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۳. طراحی پیشنهادی (TO-BE)
|
||||||
|
|
||||||
|
### ۳.۱ فلوی جدید فعالسازی
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A["کاربر وارد سیستم"] --> B["صفحه پکیجها<br/>(کاشیهای نقرهای/طلایی/الماسی/...)"]
|
||||||
|
|
||||||
|
B -->|"کلیک روی پکیج"| C{"نوع پکیج"}
|
||||||
|
|
||||||
|
C -->|"پکیج پایه (طلایی)"| D["مدال با دو گزینه:<br/>۱. خرید الماس دایا<br/>۲. پرداخت مستقیم"]
|
||||||
|
C -->|"پکیجهای دیگر"| E["مدال با یک گزینه:<br/>فقط پرداخت مستقیم<br/>+ توضیحات + قیمت"]
|
||||||
|
|
||||||
|
D -->|"دایا"| F["فلوی دایا"]
|
||||||
|
D -->|"مستقیم"| G["درگاه پرداخت"]
|
||||||
|
E --> G
|
||||||
|
|
||||||
|
F --> H["پرداخت موفق"]
|
||||||
|
G --> H
|
||||||
|
|
||||||
|
H --> I["شارژ کیف پول<br/>(متناسب با قیمت پکیج)"]
|
||||||
|
I --> J["مدال قرارداد باشگاه"]
|
||||||
|
J --> K["OTP + امضا"]
|
||||||
|
K --> L["فعالسازی<br/>+ اختصاص فیچرهای پکیج"]
|
||||||
|
L --> M["✅ کاربر فعال"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۳.۲ تغییرات Entity — Package
|
||||||
|
|
||||||
|
**فعلی:**
|
||||||
|
```csharp
|
||||||
|
public class Package : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string ImagePath { get; set; }
|
||||||
|
public long Price { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**پیشنهادی:**
|
||||||
|
```csharp
|
||||||
|
public class Package : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string ImagePath { get; set; }
|
||||||
|
public long Price { get; set; } // قیمت پکیج (ریال)
|
||||||
|
|
||||||
|
// === فیلدهای جدید ===
|
||||||
|
public int SortOrder { get; set; } // ترتیب نمایش
|
||||||
|
public bool IsActive { get; set; } = true; // فعال/غیرفعال
|
||||||
|
public bool IsBasePackage { get; set; } // آیا پکیج پایه است؟
|
||||||
|
public bool SupportsDayaPurchase { get; set; } // پشتیبانی از خرید دایا
|
||||||
|
public bool SupportsDirectPurchase { get; set; } = true; // پشتیبانی از پرداخت مستقیم
|
||||||
|
|
||||||
|
// === محاسبات مالی ===
|
||||||
|
public long ActivationFee { get; set; } // سهم Commission Pool
|
||||||
|
public long GiftValue { get; set; } // ارزش هدیه
|
||||||
|
public decimal DiscountMultiplier { get; set; } = 2.0m; // ضریب شارژ DiscountBalance
|
||||||
|
|
||||||
|
// === Navigation ===
|
||||||
|
public virtual ICollection<PackageFeature> PackageFeatures { get; set; }
|
||||||
|
public virtual ICollection<UserPackagePurchase> Purchases { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۳.۳ Entity جدید — PackageFeature (پل بین پکیج و فیچر)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
/// <summary>
|
||||||
|
/// مشخص میکند هر پکیج چه فیچرهایی را فعال میکند
|
||||||
|
/// </summary>
|
||||||
|
public class PackageFeature : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
|
||||||
|
public long ClubFeatureId { get; set; }
|
||||||
|
public virtual ClubFeature ClubFeature { get; set; }
|
||||||
|
|
||||||
|
public bool IsIncluded { get; set; } = true; // آیا این فیچر در پکیج هست؟
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۳.۴ تغییرات Entity — ClubMembership
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class ClubMembership : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
// ... فیلدهای فعلی حفظ میشوند ...
|
||||||
|
|
||||||
|
// === فیلد جدید ===
|
||||||
|
public long PackageId { get; set; } // کدام پکیج خریداری شده
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۳.۵ تغییرات Entity — ClubMembershipCycle
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class ClubMembershipCycle : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
// ... فیلدهای فعلی حفظ میشوند ...
|
||||||
|
|
||||||
|
// === فیلد جدید ===
|
||||||
|
public long PackageId { get; set; } // پکیج این سایکل
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
// PackageAmount قبلاً وجود دارد — از Package.Price پر میشود
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۳.۶ تغییرات Entity — WeeklyCommissionPool
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class WeeklyCommissionPool : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
// ... فیلدهای فعلی حفظ میشوند ...
|
||||||
|
|
||||||
|
// === فیلد جدید ===
|
||||||
|
public long PackageId { get; set; } // Pool جداگانه برای هر پکیج
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۳.۷ جریان پول جدید
|
||||||
|
|
||||||
|
```
|
||||||
|
پکیج نقرهای (۵.۶M):
|
||||||
|
├── Balance += ۵,۶۰۰,۰۰۰
|
||||||
|
├── DiscountBalance += ۱۱,۲۰۰,۰۰۰ (×۲)
|
||||||
|
└── CommissionPool += ActivationFee مخصوص نقرهای
|
||||||
|
|
||||||
|
پکیج طلایی/پایه (۵۶M):
|
||||||
|
├── Balance += ۵۶,۰۰۰,۰۰۰
|
||||||
|
├── DiscountBalance += ۱۱۲,۰۰۰,۰۰۰ (×۲)
|
||||||
|
└── CommissionPool += ۲۵,۲۰۰,۰۰۰
|
||||||
|
|
||||||
|
پکیج الماسی (??M):
|
||||||
|
├── Balance += ??
|
||||||
|
├── DiscountBalance += ?? (×۲)
|
||||||
|
└── CommissionPool += ActivationFee مخصوص الماسی
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۴. محاسبه پورسانت — تغییرات
|
||||||
|
|
||||||
|
### ۴.۱ وضعیت فعلی
|
||||||
|
|
||||||
|
```
|
||||||
|
یک WeeklyCommissionPool برای کل هفته
|
||||||
|
↓
|
||||||
|
TotalAmount = مجموع ActivationFee همه فعالسازیها
|
||||||
|
↓
|
||||||
|
ValuePerBalance = TotalAmount ÷ مجموع Balanceها
|
||||||
|
↓
|
||||||
|
همه یکسان محاسبه میشوند
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۴.۲ وضعیت هدف
|
||||||
|
|
||||||
|
```
|
||||||
|
برای هر پکیج، یک WeeklyCommissionPool جداگانه:
|
||||||
|
|
||||||
|
Pool_نقرهای:
|
||||||
|
TotalAmount = مجموع ActivationFee خریداران نقرهای این هفته
|
||||||
|
Balanceها = فقط از شبکه خریداران نقرهای
|
||||||
|
ValuePerBalance = Pool_نقرهای ÷ Balance_نقرهای
|
||||||
|
|
||||||
|
Pool_طلایی:
|
||||||
|
TotalAmount = مجموع ActivationFee خریداران طلایی این هفته
|
||||||
|
Balanceها = فقط از شبکه خریداران طلایی
|
||||||
|
ValuePerBalance = Pool_طلایی ÷ Balance_طلایی
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۴.۳ نکته مهم: ساختار شبکه یکی است
|
||||||
|
|
||||||
|
```
|
||||||
|
[Ali]
|
||||||
|
/ \
|
||||||
|
[Sara] [Reza] ← شبکه باینری یکیست
|
||||||
|
/ \ / \
|
||||||
|
[M1] [M2] [M3] [M4]
|
||||||
|
|
||||||
|
ولی محاسبات جدا:
|
||||||
|
- Ali با پکیج طلایی → پورسانت از Pool طلایی
|
||||||
|
- Sara با پکیج نقرهای → پورسانت از Pool نقرهای
|
||||||
|
- Reza با پکیج طلایی → پورسانت از Pool طلایی
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۴.۴ تغییرات Stored Procedure
|
||||||
|
|
||||||
|
**`sp_CalculateWeeklyBalances`** باید:
|
||||||
|
- پارامتر `@PackageId` بگیرد
|
||||||
|
- فقط کاربرانی که این پکیج را خریدهاند فیلتر کند
|
||||||
|
- برای هر پکیج جداگانه اجرا شود
|
||||||
|
|
||||||
|
**`sp_CalculateWeeklyCommissionPool`** باید:
|
||||||
|
- پارامتر `@PackageId` بگیرد
|
||||||
|
- Pool مخصوص آن پکیج را بخواند
|
||||||
|
- پرداختها فقط به خریداران آن پکیج اختصاص یابد
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۵. فیچرهای باشگاه مشتریان بر اساس پکیج
|
||||||
|
|
||||||
|
### ۵.۱ وضعیت فعلی
|
||||||
|
|
||||||
|
وقتی کاربر فعال میشود، **همه ۴ فیچر** یکجا assign میشوند:
|
||||||
|
```csharp
|
||||||
|
// ActivateClubMembershipCommandHandler — خط ~350
|
||||||
|
var allFeatureIds = ClubFeatureTypeExtensions.GetAllFeatureIds();
|
||||||
|
foreach (var featureId in allFeatureIds)
|
||||||
|
{
|
||||||
|
userClubFeatures.Add(new UserClubFeature { ... });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۵.۲ وضعیت هدف
|
||||||
|
|
||||||
|
فیچرها بر اساس جدول `PackageFeature` تعیین میشوند:
|
||||||
|
|
||||||
|
| فیچر | نقرهای | طلایی (پایه) | الماسی |
|
||||||
|
|------|---------|-------------|--------|
|
||||||
|
| چتیکا | ❌ | ✅ | ✅ |
|
||||||
|
| بیمه | ❌ | ✅ | ✅ |
|
||||||
|
| تریپ | ✅ | ✅ | ✅ |
|
||||||
|
| لرن | ✅ | ✅ | ✅ |
|
||||||
|
| فیچر VIP | ❌ | ❌ | ✅ |
|
||||||
|
|
||||||
|
*مقادیر بالا نمونهای هستند — قابل تنظیم از BackOffice*
|
||||||
|
|
||||||
|
### ۵.۳ تغییر در ActivateClubMembershipHandler
|
||||||
|
|
||||||
|
```
|
||||||
|
قبلی:
|
||||||
|
GetAllFeatureIds() → assign all
|
||||||
|
|
||||||
|
جدید:
|
||||||
|
Package.PackageFeatures
|
||||||
|
.Where(pf => pf.IsIncluded)
|
||||||
|
.Select(pf => pf.ClubFeatureId)
|
||||||
|
→ assign only included features
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۶. تغییرات UI — FrontOffice
|
||||||
|
|
||||||
|
### ۶.۱ صفحه پکیجها (کاشیها)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ انتخاب پکیج باشگاه مشتریان │
|
||||||
|
├─────────────┬──────────────┬──────────────┬─────────┤
|
||||||
|
│ │ │ │ │
|
||||||
|
│ 🥈 نقرهای │ 🥇 طلایی │ 💎 الماسی │ ⭐ ویژه │
|
||||||
|
│ ۵.۶M │ ۵۶M │ ؟؟M │ ؟؟M │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ ● لرن │ ● چتیکا │ ● همه │ ● همه │
|
||||||
|
│ ● تریپ │ ● بیمه │ ● + VIP │ ● +... │
|
||||||
|
│ │ ● تریپ │ │ │
|
||||||
|
│ │ ● لرن │ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ [انتخاب] │ [انتخاب] │ [انتخاب] │[انتخاب]│
|
||||||
|
└─────────────┴──────────────┴──────────────┴─────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۶.۲ مدال پرداخت — پکیج پایه (طلایی)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ خرید پکیج طلایی — ۵۶M تومان │
|
||||||
|
│ │
|
||||||
|
│ توضیحات: ... │
|
||||||
|
│ │
|
||||||
|
│ روشهای پرداخت: │
|
||||||
|
│ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ 💎 خرید از طریق الماس دایا │ │
|
||||||
|
│ └─────────────────────────────────┘ │
|
||||||
|
│ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ 💳 پرداخت مستقیم (درگاه بانکی) │ │
|
||||||
|
│ └─────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۶.۳ مدال پرداخت — پکیجهای دیگر (نقرهای و بالاتر)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ خرید پکیج نقرهای — ۵.۶M تومان │
|
||||||
|
│ │
|
||||||
|
│ توضیحات: ... │
|
||||||
|
│ ویژگیها: لرن، تریپ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ 💳 پرداخت و فعالسازی │ │
|
||||||
|
│ └─────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۷. بخشهای تحت تاثیر (Impact Analysis)
|
||||||
|
|
||||||
|
### ۷.۱ جدول تاثیرپذیری
|
||||||
|
|
||||||
|
| # | لایه | فایل/بخش | نوع تغییر | شدت |
|
||||||
|
|---|------|----------|-----------|-----|
|
||||||
|
| ۱ | **Domain** | `Package.cs` | اضافه فیلد | 🟡 متوسط |
|
||||||
|
| ۲ | **Domain** | `PackageFeature.cs` — **جدید** | Entity جدید | 🔴 زیاد |
|
||||||
|
| ۳ | **Domain** | `ClubMembership.cs` | اضافه `PackageId` | 🟡 متوسط |
|
||||||
|
| ۴ | **Domain** | `ClubMembershipCycle.cs` | اضافه `PackageId` | 🟡 متوسط |
|
||||||
|
| ۵ | **Domain** | `WeeklyCommissionPool.cs` | اضافه `PackageId` | 🔴 زیاد |
|
||||||
|
| ۶ | **Domain** | `SystemConstants.cs` | حذف hardcodeها → خوانش از Package | 🟡 متوسط |
|
||||||
|
| ۷ | **Application** | `ActivateClubMembershipCommandHandler` | فیچر بر اساس پکیج | 🔴 زیاد |
|
||||||
|
| ۸ | **Application** | `InitiateBasePackagePaymentCommandHandler` | حذف ID=4 hardcoded | 🟡 متوسط |
|
||||||
|
| ۹ | **Application** | `VerifyBasePackagePaymentCommandHandler` | شارژ متناسب با پکیج | 🔴 زیاد |
|
||||||
|
| ۱۰ | **Application** | `VerifyPackagePurchasePaymentCommandHandler` | شارژ متناسب با پکیج | 🔴 زیاد |
|
||||||
|
| ۱۱ | **Application** | `ManualPaymentCommandHandler` | شارژ متناسب با پکیج | 🟡 متوسط |
|
||||||
|
| ۱۲ | **Application** | `CustomerPurchasePackageCommandHandler` | پشتیبانی روشهای پرداخت پکیج | 🟡 متوسط |
|
||||||
|
| ۱۳ | **Infra** | `sp_CalculateWeeklyBalances` | پارامتر PackageId | 🔴 زیاد |
|
||||||
|
| ۱۴ | **Infra** | `sp_CalculateWeeklyCommissionPool` | Pool جداگانه هر پکیج | 🔴 زیاد |
|
||||||
|
| ۱۵ | **Infra** | `WeeklyCommissionCalculationService` | Loop روی پکیجها | 🟡 متوسط |
|
||||||
|
| ۱۶ | **Infra** | EF Configurations | جدول جدید + FKها | 🟡 متوسط |
|
||||||
|
| ۱۷ | **Infra** | Database Migration | schema changes | 🟡 متوسط |
|
||||||
|
| ۱۸ | **Proto** | `package.proto` | فیلدهای جدید پکیج | 🟢 کم |
|
||||||
|
| ۱۹ | **Proto** | `clubmembership.proto` | PackageId در response | 🟢 کم |
|
||||||
|
| ۲۰ | **Proto** | `commission.proto` | PackageId در pool/payout | 🟢 کم |
|
||||||
|
| ۲۱ | **FrontOffice** | صفحه انتخاب پکیج | UI جدید (کاشیها) | 🔴 زیاد |
|
||||||
|
| ۲۲ | **FrontOffice** | مدال پرداخت | دو مدال متفاوت | 🔴 زیاد |
|
||||||
|
| ۲۳ | **FrontOffice** | `MyPackages.razor` | نمایش نوع پکیج | 🟡 متوسط |
|
||||||
|
| ۲۴ | **FrontOffice** | `ActivateClubDialog.razor` | ارتباط با پکیج | 🟡 متوسط |
|
||||||
|
| ۲۵ | **BackOffice** | صفحه مدیریت پکیجها | CRUD فیلدهای جدید | 🟡 متوسط |
|
||||||
|
| ۲۶ | **BackOffice** | صفحه فیچر پکیجها — **جدید** | ماتریس پکیج×فیچر | 🔴 زیاد |
|
||||||
|
| ۲۷ | **BackOffice** | `ActivateClubDialog.razor` | انتخاب پکیج | 🟡 متوسط |
|
||||||
|
|
||||||
|
### ۷.۲ ریسکها
|
||||||
|
|
||||||
|
| ریسک | احتمال | شدت | راهحل |
|
||||||
|
|------|--------|-----|--------|
|
||||||
|
| دادههای فعلی — کاربران بدون PackageId | قطعی | زیاد | Migration: کاربران فعلی → PackageId = پکیج پایه |
|
||||||
|
| Commission Pool فعلی بدون PackageId | قطعی | زیاد | Migration: Poolهای موجود → PackageId = پکیج پایه |
|
||||||
|
| SP تغییر → محاسبات اشتباه | متوسط | بحرانی | تست جامع + محیط staging |
|
||||||
|
| مبالغ hardcoded در جاهای پراکنده | زیاد | متوسط | Audit کامل کدبیس |
|
||||||
|
| عدم سازگاری FrontOffice/BackOffice | متوسط | متوسط | تست end-to-end |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۸. فازبندی پیادهسازی
|
||||||
|
|
||||||
|
### فاز ۱ — زیرساخت (Domain + DB) ≈ ۳-۴ روز
|
||||||
|
|
||||||
|
| تسک | شرح |
|
||||||
|
|-----|------|
|
||||||
|
| T1.1 | بروزرسانی `Package` entity (فیلدهای جدید) |
|
||||||
|
| T1.2 | ایجاد `PackageFeature` entity + EF Configuration |
|
||||||
|
| T1.3 | اضافه کردن `PackageId` به `ClubMembership` |
|
||||||
|
| T1.4 | اضافه کردن `PackageId` به `ClubMembershipCycle` |
|
||||||
|
| T1.5 | اضافه کردن `PackageId` به `WeeklyCommissionPool` |
|
||||||
|
| T1.6 | Database Migration + Seed data (پکیج پایه + فیچرها) |
|
||||||
|
| T1.7 | Migration: کاربران/Poolهای فعلی → PackageId = پکیج پایه |
|
||||||
|
| T1.8 | بروزرسانی Protoها |
|
||||||
|
|
||||||
|
### فاز ۲ — منطق کسبوکار (Application) ≈ ۴-۵ روز
|
||||||
|
|
||||||
|
| تسک | شرح |
|
||||||
|
|-----|------|
|
||||||
|
| T2.1 | بروزرسانی `ActivateClubMembershipCommandHandler` — فیچر بر اساس پکیج |
|
||||||
|
| T2.2 | بروزرسانی Verify handlers — شارژ کیف پول متناسب با پکیج |
|
||||||
|
| T2.3 | حذف مقادیر hardcoded از `SystemConstants` → خوانش از Package |
|
||||||
|
| T2.4 | بروزرسانی `InitiateBasePackagePayment` → Generic `InitiatePackagePayment` |
|
||||||
|
| T2.5 | بروزرسانی `ManualPaymentCommandHandler` — پشتیبانی پکیج متغیر |
|
||||||
|
| T2.6 | CRUD پکیج با فیلدهای جدید (gRPC handlers) |
|
||||||
|
| T2.7 | CRUD `PackageFeature` (ماتریس پکیج×فیچر) |
|
||||||
|
|
||||||
|
### فاز ۳ — محاسبه پورسانت ≈ ۳-۴ روز
|
||||||
|
|
||||||
|
| تسک | شرح |
|
||||||
|
|-----|------|
|
||||||
|
| T3.1 | بروزرسانی `sp_CalculateWeeklyBalances` — فیلتر بر اساس PackageId |
|
||||||
|
| T3.2 | بروزرسانی `sp_CalculateWeeklyCommissionPool` — Pool جداگانه |
|
||||||
|
| T3.3 | بروزرسانی `WeeklyCommissionCalculationService` — Loop روی پکیجها |
|
||||||
|
| T3.4 | تست محاسبات با داده واقعی |
|
||||||
|
|
||||||
|
### فاز ۴ — UI (FrontOffice + BackOffice) ≈ ۴-۵ روز
|
||||||
|
|
||||||
|
| تسک | شرح |
|
||||||
|
|-----|------|
|
||||||
|
| T4.1 | صفحه کاشیهای پکیج (FrontOffice) |
|
||||||
|
| T4.2 | مدال پرداخت پکیج پایه (دایا + مستقیم) |
|
||||||
|
| T4.3 | مدال پرداخت پکیجهای دیگر (فقط مستقیم) |
|
||||||
|
| T4.4 | بروزرسانی `MyPackages.razor` — نمایش نوع پکیج |
|
||||||
|
| T4.5 | بروزرسانی `ActivateClubDialog.razor` — ارتباط با پکیج |
|
||||||
|
| T4.6 | BackOffice: CRUD پکیج با فیلدهای جدید |
|
||||||
|
| T4.7 | BackOffice: صفحه ماتریس فیچرهای پکیج |
|
||||||
|
| T4.8 | BackOffice: `ActivateClubDialog` — انتخاب پکیج |
|
||||||
|
|
||||||
|
### فاز ۵ — تست و استقرار ≈ ۲-۳ روز
|
||||||
|
|
||||||
|
| تسک | شرح |
|
||||||
|
|-----|------|
|
||||||
|
| T5.1 | تست end-to-end فلوی خرید هر پکیج |
|
||||||
|
| T5.2 | تست محاسبه پورسانت جداگانه |
|
||||||
|
| T5.3 | تست migration دادههای فعلی |
|
||||||
|
| T5.4 | Deploy به staging + تست |
|
||||||
|
| T5.5 | Deploy به production |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۹. Seed Data — پکیجهای اولیه
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Migration: Seed packages
|
||||||
|
INSERT INTO Packages (Title, Description, Price, IsActive, IsBasePackage,
|
||||||
|
SupportsDayaPurchase, SupportsDirectPurchase, ActivationFee, GiftValue,
|
||||||
|
DiscountMultiplier, SortOrder)
|
||||||
|
VALUES
|
||||||
|
('نقرهای', 'پکیج نقرهای باشگاه مشتریان', 5600000, 1, 0,
|
||||||
|
0, 1, ???, ???, 2.0, 1),
|
||||||
|
('طلایی', 'پکیج طلایی باشگاه مشتریان (پایه)', 56000000, 1, 1,
|
||||||
|
1, 1, 25200000, 25200000, 2.0, 2);
|
||||||
|
|
||||||
|
-- Migration: ربط فیچرها به پکیجها
|
||||||
|
INSERT INTO PackageFeatures (PackageId, ClubFeatureId, IsIncluded) VALUES
|
||||||
|
-- نقرهای: فقط تریپ و لرن
|
||||||
|
(@silverId, @tripId, 1),
|
||||||
|
(@silverId, @learnId, 1),
|
||||||
|
-- طلایی: همه فیچرها
|
||||||
|
(@goldId, @chatikaId, 1),
|
||||||
|
(@goldId, @bimeId, 1),
|
||||||
|
(@goldId, @tripId, 1),
|
||||||
|
(@goldId, @learnId, 1);
|
||||||
|
|
||||||
|
-- Migration: کاربران فعلی → پکیج پایه
|
||||||
|
UPDATE ClubMemberships SET PackageId = @goldId WHERE PackageId IS NULL;
|
||||||
|
UPDATE ClubMembershipCycles SET PackageId = @goldId WHERE PackageId IS NULL;
|
||||||
|
UPDATE WeeklyCommissionPools SET PackageId = @goldId WHERE PackageId IS NULL;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۱۰. سوالات باز (نیاز به تصمیمگیری)
|
||||||
|
|
||||||
|
| # | سوال | گزینهها |
|
||||||
|
|---|------|---------|
|
||||||
|
| ۱ | `ActivationFee` و `GiftValue` پکیج نقرهای چقدر باشد؟ | نسبت به قیمت؟ مقدار ثابت؟ |
|
||||||
|
| ۲ | آیا کاربر میتواند بعداً پکیج خود را ارتقا دهد (upgrade)؟ | بله → فقط مابهالتفاوت / خیر |
|
||||||
|
| ۳ | `DiscountMultiplier` برای همه پکیجها ×۲ باشد؟ | یکسان / متفاوت به ازای هر پکیج |
|
||||||
|
| ۴ | ضریب `MagicWallet` (×۲.۵) برای پکیجهای کوچکتر هم همان باشد؟ | بله / خیر |
|
||||||
|
| ۵ | فیچرهای پکیج نقرهای دقیقاً کدامها هستند؟ | لرن+تریپ؟ فقط لرن؟ |
|
||||||
|
| ۶ | آیا یک کاربر میتواند چند پکیج همزمان داشته باشد؟ | فقط یکی / امکان خرید چندتا |
|
||||||
|
| ۷ | نام و تعداد دقیق پکیجها چیست؟ | نقرهای+طلایی؟ بیشتر؟ |
|
||||||
|
| ۸ | کاربرانی که با دایا فعال شدن، چه پکیجی دارند؟ | طلایی (پایه) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۱۱. تخمین زمانی
|
||||||
|
|
||||||
|
| فاز | مدت | وابستگی |
|
||||||
|
|-----|------|---------|
|
||||||
|
| فاز ۱ — زیرساخت | ۳-۴ روز | — |
|
||||||
|
| فاز ۲ — منطق | ۴-۵ روز | فاز ۱ |
|
||||||
|
| فاز ۳ — پورسانت | ۳-۴ روز | فاز ۱ |
|
||||||
|
| فاز ۴ — UI | ۴-۵ روز | فاز ۲ |
|
||||||
|
| فاز ۵ — تست | ۲-۳ روز | فاز ۳, ۴ |
|
||||||
|
| **مجموع** | **~۱۶-۲۱ روز کاری** | |
|
||||||
|
|
||||||
|
> فازهای ۲ و ۳ قابل موازیسازی هستند.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# 💰 سیستم مالی، پرداخت و درگاهها
|
# 💰 سیستم مالی، پرداخت و درگاهها
|
||||||
|
|
||||||
> **منابع ادغامشده:** `payment-gateway.md`, `payment-architecture-pyms.md`, `daya-loan-integration.md`, `manual-payment-system.md`, `discount-shop-business.md`
|
> **منابع ادغامشده:** `payment-gateway.md`, `payment-architecture-pyms.md`, `daya-loan-integration.md`, `manual-payment-system.md`, `discount-shop-business.md`
|
||||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: فعالسازی درگاه ZarinPal + تنظیمات محیطی)
|
> **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: فیکس WalletChangeLog + validation کیفپول اعتباری + نامگذاری جدید کیفپولها)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@ flowchart TD
|
|||||||
PYMS --> WALLETS
|
PYMS --> WALLETS
|
||||||
|
|
||||||
subgraph WALLETS["3 Wallet System"]
|
subgraph WALLETS["3 Wallet System"]
|
||||||
W1["💰 Balance\nنقدی"]
|
W1["💰 Balance\nکیف پول اصلی"]
|
||||||
W2["🌟 NetworkBalance\nشبکه"]
|
W2["🌟 NetworkBalance\nپاداش تیمی"]
|
||||||
W3["🏷️ DiscountBalance\nاعتباری"]
|
W3["🏷️ DiscountBalance\nکیف پول اعتباری"]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -137,7 +137,11 @@ flowchart TD
|
|||||||
قیمت محصول = 1,000,000 ریال
|
قیمت محصول = 1,000,000 ریال
|
||||||
MaxDiscountPercent محصول = 40% (هر محصول درصد تخفیف مخصوص خود را دارد)
|
MaxDiscountPercent محصول = 40% (هر محصول درصد تخفیف مخصوص خود را دارد)
|
||||||
|
|
||||||
سهم تخفیف = MIN(1,000,000 × 40%, DiscountBalanceکاربر) = 400,000
|
سهم تخفیف = قیمت × MaxDiscountPercent% = 400,000
|
||||||
|
|
||||||
|
⚠️ اگر DiscountBalance < سهم تخفیف → خطا: «موجودی کیف پول اعتباری کافی نیست»
|
||||||
|
(دیگر MIN استفاده نمیشود — کاربر باید موجودی کافی داشته باشد)
|
||||||
|
|
||||||
باقیمانده → ZarinPal IPG = 1,000,000 - 400,000 = 600,000
|
باقیمانده → ZarinPal IPG = 1,000,000 - 400,000 = 600,000
|
||||||
─────────
|
─────────
|
||||||
مجموع = 1,000,000
|
مجموع = 1,000,000
|
||||||
@@ -151,11 +155,15 @@ MaxDiscountPercent محصول = 40% (هر محصول درصد تخفیف مخص
|
|||||||
flowchart TD
|
flowchart TD
|
||||||
A["کاربر عضو باشگاه\nمشاهده محصول"] --> B["قیمت تخفیفخورده نمایش داده میشود"]
|
A["کاربر عضو باشگاه\nمشاهده محصول"] --> B["قیمت تخفیفخورده نمایش داده میشود"]
|
||||||
B --> C["افزودن به سبد\nمحاسبه MaxDiscount% هر محصول"]
|
B --> C["افزودن به سبد\nمحاسبه MaxDiscount% هر محصول"]
|
||||||
C --> D["سهم تخفیف = MIN(قیمت×MaxDiscount%, DiscountBalance)"]
|
C --> D["سهم تخفیف = قیمت × MaxDiscount%"]
|
||||||
D --> E["باقیمانده = مجموع - سهم تخفیف"]
|
D --> V{"DiscountBalance >= سهم تخفیف?"}
|
||||||
|
V -->|خیر| X["❌ خطا: موجودی کیف پول اعتباری کافی نیست"]
|
||||||
|
V -->|بله| E["باقیمانده = مجموع - سهم تخفیف"]
|
||||||
E --> F{"باقیمانده > 0?"}
|
E --> F{"باقیمانده > 0?"}
|
||||||
F -->|بله| G["کسر DiscountBalance\n+ Redirect → ZarinPal IPG\nباقیمانده + 10% VAT"]
|
F -->|بله| G["کسر DiscountBalance\n+ Redirect → ZarinPal IPG\nباقیمانده + 10% VAT"]
|
||||||
F -->|خیر| H["فقط کسر از DiscountBalance\nبدون درگاه → ثبت مستقیم"]
|
F -->|خیر| H["فقط کسر از DiscountBalance\nبدون درگاه → ثبت مستقیم"]
|
||||||
|
G --> LOG["ثبت UserWalletChangeLog"]
|
||||||
|
H --> LOG
|
||||||
```
|
```
|
||||||
|
|
||||||
### ۵.۳ دسترسی فروشگاه اعتباری
|
### ۵.۳ دسترسی فروشگاه اعتباری
|
||||||
@@ -164,8 +172,21 @@ flowchart TD
|
|||||||
|------|--------|
|
|------|--------|
|
||||||
| `IsClubMember = true` | دسترسی به Discount Store |
|
| `IsClubMember = true` | دسترسی به Discount Store |
|
||||||
| `IsClubMember = false` | فقط Regular Store |
|
| `IsClubMember = false` | فقط Regular Store |
|
||||||
| `DiscountBalance > 0` | میتواند از تخفیف استفاده کند |
|
| `DiscountBalance >= سهم تخفیف` | خرید مجاز |
|
||||||
| `DiscountBalance = 0` | پرداخت ۱۰۰% از طریق ZarinPal IPG |
|
| `DiscountBalance < سهم تخفیف` | ❌ خطا: موجودی کیف پول اعتباری کافی نیست |
|
||||||
|
|
||||||
|
### ۵.۴ UserWalletChangeLog (اسفند ۱۴۰۴ — فیکس)
|
||||||
|
|
||||||
|
> **باگ:** هنگام خرید از فروشگاه اعتباری، `DiscountBalance` در دیتابیس کم میشد ولی هیچ
|
||||||
|
> `UserWalletChangeLog` ثبت نمیشد → کاربر در تاریخچه کیفپول چیزی نمیدید.
|
||||||
|
|
||||||
|
فیکس در ۳ هندلر:
|
||||||
|
|
||||||
|
| هندلر | سناریو | فیکس |
|
||||||
|
|--------|---------|------|
|
||||||
|
| `PlaceOrderCommandHandler` | پرداخت کامل با DiscountBalance (بدون درگاه) | ✅ ثبت log با `ChangeDiscountValue = -amount` |
|
||||||
|
| `CompleteOrderPaymentCommandHandler` | پرداخت ترکیبی (درگاه + DiscountBalance) | ✅ ثبت log بعد از verify موفق درگاه |
|
||||||
|
| `VerifyDiscountWalletChargeCommandHandler` | شارژ کیفپول اعتباری | ✅ ثبت log با `ChangeDiscountValue = +amount` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -221,6 +242,18 @@ service PaymentService {
|
|||||||
| وام دایا | ✅ کامل | Mock mode فعال در staging |
|
| وام دایا | ✅ کامل | Mock mode فعال در staging |
|
||||||
| پرداخت ترکیبی | ✅ کامل | Discount + IPG |
|
| پرداخت ترکیبی | ✅ کامل | Discount + IPG |
|
||||||
| Pool هفتگی | ✅ کامل | SP + Hangfire |
|
| Pool هفتگی | ✅ کامل | SP + Hangfire |
|
||||||
|
| WalletChangeLog | ✅ فیکس شده | لاگ تغییرات کیفپول در ۳ هندلر اضافه شد |
|
||||||
|
| Validation کیفپول اعتباری | ✅ فیکس شده | ارور اگر موجودی کافی نباشد |
|
||||||
| پرداخت دستی | ⬜ طراحی | نیاز به تصمیم مدیریت |
|
| پرداخت دستی | ⬜ طراحی | نیاز به تصمیم مدیریت |
|
||||||
| Refund | ⬜ طراحی | فقط در PYMS تعریفشده |
|
| Refund | ⬜ طراحی | فقط در PYMS تعریفشده |
|
||||||
| کیفپول جادویی (Magic) | ✅ کامل | فاز 1-6 پیادهسازی شده — Production فعال |
|
| کیفپول جادویی (Magic) | ✅ کامل | فاز 1-6 پیادهسازی شده — Production فعال |
|
||||||
|
|
||||||
|
### ۸.۱ نامگذاری استاندارد کیفپولها (اسفند ۱۴۰۴)
|
||||||
|
|
||||||
|
| فیلد دیتابیس | نام قدیم (UI) | نام جدید (UI) |
|
||||||
|
|-------------|--------------|---------------|
|
||||||
|
| `Balance` | عادی / اعتباری / نقدی | **کیف پول اصلی** |
|
||||||
|
| `DiscountBalance` | تخفیفی / تخفیف | **کیف پول اعتباری** |
|
||||||
|
| `NetworkBalance` | شبکه / طلایی / پورسانت | **پاداش تیمی** |
|
||||||
|
|
||||||
|
> تغییرات UI در ۱۲ فایل (FrontOffice: 5, BackOffice: 7) اعمال شد.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# 🛒 فروشگاه، موجودی و محصولات
|
# 🛒 فروشگاه، موجودی و محصولات
|
||||||
|
|
||||||
> **منابع ادغامشده:** `discount-shop-business.md`, `DISCOUNT-STORE-STATUS.md`, `package-purchase-system.md`, `INVENTORY-IMPROVEMENTS.md`, `INVENTORY-REFACTORING-STATUS.md`, `PRODUCT-BUNDLE-FEATURE.md`, `SHOP-UNIFICATION.md`
|
> **منابع ادغامشده:** `discount-shop-business.md`, `DISCOUNT-STORE-STATUS.md`, `package-purchase-system.md`, `INVENTORY-IMPROVEMENTS.md`, `INVENTORY-REFACTORING-STATUS.md`, `PRODUCT-BUNDLE-FEATURE.md`, `SHOP-UNIFICATION.md`
|
||||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: VAT 10%)
|
> **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: ExpirePendingOrders ۱۵ دقیقه + فروشگاه اعتباری نامگذاری)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -226,5 +226,44 @@ graph TD
|
|||||||
| Lazy Loading | ✅ کامل | 100% |
|
| Lazy Loading | ✅ کامل | 100% |
|
||||||
| موجودی خودکار | ✅ کامل | 100% |
|
| موجودی خودکار | ✅ کامل | 100% |
|
||||||
| تصاویر مربعی | ✅ کامل | 100% |
|
| تصاویر مربعی | ✅ کامل | 100% |
|
||||||
|
| انقضای سفارشات Pending | ✅ کامل | 100% |
|
||||||
| باندل محصولات | ⬜ طراحی | 30% |
|
| باندل محصولات | ⬜ طراحی | 30% |
|
||||||
| مقایسه محصول | ⬜ ایده | 0% |
|
| مقایسه محصول | ⬜ ایده | 0% |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ۹. انقضای خودکار سفارشات Pending (ExpirePendingOrdersService)
|
||||||
|
|
||||||
|
> سرویس پسزمینهای که سفارشات فروشگاه اعتباری را بعد از ۱۵ دقیقه منقضی میکند.
|
||||||
|
|
||||||
|
### ۹.۱ پارامترها
|
||||||
|
|
||||||
|
| پارامتر | مقدار | توضیح |
|
||||||
|
|---------|-------|-------|
|
||||||
|
| `ExpirationTime` | **۱۵ دقیقه** | مدت زمان مجاز برای پرداخت |
|
||||||
|
| `CheckInterval` | ۵ دقیقه | فاصله بررسی |
|
||||||
|
|
||||||
|
### ۹.۲ عملکرد
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A["هر ۵ دقیقه\nExpirePendingOrdersService"] --> B["جستجوی DiscountOrders\nPaymentStatus=Pending\nCreated < (now - 15 min)"]
|
||||||
|
B --> C{"سفارشی یافت شد?"}
|
||||||
|
C -->|خیر| A
|
||||||
|
C -->|بله| D["آزادسازی رزرو موجودی\nReleaseReservationAsync"]
|
||||||
|
D --> E["PaymentStatus → Reject\nDeliveryStatus → Cancelled"]
|
||||||
|
E --> F["Transaction.PaymentStatus → Reject"]
|
||||||
|
F --> G["Log: Expired order #X"]
|
||||||
|
G --> A
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۹.۳ فایل
|
||||||
|
|
||||||
|
```
|
||||||
|
CMS/src/CMSMicroservice.Infrastructure/BackgroundServices/ExpirePendingOrdersService.cs
|
||||||
|
```
|
||||||
|
|
||||||
|
رجیستر شده در `ConfigureServices.cs`:
|
||||||
|
```csharp
|
||||||
|
services.AddHostedService<ExpirePendingOrdersService>();
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# 🚀 استقرار، CI/CD و زیرساخت
|
# 🚀 استقرار، CI/CD و زیرساخت
|
||||||
|
|
||||||
> **منابع ادغامشده:** `CICD-PIPELINE-GUIDE.md`, `DEPLOYMENT-README.md`, `INFRASTRUCTURE-GUIDE.md`, `INGRESS-NGINX-WARNING.md`, `OFFLINE-DEPLOYMENT-GUIDE.md`, `SERVER-MIRRORS-CONFIG.md`
|
> **منابع ادغامشده:** `CICD-PIPELINE-GUIDE.md`, `DEPLOYMENT-README.md`, `INFRASTRUCTURE-GUIDE.md`, `INGRESS-NGINX-WARNING.md`, `OFFLINE-DEPLOYMENT-GUIDE.md`, `SERVER-MIRRORS-CONFIG.md`
|
||||||
> **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: مرج پروداکشن + تنظیمات محیطی)
|
> **آخرین بروزرسانی:** اسفند ۱۴۰۴ (بروزرسانی: فیکس URL پروداکشن + چریپیک فیکسهای WalletChangeLog/Validation/Expiry)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -77,31 +77,126 @@ ENTRYPOINT ["dotnet", "CMSMicroservice.dll"]
|
|||||||
|
|
||||||
### ۳.۱ Manifests ساختار
|
### ۳.۱ Manifests ساختار
|
||||||
|
|
||||||
|
مانیفستهای K8s **داخل ریپوی CMS** نگهداری میشن و توسط CI/CD اعمال میشن:
|
||||||
|
|
||||||
```
|
```
|
||||||
deployment/k8s-manifests/
|
CMS/
|
||||||
├── cms-deployment.yaml
|
k8s/
|
||||||
├── cms-service.yaml
|
staging/
|
||||||
├── backoffice-deployment.yaml
|
cms-config.yaml ← K8s Secret (appsettings.Staging.json)
|
||||||
├── backoffice-service.yaml
|
cms-deployment.yaml ← PVC + Deployment + Service + Ingress
|
||||||
├── frontoffice-deployment.yaml
|
production/
|
||||||
├── frontoffice-service.yaml
|
cms-config.yaml ← K8s Secret (appsettings.Production.json)
|
||||||
├── db-statefulset.yaml
|
cms-deployment.yaml ← PVC + Deployment + Service + Ingress
|
||||||
├── db-service.yaml
|
|
||||||
├── ingress.yaml
|
|
||||||
├── configmap.yaml
|
|
||||||
└── secrets.yaml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### ۳.۲ مثال Deployment
|
> ⚠️ **هر دو محیط از namespace `default` استفاده میکنن.**
|
||||||
|
|
||||||
|
### ۳.۲ PersistentVolume برای آپلود فایل
|
||||||
|
|
||||||
|
فایلهای آپلودشده (عکس محصولات، بلاگ، آواتار و ...) در `/app/Uploads` ذخیره میشن.
|
||||||
|
برای جلوگیری از حذف فایلها با ریستارت Pod، یک **PersistentVolumeClaim** مونت شده:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# PVC — 20Gi ذخیرهسازی دائمی
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: cms-uploads-pvc
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
accessModes: [ReadWriteOnce]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 20Gi
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Volume Mount در Deployment
|
||||||
|
volumeMounts:
|
||||||
|
- name: cms-uploads
|
||||||
|
mountPath: /app/Uploads
|
||||||
|
volumes:
|
||||||
|
- name: cms-uploads
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: cms-uploads-pvc
|
||||||
|
```
|
||||||
|
|
||||||
|
| تنظیم | مقدار |
|
||||||
|
|--------|-------|
|
||||||
|
| **PVC Name** | `cms-uploads-pvc` |
|
||||||
|
| **Mount Path** | `/app/Uploads` |
|
||||||
|
| **Access Mode** | `ReadWriteOnce` |
|
||||||
|
| **حجم** | `20Gi` |
|
||||||
|
| **StorageClass** | `local-path` (K3s default) |
|
||||||
|
| **Replicas** | `1` (محدودیت RWO) |
|
||||||
|
|
||||||
|
> 💡 **نکته مهم:** چون `ReadWriteOnce` هست، فقط **1 replica** میتونه بنویسه. برای 2+ replica نیاز به NFS/CephFS با `ReadWriteMany` هست.
|
||||||
|
|
||||||
|
### ۳.۳ تنظیمات محیطی (K8s Secret)
|
||||||
|
|
||||||
|
تنظیمات حساس (ConnectionString, Email, SMS, ZarinPal) **در K8s Secret** نگهداری میشن — نه داخل Docker image.
|
||||||
|
فایل `appsettings.{Environment}.json` از Secret به `/app/` مونت میشه و .NET اون رو override میخونه.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
S["K8s Secret<br/>cms-appsettings"] -->|volumeMount| F["/app/appsettings.*.json"]
|
||||||
|
F --> D[".NET reads config"]
|
||||||
|
I["Docker Image<br/>appsettings.json (base)"] --> D
|
||||||
|
```
|
||||||
|
|
||||||
|
| محیط | `ASPNETCORE_ENVIRONMENT` | فایل Config (از Secret) |
|
||||||
|
|------|---------------------------|-------------|
|
||||||
|
| **Staging** | `Staging` | `appsettings.Staging.json` |
|
||||||
|
| **Production** | `Production` | `appsettings.Production.json` |
|
||||||
|
|
||||||
|
**Secret manifest** (`cms-config.yaml`):
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: cms-appsettings
|
||||||
|
namespace: default
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
appsettings.Staging.json: | # یا appsettings.Production.json
|
||||||
|
{ "ConnectionStrings": { ... }, "ZarinPal": { ... }, ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Volume mount در Deployment:**
|
||||||
|
```yaml
|
||||||
|
volumeMounts:
|
||||||
|
- name: cms-config
|
||||||
|
mountPath: /app/appsettings.Staging.json
|
||||||
|
subPath: appsettings.Staging.json
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: cms-config
|
||||||
|
secret:
|
||||||
|
secretName: cms-appsettings
|
||||||
|
```
|
||||||
|
|
||||||
|
env varهای K8s manifest (فقط environment و URL):
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
- name: ASPNETCORE_ENVIRONMENT
|
||||||
|
value: "Staging" # یا "Production"
|
||||||
|
- name: ASPNETCORE_URLS
|
||||||
|
value: "http://+:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💡 **تغییر config بدون deploy:** `kubectl edit secret cms-appsettings && kubectl rollout restart deployment/cms`
|
||||||
|
|
||||||
|
### ۳.۴ مثال Deployment (واقعی)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: cms
|
name: cms
|
||||||
namespace: foursat
|
namespace: default
|
||||||
spec:
|
spec:
|
||||||
replicas: 2
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: cms
|
app: cms
|
||||||
@@ -109,104 +204,141 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: cms
|
- name: cms
|
||||||
image: foursat/cms:latest
|
image: 194.5.195.53:30080/admin/cms:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 5001
|
- containerPort: 8080
|
||||||
|
env:
|
||||||
|
- name: ASPNETCORE_ENVIRONMENT
|
||||||
|
value: "Staging"
|
||||||
|
- name: ASPNETCORE_URLS
|
||||||
|
value: "http://+:8080"
|
||||||
|
volumeMounts:
|
||||||
|
- name: cms-uploads
|
||||||
|
mountPath: /app/Uploads
|
||||||
|
- name: cms-config
|
||||||
|
mountPath: /app/appsettings.Staging.json
|
||||||
|
subPath: appsettings.Staging.json
|
||||||
|
readOnly: true
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests: { memory: "512Mi", cpu: "500m" }
|
||||||
memory: "256Mi"
|
limits: { memory: "1Gi", cpu: "1000m" }
|
||||||
cpu: "250m"
|
volumes:
|
||||||
limits:
|
- name: cms-uploads
|
||||||
memory: "512Mi"
|
persistentVolumeClaim:
|
||||||
cpu: "500m"
|
claimName: cms-uploads-pvc
|
||||||
livenessProbe:
|
- name: cms-config
|
||||||
grpc:
|
secret:
|
||||||
port: 5001
|
secretName: cms-appsettings
|
||||||
initialDelaySeconds: 15
|
|
||||||
readinessProbe:
|
|
||||||
grpc:
|
|
||||||
port: 5001
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### ۳.۳ Ingress
|
### ۳.۵ Ingress
|
||||||
|
|
||||||
|
**Staging:**
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: foursat-ingress
|
|
||||||
annotations:
|
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
|
||||||
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
|
||||||
spec:
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
rules:
|
rules:
|
||||||
- host: foursat.ir
|
- host: cms.se.kbs1.ir
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: frontoffice
|
|
||||||
port: { number: 5003 }
|
|
||||||
- path: /admin
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: backoffice
|
|
||||||
port: { number: 80 }
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> ⚠️ **هشدار:** Ingress-nginx نسخههای قبل از 1.9.0 مشکل امنیتی CVE-2023-5044 دارند. حتماً بروزرسانی کنید.
|
**Production:**
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
tls:
|
||||||
|
- hosts: [cms.kbs1.ir, cms.kbs2.ir]
|
||||||
|
secretName: cms-tls
|
||||||
|
rules:
|
||||||
|
- host: cms.kbs2.ir
|
||||||
|
- host: cms.kbs1.ir
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ **هشدار:** از `spec.ingressClassName: nginx` استفاده کنید، نه `kubernetes.io/ingress.class` annotation (deprecated).
|
||||||
|
|
||||||
|
### ۳.۶ جداسازی appsettings در Git
|
||||||
|
|
||||||
|
هر برنچ فقط فایل config مربوط به محیط خودش رو داره:
|
||||||
|
|
||||||
|
| برنچ | `appsettings.json` | `appsettings.Staging.json` | `appsettings.Production.json` |
|
||||||
|
|------|---|---|---|
|
||||||
|
| `kub-stage` | ✅ | ✅ | ❌ حذف شده |
|
||||||
|
| `production` | ✅ | ❌ حذف شده | ✅ |
|
||||||
|
|
||||||
|
**چرا؟** چون config اصلی از K8s Secret میاد (`cms-config.yaml`)، فایلهای محیط دیگه داخل ایمیج اضافی و گمراهکنندهان.
|
||||||
|
همچنین وقتی merge/cherry-pick میکنید، فایل config محیط دیگه دیگه conflict ایجاد نمیکنه.
|
||||||
|
|
||||||
|
> ⚠️ **کامیتهای حذف فایل config رو هرگز cherry-pick نکنید به برنچ دیگه!**
|
||||||
|
> `e72673c` (حذف Production از staging) و `3ebe0f9` (حذف Staging از production)
|
||||||
|
|
||||||
|
### ۳.۷ خلاصه: چه چیزهایی دائمی هستند (مستقل از ایمیج)
|
||||||
|
|
||||||
|
| چه چیزی | مکانیزم K8s | محل Mount |
|
||||||
|
|---------|-------------|------------|
|
||||||
|
| **فایلهای آپلود** (عکس، آواتار، ...) | `PersistentVolumeClaim` | `/app/Uploads` |
|
||||||
|
| **تنظیمات اپلیکیشن** (DB, SMS, IPG, ...) | `Secret` (`cms-appsettings`) | `/app/appsettings.{Env}.json` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ۴. CI/CD Pipeline
|
## ۴. CI/CD Pipeline
|
||||||
|
|
||||||
### ۴.۱ Gitea Actions Workflow
|
### ۴.۱ Gitea Actions Workflows (CMS)
|
||||||
|
|
||||||
```yaml
|
فایلهای پایپلاین:
|
||||||
name: Build and Deploy
|
```
|
||||||
on:
|
CMS/.gitea/workflows/
|
||||||
push:
|
├── kub-deploy.yml ← Staging (branch: kub-stage)
|
||||||
branches: [kub-stage, production]
|
├── prod-deploy.yml ← Production (branch: production)
|
||||||
|
└── cms-stage.yml ← قدیمی (IIS روی Windows — غیرفعال)
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup .NET
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: '9.0.x'
|
|
||||||
|
|
||||||
- name: Restore
|
|
||||||
run: dotnet restore
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: dotnet build --no-restore -c Release
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: dotnet test --no-build -c Release
|
|
||||||
|
|
||||||
- name: Docker Build & Push
|
|
||||||
run: |
|
|
||||||
docker build -t $REGISTRY/foursat/cms:${{ github.sha }} .
|
|
||||||
docker push $REGISTRY/foursat/cms:${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Deploy to K8s
|
|
||||||
if: github.ref == 'refs/heads/production'
|
|
||||||
run: |
|
|
||||||
kubectl set image deployment/cms cms=$REGISTRY/foursat/cms:${{ github.sha }}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### ۴.۲ شاخهها
|
### ۴.۲ فلوی Staging (`kub-deploy.yml`)
|
||||||
|
|
||||||
| شاخه | محیط | Deploy |
|
```mermaid
|
||||||
|------|------|--------|
|
flowchart TD
|
||||||
| `kub-stage` | Staging (194.5.195.53) | Auto |
|
A["Push to kub-stage"] --> B["Start Docker daemon"]
|
||||||
| `production` | Production (45.149.79.127) | Manual trigger |
|
B --> C["Clone repo"]
|
||||||
| `main` | — | Development only |
|
C --> D["Pack & Push Proto NuGet"]
|
||||||
|
D --> E["Docker build → tag :latest"]
|
||||||
|
E --> F["Push to 194.5.195.53:30080"]
|
||||||
|
F --> G["SCP cms-config.yaml + cms-deployment.yaml"]
|
||||||
|
G --> H["kubectl apply -f cms-config.yaml (Secret)"]
|
||||||
|
H --> I["kubectl apply -f cms-deployment.yaml"]
|
||||||
|
I --> J["kubectl rollout restart"]
|
||||||
|
J --> K["✅ Deployed to Staging"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۴.۳ فلوی Production (`prod-deploy.yml`)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A["Push to production"] --> B["Start Docker daemon"]
|
||||||
|
B --> C["Clone repo"]
|
||||||
|
C --> D["Pack & Push Proto NuGet"]
|
||||||
|
D --> E["Docker build → tag :sha + :prod"]
|
||||||
|
E --> F["Push to 194.5.195.53:30080"]
|
||||||
|
F --> G["SCP cms-config.yaml + cms-deployment.yaml"]
|
||||||
|
G --> H["kubectl apply -f cms-config.yaml (Secret)"]
|
||||||
|
H --> I["kubectl apply -f cms-deployment.yaml"]
|
||||||
|
I --> J["kubectl set image → sha"]
|
||||||
|
J --> K["✅ Deployed to Production"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۴.۴ شاخهها و محیطها
|
||||||
|
|
||||||
|
| شاخه | محیط | سرور | Image Tag | Deploy |
|
||||||
|
|------|------|------|-----------|--------|
|
||||||
|
| `kub-stage` | Staging | 194.5.195.53 | `:latest` | Auto |
|
||||||
|
| `production` | Production | 45.149.79.127 | `:sha` + `:prod` | Auto |
|
||||||
|
|
||||||
|
### ۴.۵ نکات مهم CI/CD
|
||||||
|
|
||||||
|
- **Proto NuGet:** هر deploy ابتدا proto packages رو build و به Nexus push میکنه
|
||||||
|
- **Manifest apply:** پایپلاین ابتدا `cms-config.yaml` (Secret) رو apply میکنه، بعد `cms-deployment.yaml`
|
||||||
|
→ Secret + PVC + Deployment + Service + Ingress هر بار اعمال میشه
|
||||||
|
- **Image registry:** `194.5.195.53:30080` (داخلی Nexus) — نه `git.se.kbs1.ir`
|
||||||
|
- **Config دائمی:** تنظیمات در K8s Secret هست، نه داخل Docker image — تغییر config بدون rebuild ایمیج ممکنه
|
||||||
|
- **جداسازی برنچ:** هر برنچ فقط appsettings محیط خودش رو داره (بخش ۳.۶)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -328,22 +460,77 @@ flowchart TD
|
|||||||
| **FrontOffice** | `kub-stage` → `production` | `f02d082` | 21 فایل، 400 insertion + فیکس GwUrl به `cms.kbs2.ir` |
|
| **FrontOffice** | `kub-stage` → `production` | `f02d082` | 21 فایل، 400 insertion + فیکس GwUrl به `cms.kbs2.ir` |
|
||||||
| **BackOffice** | `kub-stage` → `production` | `bdea2e8` | 36 فایل، بدون conflict |
|
| **BackOffice** | `kub-stage` → `production` | `bdea2e8` | 36 فایل، بدون conflict |
|
||||||
|
|
||||||
|
### ۹.۲ کامیتهای PVC و اصلاحات K8s (تیر ۱۴۰۴)
|
||||||
|
|
||||||
|
| commit | شرح |
|
||||||
|
|--------|------|
|
||||||
|
| `3153fd8` | feat: add PersistentVolume for CMS uploads + apply manifests in CI/CD |
|
||||||
|
| `68da3f4` | fix: staging uses namespace default, not foursat |
|
||||||
|
| `e41747a` | fix: production ingress — add cms.kbs2.ir, use ingressClassName |
|
||||||
|
| `2d6c95e` | fix: use local registry 194.5.195.53:30080 instead of git.se.kbs1.ir |
|
||||||
|
| `f8dc4ab` | fix: staging ASPNETCORE_ENVIRONMENT=Staging, remove secretKeyRef |
|
||||||
|
| `de83c31` | fix: production uses namespace default + remove foursat namespace references |
|
||||||
|
| `9288d06` | feat: externalize appsettings to K8s Secret — config persists independently |
|
||||||
|
| `e72673c` | chore(staging): remove appsettings.Production.json (فقط kub-stage) |
|
||||||
|
| `3ebe0f9` | chore(production): remove appsettings.Staging.json (فقط production) |
|
||||||
|
| `f3ac5ad` | fix: add missing UserWalletChangeLog for discount shop purchases |
|
||||||
|
| `0457ef6` | fix: validate discount wallet balance before applying discount |
|
||||||
|
| `e206b71` | fix: reduce discount order expiry from 30 to 15 minutes |
|
||||||
|
| `2620a24` | fix: correct production URLs from kbs1 to kbs2 in cms-config |
|
||||||
|
|
||||||
|
> کامیتهای PVC و Secret به هر دو شاخه push شدهاند.
|
||||||
|
> ⚠️ کامیتهای حذف appsettings فقط به برنچ مربوطه push شده — cherry-pick نکنید!
|
||||||
|
|
||||||
|
### ۹.۳ فیکس URL پروداکشن (اسفند ۱۴۰۴)
|
||||||
|
|
||||||
|
> **مشکل:** در `cms-config.yaml` پروداکشن، URLها به اشتباه `kbs1.ir` (استیج) بودند.
|
||||||
|
> زرینپال callback را به سرور استیج میفرستاد → خطای 401 → `Code=-1` (خطای ناشناخته).
|
||||||
|
|
||||||
|
| فیلد | مقدار اشتباه | مقدار صحیح |
|
||||||
|
|------|-------------|------------|
|
||||||
|
| `CmsBaseUrl` | `https://cms.kbs1.ir` | `https://cms.kbs2.ir` |
|
||||||
|
| `FrontOfficeBaseUrl` | `https://kbs1.ir` | `https://kbs2.ir` |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# فیکس مستقیم روی سرور (بدون نیاز به rebuild)
|
||||||
|
kubectl apply -f cms-config.yaml
|
||||||
|
kubectl rollout restart deployment/cms
|
||||||
|
```
|
||||||
|
|
||||||
|
### ۹.۴ نامگذاری کیفپولها (اسفند ۱۴۰۴)
|
||||||
|
|
||||||
|
> تغییر عنوان کیفپولها در تمام UI (FrontOffice: 5 فایل، BackOffice: 7 فایل):
|
||||||
|
|
||||||
|
| فیلد | نام قدیم | نام جدید |
|
||||||
|
|------|---------|----------|
|
||||||
|
| `Balance` | عادی / نقدی | **کیف پول اصلی** |
|
||||||
|
| `DiscountBalance` | تخفیفی / تخفیف | **کیف پول اعتباری** |
|
||||||
|
| `NetworkBalance` | شبکه / طلایی | **پاداش تیمی** |
|
||||||
|
|
||||||
**تنظیمات محیطی Production (`appsettings.Production.json`):**
|
**تنظیمات محیطی Production (`appsettings.Production.json`):**
|
||||||
|
|
||||||
| تنظیم | مقدار |
|
| تنظیم | مقدار |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
| `ZarinPal.MerchantId` | `4225d555-5fa9-4df0-9b61-1ce152cbbba8` |
|
| `ZarinPal.MerchantId` | `4225d555-5fa9-4df0-9b61-1ce152cbbba8` |
|
||||||
| `ZarinPal.UseSandbox` | `false` |
|
| `ZarinPal.UseSandbox` | `false` |
|
||||||
|
| `CmsBaseUrl` | `https://cms.kbs2.ir` |
|
||||||
|
| `FrontOfficeBaseUrl` | `https://kbs2.ir` |
|
||||||
| `SeedWorkers.MagicWalletCycleSeed.Enabled` | `true` |
|
| `SeedWorkers.MagicWalletCycleSeed.Enabled` | `true` |
|
||||||
| `Kestrel.Endpoints.Grpc.Protocols` | `Http2` |
|
| `Kestrel.Endpoints.Grpc.Protocols` | `Http2` |
|
||||||
| `Seq.ServerUrl` | `http://seq-svc:5341` |
|
| `Seq.ServerUrl` | `http://seq-svc:5341` |
|
||||||
| `ConnectionStrings.Default` | `Server=mssql-svc;Database=KBS` |
|
| `ConnectionStrings.Default` | `Server=mssql-svc;Database=KBS` |
|
||||||
|
|
||||||
|
> ⚠️ **مهم:** URLها باید `kbs2.ir` باشند نه `kbs1.ir` — اشتباه در URL باعث خطای 401 زرینپال میشود.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# k8s-health-check.sh
|
# k8s-health-check.sh (namespace = default)
|
||||||
kubectl get pods -n foursat
|
kubectl get pods
|
||||||
kubectl top pods -n foursat
|
kubectl top pods
|
||||||
kubectl logs deployment/cms -n foursat --tail=50
|
kubectl logs deployment/cms --tail=50
|
||||||
|
|
||||||
|
# بررسی PVC
|
||||||
|
kubectl get pvc cms-uploads-pvc
|
||||||
|
kubectl exec deployment/cms -- ls /app/Uploads | wc -l
|
||||||
|
|
||||||
# تست سرویسها
|
# تست سرویسها
|
||||||
grpcurl -plaintext localhost:5001 list # لیست سرویسها
|
grpcurl -plaintext localhost:5001 list # لیست سرویسها
|
||||||
|
|||||||
Reference in New Issue
Block a user