751 lines
23 KiB
Markdown
751 lines
23 KiB
Markdown
# Club Discount Shop System - سیستم فروشگاه باشگاه مشتریان با تخفیف ترکیبی
|
||
|
||
**تاریخ ایجاد:** 2024-12-02
|
||
**تاریخ آپدیت:** 2024-12-02
|
||
**وضعیت:** طراحی (Phase 9)
|
||
**اولویت:** 🔴 بالا (یکی از دو فاز باقیمانده)
|
||
|
||
---
|
||
|
||
## 📋 فهرست
|
||
|
||
1. [مقدمه](#مقدمه)
|
||
2. [مفهوم اصلی: پرداخت ترکیبی](#مفهوم-اصلی-پرداخت-ترکیبی)
|
||
3. [تفاوت با Regular Shop](#تفاوت-با-regular-shop)
|
||
4. [معماری جداسازی](#معماری-جداسازی)
|
||
5. [Entity Design](#entity-design)
|
||
6. [Business Rules](#business-rules)
|
||
7. [تسکهای پیادهسازی](#تسک-های-پیاده-سازی)
|
||
|
||
---
|
||
|
||
## 🎯 مقدمه
|
||
|
||
### هدف:
|
||
ایجاد **فروشگاه باشگاه مشتریان** که در آن کاربران میتوانند با **پرداخت ترکیبی** خرید کنند:
|
||
|
||
**🔑 قانون اصلی**:
|
||
- کاربر **نمیتواند** کل محصول را فقط با `DiscountBalance` بخرد
|
||
- کاربر میتواند **درصدی از قیمت** را با `DiscountBalance` پرداخت کند
|
||
- **مابقی مبلغ** باید از طریق **درگاه پرداخت واقعی در Gateway/PYMS** پرداخت شود (نه در CMS)
|
||
|
||
### مثال عملی:
|
||
```
|
||
قیمت محصول: 1,000,000 تومان
|
||
حداکثر تخفیف مجاز: 30%
|
||
DiscountBalance کاربر: 500,000 تومان
|
||
|
||
محاسبه:
|
||
- حداکثر تخفیف قابل استفاده: 1,000,000 × 30% = 300,000 تومان
|
||
- DiscountBalance کاربر: 500,000 تومان (بیشتر از 300,000)
|
||
- مبلغ تخفیف نهایی: 300,000 تومان (محدود به 30%)
|
||
- مبلغ قابل پرداخت از درگاه: 1,000,000 - 300,000 = 700,000 تومان
|
||
|
||
نتیجه:
|
||
✅ کسر از DiscountBalance: 300,000 تومان
|
||
✅ پرداخت از درگاه: 700,000 تومان
|
||
✅ DiscountBalance باقیمانده: 200,000 تومان
|
||
```
|
||
|
||
---
|
||
|
||
## 🔄 مفهوم اصلی: پرداخت ترکیبی
|
||
|
||
### Flow خرید:
|
||
|
||
```
|
||
1. کاربر محصول را انتخاب میکند
|
||
2. سیستم چک میکند:
|
||
- قیمت محصول: X تومان
|
||
- حداکثر تخفیف مجاز: Y%
|
||
- DiscountBalance کاربر: Z تومان
|
||
|
||
3. محاسبه تخفیف:
|
||
MaxDiscountAmount = X × (Y / 100)
|
||
ActualDiscountAmount = Min(Z, MaxDiscountAmount)
|
||
|
||
4. محاسبه مبلغ درگاه:
|
||
GatewayAmount = X - ActualDiscountAmount
|
||
|
||
5. ریدایرکت به درگاه پرداخت (GatewayAmount)
|
||
|
||
6. بعد از بازگشت موفق از درگاه:
|
||
- Verify payment از درگاه
|
||
- کسر ActualDiscountAmount از DiscountBalance
|
||
- ثبت سفارش با دو مبلغ جدا
|
||
- ارسال اطلاعیه به کاربر
|
||
```
|
||
|
||
### مزایا:
|
||
✅ کاربر نمیتواند کل محصول را با تخفیف بخرد (محدودیت درصد)
|
||
✅ کاربر میتواند از موجودی تخفیف خود استفاده کند
|
||
✅ فروشنده مطمئن است مبلغی واقعی دریافت میکند
|
||
✅ سیستم از سوءاستفاده جلوگیری میکند
|
||
|
||
---
|
||
|
||
## 🔄 تفاوت با Regular Shop
|
||
|
||
| ویژگی | فروشگاه عادی (Regular) | فروشگاه تخفیفی (Club Discount) |
|
||
|-------|------------------------|---------------------------|
|
||
| **نوع کیف پول** | `UserWallet.Balance` | `UserWallet.DiscountBalance` + درگاه |
|
||
| **نحوه پرداخت** | 100% از Balance یا IPG | **ترکیبی**: X% از DiscountBalance + مابقی از IPG |
|
||
| **محدودیت تخفیف** | ندارد | **دارد** (MaxDiscountPercent per product) |
|
||
| **نحوه شارژ** | خرید پکیج طلایی (56M) | کمیسیون برداشت Diamond |
|
||
| **ارتباط با باشگاه** | ✅ دارد | ✅ دارد (اعضای باشگاه) |
|
||
| **محصولات** | `Products` | `DiscountProduct` (یا flag در Products) |
|
||
| **سفارش** | `UserOrder` | `DiscountOrder` (با دو مبلغ جدا) |
|
||
| **پرداخت** | یک مرحلهای | **دو مرحلهای**: 1) Verify IPG، 2) Deduct DiscountBalance |
|
||
| **TransactionType** | `DepositIpg` | `DiscountPurchase` (hybrid) |
|
||
|
||
---
|
||
|
||
## 🏗️ معماری جداسازی
|
||
|
||
### اصل طراحی:
|
||
> **"همه چیز جدا، جز درگاه پرداخت و کیف پول"**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ User │
|
||
│ - Id │
|
||
│ - FirstName, LastName, Mobile │
|
||
│ - PackagePurchaseMethod │
|
||
└────────────┬────────────────────────────────────────────────────┘
|
||
│
|
||
├──────────────────────────────────────────┐
|
||
│ │
|
||
▼ ▼
|
||
┌────────────────────────────┐ ┌──────────────────────────┐
|
||
│ UserWallet │ │ Transactions (مشترک) │
|
||
│ - Balance │ │ - Type │
|
||
│ - DiscountBalance │ │ - RefId │
|
||
│ - NetworkBalance │ │ - Amount │
|
||
└────────────┬───────────────┘ └──────────────────────────┘
|
||
│
|
||
├──────────────────────────────────────────┐
|
||
│ │
|
||
▼ ▼
|
||
┌────────────────────────────┐ ┌──────────────────────────┐
|
||
│ Regular Shop │ │ Discount Shop │
|
||
│ - Products │ │ - DiscountProduct │
|
||
│ - Category │ │ - DiscountCategory │
|
||
│ - UserCarts │ │ - DiscountShoppingCart │
|
||
│ - UserOrder │ │ - DiscountOrder │
|
||
│ - FactorDetails │ │ - DiscountOrderDetail │
|
||
└────────────────────────────┘ └──────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 🗄️ Entity Design
|
||
|
||
### 1️⃣ `DiscountProduct`
|
||
|
||
```csharp
|
||
namespace CMSMicroservice.Domain.Entities.DiscountShop;
|
||
|
||
/// <summary>
|
||
/// محصول فروشگاه تخفیفی
|
||
/// </summary>
|
||
public class DiscountProduct : BaseAuditableEntity
|
||
{
|
||
/// <summary>
|
||
/// عنوان محصول
|
||
/// </summary>
|
||
public string Title { get; set; }
|
||
|
||
/// <summary>
|
||
/// توضیحات مختصر
|
||
/// </summary>
|
||
public string ShortInfomation { get; set; }
|
||
|
||
/// <summary>
|
||
/// توضیحات کامل
|
||
/// </summary>
|
||
public string FullInformation { get; set; }
|
||
|
||
/// <summary>
|
||
/// قیمت (ریال)
|
||
/// </summary>
|
||
public long Price { get; set; }
|
||
|
||
/// <summary>
|
||
/// درصد تخفیف
|
||
/// </summary>
|
||
public int DiscountPercent { get; set; }
|
||
|
||
/// <summary>
|
||
/// امتیاز (0 تا 5)
|
||
/// </summary>
|
||
public int Rate { get; set; }
|
||
|
||
/// <summary>
|
||
/// آدرس تصویر اصلی
|
||
/// </summary>
|
||
public string ImagePath { get; set; }
|
||
|
||
/// <summary>
|
||
/// آدرس تصویر کوچک
|
||
/// </summary>
|
||
public string ThumbnailPath { get; set; }
|
||
|
||
/// <summary>
|
||
/// تعداد فروش
|
||
/// </summary>
|
||
public int SaleCount { get; set; }
|
||
|
||
/// <summary>
|
||
/// تعداد بازدید
|
||
/// </summary>
|
||
public int ViewCount { get; set; }
|
||
|
||
/// <summary>
|
||
/// موجودی انبار
|
||
/// </summary>
|
||
public int RemainingCount { get; set; }
|
||
|
||
/// <summary>
|
||
/// وضعیت فعال/غیرفعال
|
||
/// </summary>
|
||
public bool IsActive { get; set; }
|
||
|
||
// Navigation Properties
|
||
public virtual ICollection<DiscountShoppingCart> ShoppingCarts { get; set; }
|
||
public virtual ICollection<DiscountOrderDetail> OrderDetails { get; set; }
|
||
public virtual ICollection<DiscountProductCategory> ProductCategories { get; set; }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2️⃣ `DiscountCategory`
|
||
|
||
```csharp
|
||
namespace CMSMicroservice.Domain.Entities.DiscountShop;
|
||
|
||
/// <summary>
|
||
/// دستهبندی فروشگاه تخفیفی
|
||
/// </summary>
|
||
public class DiscountCategory : BaseAuditableEntity
|
||
{
|
||
/// <summary>
|
||
/// نام لاتین (برای URL)
|
||
/// </summary>
|
||
public string Name { get; set; }
|
||
|
||
/// <summary>
|
||
/// عنوان فارسی
|
||
/// </summary>
|
||
public string Title { get; set; }
|
||
|
||
/// <summary>
|
||
/// توضیحات
|
||
/// </summary>
|
||
public string? Description { get; set; }
|
||
|
||
/// <summary>
|
||
/// آدرس تصویر
|
||
/// </summary>
|
||
public string? ImagePath { get; set; }
|
||
|
||
/// <summary>
|
||
/// شناسه والد (برای دستهبندی چند سطحی)
|
||
/// </summary>
|
||
public long? ParentId { get; set; }
|
||
|
||
/// <summary>
|
||
/// Parent Navigation Property
|
||
/// </summary>
|
||
public virtual DiscountCategory? Parent { get; set; }
|
||
|
||
/// <summary>
|
||
/// فعال/غیرفعال
|
||
/// </summary>
|
||
public bool IsActive { get; set; }
|
||
|
||
/// <summary>
|
||
/// ترتیب نمایش
|
||
/// </summary>
|
||
public int SortOrder { get; set; }
|
||
|
||
// Navigation Properties
|
||
public virtual ICollection<DiscountCategory> Children { get; set; }
|
||
public virtual ICollection<DiscountProductCategory> ProductCategories { get; set; }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 3️⃣ `DiscountProductCategory` (Many-to-Many)
|
||
|
||
```csharp
|
||
namespace CMSMicroservice.Domain.Entities.DiscountShop;
|
||
|
||
/// <summary>
|
||
/// رابطه محصول و دستهبندی در فروشگاه تخفیفی
|
||
/// </summary>
|
||
public class DiscountProductCategory : BaseAuditableEntity
|
||
{
|
||
public long DiscountProductId { get; set; }
|
||
public virtual DiscountProduct DiscountProduct { get; set; }
|
||
|
||
public long DiscountCategoryId { get; set; }
|
||
public virtual DiscountCategory DiscountCategory { get; set; }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 4️⃣ `DiscountShoppingCart`
|
||
|
||
```csharp
|
||
namespace CMSMicroservice.Domain.Entities.DiscountShop;
|
||
|
||
/// <summary>
|
||
/// سبد خرید فروشگاه تخفیفی
|
||
/// </summary>
|
||
public class DiscountShoppingCart : BaseAuditableEntity
|
||
{
|
||
/// <summary>
|
||
/// شناسه کاربر
|
||
/// </summary>
|
||
public long UserId { get; set; }
|
||
|
||
/// <summary>
|
||
/// User Navigation Property
|
||
/// </summary>
|
||
public virtual User User { get; set; }
|
||
|
||
/// <summary>
|
||
/// شناسه محصول
|
||
/// </summary>
|
||
public long DiscountProductId { get; set; }
|
||
|
||
/// <summary>
|
||
/// DiscountProduct Navigation Property
|
||
/// </summary>
|
||
public virtual DiscountProduct DiscountProduct { get; set; }
|
||
|
||
/// <summary>
|
||
/// تعداد
|
||
/// </summary>
|
||
public int Count { get; set; }
|
||
|
||
/// <summary>
|
||
/// قیمت واحد در زمان افزودن به سبد
|
||
/// </summary>
|
||
public long UnitPrice { get; set; }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5️⃣ `DiscountOrder`
|
||
|
||
```csharp
|
||
namespace CMSMicroservice.Domain.Entities.DiscountShop;
|
||
|
||
/// <summary>
|
||
/// سفارش از فروشگاه تخفیفی
|
||
/// </summary>
|
||
public class DiscountOrder : BaseAuditableEntity
|
||
{
|
||
/// <summary>
|
||
/// شناسه کاربر
|
||
/// </summary>
|
||
public long UserId { get; set; }
|
||
|
||
/// <summary>
|
||
/// User Navigation Property
|
||
/// </summary>
|
||
public virtual User User { get; set; }
|
||
|
||
/// <summary>
|
||
/// مبلغ کل سفارش
|
||
/// </summary>
|
||
public long TotalAmount { get; set; }
|
||
|
||
/// <summary>
|
||
/// مبلغ تخفیف
|
||
/// </summary>
|
||
public long DiscountAmount { get; set; }
|
||
|
||
/// <summary>
|
||
/// مبلغ قابل پرداخت
|
||
/// </summary>
|
||
public long PayableAmount { get; set; }
|
||
|
||
/// <summary>
|
||
/// وضعیت پرداخت
|
||
/// </summary>
|
||
public PaymentStatus PaymentStatus { get; set; }
|
||
|
||
/// <summary>
|
||
/// تاریخ پرداخت
|
||
/// </summary>
|
||
public DateTime? PaymentDate { get; set; }
|
||
|
||
/// <summary>
|
||
/// شناسه تراکنش (اگر پرداخت موفق باشد)
|
||
/// </summary>
|
||
public long? TransactionId { get; set; }
|
||
|
||
/// <summary>
|
||
/// Transaction Navigation Property
|
||
/// </summary>
|
||
public virtual Transactions? Transaction { get; set; }
|
||
|
||
/// <summary>
|
||
/// شناسه آدرس کاربر
|
||
/// </summary>
|
||
public long UserAddressId { get; set; }
|
||
|
||
/// <summary>
|
||
/// UserAddress Navigation Property
|
||
/// </summary>
|
||
public virtual UserAddress UserAddress { get; set; }
|
||
|
||
/// <summary>
|
||
/// وضعیت ارسال
|
||
/// </summary>
|
||
public DeliveryStatus DeliveryStatus { get; set; }
|
||
|
||
/// <summary>
|
||
/// کد رهگیری مرسوله
|
||
/// </summary>
|
||
public string? TrackingCode { get; set; }
|
||
|
||
/// <summary>
|
||
/// توضیحات وضعیت ارسال
|
||
/// </summary>
|
||
public string? DeliveryDescription { get; set; }
|
||
|
||
// Navigation Properties
|
||
public virtual ICollection<DiscountOrderDetail> OrderDetails { get; set; }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 6️⃣ `DiscountOrderDetail`
|
||
|
||
```csharp
|
||
namespace CMSMicroservice.Domain.Entities.DiscountShop;
|
||
|
||
/// <summary>
|
||
/// جزئیات سفارش از فروشگاه تخفیفی
|
||
/// </summary>
|
||
public class DiscountOrderDetail : BaseAuditableEntity
|
||
{
|
||
/// <summary>
|
||
/// شناسه سفارش
|
||
/// </summary>
|
||
public long DiscountOrderId { get; set; }
|
||
|
||
/// <summary>
|
||
/// DiscountOrder Navigation Property
|
||
/// </summary>
|
||
public virtual DiscountOrder DiscountOrder { get; set; }
|
||
|
||
/// <summary>
|
||
/// شناسه محصول
|
||
/// </summary>
|
||
public long DiscountProductId { get; set; }
|
||
|
||
/// <summary>
|
||
/// DiscountProduct Navigation Property
|
||
/// </summary>
|
||
public virtual DiscountProduct DiscountProduct { get; set; }
|
||
|
||
/// <summary>
|
||
/// تعداد
|
||
/// </summary>
|
||
public int Quantity { get; set; }
|
||
|
||
/// <summary>
|
||
/// قیمت واحد در زمان ثبت سفارش
|
||
/// </summary>
|
||
public long UnitPrice { get; set; }
|
||
|
||
/// <summary>
|
||
/// درصد تخفیف در زمان ثبت سفارش
|
||
/// </summary>
|
||
public int DiscountPercent { get; set; }
|
||
|
||
/// <summary>
|
||
/// مبلغ کل این آیتم (بعد از تخفیف)
|
||
/// </summary>
|
||
public long TotalPrice { get; set; }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📐 Business Rules
|
||
|
||
### قانون 1: خرید از Discount Shop فقط با DiscountBalance
|
||
|
||
```csharp
|
||
// در زمان Checkout از Discount Shop:
|
||
var wallet = await _context.UserWallets
|
||
.FirstOrDefaultAsync(w => w.UserId == userId);
|
||
|
||
if (wallet.DiscountBalance < order.PayableAmount)
|
||
{
|
||
throw new ValidationException(
|
||
$"موجودی کیف پول تخفیفی شما کافی نیست. " +
|
||
$"موجودی فعلی: {wallet.DiscountBalance:N0} تومان، " +
|
||
$"مبلغ مورد نیاز: {order.PayableAmount:N0} تومان"
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### قانون 2: خرید از Regular Shop فقط با Balance
|
||
|
||
```csharp
|
||
// در زمان Checkout از Regular Shop:
|
||
var wallet = await _context.UserWallets
|
||
.FirstOrDefaultAsync(w => w.UserId == userId);
|
||
|
||
if (wallet.Balance < order.Amount)
|
||
{
|
||
throw new ValidationException(
|
||
$"موجودی کیف پول اصلی شما کافی نیست. " +
|
||
$"موجودی فعلی: {wallet.Balance:N0} تومان، " +
|
||
$"مبلغ مورد نیاز: {order.Amount:N0} تومان"
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### قانون 3: شارژ DiscountBalance از طریق درگاه
|
||
|
||
```csharp
|
||
// در VerifyDiscountWalletChargeCommand:
|
||
wallet.DiscountBalance += amount;
|
||
|
||
var transaction = new Transactions
|
||
{
|
||
Type = TransactionType.DiscountWalletCharge,
|
||
Amount = amount,
|
||
RefId = verifyResult.RefId
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### قانون 4: محصولات Discount Shop جدا از Regular Shop
|
||
|
||
- یک محصول **نمیتواند** هم در `Products` باشد، هم در `DiscountProduct`
|
||
- Admin باید محصولات را جداگانه مدیریت کند
|
||
- هیچ رابطهای بین `Products` و `DiscountProduct` نیست
|
||
|
||
---
|
||
|
||
## 🔄 Flow Diagram: خرید از Discount Shop
|
||
|
||
```
|
||
کاربر → مشاهده محصولات Discount Shop
|
||
↓
|
||
افزودن به DiscountShoppingCart
|
||
↓
|
||
Checkout (بررسی DiscountBalance)
|
||
↓
|
||
ثبت DiscountOrder (PaymentStatus: Pending)
|
||
↓
|
||
کم کردن DiscountBalance از کیف پول
|
||
↓
|
||
ثبت Transaction (Type: Buy) ← این تراکنش برای خرید است
|
||
↓
|
||
ثبت DiscountOrderDetail برای هر محصول
|
||
↓
|
||
بهروزرسانی DiscountOrder (PaymentStatus: Success)
|
||
↓
|
||
خالی کردن DiscountShoppingCart
|
||
↓
|
||
نمایش پیام موفقیت + کد رهگیری
|
||
```
|
||
|
||
**نکته:** در این فلو از درگاه استفاده **نمیشود** چون موجودی از قبل شارژ شده است.
|
||
|
||
---
|
||
|
||
## 📝 تسکهای پیادهسازی
|
||
|
||
### Phase 1: Entity Creation (2 روز)
|
||
|
||
1. **ایجاد namespace جدید**:
|
||
- `CMSMicroservice.Domain/Entities/DiscountShop/`
|
||
|
||
2. **ایجاد Entityها**:
|
||
- `DiscountProduct`
|
||
- `DiscountCategory`
|
||
- `DiscountProductCategory`
|
||
- `DiscountShoppingCart`
|
||
- `DiscountOrder`
|
||
- `DiscountOrderDetail`
|
||
|
||
3. **ایجاد Configurationها**:
|
||
- `DiscountProductConfiguration`
|
||
- `DiscountCategoryConfiguration`
|
||
- و غیره...
|
||
|
||
4. **بهروزرسانی `DbContext`**:
|
||
```csharp
|
||
public DbSet<DiscountProduct> DiscountProducts { get; set; }
|
||
public DbSet<DiscountCategory> DiscountCategories { get; set; }
|
||
// ...
|
||
```
|
||
|
||
5. **ایجاد Migration**:
|
||
```bash
|
||
dotnet ef migrations add AddDiscountShopTables
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 2: Commands & Queries (3 روز)
|
||
|
||
#### DiscountProduct CRUD:
|
||
- `CreateDiscountProductCommand`
|
||
- `UpdateDiscountProductCommand`
|
||
- `DeleteDiscountProductCommand`
|
||
- `GetDiscountProductByIdQuery`
|
||
- `GetDiscountProductsListQuery`
|
||
|
||
#### DiscountCategory CRUD:
|
||
- `CreateDiscountCategoryCommand`
|
||
- `UpdateDiscountCategoryCommand`
|
||
- `DeleteDiscountCategoryCommand`
|
||
- `GetDiscountCategoriesTreeQuery`
|
||
|
||
#### Shopping Cart:
|
||
- `AddToDiscountCartCommand`
|
||
- `RemoveFromDiscountCartCommand`
|
||
- `GetDiscountCartQuery`
|
||
|
||
#### Order:
|
||
- `CreateDiscountOrderCommand` (Checkout)
|
||
- `GetDiscountOrderByIdQuery`
|
||
- `GetMyDiscountOrdersQuery` (برای کاربر)
|
||
- `UpdateDiscountOrderDeliveryCommand` (برای Admin)
|
||
|
||
---
|
||
|
||
### Phase 3: BackOffice.BFF APIs (1 روز)
|
||
|
||
**Proto file**: `DiscountShopContract.proto`
|
||
|
||
```protobuf
|
||
service DiscountShopContract {
|
||
// Product
|
||
rpc CreateDiscountProduct(CreateDiscountProductRequest) returns (CreateDiscountProductResponse);
|
||
rpc UpdateDiscountProduct(UpdateDiscountProductRequest) returns (UpdateDiscountProductResponse);
|
||
rpc GetDiscountProducts(GetDiscountProductsRequest) returns (GetDiscountProductsResponse);
|
||
|
||
// Category
|
||
rpc CreateDiscountCategory(CreateDiscountCategoryRequest) returns (CreateDiscountCategoryResponse);
|
||
rpc GetDiscountCategoriesTree(Empty) returns (GetDiscountCategoriesTreeResponse);
|
||
|
||
// Orders
|
||
rpc GetDiscountOrders(GetDiscountOrdersRequest) returns (GetDiscountOrdersResponse);
|
||
rpc UpdateDiscountOrderDelivery(UpdateDiscountOrderDeliveryRequest) returns (UpdateDiscountOrderDeliveryResponse);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 4: FrontOffice.BFF APIs (1 روز)
|
||
|
||
**Proto file**: `DiscountShopContract.proto` (در FrontOffice.BFF)
|
||
|
||
```protobuf
|
||
service DiscountShopContract {
|
||
// Browse
|
||
rpc GetDiscountProducts(GetDiscountProductsRequest) returns (GetDiscountProductsResponse);
|
||
rpc GetDiscountProductById(GetDiscountProductByIdRequest) returns (GetDiscountProductByIdResponse);
|
||
|
||
// Cart
|
||
rpc AddToDiscountCart(AddToDiscountCartRequest) returns (AddToDiscountCartResponse);
|
||
rpc GetMyDiscountCart(Empty) returns (GetMyDiscountCartResponse);
|
||
rpc RemoveFromDiscountCart(RemoveFromDiscountCartRequest) returns (RemoveFromDiscountCartResponse);
|
||
|
||
// Order
|
||
rpc CheckoutDiscountCart(CheckoutDiscountCartRequest) returns (CheckoutDiscountCartResponse);
|
||
rpc GetMyDiscountOrders(Empty) returns (GetMyDiscountOrdersResponse);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 5: BackOffice UI (3 روز)
|
||
|
||
**صفحات مدیریت:**
|
||
1. **لیست محصولات تخفیفی** + CRUD
|
||
2. **دستهبندیها** (Tree View) + CRUD
|
||
3. **سفارشات تخفیفی** + تغییر وضعیت ارسال
|
||
4. **گزارش فروش** Discount Shop
|
||
|
||
---
|
||
|
||
### Phase 6: FrontOffice UI (3 روز)
|
||
|
||
**صفحات کاربر:**
|
||
1. **لیست محصولات تخفیفی** (با فیلتر دستهبندی)
|
||
2. **جزئیات محصول تخفیفی**
|
||
3. **سبد خرید تخفیفی**
|
||
4. **Checkout** (با نمایش `DiscountBalance`)
|
||
5. **لیست سفارشات تخفیفی کاربر**
|
||
|
||
---
|
||
|
||
### Phase 7: Unit Tests (2 روز)
|
||
|
||
1. تست **CRUD محصولات تخفیفی**
|
||
2. تست **AddToDiscountCart**
|
||
3. تست **CheckoutDiscountCart**:
|
||
- کاربر با موجودی کافی → موفق
|
||
- کاربر با موجودی ناکافی → خطا
|
||
|
||
---
|
||
|
||
### Phase 8: Documentation (0.5 روز)
|
||
|
||
- بهروزرسانی `implementation-progress.md`
|
||
- لینک از `REMAINING-TASKS-CONSOLIDATED.md`
|
||
|
||
---
|
||
|
||
## 📊 خلاصه Timeline
|
||
|
||
| Phase | عنوان | زمان |
|
||
|-------|-------|------|
|
||
| 1 | Entity Creation | 2 روز |
|
||
| 2 | Commands & Queries (CMS) | 3 روز |
|
||
| 3 | BackOffice.BFF APIs | 1 روز |
|
||
| 4 | FrontOffice.BFF APIs | 1 روز |
|
||
| 5 | BackOffice UI | 3 روز |
|
||
| 6 | FrontOffice UI | 3 روز |
|
||
| 7 | Unit Tests | 2 روز |
|
||
| 8 | Documentation | 0.5 روز |
|
||
| **جمع** | | **15.5 روز** (~3 هفته) |
|
||
|
||
---
|
||
|
||
## 🔗 مراجع
|
||
|
||
- [Package Purchase System](./package-purchase-system.md)
|
||
- [Manual Payment System](./manual-payment-system.md)
|
||
- [Implementation Progress](./implementation-progress.md)
|
||
- [REMAINING-TASKS](../REMAINING-TASKS-CONSOLIDATED.md)
|
||
|
||
---
|
||
|
||
**تاریخ آخرین بهروزرسانی:** 2024-12-02
|
||
**نویسنده:** GitHub Copilot
|
||
**وضعیت:** ✅ تایید شده توسط کاربر
|