73e1971cc3
- Added DiscountProductImage entity and related configurations for product image gallery. - Created commands and queries for managing product images. - Developed GetAllDiscountOrders API for admin order management with various filters. - Implemented VAT calculation service and integrated it into order processing. - Created Sales Reports API with support for daily, weekly, and monthly reports. - Completed gRPC services for BackOffice.BFF to expose new APIs. - Updated Proto files and project references accordingly.
508 lines
16 KiB
Markdown
508 lines
16 KiB
Markdown
# 🛒 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;
|
|
|
|
/// <summary>
|
|
/// تصویر گالری محصول تخفیفی
|
|
/// </summary>
|
|
public class DiscountProductImage : BaseAuditableEntity
|
|
{
|
|
/// <summary>
|
|
/// شناسه محصول
|
|
/// </summary>
|
|
public long DiscountProductId { get; set; }
|
|
|
|
/// <summary>
|
|
/// محصول
|
|
/// </summary>
|
|
public virtual DiscountProduct DiscountProduct { get; set; }
|
|
|
|
/// <summary>
|
|
/// عنوان تصویر
|
|
/// </summary>
|
|
public string? Title { get; set; }
|
|
|
|
/// <summary>
|
|
/// متن جایگزین (Alt)
|
|
/// </summary>
|
|
public string? AltText { get; set; }
|
|
|
|
/// <summary>
|
|
/// مسیر تصویر اصلی
|
|
/// </summary>
|
|
public string ImagePath { get; set; }
|
|
|
|
/// <summary>
|
|
/// مسیر تصویر کوچک
|
|
/// </summary>
|
|
public string? ThumbnailPath { get; set; }
|
|
|
|
/// <summary>
|
|
/// ترتیب نمایش
|
|
/// </summary>
|
|
public int SortOrder { get; set; }
|
|
|
|
/// <summary>
|
|
/// آیا تصویر اصلی محصول است؟
|
|
/// </summary>
|
|
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<DiscountProductImage>
|
|
{
|
|
public void Configure(EntityTypeBuilder<DiscountProductImage> 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
|
|
/// <summary>
|
|
/// تصاویر گالری محصول
|
|
/// </summary>
|
|
public virtual ICollection<DiscountProductImage> Images { get; set; }
|
|
```
|
|
|
|
### 1.4 بهروزرسانی DbContext
|
|
|
|
**فایل**: `CMS/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs`
|
|
|
|
```csharp
|
|
public DbSet<DiscountProductImage> 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<GetAllDiscountOrdersResponseDto>
|
|
{
|
|
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<TopProductDto> TopProducts { get; set; }
|
|
|
|
// فروش روزانه (برای نمودار)
|
|
public List<DailySalesDto> 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<List<DiscountProductImageDto>> GetProductImagesAsync(long productId);
|
|
Task<long> AddProductImageAsync(AddProductImageDto dto);
|
|
Task UpdateProductImageAsync(UpdateProductImageDto dto);
|
|
Task DeleteProductImageAsync(long imageId);
|
|
Task ReorderProductImagesAsync(long productId, List<long> 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 موجود |
|
|
|
|
---
|
|
|
|
**آخرین بهروزرسانی**: ۱۰ دی ۱۴۰۴
|