# 🛒 Discount Shop - Implementation Plan
**تاریخ ایجاد**: ۱۰ دی ۱۴۰۴ (30 December 2025)
**وضعیت**: 🚧 در حال اجرا
**اولویت**: 🔴 بالا
---
## 📊 وضعیت فعلی
### ✅ موارد کامل شده:
| بخش | فایلها | وضعیت |
|-----|---------|-------|
| **Domain Entities** | `DiscountProduct`, `DiscountCategory`, `DiscountOrder`, `DiscountOrderDetail`, `DiscountShoppingCart`, `DiscountProductCategory` | ✅ کامل |
| **CMS Commands** | Create/Update/Delete برای Product, Category, Order, Cart | ✅ کامل |
| **CMS Queries** | GetProducts, GetCategories, GetUserOrders, GetUserCart, GetOrderById | ✅ کامل |
| **Proto Files** | `discountproduct.proto`, `discountcategory.proto`, `discountorder.proto`, `discountshoppingcart.proto` | ✅ کامل |
| **BackOffice UI** | DiscountProductsMainPage, DiscountCategoriesMainPage, DiscountOrdersMainPage, SalesReports | ✅ کامل |
| **BackOffice Services** | IDiscountProductService, IDiscountCategoryService, IDiscountOrderService | ✅ کامل |
| **UI کامپوننت گالری** | ProductImageGallery.razor (فقط کلاینت، بدون Backend) | ⚠️ ناقص |
### ❌ موارد باقیمانده:
| # | مورد | توضیح | تخمین زمان |
|---|------|-------|------------|
| 1 | گالری تصاویر محصول | Entity + CRUD + Proto + Backend | 4 ساعت |
| 2 | API لیست سفارشات ادمین | GetAllDiscountOrders با فیلترها | 2 ساعت |
| 3 | محاسبه VAT | فعالسازی مالیات 10% در سفارشات | 1 ساعت |
| 4 | گزارش فروش | API آماری برای SalesReports | 2 ساعت |
| 5 | اتصال گالری به Backend | Upload + Service در BackOffice | 2 ساعت |
| **جمع** | | | **11 ساعت** |
---
## 📋 مراحل پیادهسازی
---
## مرحله ۱: گالری تصاویر محصول (Backend)
### 1.1 ایجاد Entity
**فایل**: `CMS/src/CMSMicroservice.Domain/Entities/DiscountShop/DiscountProductImage.cs`
```csharp
namespace CMSMicroservice.Domain.Entities.DiscountShop;
///
/// تصویر گالری محصول تخفیفی
///
public class DiscountProductImage : BaseAuditableEntity
{
///
/// شناسه محصول
///
public long DiscountProductId { get; set; }
///
/// محصول
///
public virtual DiscountProduct DiscountProduct { get; set; }
///
/// عنوان تصویر
///
public string? Title { get; set; }
///
/// متن جایگزین (Alt)
///
public string? AltText { get; set; }
///
/// مسیر تصویر اصلی
///
public string ImagePath { get; set; }
///
/// مسیر تصویر کوچک
///
public string? ThumbnailPath { get; set; }
///
/// ترتیب نمایش
///
public int SortOrder { get; set; }
///
/// آیا تصویر اصلی محصول است؟
///
public bool IsPrimary { get; set; }
}
```
### 1.2 ایجاد Configuration
**فایل**: `CMS/src/CMSMicroservice.Infrastructure/Persistence/Configurations/DiscountShop/DiscountProductImageConfiguration.cs`
```csharp
using CMSMicroservice.Domain.Entities.DiscountShop;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations.DiscountShop;
public class DiscountProductImageConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable("DiscountProductImages");
builder.HasKey(x => x.Id);
builder.Property(x => x.Title)
.HasMaxLength(200);
builder.Property(x => x.AltText)
.HasMaxLength(500);
builder.Property(x => x.ImagePath)
.IsRequired()
.HasMaxLength(500);
builder.Property(x => x.ThumbnailPath)
.HasMaxLength(500);
builder.HasOne(x => x.DiscountProduct)
.WithMany(p => p.Images)
.HasForeignKey(x => x.DiscountProductId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(x => x.DiscountProductId);
builder.HasIndex(x => new { x.DiscountProductId, x.SortOrder });
}
}
```
### 1.3 بهروزرسانی DiscountProduct Entity
**فایل**: `CMS/src/CMSMicroservice.Domain/Entities/DiscountShop/DiscountProduct.cs`
اضافه کردن Navigation Property:
```csharp
///
/// تصاویر گالری محصول
///
public virtual ICollection Images { get; set; }
```
### 1.4 بهروزرسانی DbContext
**فایل**: `CMS/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs`
```csharp
public DbSet DiscountProductImages { get; set; }
```
### 1.5 ایجاد Migration
```bash
cd CMS/src/CMSMicroservice.Infrastructure
dotnet ef migrations add AddDiscountProductImages -s ../CMSMicroservice.WebApi
```
### 1.6 ایجاد Commands
**پوشه**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Commands/`
| Command | فایلها |
|---------|---------|
| `AddDiscountProductImage` | Command.cs, Handler.cs, Validator.cs |
| `UpdateDiscountProductImage` | Command.cs, Handler.cs, Validator.cs |
| `DeleteDiscountProductImage` | Command.cs, Handler.cs |
| `ReorderDiscountProductImages` | Command.cs, Handler.cs |
### 1.7 ایجاد Query
**پوشه**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Queries/GetDiscountProductImages/`
| فایل | توضیح |
|------|-------|
| `GetDiscountProductImagesQuery.cs` | Request با ProductId |
| `GetDiscountProductImagesQueryHandler.cs` | Handler |
| `GetDiscountProductImagesResponseDto.cs` | Response با لیست تصاویر |
### 1.8 بهروزرسانی Proto
**فایل**: `CMS/src/CMSMicroservice.Protobuf/Protos/discountproduct.proto`
```protobuf
// اضافه کردن به service DiscountProductContract:
rpc AddDiscountProductImage(AddDiscountProductImageRequest) returns (AddDiscountProductImageResponse);
rpc UpdateDiscountProductImage(UpdateDiscountProductImageRequest) returns (google.protobuf.Empty);
rpc DeleteDiscountProductImage(DeleteDiscountProductImageRequest) returns (google.protobuf.Empty);
rpc ReorderDiscountProductImages(ReorderDiscountProductImagesRequest) returns (google.protobuf.Empty);
rpc GetDiscountProductImages(GetDiscountProductImagesRequest) returns (GetDiscountProductImagesResponse);
// Messages:
message AddDiscountProductImageRequest {
int64 product_id = 1;
string title = 2;
string alt_text = 3;
string image_path = 4;
string thumbnail_path = 5;
int32 sort_order = 6;
bool is_primary = 7;
}
message AddDiscountProductImageResponse {
int64 image_id = 1;
}
message UpdateDiscountProductImageRequest {
int64 image_id = 1;
string title = 2;
string alt_text = 3;
string image_path = 4;
string thumbnail_path = 5;
int32 sort_order = 6;
bool is_primary = 7;
}
message DeleteDiscountProductImageRequest {
int64 image_id = 1;
}
message ReorderDiscountProductImagesRequest {
int64 product_id = 1;
repeated int64 image_ids = 2; // ترتیب جدید
}
message GetDiscountProductImagesRequest {
int64 product_id = 1;
}
message GetDiscountProductImagesResponse {
repeated DiscountProductImageDto images = 1;
}
message DiscountProductImageDto {
int64 id = 1;
int64 product_id = 2;
string title = 3;
string alt_text = 4;
string image_path = 5;
string thumbnail_path = 6;
int32 sort_order = 7;
bool is_primary = 8;
}
```
### 1.9 ایجاد gRPC Service Methods
**فایل**: `CMS/src/CMSMicroservice.WebApi/Services/DiscountProductService.cs`
اضافه کردن 5 متد جدید برای Image CRUD.
---
## مرحله ۲: API لیست سفارشات ادمین
### 2.1 ایجاد Query
**پوشه**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Queries/GetAllDiscountOrders/`
```csharp
// GetAllDiscountOrdersQuery.cs
public class GetAllDiscountOrdersQuery : IRequest
{
public long? UserId { get; set; }
public bool? PaymentCompleted { get; set; }
public int? DeliveryStatus { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public string? SearchQuery { get; set; } // جستجو در شماره سفارش
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 20;
}
```
### 2.2 بهروزرسانی Proto
**فایل**: `CMS/src/CMSMicroservice.Protobuf/Protos/discountorder.proto`
```protobuf
// اضافه کردن به service:
rpc GetAllDiscountOrders(GetAllDiscountOrdersRequest) returns (GetAllDiscountOrdersResponse);
message GetAllDiscountOrdersRequest {
google.protobuf.Int64Value user_id = 1;
google.protobuf.BoolValue payment_completed = 2;
google.protobuf.Int32Value delivery_status = 3;
google.protobuf.Timestamp from_date = 4;
google.protobuf.Timestamp to_date = 5;
google.protobuf.StringValue search_query = 6;
int32 page_number = 7;
int32 page_size = 8;
}
message GetAllDiscountOrdersResponse {
messages.MetaData meta_data = 1;
repeated OrderSummaryDto models = 2;
}
```
### 2.3 بهروزرسانی BackOffice Service
**فایل**: `BackOffice/src/BackOffice/Services/DiscountOrder/DiscountOrderService.cs`
تغییر `GetOrdersAsync` برای استفاده از API جدید (GetAllDiscountOrders به جای GetUserOrders با UserId=0).
---
## مرحله ۳: فعالسازی محاسبه VAT
### 3.1 بهروزرسانی PlaceOrderCommandHandler
**فایل**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Commands/PlaceOrder/PlaceOrderCommandHandler.cs`
```csharp
// محاسبه VAT (10%)
const decimal VatRate = 0.10m;
var vatAmount = (long)(totalAmount * VatRate);
order.VatAmount = vatAmount;
// مبلغ نهایی شامل VAT
var finalAmount = totalAmount + vatAmount;
```
### 3.2 بهروزرسانی Proto Response
**فایل**: `CMS/src/CMSMicroservice.Protobuf/Protos/discountorder.proto`
اضافه کردن `int64 vat_amount` به `PlaceOrderResponse` و `GetOrderByIdResponse`.
### 3.3 بهروزرسانی UI
**فایل**: `BackOffice/src/BackOffice/Pages/DiscountShop/Components/OrderDetailsDialog.razor`
نمایش VAT در جزئیات سفارش.
---
## مرحله ۴: گزارش فروش (Statistics API)
### 4.1 ایجاد Query
**پوشه**: `CMS/src/CMSMicroservice.Application/DiscountShopCQ/Queries/GetDiscountShopStatistics/`
```csharp
public class GetDiscountShopStatisticsResponseDto
{
// خلاصه کلی
public long TotalSales { get; set; }
public int TotalOrders { get; set; }
public int TotalProducts { get; set; }
public int TotalCustomers { get; set; }
// گزارش بازه زمانی
public long PeriodSales { get; set; }
public int PeriodOrders { get; set; }
// پرفروشترینها
public List TopProducts { get; set; }
// فروش روزانه (برای نمودار)
public List DailySales { get; set; }
}
public class TopProductDto
{
public long ProductId { get; set; }
public string Title { get; set; }
public int SalesCount { get; set; }
public long TotalRevenue { get; set; }
}
public class DailySalesDto
{
public DateTime Date { get; set; }
public long Amount { get; set; }
public int OrderCount { get; set; }
}
```
### 4.2 بهروزرسانی Proto
**فایل**: `CMS/src/CMSMicroservice.Protobuf/Protos/discountorder.proto`
```protobuf
rpc GetDiscountShopStatistics(GetDiscountShopStatisticsRequest) returns (GetDiscountShopStatisticsResponse);
message GetDiscountShopStatisticsRequest {
google.protobuf.Timestamp from_date = 1;
google.protobuf.Timestamp to_date = 2;
}
message GetDiscountShopStatisticsResponse {
int64 total_sales = 1;
int32 total_orders = 2;
int32 total_products = 3;
int32 total_customers = 4;
int64 period_sales = 5;
int32 period_orders = 6;
repeated TopProductDto top_products = 7;
repeated DailySalesDto daily_sales = 8;
}
```
### 4.3 تکمیل SalesReports.razor
**فایل**: `BackOffice/src/BackOffice/Pages/DiscountShop/SalesReports.razor`
اتصال به API جدید و نمایش:
- کارتهای آماری (Total Sales, Orders, Products, Customers)
- جدول پرفروشترین محصولات
- نمودار فروش روزانه (MudChart)
---
## مرحله ۵: اتصال گالری به Backend
### 5.1 ایجاد Service در BackOffice
**فایل**: `BackOffice/src/BackOffice/Services/DiscountProduct/IDiscountProductService.cs`
اضافه کردن متدهای:
```csharp
Task> GetProductImagesAsync(long productId);
Task AddProductImageAsync(AddProductImageDto dto);
Task UpdateProductImageAsync(UpdateProductImageDto dto);
Task DeleteProductImageAsync(long imageId);
Task ReorderProductImagesAsync(long productId, List imageIds);
```
### 5.2 بهروزرسانی ProductFormDialog
**فایل**: `BackOffice/src/BackOffice/Pages/DiscountShop/Components/ProductFormDialog.razor`
- در حالت Edit: بارگذاری تصاویر موجود از API
- اتصال کامپوننت `ProductImageGallery` به متدهای Service
- ذخیره تغییرات گالری همزمان با ذخیره محصول
### 5.3 آپلود فایل
بررسی سیستم آپلود موجود در پروژه:
- اگر MinIO/S3 استفاده میشود: استفاده از همان سرویس
- اگر فایلسیستم: ایجاد endpoint آپلود در CMS
---
## 📝 Checklist
### مرحله ۱: گالری تصاویر
- [ ] ایجاد Entity `DiscountProductImage`
- [ ] ایجاد Configuration
- [ ] بهروزرسانی `DiscountProduct` Entity
- [ ] بهروزرسانی DbContext
- [ ] ایجاد و اجرای Migration
- [ ] ایجاد Commands (Add, Update, Delete, Reorder)
- [ ] ایجاد Query (GetImages)
- [ ] بهروزرسانی Proto
- [ ] پیادهسازی gRPC Service Methods
- [ ] تست با Postman/gRPCurl
### مرحله ۲: API سفارشات ادمین
- [ ] ایجاد Query `GetAllDiscountOrders`
- [ ] بهروزرسانی Proto
- [ ] پیادهسازی gRPC Service Method
- [ ] بهروزرسانی BackOffice Service
- [ ] تست UI DiscountOrdersMainPage
### مرحله ۳: محاسبه VAT
- [ ] بهروزرسانی `PlaceOrderCommandHandler`
- [ ] بهروزرسانی Proto (VatAmount در responses)
- [ ] بهروزرسانی UI نمایش سفارش
- [ ] تست محاسبه VAT
### مرحله ۴: گزارش فروش
- [ ] ایجاد Query `GetDiscountShopStatistics`
- [ ] بهروزرسانی Proto
- [ ] پیادهسازی gRPC Service Method
- [ ] ایجاد Service در BackOffice
- [ ] تکمیل UI `SalesReports.razor`
### مرحله ۵: اتصال گالری
- [ ] اضافه کردن متدهای Image به Service
- [ ] بهروزرسانی ProductFormDialog
- [ ] پیادهسازی/اتصال به سیستم آپلود
- [ ] تست کامل گالری
---
## 🔗 فایلهای مرتبط
| فایل | توضیح |
|------|-------|
| `totalDoc/01-BUSINESS/discount-shop-business.md` | مستندات اصلی طراحی |
| `totalDoc/03-BACKEND/BackOffice.BFF/discount-shop-integration.md` | پلن اولیه integration |
| `CMS/src/CMSMicroservice.Domain/Entities/DiscountShop/` | Entity های موجود |
| `CMS/src/CMSMicroservice.Application/DiscountShopCQ/` | Commands و Queries موجود |
| `BackOffice/src/BackOffice/Pages/DiscountShop/` | صفحات UI موجود |
---
**آخرین بهروزرسانی**: ۱۰ دی ۱۴۰۴