Files
docs/05-TASKS/DISCOUNT-SHOP-COMPLETION-PLAN.md
T
masoodafar-web 73e1971cc3 feat: Implement Discount Shop Completion Plan with Product Image Gallery, Admin APIs, VAT Calculation, and Sales Reports
- 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.
2026-01-02 00:46:08 +03:30

16 KiB

🛒 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

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

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:

/// <summary>
/// تصاویر گالری محصول
/// </summary>
public virtual ICollection<DiscountProductImage> Images { get; set; }

1.4 به‌روزرسانی DbContext

فایل: CMS/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs

public DbSet<DiscountProductImage> DiscountProductImages { get; set; }

1.5 ایجاد Migration

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

// اضافه کردن به 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/

// 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

// اضافه کردن به 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

// محاسبه 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/

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

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

اضافه کردن متدهای:

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 موجود

آخرین به‌روزرسانی: ۱۰ دی ۱۴۰۴