feat: Implement Discount Sales Report Query and Handler
Build and Deploy / build (push) Successful in 2m33s
Build and Deploy / build (push) Successful in 2m33s
- Added GetDiscountSalesReportQuery and GetDiscountSalesReportQueryHandler to fetch discount sales reports. - Introduced DiscountSalesReportDto, SalesSummaryDto, and PeriodSalesDto for structured report data. feat: Add Discount Product Image Management - Created AddDiscountProductImageCommand and AddDiscountProductImageCommandHandler for image uploads. - Implemented DeleteDiscountProductImageCommand and DeleteDiscountProductImageCommandHandler for image deletion. - Added ReorderDiscountProductImagesCommand and ReorderDiscountProductImagesCommandHandler for image reordering. - Developed UpdateDiscountProductImageCommand and UpdateDiscountProductImageCommandHandler for image updates. feat: Enhance Product Tag Management - Introduced DeleteProductTagCommand and DeleteProductTagCommandHandler for tag deletion. - Added UpdateProductTagCommand and UpdateProductTagCommandHandler for updating product tags. - Implemented GetAllProductTagsQuery and GetAllProductTagsQueryHandler for fetching all product tags. - Created GetProductTagQuery and GetProductTagQueryHandler for retrieving specific product tags. feat: Develop Web API Services for Discount and Product Management - Implemented DiscountCategoryService for managing discount categories. - Created DiscountOrderService for handling discount orders and related queries. - Developed DiscountProductService for managing discount products and their images. - Added DiscountShoppingCartService for managing shopping cart operations. - Implemented ProductTagService for managing product tags and their associations. - Created TagService for managing tags and their related operations.
This commit is contained in:
@@ -5,14 +5,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Exclude handlers that need proto fixes -->
|
||||
<!-- All handlers now ENABLED with BFF Protobuf and Mapster profiles -->
|
||||
<!-- ProcessWithdrawal, DiscountOrder, DiscountShoppingCart fixed and enabled -->
|
||||
<ItemGroup>
|
||||
<!-- DiscountShoppingCart - FrontOffice-only feature, not needed in BackOffice -->
|
||||
<!-- این feature فقط برای مشتریان FrontOffice است. مدیریت سبد خرید توسط ادمین در آینده اضافه خواهد شد -->
|
||||
<Compile Remove="DiscountShoppingCartCQ/**/*.cs" />
|
||||
|
||||
<!-- All handlers now ENABLED with BFF Protobuf and Mapster profiles -->
|
||||
<!-- ProcessWithdrawal, DiscountOrder fixed and enabled -->
|
||||
<!-- All excluded handlers have been enabled -->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
namespace BackOffice.BFF.Application.DiscountOrderCQ.Queries.GetAllDiscountOrders;
|
||||
|
||||
/// <summary>
|
||||
/// کوئری دریافت همه سفارشات فروشگاه تخفیفی برای ادمین
|
||||
/// </summary>
|
||||
public class GetAllDiscountOrdersQuery : IRequest<GetAllDiscountOrdersResponseDto>
|
||||
{
|
||||
public int PageNumber { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
|
||||
public long? UserId { get; set; }
|
||||
public int? PaymentStatus { get; set; }
|
||||
public int? DeliveryStatus { get; set; }
|
||||
public string? UserMobile { get; set; }
|
||||
public string? TrackingCode { get; set; }
|
||||
public DateTime? FromDate { get; set; }
|
||||
public DateTime? ToDate { get; set; }
|
||||
public long? MinAmount { get; set; }
|
||||
public long? MaxAmount { get; set; }
|
||||
}
|
||||
|
||||
public class GetAllDiscountOrdersResponseDto
|
||||
{
|
||||
public MetaData MetaData { get; set; } = new();
|
||||
public List<AdminOrderDto> Orders { get; set; } = new();
|
||||
}
|
||||
|
||||
public class AdminOrderDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public string UserFullName { get; set; } = string.Empty;
|
||||
public string UserMobile { get; set; } = string.Empty;
|
||||
public long TotalAmount { get; set; }
|
||||
public long DiscountBalanceUsed { get; set; }
|
||||
public long GatewayAmountPaid { get; set; }
|
||||
public long VatAmount { get; set; }
|
||||
public int PaymentStatus { get; set; }
|
||||
public DateTime? PaymentDate { get; set; }
|
||||
public int DeliveryStatus { get; set; }
|
||||
public DateTime? DeliveryDate { get; set; }
|
||||
public string? ShippingAddress { get; set; }
|
||||
public string? ReceiverName { get; set; }
|
||||
public string? ReceiverMobile { get; set; }
|
||||
public string? TrackingCode { get; set; }
|
||||
public string? AdminNote { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime? LastModified { get; set; }
|
||||
public int ItemsCount { get; set; }
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountOrder;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
namespace BackOffice.BFF.Application.DiscountOrderCQ.Queries.GetAllDiscountOrders;
|
||||
|
||||
public class GetAllDiscountOrdersQueryHandler : IRequestHandler<GetAllDiscountOrdersQuery, GetAllDiscountOrdersResponseDto>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public GetAllDiscountOrdersQueryHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<GetAllDiscountOrdersResponseDto> Handle(GetAllDiscountOrdersQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var grpcRequest = new GetAllDiscountOrdersRequest
|
||||
{
|
||||
PageNumber = request.PageNumber,
|
||||
PageSize = request.PageSize
|
||||
};
|
||||
|
||||
if (request.UserId.HasValue)
|
||||
grpcRequest.UserId = request.UserId.Value;
|
||||
|
||||
if (request.PaymentStatus.HasValue)
|
||||
grpcRequest.PaymentStatus = request.PaymentStatus.Value;
|
||||
|
||||
if (request.DeliveryStatus.HasValue)
|
||||
grpcRequest.DeliveryStatus = request.DeliveryStatus.Value;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.UserMobile))
|
||||
grpcRequest.UserMobile = request.UserMobile;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.TrackingCode))
|
||||
grpcRequest.TrackingCode = request.TrackingCode;
|
||||
|
||||
if (request.FromDate.HasValue)
|
||||
grpcRequest.FromDate = Timestamp.FromDateTime(DateTime.SpecifyKind(request.FromDate.Value, DateTimeKind.Utc));
|
||||
|
||||
if (request.ToDate.HasValue)
|
||||
grpcRequest.ToDate = Timestamp.FromDateTime(DateTime.SpecifyKind(request.ToDate.Value, DateTimeKind.Utc));
|
||||
|
||||
if (request.MinAmount.HasValue)
|
||||
grpcRequest.MinAmount = request.MinAmount.Value;
|
||||
|
||||
if (request.MaxAmount.HasValue)
|
||||
grpcRequest.MaxAmount = request.MaxAmount.Value;
|
||||
|
||||
var response = await _context.DiscountOrders.GetAllDiscountOrdersAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||
|
||||
return new GetAllDiscountOrdersResponseDto
|
||||
{
|
||||
MetaData = new MetaData
|
||||
{
|
||||
CurrentPage = response.MetaData.CurrentPage,
|
||||
TotalPage = response.MetaData.TotalPage,
|
||||
PageSize = response.MetaData.PageSize,
|
||||
TotalCount = response.MetaData.TotalCount,
|
||||
HasPrevious = response.MetaData.HasPrevious,
|
||||
HasNext = response.MetaData.HasNext
|
||||
},
|
||||
Orders = response.Models.Select(o => new AdminOrderDto
|
||||
{
|
||||
Id = o.Id,
|
||||
UserId = o.UserId,
|
||||
UserFullName = o.UserFullName,
|
||||
UserMobile = o.UserMobile,
|
||||
TotalAmount = o.TotalAmount,
|
||||
DiscountBalanceUsed = o.DiscountBalanceUsed,
|
||||
GatewayAmountPaid = o.GatewayAmountPaid,
|
||||
VatAmount = o.VatAmount,
|
||||
PaymentStatus = (int)o.PaymentStatus,
|
||||
PaymentDate = o.PaymentDate?.ToDateTime(),
|
||||
DeliveryStatus = (int)o.DeliveryStatus,
|
||||
DeliveryDate = o.DeliveryDate?.ToDateTime(),
|
||||
ShippingAddress = o.ShippingAddress,
|
||||
ReceiverName = o.ReceiverName,
|
||||
ReceiverMobile = o.ReceiverMobile,
|
||||
TrackingCode = o.TrackingCode,
|
||||
AdminNote = o.AdminNote,
|
||||
Created = o.Created.ToDateTime(),
|
||||
LastModified = o.LastModified?.ToDateTime(),
|
||||
ItemsCount = o.ItemsCount
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
namespace BackOffice.BFF.Application.DiscountOrderCQ.Queries.GetDiscountSalesReport;
|
||||
|
||||
public class GetDiscountSalesReportQuery : IRequest<DiscountSalesReportDto>
|
||||
{
|
||||
public DateTime FromDate { get; set; }
|
||||
public DateTime ToDate { get; set; }
|
||||
public ReportPeriodType PeriodType { get; set; } = ReportPeriodType.Daily;
|
||||
}
|
||||
|
||||
public enum ReportPeriodType
|
||||
{
|
||||
Summary = 0,
|
||||
Daily = 1,
|
||||
Weekly = 2,
|
||||
Monthly = 3
|
||||
}
|
||||
|
||||
public class DiscountSalesReportDto
|
||||
{
|
||||
public DateTime FromDate { get; set; }
|
||||
public DateTime ToDate { get; set; }
|
||||
public ReportPeriodType PeriodType { get; set; }
|
||||
public SalesSummaryDto Summary { get; set; } = new();
|
||||
public List<PeriodSalesDto> Periods { get; set; } = new();
|
||||
}
|
||||
|
||||
public class SalesSummaryDto
|
||||
{
|
||||
public int TotalOrders { get; set; }
|
||||
public int SuccessfulOrders { get; set; }
|
||||
public int PendingOrders { get; set; }
|
||||
public int RejectedOrders { get; set; }
|
||||
public decimal TotalRevenue { get; set; }
|
||||
public decimal TotalDiscountUsed { get; set; }
|
||||
public decimal TotalGatewayPayments { get; set; }
|
||||
public decimal TotalVat { get; set; }
|
||||
public int TotalItemsSold { get; set; }
|
||||
public int UniqueCustomers { get; set; }
|
||||
public decimal AverageOrderValue { get; set; }
|
||||
}
|
||||
|
||||
public class PeriodSalesDto
|
||||
{
|
||||
public string PeriodLabel { get; set; } = string.Empty;
|
||||
public DateTime PeriodStart { get; set; }
|
||||
public DateTime PeriodEnd { get; set; }
|
||||
public int OrderCount { get; set; }
|
||||
public decimal Revenue { get; set; }
|
||||
public decimal DiscountUsed { get; set; }
|
||||
public decimal GatewayPayments { get; set; }
|
||||
public decimal VatAmount { get; set; }
|
||||
public int ItemsSold { get; set; }
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountOrder;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
namespace BackOffice.BFF.Application.DiscountOrderCQ.Queries.GetDiscountSalesReport;
|
||||
|
||||
public class GetDiscountSalesReportQueryHandler : IRequestHandler<GetDiscountSalesReportQuery, DiscountSalesReportDto>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public GetDiscountSalesReportQueryHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<DiscountSalesReportDto> Handle(GetDiscountSalesReportQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var grpcRequest = new GetDiscountSalesReportRequest
|
||||
{
|
||||
FromDate = Timestamp.FromDateTime(DateTime.SpecifyKind(request.FromDate, DateTimeKind.Utc)),
|
||||
ToDate = Timestamp.FromDateTime(DateTime.SpecifyKind(request.ToDate, DateTimeKind.Utc)),
|
||||
ReportType = (SalesReportType)request.PeriodType
|
||||
};
|
||||
|
||||
var response = await _context.DiscountOrders.GetDiscountSalesReportAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||
|
||||
return new DiscountSalesReportDto
|
||||
{
|
||||
FromDate = request.FromDate,
|
||||
ToDate = request.ToDate,
|
||||
PeriodType = request.PeriodType,
|
||||
Summary = new SalesSummaryDto
|
||||
{
|
||||
TotalOrders = response.Summary.TotalOrders,
|
||||
SuccessfulOrders = response.Summary.CompletedOrders,
|
||||
PendingOrders = response.Summary.PendingOrders,
|
||||
RejectedOrders = response.Summary.CancelledOrders,
|
||||
TotalRevenue = response.Summary.TotalSalesAmount,
|
||||
TotalDiscountUsed = response.Summary.TotalDiscountUsed,
|
||||
TotalGatewayPayments = response.Summary.TotalGatewayPaid,
|
||||
TotalVat = response.Summary.TotalVatAmount,
|
||||
TotalItemsSold = response.Summary.TotalProductsSold,
|
||||
UniqueCustomers = response.Summary.UniqueCustomers,
|
||||
AverageOrderValue = response.Summary.AverageOrderValue
|
||||
},
|
||||
Periods = response.Periods.Select(p => new PeriodSalesDto
|
||||
{
|
||||
PeriodLabel = p.PeriodLabel,
|
||||
PeriodStart = p.PeriodStart.ToDateTime(),
|
||||
PeriodEnd = p.PeriodEnd.ToDateTime(),
|
||||
OrderCount = p.OrdersCount,
|
||||
Revenue = p.TotalAmount,
|
||||
DiscountUsed = p.DiscountUsed,
|
||||
GatewayPayments = p.GatewayPaid,
|
||||
VatAmount = 0, // Not in proto response
|
||||
ItemsSold = 0 // Not in proto response
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Commands.AddDiscountProductImage;
|
||||
|
||||
public class AddDiscountProductImageCommand : IRequest<AddDiscountProductImageResponseDto>
|
||||
{
|
||||
public long DiscountProductId { get; set; }
|
||||
public ImageFileInput? ImageFile { get; set; }
|
||||
public ImageFileInput? ThumbnailFile { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? AltText { get; set; }
|
||||
}
|
||||
|
||||
public class ImageFileInput
|
||||
{
|
||||
public byte[] Data { get; set; } = [];
|
||||
public string FileName { get; set; } = string.Empty;
|
||||
public string MimeType { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class AddDiscountProductImageResponseDto
|
||||
{
|
||||
public long ImageId { get; set; }
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountProduct;
|
||||
using Google.Protobuf;
|
||||
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Commands.AddDiscountProductImage;
|
||||
|
||||
public class AddDiscountProductImageCommandHandler : IRequestHandler<AddDiscountProductImageCommand, AddDiscountProductImageResponseDto>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public AddDiscountProductImageCommandHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<AddDiscountProductImageResponseDto> Handle(AddDiscountProductImageCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var addRequest = new AddDiscountProductImageRequest
|
||||
{
|
||||
DiscountProductId = request.DiscountProductId
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.Title))
|
||||
addRequest.Title = request.Title;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.AltText))
|
||||
addRequest.AltText = request.AltText;
|
||||
|
||||
// Upload Image
|
||||
if (request.ImageFile != null && request.ImageFile.Data != null && request.ImageFile.Data.Length > 0)
|
||||
{
|
||||
var imageFileInfo = await _context.FileInfos.CreateNewFileInfoAsync(new()
|
||||
{
|
||||
Directory = "Images/DiscountShop/Products/Gallery",
|
||||
IsBase64 = false,
|
||||
MIME = request.ImageFile.MimeType,
|
||||
FileName = request.ImageFile.FileName,
|
||||
File = ByteString.CopyFrom(request.ImageFile.Data)
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
if (imageFileInfo != null && !string.IsNullOrWhiteSpace(imageFileInfo.File))
|
||||
addRequest.ImagePath = imageFileInfo.File;
|
||||
}
|
||||
|
||||
// Upload Thumbnail
|
||||
if (request.ThumbnailFile != null && request.ThumbnailFile.Data != null && request.ThumbnailFile.Data.Length > 0)
|
||||
{
|
||||
var thumbnailFileInfo = await _context.FileInfos.CreateNewFileInfoAsync(new()
|
||||
{
|
||||
Directory = "Images/DiscountShop/Products/Gallery/Thumbnails",
|
||||
IsBase64 = false,
|
||||
MIME = request.ThumbnailFile.MimeType,
|
||||
FileName = request.ThumbnailFile.FileName,
|
||||
File = ByteString.CopyFrom(request.ThumbnailFile.Data)
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
if (thumbnailFileInfo != null && !string.IsNullOrWhiteSpace(thumbnailFileInfo.File))
|
||||
addRequest.ThumbnailPath = thumbnailFileInfo.File;
|
||||
}
|
||||
|
||||
var response = await _context.DiscountProducts.AddDiscountProductImageAsync(addRequest, cancellationToken: cancellationToken);
|
||||
|
||||
return new AddDiscountProductImageResponseDto
|
||||
{
|
||||
ImageId = response.ImageId
|
||||
};
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Commands.DeleteDiscountProductImage;
|
||||
|
||||
public class DeleteDiscountProductImageCommand : IRequest
|
||||
{
|
||||
public long Id { get; set; }
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountProduct;
|
||||
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Commands.DeleteDiscountProductImage;
|
||||
|
||||
public class DeleteDiscountProductImageCommandHandler : IRequestHandler<DeleteDiscountProductImageCommand>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public DeleteDiscountProductImageCommandHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(DeleteDiscountProductImageCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await _context.DiscountProducts.DeleteDiscountProductImageAsync(new DeleteDiscountProductImageRequest
|
||||
{
|
||||
Id = request.Id
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Commands.ReorderDiscountProductImages;
|
||||
|
||||
public class ReorderDiscountProductImagesCommand : IRequest
|
||||
{
|
||||
public long DiscountProductId { get; set; }
|
||||
public List<long> ImageIds { get; set; } = new();
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountProduct;
|
||||
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Commands.ReorderDiscountProductImages;
|
||||
|
||||
public class ReorderDiscountProductImagesCommandHandler : IRequestHandler<ReorderDiscountProductImagesCommand>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public ReorderDiscountProductImagesCommandHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(ReorderDiscountProductImagesCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var reorderRequest = new ReorderDiscountProductImagesRequest
|
||||
{
|
||||
DiscountProductId = request.DiscountProductId
|
||||
};
|
||||
reorderRequest.ImageIds.AddRange(request.ImageIds);
|
||||
|
||||
await _context.DiscountProducts.ReorderDiscountProductImagesAsync(reorderRequest, cancellationToken: cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using BackOffice.BFF.Application.DiscountProductCQ.Commands.AddDiscountProductImage;
|
||||
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Commands.UpdateDiscountProductImage;
|
||||
|
||||
public class UpdateDiscountProductImageCommand : IRequest
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public ImageFileInput? ImageFile { get; set; }
|
||||
public ImageFileInput? ThumbnailFile { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? AltText { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountProduct;
|
||||
using Google.Protobuf;
|
||||
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Commands.UpdateDiscountProductImage;
|
||||
|
||||
public class UpdateDiscountProductImageCommandHandler : IRequestHandler<UpdateDiscountProductImageCommand>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public UpdateDiscountProductImageCommandHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(UpdateDiscountProductImageCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var updateRequest = new UpdateDiscountProductImageRequest
|
||||
{
|
||||
Id = request.Id,
|
||||
IsActive = request.IsActive
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.Title))
|
||||
updateRequest.Title = request.Title;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.AltText))
|
||||
updateRequest.AltText = request.AltText;
|
||||
|
||||
// Upload new Image if provided
|
||||
if (request.ImageFile != null && request.ImageFile.Data != null && request.ImageFile.Data.Length > 0)
|
||||
{
|
||||
var imageFileInfo = await _context.FileInfos.CreateNewFileInfoAsync(new()
|
||||
{
|
||||
Directory = "Images/DiscountShop/Products/Gallery",
|
||||
IsBase64 = false,
|
||||
MIME = request.ImageFile.MimeType,
|
||||
FileName = request.ImageFile.FileName,
|
||||
File = ByteString.CopyFrom(request.ImageFile.Data)
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
if (imageFileInfo != null && !string.IsNullOrWhiteSpace(imageFileInfo.File))
|
||||
updateRequest.ImagePath = imageFileInfo.File;
|
||||
}
|
||||
|
||||
// Upload new Thumbnail if provided
|
||||
if (request.ThumbnailFile != null && request.ThumbnailFile.Data != null && request.ThumbnailFile.Data.Length > 0)
|
||||
{
|
||||
var thumbnailFileInfo = await _context.FileInfos.CreateNewFileInfoAsync(new()
|
||||
{
|
||||
Directory = "Images/DiscountShop/Products/Gallery/Thumbnails",
|
||||
IsBase64 = false,
|
||||
MIME = request.ThumbnailFile.MimeType,
|
||||
FileName = request.ThumbnailFile.FileName,
|
||||
File = ByteString.CopyFrom(request.ThumbnailFile.Data)
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
if (thumbnailFileInfo != null && !string.IsNullOrWhiteSpace(thumbnailFileInfo.File))
|
||||
updateRequest.ThumbnailPath = thumbnailFileInfo.File;
|
||||
}
|
||||
|
||||
await _context.DiscountProducts.UpdateDiscountProductImageAsync(updateRequest, cancellationToken: cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Queries.GetDiscountProductImages;
|
||||
|
||||
public class GetDiscountProductImagesQuery : IRequest<GetDiscountProductImagesResponseDto>
|
||||
{
|
||||
public long DiscountProductId { get; set; }
|
||||
public bool OnlyActive { get; set; } = false; // Admin can see all
|
||||
}
|
||||
|
||||
public class GetDiscountProductImagesResponseDto
|
||||
{
|
||||
public List<DiscountProductImageDto> Images { get; set; } = new();
|
||||
}
|
||||
|
||||
public class DiscountProductImageDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long DiscountProductId { get; set; }
|
||||
public string ImagePath { get; set; } = string.Empty;
|
||||
public string? ThumbnailPath { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? AltText { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using CMSMicroservice.Protobuf.Protos.DiscountProduct;
|
||||
|
||||
namespace BackOffice.BFF.Application.DiscountProductCQ.Queries.GetDiscountProductImages;
|
||||
|
||||
public class GetDiscountProductImagesQueryHandler : IRequestHandler<GetDiscountProductImagesQuery, GetDiscountProductImagesResponseDto>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public GetDiscountProductImagesQueryHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<GetDiscountProductImagesResponseDto> Handle(GetDiscountProductImagesQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var grpcRequest = new GetDiscountProductImagesRequest
|
||||
{
|
||||
DiscountProductId = request.DiscountProductId,
|
||||
OnlyActive = request.OnlyActive
|
||||
};
|
||||
|
||||
var response = await _context.DiscountProducts.GetDiscountProductImagesAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||
|
||||
return new GetDiscountProductImagesResponseDto
|
||||
{
|
||||
Images = response.Images.Select(i => new DiscountProductImageDto
|
||||
{
|
||||
Id = i.Id,
|
||||
DiscountProductId = i.DiscountProductId,
|
||||
ImagePath = i.ImagePath,
|
||||
ThumbnailPath = i.ThumbnailPath,
|
||||
Title = i.Title,
|
||||
AltText = i.AltText,
|
||||
SortOrder = i.SortOrder,
|
||||
IsActive = i.IsActive
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -5,6 +5,6 @@ public record RemoveFromCartCommand : IRequest
|
||||
/// <summary>شناسه کاربر</summary>
|
||||
public long UserId { get; init; }
|
||||
|
||||
/// <summary>شناسه آیتم سبد خرید</summary>
|
||||
public long CartItemId { get; init; }
|
||||
/// <summary>شناسه محصول</summary>
|
||||
public long ProductId { get; init; }
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class RemoveFromCartCommandHandler : IRequestHandler<RemoveFromCartComman
|
||||
new RemoveFromCartRequest
|
||||
{
|
||||
UserId = request.UserId,
|
||||
CartItemId = request.CartItemId
|
||||
ProductId = request.ProductId
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ public class RemoveFromCartCommandValidator : AbstractValidator<RemoveFromCartCo
|
||||
RuleFor(x => x.UserId)
|
||||
.GreaterThan(0).WithMessage("شناسه کاربر نامعتبر است");
|
||||
|
||||
RuleFor(x => x.CartItemId)
|
||||
.GreaterThan(0).WithMessage("شناسه آیتم سبد خرید نامعتبر است");
|
||||
RuleFor(x => x.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول نامعتبر است");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ public record UpdateCartItemCountCommand : IRequest
|
||||
/// <summary>شناسه کاربر</summary>
|
||||
public long UserId { get; init; }
|
||||
|
||||
/// <summary>شناسه آیتم سبد خرید</summary>
|
||||
public long CartItemId { get; init; }
|
||||
/// <summary>شناسه محصول</summary>
|
||||
public long ProductId { get; init; }
|
||||
|
||||
/// <summary>تعداد جدید</summary>
|
||||
public int NewCount { get; init; }
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class UpdateCartItemCountCommandHandler : IRequestHandler<UpdateCartItemC
|
||||
new UpdateCartItemCountRequest
|
||||
{
|
||||
UserId = request.UserId,
|
||||
CartItemId = request.CartItemId,
|
||||
ProductId = request.ProductId,
|
||||
NewCount = request.NewCount
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ public class UpdateCartItemCountCommandValidator : AbstractValidator<UpdateCartI
|
||||
RuleFor(x => x.UserId)
|
||||
.GreaterThan(0).WithMessage("شناسه کاربر نامعتبر است");
|
||||
|
||||
RuleFor(x => x.CartItemId)
|
||||
.GreaterThan(0).WithMessage("شناسه آیتم سبد خرید نامعتبر است");
|
||||
RuleFor(x => x.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول نامعتبر است");
|
||||
|
||||
RuleFor(x => x.NewCount)
|
||||
.GreaterThan(0).WithMessage("تعداد جدید باید بیشتر از صفر باشد");
|
||||
|
||||
+6
-6
@@ -19,13 +19,11 @@ public class GetUserCartQueryHandler : IRequestHandler<GetUserCartQuery, GetUser
|
||||
|
||||
return new GetUserCartResponseDto
|
||||
{
|
||||
UserId = response.UserId,
|
||||
TotalPrice = response.TotalPrice,
|
||||
TotalDiscountedPrice = response.TotalDiscountedPrice,
|
||||
TotalSavings = response.TotalSavings,
|
||||
TotalDiscountAmount = response.TotalDiscountAmount,
|
||||
FinalPrice = response.FinalPrice,
|
||||
Items = response.Items.Select(item => new CartItemDto
|
||||
{
|
||||
Id = item.Id,
|
||||
ProductId = item.ProductId,
|
||||
ProductTitle = item.ProductTitle,
|
||||
ProductImagePath = item.ProductImagePath,
|
||||
@@ -33,8 +31,10 @@ public class GetUserCartQueryHandler : IRequestHandler<GetUserCartQuery, GetUser
|
||||
MaxDiscountPercent = item.MaxDiscountPercent,
|
||||
Count = item.Count,
|
||||
TotalPrice = item.TotalPrice,
|
||||
DiscountedPrice = item.DiscountedPrice,
|
||||
AddedAt = item.AddedAt.ToDateTime()
|
||||
DiscountAmount = item.DiscountAmount,
|
||||
FinalPrice = item.FinalPrice,
|
||||
ProductRemainingCount = item.ProductRemainingCount,
|
||||
Created = item.Created.ToDateTime()
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
+13
-8
@@ -2,23 +2,28 @@ namespace BackOffice.BFF.Application.DiscountShoppingCartCQ.Queries.GetUserCart;
|
||||
|
||||
public class GetUserCartResponseDto
|
||||
{
|
||||
public long UserId { get; set; }
|
||||
public List<CartItemDto> Items { get; set; } = new();
|
||||
/// <summary>جمع کل قیمت (بدون تخفیف)</summary>
|
||||
public long TotalPrice { get; set; }
|
||||
public long TotalDiscountedPrice { get; set; }
|
||||
public long TotalSavings { get; set; }
|
||||
/// <summary>جمع تخفیفها</summary>
|
||||
public long TotalDiscountAmount { get; set; }
|
||||
/// <summary>قیمت نهایی (با تخفیف)</summary>
|
||||
public long FinalPrice { get; set; }
|
||||
}
|
||||
|
||||
public class CartItemDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long ProductId { get; set; }
|
||||
public string ProductTitle { get; set; }
|
||||
public string ProductImagePath { get; set; }
|
||||
public string ProductTitle { get; set; } = string.Empty;
|
||||
public string ProductImagePath { get; set; } = string.Empty;
|
||||
public long UnitPrice { get; set; }
|
||||
public int MaxDiscountPercent { get; set; }
|
||||
public int Count { get; set; }
|
||||
public long TotalPrice { get; set; }
|
||||
public long DiscountedPrice { get; set; }
|
||||
public DateTime AddedAt { get; set; }
|
||||
/// <summary>مبلغ تخفیف</summary>
|
||||
public long DiscountAmount { get; set; }
|
||||
/// <summary>قیمت نهایی آیتم</summary>
|
||||
public long FinalPrice { get; set; }
|
||||
public int ProductRemainingCount { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace BackOffice.BFF.Application.ProductTagCQ.Commands.DeleteProductTag;
|
||||
|
||||
public record DeleteProductTagCommand : IRequest<Unit>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using CMSMicroservice.Protobuf.Protos.ProductTag;
|
||||
|
||||
namespace BackOffice.BFF.Application.ProductTagCQ.Commands.DeleteProductTag;
|
||||
|
||||
public class DeleteProductTagCommandHandler : IRequestHandler<DeleteProductTagCommand, Unit>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public DeleteProductTagCommandHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(DeleteProductTagCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await _context.ProductTags.DeleteProductTagAsync(
|
||||
new DeleteProductTagRequest
|
||||
{
|
||||
Id = request.Id
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
namespace BackOffice.BFF.Application.ProductTagCQ.Commands.UpdateProductTag;
|
||||
|
||||
public record UpdateProductTagCommand : IRequest<Unit>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public long ProductId { get; init; }
|
||||
public long TagId { get; init; }
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using CMSMicroservice.Protobuf.Protos.ProductTag;
|
||||
|
||||
namespace BackOffice.BFF.Application.ProductTagCQ.Commands.UpdateProductTag;
|
||||
|
||||
public class UpdateProductTagCommandHandler : IRequestHandler<UpdateProductTagCommand, Unit>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public UpdateProductTagCommandHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(UpdateProductTagCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
await _context.ProductTags.UpdateProductTagAsync(
|
||||
new UpdateProductTagRequest
|
||||
{
|
||||
Id = request.Id,
|
||||
ProductId = request.ProductId,
|
||||
TagId = request.TagId
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
namespace BackOffice.BFF.Application.ProductTagCQ.Queries.GetAllProductTags;
|
||||
|
||||
public record GetAllProductTagsQuery : IRequest<GetAllProductTagsResponseDto>
|
||||
{
|
||||
public int PageNumber { get; init; } = 1;
|
||||
public int PageSize { get; init; } = 10;
|
||||
public string? SortBy { get; init; }
|
||||
public long? ProductId { get; init; }
|
||||
public long? TagId { get; init; }
|
||||
}
|
||||
|
||||
public record GetAllProductTagsResponseDto
|
||||
{
|
||||
public MetaData MetaData { get; init; } = default!;
|
||||
public List<ProductTagItemDto> Items { get; init; } = new();
|
||||
}
|
||||
|
||||
public record ProductTagItemDto
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public long ProductId { get; init; }
|
||||
public long TagId { get; init; }
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using CMSMicroservice.Protobuf.Protos.ProductTag;
|
||||
using CmsPaginationState = CMSMicroservice.Protobuf.Protos.PaginationState;
|
||||
|
||||
namespace BackOffice.BFF.Application.ProductTagCQ.Queries.GetAllProductTags;
|
||||
|
||||
public class GetAllProductTagsQueryHandler : IRequestHandler<GetAllProductTagsQuery, GetAllProductTagsResponseDto>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public GetAllProductTagsQueryHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<GetAllProductTagsResponseDto> Handle(GetAllProductTagsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _context.ProductTags.GetAllProductTagByFilterAsync(
|
||||
new GetAllProductTagByFilterRequest
|
||||
{
|
||||
PaginationState = new CmsPaginationState
|
||||
{
|
||||
PageNumber = request.PageNumber,
|
||||
PageSize = request.PageSize
|
||||
},
|
||||
SortBy = request.SortBy ?? "",
|
||||
Filter = new GetAllProductTagByFilterFilter
|
||||
{
|
||||
ProductId = request.ProductId ?? 0,
|
||||
TagId = request.TagId ?? 0
|
||||
}
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
return new GetAllProductTagsResponseDto
|
||||
{
|
||||
MetaData = new MetaData
|
||||
{
|
||||
CurrentPage = response.MetaData.CurrentPage,
|
||||
PageSize = response.MetaData.PageSize,
|
||||
TotalCount = response.MetaData.TotalCount,
|
||||
TotalPage = response.MetaData.TotalPage
|
||||
},
|
||||
Items = response.Models.Select(pt => new ProductTagItemDto
|
||||
{
|
||||
Id = pt.Id,
|
||||
ProductId = pt.ProductId,
|
||||
TagId = pt.TagId
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
namespace BackOffice.BFF.Application.ProductTagCQ.Queries.GetProductTag;
|
||||
|
||||
public record GetProductTagQuery : IRequest<GetProductTagResponseDto>
|
||||
{
|
||||
public long Id { get; init; }
|
||||
}
|
||||
|
||||
public record GetProductTagResponseDto
|
||||
{
|
||||
public long Id { get; init; }
|
||||
public long ProductId { get; init; }
|
||||
public long TagId { get; init; }
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using CMSMicroservice.Protobuf.Protos.ProductTag;
|
||||
|
||||
namespace BackOffice.BFF.Application.ProductTagCQ.Queries.GetProductTag;
|
||||
|
||||
public class GetProductTagQueryHandler : IRequestHandler<GetProductTagQuery, GetProductTagResponseDto>
|
||||
{
|
||||
private readonly IApplicationContractContext _context;
|
||||
|
||||
public GetProductTagQueryHandler(IApplicationContractContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<GetProductTagResponseDto> Handle(GetProductTagQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _context.ProductTags.GetProductTagAsync(
|
||||
new GetProductTagRequest
|
||||
{
|
||||
Id = request.Id
|
||||
},
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
return new GetProductTagResponseDto
|
||||
{
|
||||
Id = response.Id,
|
||||
ProductId = response.ProductId,
|
||||
TagId = response.TagId
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user