Complete FrontOffice BFF to CMS Migration
- Migrated all 9 services from FrontOffice.BFF to CMS architecture - Enhanced user.proto with 7 additional Customer API endpoints: * UpdateCustomerProfile, GetCustomerProfile * ChangeCustomerPassword with validation * GetCustomerReferrals with commission stats * UploadCustomerAvatar with file validation * GetCustomerSettings, UpdateCustomerSettings - All services now support Customer endpoints with /Customer/ prefix - Mock implementations with realistic Persian data - Fixed namespace conflicts and compilation issues - Comprehensive testing completed for all endpoints - Services migrated: Categories, City, UserCarts, Products, UserWallet, Transaction, UserOrder, Package, User (enhanced)
This commit is contained in:
-64
@@ -1,64 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices;
|
||||
|
||||
/// <summary>
|
||||
/// بهروزرسانی دستهای قیمت محصولات
|
||||
/// </summary>
|
||||
public record BulkUpdateProductPricesCommand : IRequest<BulkUpdateProductPricesResponseDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// لیست محصولات و قیمتهای جدید
|
||||
/// </summary>
|
||||
public List<ProductPriceUpdate> Products { get; init; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// مدل بهروزرسانی قیمت یک محصول
|
||||
/// </summary>
|
||||
public class ProductPriceUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// شناسه محصول
|
||||
/// </summary>
|
||||
public long ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// قیمت جدید (ریال)
|
||||
/// </summary>
|
||||
public long NewPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// درصد تخفیف جدید (اختیاری)
|
||||
/// </summary>
|
||||
public int? NewDiscount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// درصد تخفیف باشگاه جدید (اختیاری)
|
||||
/// </summary>
|
||||
public int? NewClubDiscountPercent { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// پاسخ بهروزرسانی دستهای قیمت
|
||||
/// </summary>
|
||||
public class BulkUpdateProductPricesResponseDto
|
||||
{
|
||||
/// <summary>
|
||||
/// تعداد محصولات بهروزرسانی شده
|
||||
/// </summary>
|
||||
public int UpdatedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// تعداد محصولات ناموفق
|
||||
/// </summary>
|
||||
public int FailedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// جزئیات خطاها
|
||||
/// </summary>
|
||||
public List<string> Errors { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// آیا همه موفق بودند
|
||||
/// </summary>
|
||||
public bool IsSuccess => FailedCount == 0;
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices;
|
||||
|
||||
public class BulkUpdateProductPricesCommandHandler : IRequestHandler<BulkUpdateProductPricesCommand, BulkUpdateProductPricesResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<BulkUpdateProductPricesCommandHandler> _logger;
|
||||
|
||||
public BulkUpdateProductPricesCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<BulkUpdateProductPricesCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<BulkUpdateProductPricesResponseDto> Handle(BulkUpdateProductPricesCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new BulkUpdateProductPricesResponseDto();
|
||||
var productIds = request.Products.Select(p => p.ProductId).ToList();
|
||||
|
||||
// دریافت محصولات از دیتابیس
|
||||
var products = await _context.Products
|
||||
.Where(p => productIds.Contains(p.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var productDict = products.ToDictionary(p => p.Id);
|
||||
|
||||
foreach (var update in request.Products)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!productDict.TryGetValue(update.ProductId, out var product))
|
||||
{
|
||||
response.FailedCount++;
|
||||
response.Errors.Add($"محصول با شناسه {update.ProductId} یافت نشد");
|
||||
continue;
|
||||
}
|
||||
|
||||
// بهروزرسانی قیمت
|
||||
product.Price = update.NewPrice;
|
||||
|
||||
// بهروزرسانی تخفیف (اگر ارسال شده باشد)
|
||||
if (update.NewDiscount.HasValue)
|
||||
{
|
||||
product.Discount = update.NewDiscount.Value;
|
||||
}
|
||||
|
||||
// بهروزرسانی تخفیف باشگاه (اگر ارسال شده باشد)
|
||||
if (update.NewClubDiscountPercent.HasValue)
|
||||
{
|
||||
product.ClubDiscountPercent = update.NewClubDiscountPercent.Value;
|
||||
}
|
||||
|
||||
response.UpdatedCount++;
|
||||
|
||||
_logger.LogInformation(
|
||||
"Product {ProductId} price updated to {NewPrice} (Discount: {Discount}%, ClubDiscount: {ClubDiscount}%)",
|
||||
product.Id, product.Price, product.Discount, product.ClubDiscountPercent);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.FailedCount++;
|
||||
response.Errors.Add($"خطا در بهروزرسانی محصول {update.ProductId}: {ex.Message}");
|
||||
_logger.LogError(ex, "Error updating product {ProductId} price", update.ProductId);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.UpdatedCount > 0)
|
||||
{
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("Bulk price update completed: {UpdatedCount} succeeded, {FailedCount} failed",
|
||||
response.UpdatedCount, response.FailedCount);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices;
|
||||
|
||||
public class BulkUpdateProductPricesCommandValidator : AbstractValidator<BulkUpdateProductPricesCommand>
|
||||
{
|
||||
public BulkUpdateProductPricesCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.Products)
|
||||
.NotEmpty().WithMessage("لیست محصولات نمیتواند خالی باشد")
|
||||
.Must(x => x.Count <= 100).WithMessage("حداکثر 100 محصول در هر بار قابل بهروزرسانی است");
|
||||
|
||||
RuleForEach(x => x.Products).ChildRules(product =>
|
||||
{
|
||||
product.RuleFor(p => p.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول باید بزرگتر از 0 باشد");
|
||||
|
||||
product.RuleFor(p => p.NewPrice)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("قیمت نمیتواند منفی باشد");
|
||||
|
||||
product.RuleFor(p => p.NewDiscount)
|
||||
.InclusiveBetween(0, 100)
|
||||
.When(p => p.NewDiscount.HasValue)
|
||||
.WithMessage("درصد تخفیف باید بین 0 تا 100 باشد");
|
||||
|
||||
product.RuleFor(p => p.NewClubDiscountPercent)
|
||||
.InclusiveBetween(0, 100)
|
||||
.When(p => p.NewClubDiscountPercent.HasValue)
|
||||
.WithMessage("درصد تخفیف باشگاه باید بین 0 تا 100 باشد");
|
||||
});
|
||||
}
|
||||
}
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock;
|
||||
|
||||
/// <summary>
|
||||
/// بهروزرسانی دستهای موجودی محصولات
|
||||
/// </summary>
|
||||
public record BulkUpdateProductStockCommand : IRequest<BulkUpdateProductStockResponseDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// لیست محصولات و موجودیهای جدید
|
||||
/// </summary>
|
||||
public List<ProductStockUpdate> Products { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// نوع بهروزرسانی
|
||||
/// </summary>
|
||||
public StockUpdateType UpdateType { get; init; } = StockUpdateType.Set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// نوع بهروزرسانی موجودی
|
||||
/// </summary>
|
||||
public enum StockUpdateType
|
||||
{
|
||||
/// <summary>
|
||||
/// تنظیم مقدار مطلق
|
||||
/// </summary>
|
||||
Set = 1,
|
||||
|
||||
/// <summary>
|
||||
/// اضافه کردن به موجودی فعلی
|
||||
/// </summary>
|
||||
Add = 2,
|
||||
|
||||
/// <summary>
|
||||
/// کم کردن از موجودی فعلی
|
||||
/// </summary>
|
||||
Subtract = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// مدل بهروزرسانی موجودی یک محصول
|
||||
/// </summary>
|
||||
public class ProductStockUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// شناسه محصول
|
||||
/// </summary>
|
||||
public long ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// مقدار جدید/تغییر موجودی
|
||||
/// </summary>
|
||||
public int Quantity { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// پاسخ بهروزرسانی دستهای موجودی
|
||||
/// </summary>
|
||||
public class BulkUpdateProductStockResponseDto
|
||||
{
|
||||
/// <summary>
|
||||
/// تعداد محصولات بهروزرسانی شده
|
||||
/// </summary>
|
||||
public int UpdatedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// تعداد محصولات ناموفق
|
||||
/// </summary>
|
||||
public int FailedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// جزئیات خطاها
|
||||
/// </summary>
|
||||
public List<string> Errors { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// آیا همه موفق بودند
|
||||
/// </summary>
|
||||
public bool IsSuccess => FailedCount == 0;
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock;
|
||||
|
||||
public class BulkUpdateProductStockCommandHandler : IRequestHandler<BulkUpdateProductStockCommand, BulkUpdateProductStockResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<BulkUpdateProductStockCommandHandler> _logger;
|
||||
|
||||
public BulkUpdateProductStockCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<BulkUpdateProductStockCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<BulkUpdateProductStockResponseDto> Handle(BulkUpdateProductStockCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new BulkUpdateProductStockResponseDto();
|
||||
var productIds = request.Products.Select(p => p.ProductId).ToList();
|
||||
|
||||
// دریافت محصولات از دیتابیس
|
||||
var products = await _context.Products
|
||||
.Where(p => productIds.Contains(p.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var productDict = products.ToDictionary(p => p.Id);
|
||||
|
||||
foreach (var update in request.Products)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!productDict.TryGetValue(update.ProductId, out var product))
|
||||
{
|
||||
response.FailedCount++;
|
||||
response.Errors.Add($"محصول با شناسه {update.ProductId} یافت نشد");
|
||||
continue;
|
||||
}
|
||||
|
||||
var oldStock = product.RemainingCount;
|
||||
|
||||
// بهروزرسانی موجودی بر اساس نوع
|
||||
switch (request.UpdateType)
|
||||
{
|
||||
case StockUpdateType.Set:
|
||||
product.RemainingCount = update.Quantity;
|
||||
break;
|
||||
|
||||
case StockUpdateType.Add:
|
||||
product.RemainingCount += update.Quantity;
|
||||
break;
|
||||
|
||||
case StockUpdateType.Subtract:
|
||||
product.RemainingCount -= update.Quantity;
|
||||
// جلوگیری از موجودی منفی
|
||||
if (product.RemainingCount < 0)
|
||||
{
|
||||
response.FailedCount++;
|
||||
response.Errors.Add($"محصول {update.ProductId}: موجودی منفی شد (موجودی فعلی: {oldStock}, کم کردن: {update.Quantity})");
|
||||
product.RemainingCount = oldStock; // بازگرداندن مقدار قبلی
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
response.UpdatedCount++;
|
||||
|
||||
_logger.LogInformation(
|
||||
"Product {ProductId} stock updated from {OldStock} to {NewStock} (Type: {UpdateType})",
|
||||
product.Id, oldStock, product.RemainingCount, request.UpdateType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.FailedCount++;
|
||||
response.Errors.Add($"خطا در بهروزرسانی محصول {update.ProductId}: {ex.Message}");
|
||||
_logger.LogError(ex, "Error updating product {ProductId} stock", update.ProductId);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.UpdatedCount > 0)
|
||||
{
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation("Bulk stock update completed: {UpdatedCount} succeeded, {FailedCount} failed",
|
||||
response.UpdatedCount, response.FailedCount);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock;
|
||||
|
||||
public class BulkUpdateProductStockCommandValidator : AbstractValidator<BulkUpdateProductStockCommand>
|
||||
{
|
||||
public BulkUpdateProductStockCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.Products)
|
||||
.NotEmpty().WithMessage("لیست محصولات نمیتواند خالی باشد")
|
||||
.Must(x => x.Count <= 100).WithMessage("حداکثر 100 محصول در هر بار قابل بهروزرسانی است");
|
||||
|
||||
RuleForEach(x => x.Products).ChildRules(product =>
|
||||
{
|
||||
product.RuleFor(p => p.ProductId)
|
||||
.GreaterThan(0).WithMessage("شناسه محصول باید بزرگتر از 0 باشد");
|
||||
|
||||
// برای Set mode، مقدار نمیتواند منفی باشد (چک در Handler انجام میشود)
|
||||
product.RuleFor(p => p.Quantity)
|
||||
.GreaterThanOrEqualTo(-10000)
|
||||
.WithMessage("مقدار موجودی نامعتبر است");
|
||||
});
|
||||
}
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.CreateNewProducts;
|
||||
public record CreateNewProductsCommand : IRequest<CreateNewProductsResponseDto>
|
||||
{
|
||||
//
|
||||
public string Title { get; init; }
|
||||
//
|
||||
public string Description { get; init; }
|
||||
//
|
||||
public string ShortInfomation { get; init; }
|
||||
//
|
||||
public string FullInformation { get; init; }
|
||||
//
|
||||
public long Price { get; init; }
|
||||
//
|
||||
public int Discount { get; init; }
|
||||
//
|
||||
public int Rate { get; init; }
|
||||
//
|
||||
public string ImagePath { get; init; }
|
||||
//
|
||||
public string ThumbnailPath { get; init; }
|
||||
//
|
||||
public int SaleCount { get; init; }
|
||||
//
|
||||
public int ViewCount { get; init; }
|
||||
// لیست شناسه دستهبندیهای محصول
|
||||
public ICollection<long>? CategoryIds { get; init; }
|
||||
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Enums;
|
||||
using CMSMicroservice.Domain.Events;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.CreateNewProducts;
|
||||
|
||||
public class CreateNewProductsCommandHandler : IRequestHandler<CreateNewProductsCommand, CreateNewProductsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IInventoryService _inventoryService;
|
||||
|
||||
public CreateNewProductsCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
IInventoryService inventoryService)
|
||||
{
|
||||
_context = context;
|
||||
_inventoryService = inventoryService;
|
||||
}
|
||||
|
||||
public async Task<CreateNewProductsResponseDto> Handle(CreateNewProductsCommand request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = request.Adapt<Product>();
|
||||
await _context.Products.AddAsync(entity, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// ایجاد رکورد موجودی در سیستم انبارداری با موجودی اولیه صفر
|
||||
await _inventoryService.InitializeInventoryAsync(
|
||||
entity.Id,
|
||||
ProductType.RegularProduct,
|
||||
0, // موجودی اولیه صفر - باید از طریق Inventory اضافه شود
|
||||
ct: cancellationToken);
|
||||
|
||||
// ثبت دستهبندیهای محصول (در صورت ارسال)
|
||||
if (request.CategoryIds is { Count: > 0 })
|
||||
{
|
||||
var distinctCategoryIds = request.CategoryIds
|
||||
.Where(id => id > 0)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
foreach (var categoryId in distinctCategoryIds)
|
||||
{
|
||||
var rel = new ProductCategory
|
||||
{
|
||||
ProductId = entity.Id,
|
||||
CategoryId = categoryId
|
||||
};
|
||||
await _context.ProductCategories.AddAsync(rel, cancellationToken);
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
entity.AddDomainEvent(new CreateNewProductsEvent(entity));
|
||||
return entity.Adapt<CreateNewProductsResponseDto>();
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.CreateNewProducts;
|
||||
public class CreateNewProductsCommandValidator : AbstractValidator<CreateNewProductsCommand>
|
||||
{
|
||||
public CreateNewProductsCommandValidator()
|
||||
{
|
||||
RuleFor(model => model.Title)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.Description)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.ShortInfomation)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.FullInformation)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.Price)
|
||||
.NotNull();
|
||||
RuleFor(model => model.Discount)
|
||||
.NotNull();
|
||||
RuleFor(model => model.Rate)
|
||||
.NotNull();
|
||||
RuleFor(model => model.ImagePath)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.ThumbnailPath)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.SaleCount)
|
||||
.NotNull();
|
||||
RuleFor(model => model.ViewCount)
|
||||
.NotNull();
|
||||
}
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
{
|
||||
var result = await ValidateAsync(ValidationContext<CreateNewProductsCommand>.CreateWithOptions((CreateNewProductsCommand)model, x => x.IncludeProperties(propertyName)));
|
||||
if (result.IsValid)
|
||||
return Array.Empty<string>();
|
||||
return result.Errors.Select(e => e.ErrorMessage);
|
||||
};
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.CreateNewProducts;
|
||||
public class CreateNewProductsResponseDto
|
||||
{
|
||||
//
|
||||
public long Id { get; set; }
|
||||
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.DeleteProducts;
|
||||
public record DeleteProductsCommand : IRequest<Unit>
|
||||
{
|
||||
//
|
||||
public long Id { get; init; }
|
||||
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
using CMSMicroservice.Domain.Events;
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.DeleteProducts;
|
||||
public class DeleteProductsCommandHandler : IRequestHandler<DeleteProductsCommand, Unit>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public DeleteProductsCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(DeleteProductsCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await _context.Products
|
||||
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken) ?? throw new NotFoundException(nameof(Product), request.Id);
|
||||
entity.IsDeleted = true;
|
||||
_context.Products.Update(entity);
|
||||
entity.AddDomainEvent(new DeleteProductsEvent(entity));
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.DeleteProducts;
|
||||
public class DeleteProductsCommandValidator : AbstractValidator<DeleteProductsCommand>
|
||||
{
|
||||
public DeleteProductsCommandValidator()
|
||||
{
|
||||
RuleFor(model => model.Id)
|
||||
.NotNull();
|
||||
}
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
{
|
||||
var result = await ValidateAsync(ValidationContext<DeleteProductsCommand>.CreateWithOptions((DeleteProductsCommand)model, x => x.IncludeProperties(propertyName)));
|
||||
if (result.IsValid)
|
||||
return Array.Empty<string>();
|
||||
return result.Errors.Select(e => e.ErrorMessage);
|
||||
};
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.ToggleProductStatus;
|
||||
|
||||
/// <summary>
|
||||
/// فعال/غیرفعال کردن دستهای محصولات
|
||||
/// (با تنظیم موجودی به 0 برای غیرفعال کردن)
|
||||
/// </summary>
|
||||
public record ToggleProductStatusCommand : IRequest<ToggleProductStatusResponseDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// لیست شناسه محصولات
|
||||
/// </summary>
|
||||
public List<long> ProductIds { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// فعال کردن یا غیرفعال کردن
|
||||
/// </summary>
|
||||
public bool Enable { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// موجودی پیشفرض برای فعالسازی (پیشفرض: 1)
|
||||
/// </summary>
|
||||
public int DefaultStock { get; init; } = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// پاسخ فعال/غیرفعال کردن دستهای
|
||||
/// </summary>
|
||||
public class ToggleProductStatusResponseDto
|
||||
{
|
||||
/// <summary>
|
||||
/// تعداد محصولات بهروزرسانی شده
|
||||
/// </summary>
|
||||
public int UpdatedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// تعداد محصولات ناموفق
|
||||
/// </summary>
|
||||
public int FailedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// جزئیات خطاها
|
||||
/// </summary>
|
||||
public List<string> Errors { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// آیا همه موفق بودند
|
||||
/// </summary>
|
||||
public bool IsSuccess => FailedCount == 0;
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.ToggleProductStatus;
|
||||
|
||||
public class ToggleProductStatusCommandHandler : IRequestHandler<ToggleProductStatusCommand, ToggleProductStatusResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<ToggleProductStatusCommandHandler> _logger;
|
||||
|
||||
public ToggleProductStatusCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<ToggleProductStatusCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<ToggleProductStatusResponseDto> Handle(ToggleProductStatusCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new ToggleProductStatusResponseDto();
|
||||
|
||||
// دریافت محصولات از دیتابیس
|
||||
var products = await _context.Products
|
||||
.Where(p => request.ProductIds.Contains(p.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (products.Count == 0)
|
||||
{
|
||||
response.Errors.Add("هیچ محصولی با شناسههای داده شده یافت نشد");
|
||||
return response;
|
||||
}
|
||||
|
||||
foreach (var product in products)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (request.Enable)
|
||||
{
|
||||
// فعالسازی: اگر موجودی 0 است، آن را به مقدار پیشفرض تنظیم کن
|
||||
if (product.RemainingCount == 0)
|
||||
{
|
||||
product.RemainingCount = request.DefaultStock;
|
||||
_logger.LogInformation(
|
||||
"Product {ProductId} enabled with stock {Stock}",
|
||||
product.Id, request.DefaultStock);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Product {ProductId} already has stock {Stock}, no change needed",
|
||||
product.Id, product.RemainingCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// غیرفعالسازی: موجودی را به 0 تنظیم کن
|
||||
var oldStock = product.RemainingCount;
|
||||
product.RemainingCount = 0;
|
||||
_logger.LogInformation(
|
||||
"Product {ProductId} disabled (stock changed from {OldStock} to 0)",
|
||||
product.Id, oldStock);
|
||||
}
|
||||
|
||||
response.UpdatedCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.FailedCount++;
|
||||
response.Errors.Add($"خطا در بهروزرسانی محصول {product.Id}: {ex.Message}");
|
||||
_logger.LogError(ex, "Error toggling product {ProductId} status", product.Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.UpdatedCount > 0)
|
||||
{
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
_logger.LogInformation(
|
||||
"Toggle product status completed: {UpdatedCount} succeeded, {FailedCount} failed (Enable: {Enable})",
|
||||
response.UpdatedCount, response.FailedCount, request.Enable);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.ToggleProductStatus;
|
||||
|
||||
public class ToggleProductStatusCommandValidator : AbstractValidator<ToggleProductStatusCommand>
|
||||
{
|
||||
public ToggleProductStatusCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.ProductIds)
|
||||
.NotEmpty().WithMessage("لیست محصولات نمیتواند خالی باشد")
|
||||
.Must(x => x.Count <= 100).WithMessage("حداکثر 100 محصول در هر بار قابل بهروزرسانی است");
|
||||
|
||||
RuleFor(x => x.DefaultStock)
|
||||
.GreaterThanOrEqualTo(0)
|
||||
.When(x => x.Enable)
|
||||
.WithMessage("موجودی پیشفرض نمیتواند منفی باشد");
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.UpdateProductBulk;
|
||||
|
||||
/// <summary>
|
||||
/// دستور بهروزرسانی گروهی محصولات
|
||||
/// Admin میتواند چندین محصول را همزمان ویرایش کند
|
||||
/// </summary>
|
||||
public class UpdateProductBulkCommand : IRequest<UpdateProductBulkResponseDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// لیست شناسه محصولات برای بهروزرسانی
|
||||
/// </summary>
|
||||
public List<long> ProductIds { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// قیمت جدید (اختیاری - اگر null باشد تغییر نمیکند)
|
||||
/// </summary>
|
||||
public long? NewPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// درصد افزایش/کاهش قیمت (اختیاری)
|
||||
/// مثلاً: 10 = افزایش 10%، -15 = کاهش 15%
|
||||
/// </summary>
|
||||
public decimal? PriceChangePercent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// موجودی (اختیاری)
|
||||
/// </summary>
|
||||
public int? Stock { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// افزودن مقدار به موجودی (اختیاری)
|
||||
/// </summary>
|
||||
public int? StockIncrement { get; set; }
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.UpdateProductBulk;
|
||||
|
||||
public class UpdateProductBulkCommandHandler : IRequestHandler<UpdateProductBulkCommand, UpdateProductBulkResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<UpdateProductBulkCommandHandler> _logger;
|
||||
|
||||
public UpdateProductBulkCommandHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<UpdateProductBulkCommandHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<UpdateProductBulkResponseDto> Handle(UpdateProductBulkCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new UpdateProductBulkResponseDto
|
||||
{
|
||||
TotalRequested = request.ProductIds.Count
|
||||
};
|
||||
|
||||
var products = await _context.Products
|
||||
.Where(x => request.ProductIds.Contains(x.Id) && !x.IsDeleted)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (products.Count == 0)
|
||||
{
|
||||
response.Errors.Add("هیچ محصولی با شناسههای داده شده یافت نشد");
|
||||
return response;
|
||||
}
|
||||
|
||||
foreach (var product in products)
|
||||
{
|
||||
try
|
||||
{
|
||||
// تغییر قیمت
|
||||
if (request.NewPrice.HasValue)
|
||||
{
|
||||
product.Price = request.NewPrice.Value;
|
||||
}
|
||||
else if (request.PriceChangePercent.HasValue)
|
||||
{
|
||||
var changeAmount = (long)(product.Price * (request.PriceChangePercent.Value / 100));
|
||||
product.Price += changeAmount;
|
||||
|
||||
// اطمینان از مثبت بودن قیمت
|
||||
if (product.Price < 0)
|
||||
product.Price = 0;
|
||||
}
|
||||
|
||||
// تغییر موجودی
|
||||
if (request.Stock.HasValue)
|
||||
{
|
||||
product.RemainingCount = request.Stock.Value;
|
||||
}
|
||||
else if (request.StockIncrement.HasValue)
|
||||
{
|
||||
product.RemainingCount += request.StockIncrement.Value;
|
||||
|
||||
// اطمینان از غیرمنفی بودن موجودی
|
||||
if (product.RemainingCount < 0)
|
||||
product.RemainingCount = 0;
|
||||
}
|
||||
|
||||
response.UpdatedProductIds.Add(product.Id);
|
||||
response.SuccessCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Errors.Add($"خطا در بهروزرسانی محصول {product.Id}: {ex.Message}");
|
||||
response.FailedCount++;
|
||||
_logger.LogError(ex, "Error updating product {ProductId}", product.Id);
|
||||
}
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Bulk update completed. Success: {Success}, Failed: {Failed}",
|
||||
response.SuccessCount,
|
||||
response.FailedCount
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.UpdateProductBulk;
|
||||
|
||||
public class UpdateProductBulkCommandValidator : AbstractValidator<UpdateProductBulkCommand>
|
||||
{
|
||||
public UpdateProductBulkCommandValidator()
|
||||
{
|
||||
RuleFor(x => x.ProductIds)
|
||||
.NotEmpty().WithMessage("حداقل یک محصول باید انتخاب شود")
|
||||
.Must(x => x.Count <= 100).WithMessage("حداکثر 100 محصول را میتوان همزمان بهروزرسانی کرد");
|
||||
|
||||
RuleFor(x => x.NewPrice)
|
||||
.GreaterThan(0).WithMessage("قیمت باید بزرگتر از صفر باشد")
|
||||
.LessThanOrEqualTo(1_000_000_000).WithMessage("قیمت نامعتبر است")
|
||||
.When(x => x.NewPrice.HasValue);
|
||||
|
||||
RuleFor(x => x.PriceChangePercent)
|
||||
.GreaterThanOrEqualTo(-100).WithMessage("درصد تخفیف نمیتواند بیشتر از 100% باشد")
|
||||
.LessThanOrEqualTo(1000).WithMessage("درصد افزایش نامعتبر است")
|
||||
.When(x => x.PriceChangePercent.HasValue);
|
||||
|
||||
RuleFor(x => x.Stock)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("موجودی نمیتواند منفی باشد")
|
||||
.When(x => x.Stock.HasValue);
|
||||
|
||||
RuleFor(x => x)
|
||||
.Must(x => x.NewPrice.HasValue || x.PriceChangePercent.HasValue ||
|
||||
x.Stock.HasValue || x.StockIncrement.HasValue)
|
||||
.WithMessage("حداقل یک فیلد برای بهروزرسانی باید مشخص شود");
|
||||
|
||||
RuleFor(x => x)
|
||||
.Must(x => !(x.NewPrice.HasValue && x.PriceChangePercent.HasValue))
|
||||
.WithMessage("نمیتوان همزمان قیمت جدید و درصد تغییر قیمت را مشخص کرد");
|
||||
|
||||
RuleFor(x => x)
|
||||
.Must(x => !(x.Stock.HasValue && x.StockIncrement.HasValue))
|
||||
.WithMessage("نمیتوان همزمان موجودی جدید و افزایش موجودی را مشخص کرد");
|
||||
}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.UpdateProductBulk;
|
||||
|
||||
public class UpdateProductBulkResponseDto
|
||||
{
|
||||
public int TotalRequested { get; set; }
|
||||
public int SuccessCount { get; set; }
|
||||
public int FailedCount { get; set; }
|
||||
public List<long> UpdatedProductIds { get; set; } = new();
|
||||
public List<string> Errors { get; set; } = new();
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.UpdateProducts;
|
||||
public record UpdateProductsCommand : IRequest<Unit>
|
||||
{
|
||||
//
|
||||
public long Id { get; init; }
|
||||
//
|
||||
public string Title { get; init; }
|
||||
//
|
||||
public string Description { get; init; }
|
||||
//
|
||||
public string ShortInfomation { get; init; }
|
||||
//
|
||||
public string FullInformation { get; init; }
|
||||
//
|
||||
public long Price { get; init; }
|
||||
//
|
||||
public int Discount { get; init; }
|
||||
//
|
||||
public int Rate { get; init; }
|
||||
//
|
||||
public string ImagePath { get; init; }
|
||||
//
|
||||
public string ThumbnailPath { get; init; }
|
||||
//
|
||||
public int SaleCount { get; init; }
|
||||
//
|
||||
public int ViewCount { get; init; }
|
||||
// لیست شناسه دستهبندیهای محصول
|
||||
public ICollection<long>? CategoryIds { get; init; }
|
||||
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Exceptions;
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Domain.Entities;
|
||||
using CMSMicroservice.Domain.Events;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.UpdateProducts;
|
||||
public class UpdateProductsCommandHandler : IRequestHandler<UpdateProductsCommand, Unit>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public UpdateProductsCommandHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(UpdateProductsCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await _context.Products
|
||||
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken)
|
||||
?? throw new NotFoundException(nameof(Product), request.Id);
|
||||
|
||||
request.Adapt(entity);
|
||||
_context.Products.Update(entity);
|
||||
|
||||
// بهروزرسانی دستهبندیهای محصول در صورت ارسال CategoryIds
|
||||
if (request.CategoryIds is not null)
|
||||
{
|
||||
var targetIds = (request.CategoryIds ?? Array.Empty<long>())
|
||||
.Where(id => id > 0)
|
||||
.Distinct()
|
||||
.ToHashSet();
|
||||
|
||||
var existingRelations = await _context.ProductCategories
|
||||
.Where(x => x.ProductId == entity.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var existingIds = existingRelations
|
||||
.Select(x => x.CategoryId)
|
||||
.ToHashSet();
|
||||
|
||||
var toAdd = targetIds.Except(existingIds).ToList();
|
||||
var toRemove = existingRelations.Where(x => !targetIds.Contains(x.CategoryId)).ToList();
|
||||
|
||||
foreach (var categoryId in toAdd)
|
||||
{
|
||||
var rel = new ProductCategory
|
||||
{
|
||||
ProductId = entity.Id,
|
||||
CategoryId = categoryId
|
||||
};
|
||||
await _context.ProductCategories.AddAsync(rel, cancellationToken);
|
||||
}
|
||||
|
||||
if (toRemove.Count > 0)
|
||||
{
|
||||
_context.ProductCategories.RemoveRange(toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
entity.AddDomainEvent(new UpdateProductsEvent(entity));
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Commands.UpdateProducts;
|
||||
public class UpdateProductsCommandValidator : AbstractValidator<UpdateProductsCommand>
|
||||
{
|
||||
public UpdateProductsCommandValidator()
|
||||
{
|
||||
RuleFor(model => model.Id)
|
||||
.NotNull();
|
||||
RuleFor(model => model.Title)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.Description)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.ShortInfomation)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.FullInformation)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.Price)
|
||||
.NotNull();
|
||||
RuleFor(model => model.Discount)
|
||||
.NotNull();
|
||||
RuleFor(model => model.Rate)
|
||||
.NotNull();
|
||||
RuleFor(model => model.ImagePath)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.ThumbnailPath)
|
||||
.NotEmpty();
|
||||
RuleFor(model => model.SaleCount)
|
||||
.NotNull();
|
||||
RuleFor(model => model.ViewCount)
|
||||
.NotNull();
|
||||
}
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
{
|
||||
var result = await ValidateAsync(ValidationContext<UpdateProductsCommand>.CreateWithOptions((UpdateProductsCommand)model, x => x.IncludeProperties(propertyName)));
|
||||
if (result.IsValid)
|
||||
return Array.Empty<string>();
|
||||
return result.Errors.Select(e => e.ErrorMessage);
|
||||
};
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
using CMSMicroservice.Domain.Events;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.EventHandlers;
|
||||
|
||||
public class CreateNewProductsEventHandler : INotificationHandler<CreateNewProductsEvent>
|
||||
{
|
||||
private readonly ILogger<
|
||||
CreateNewProductsEventHandler> _logger;
|
||||
|
||||
public CreateNewProductsEventHandler(ILogger<CreateNewProductsEventHandler> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task Handle(CreateNewProductsEvent notification, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
using CMSMicroservice.Domain.Events;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.EventHandlers;
|
||||
|
||||
public class DeleteProductsEventHandler : INotificationHandler<DeleteProductsEvent>
|
||||
{
|
||||
private readonly ILogger<
|
||||
DeleteProductsEventHandler> _logger;
|
||||
|
||||
public DeleteProductsEventHandler(ILogger<DeleteProductsEventHandler> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task Handle(DeleteProductsEvent notification, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
using CMSMicroservice.Domain.Events;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.EventHandlers;
|
||||
|
||||
public class UpdateProductsEventHandler : INotificationHandler<UpdateProductsEvent>
|
||||
{
|
||||
private readonly ILogger<
|
||||
UpdateProductsEventHandler> _logger;
|
||||
|
||||
public UpdateProductsEventHandler(ILogger<UpdateProductsEventHandler> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task Handle(UpdateProductsEvent notification, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetAllProductsByFilter;
|
||||
public record GetAllProductsByFilterQuery : IRequest<GetAllProductsByFilterResponseDto>
|
||||
{
|
||||
//موقعیت صفحه بندی
|
||||
public PaginationState? PaginationState { get; init; }
|
||||
//مرتب سازی بر اساس
|
||||
public string? SortBy { get; init; }
|
||||
//فیلتر
|
||||
public GetAllProductsByFilterFilter? Filter { get; init; }
|
||||
|
||||
}public class GetAllProductsByFilterFilter
|
||||
{
|
||||
//
|
||||
public long? Id { get; set; }
|
||||
//
|
||||
public string? Title { get; set; }
|
||||
//
|
||||
public string? Description { get; set; }
|
||||
//
|
||||
public string? ShortInfomation { get; set; }
|
||||
//
|
||||
public string? FullInformation { get; set; }
|
||||
//
|
||||
public long? Price { get; set; }
|
||||
//
|
||||
public int? Discount { get; set; }
|
||||
//
|
||||
public int? Rate { get; set; }
|
||||
//
|
||||
public long? CategoryId { get; set; }
|
||||
//
|
||||
public string? ImagePath { get; set; }
|
||||
//
|
||||
public string? ThumbnailPath { get; set; }
|
||||
//
|
||||
public int? SaleCount { get; set; }
|
||||
//
|
||||
public int? ViewCount { get; set; }
|
||||
//
|
||||
public int? RemainingCount { get; set; }
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetAllProductsByFilter;
|
||||
public class GetAllProductsByFilterQueryHandler : IRequestHandler<GetAllProductsByFilterQuery, GetAllProductsByFilterResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public GetAllProductsByFilterQueryHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<GetAllProductsByFilterResponseDto> Handle(GetAllProductsByFilterQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Products
|
||||
.ApplyOrder(sortBy: request.SortBy)
|
||||
.AsNoTracking()
|
||||
.AsQueryable();
|
||||
if (request.Filter is not null)
|
||||
{
|
||||
query = query
|
||||
.Where(x => request.Filter.Id == null || x.Id == request.Filter.Id)
|
||||
.Where(x => request.Filter.Title == null || x.Title.Contains(request.Filter.Title))
|
||||
.Where(x => request.Filter.Description == null || x.Description.Contains(request.Filter.Description))
|
||||
.Where(x => request.Filter.ShortInfomation == null || x.ShortInfomation.Contains(request.Filter.ShortInfomation))
|
||||
.Where(x => request.Filter.FullInformation == null || x.FullInformation.Contains(request.Filter.FullInformation))
|
||||
.Where(x => request.Filter.Price == null || x.Price == request.Filter.Price)
|
||||
.Where(x => request.Filter.Discount == null || x.Discount == request.Filter.Discount)
|
||||
.Where(x => request.Filter.Rate == null || x.Rate == request.Filter.Rate)
|
||||
.Where(x => request.Filter.CategoryId == null || x.ProductCategories.Any(pc => pc.CategoryId == request.Filter.CategoryId))
|
||||
.Where(x => request.Filter.ImagePath == null || x.ImagePath.Contains(request.Filter.ImagePath))
|
||||
.Where(x => request.Filter.ThumbnailPath == null || x.ThumbnailPath.Contains(request.Filter.ThumbnailPath))
|
||||
.Where(x => request.Filter.SaleCount == null || x.SaleCount == request.Filter.SaleCount)
|
||||
.Where(x => request.Filter.ViewCount == null || x.ViewCount == request.Filter.ViewCount)
|
||||
.Where(x => request.Filter.RemainingCount == null || x.RemainingCount == request.Filter.RemainingCount)
|
||||
;
|
||||
}
|
||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||
|
||||
var models = await query
|
||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
.Select(x => new GetAllProductsByFilterResponseModel
|
||||
{
|
||||
Id = x.Id,
|
||||
Title = x.Title,
|
||||
Description = x.Description,
|
||||
ShortInfomation = x.ShortInfomation,
|
||||
FullInformation = x.FullInformation,
|
||||
Price = x.Price,
|
||||
Discount = x.Discount,
|
||||
Rate = x.Rate,
|
||||
ImagePath = x.ImagePath,
|
||||
ThumbnailPath = x.ThumbnailPath,
|
||||
SaleCount = x.SaleCount,
|
||||
ViewCount = x.ViewCount,
|
||||
RemainingCount = x.RemainingCount,
|
||||
CategoryIds = x.ProductCategories
|
||||
.Select(pc => pc.CategoryId)
|
||||
.ToList()
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return new GetAllProductsByFilterResponseDto
|
||||
{
|
||||
MetaData = meta,
|
||||
Models = models
|
||||
};
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetAllProductsByFilter;
|
||||
public class GetAllProductsByFilterQueryValidator : AbstractValidator<GetAllProductsByFilterQuery>
|
||||
{
|
||||
public GetAllProductsByFilterQueryValidator()
|
||||
{
|
||||
}
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
{
|
||||
var result = await ValidateAsync(ValidationContext<GetAllProductsByFilterQuery>.CreateWithOptions((GetAllProductsByFilterQuery)model, x => x.IncludeProperties(propertyName)));
|
||||
if (result.IsValid)
|
||||
return Array.Empty<string>();
|
||||
return result.Errors.Select(e => e.ErrorMessage);
|
||||
};
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetAllProductsByFilter;
|
||||
public class GetAllProductsByFilterResponseDto
|
||||
{
|
||||
//متادیتا
|
||||
public MetaData MetaData { get; set; }
|
||||
//مدل خروجی
|
||||
public List<GetAllProductsByFilterResponseModel>? Models { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class GetAllProductsByFilterResponseModel
|
||||
{
|
||||
//
|
||||
public long Id { get; set; }
|
||||
//
|
||||
public string Title { get; set; }
|
||||
//
|
||||
public string Description { get; set; }
|
||||
//
|
||||
public string ShortInfomation { get; set; }
|
||||
//
|
||||
public string FullInformation { get; set; }
|
||||
//
|
||||
public long Price { get; set; }
|
||||
//
|
||||
public int Discount { get; set; }
|
||||
//
|
||||
public int Rate { get; set; }
|
||||
//
|
||||
public string ImagePath { get; set; }
|
||||
//
|
||||
public string ThumbnailPath { get; set; }
|
||||
//
|
||||
public int SaleCount { get; set; }
|
||||
//
|
||||
public int ViewCount { get; set; }
|
||||
//
|
||||
public int RemainingCount { get; set; }
|
||||
// لیست شناسه دستهبندیهای محصول
|
||||
public List<long> CategoryIds { get; set; } = new();
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetLowStockProducts;
|
||||
|
||||
/// <summary>
|
||||
/// دریافت محصولات کم موجودی
|
||||
/// </summary>
|
||||
public record GetLowStockProductsQuery : IRequest<GetLowStockProductsResponseDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// آستانه موجودی (پیشفرض: 10)
|
||||
/// </summary>
|
||||
public int Threshold { get; init; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// شماره صفحه (پیشفرض: 1)
|
||||
/// </summary>
|
||||
public int PageIndex { get; init; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// تعداد در هر صفحه (پیشفرض: 20)
|
||||
/// </summary>
|
||||
public int PageSize { get; init; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// فقط محصولات انحصاری باشگاه (اختیاری)
|
||||
/// </summary>
|
||||
public bool? IsClubExclusive { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// پاسخ لیست محصولات کم موجودی
|
||||
/// </summary>
|
||||
public class GetLowStockProductsResponseDto
|
||||
{
|
||||
public MetaData MetaData { get; set; } = new();
|
||||
public List<LowStockProductDto> Products { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// اطلاعات محصول کم موجودی
|
||||
/// </summary>
|
||||
public class LowStockProductDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public long Price { get; set; }
|
||||
public int Discount { get; set; }
|
||||
public int RemainingCount { get; set; }
|
||||
public int SaleCount { get; set; }
|
||||
public bool IsClubExclusive { get; set; }
|
||||
public string ImagePath { get; set; } = string.Empty;
|
||||
public string ThumbnailPath { get; set; } = string.Empty;
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime? LastModified { get; set; }
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetLowStockProducts;
|
||||
|
||||
public class GetLowStockProductsQueryHandler : IRequestHandler<GetLowStockProductsQuery, GetLowStockProductsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<GetLowStockProductsQueryHandler> _logger;
|
||||
|
||||
public GetLowStockProductsQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<GetLowStockProductsQueryHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<GetLowStockProductsResponseDto> Handle(GetLowStockProductsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Query اصلی: محصولاتی که موجودی کمتر یا مساوی آستانه دارند
|
||||
var query = _context.Products
|
||||
.Where(p => p.RemainingCount <= request.Threshold);
|
||||
|
||||
// فیلتر محصولات انحصاری باشگاه (اگر مشخص شده باشد)
|
||||
if (request.IsClubExclusive.HasValue)
|
||||
{
|
||||
query = query.Where(p => p.IsClubExclusive == request.IsClubExclusive.Value);
|
||||
}
|
||||
|
||||
// مرتبسازی بر اساس موجودی (کمترین موجودی اول)
|
||||
query = query.OrderBy(p => p.RemainingCount)
|
||||
.ThenByDescending(p => p.SaleCount); // محصولات پرفروش اولویت بیشتری دارند
|
||||
|
||||
// شمارش کل
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
|
||||
// Pagination
|
||||
var products = await query
|
||||
.Skip((request.PageIndex - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.Select(p => new LowStockProductDto
|
||||
{
|
||||
Id = p.Id,
|
||||
Title = p.Title,
|
||||
Price = p.Price,
|
||||
Discount = p.Discount,
|
||||
RemainingCount = p.RemainingCount,
|
||||
SaleCount = p.SaleCount,
|
||||
IsClubExclusive = p.IsClubExclusive,
|
||||
ImagePath = p.ImagePath,
|
||||
ThumbnailPath = p.ThumbnailPath,
|
||||
Created = p.Created,
|
||||
LastModified = p.LastModified
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Found {Count} low stock products (threshold: {Threshold}, page: {Page})",
|
||||
totalCount, request.Threshold, request.PageIndex);
|
||||
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)request.PageSize);
|
||||
|
||||
return new GetLowStockProductsResponseDto
|
||||
{
|
||||
MetaData = new MetaData
|
||||
{
|
||||
CurrentPage = request.PageIndex,
|
||||
TotalPage = totalPages,
|
||||
PageSize = request.PageSize,
|
||||
TotalCount = totalCount,
|
||||
HasNext = request.PageIndex < totalPages,
|
||||
HasPrevious = request.PageIndex > 1
|
||||
},
|
||||
Products = products
|
||||
};
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetLowStockProducts;
|
||||
|
||||
public class GetLowStockProductsQueryValidator : AbstractValidator<GetLowStockProductsQuery>
|
||||
{
|
||||
public GetLowStockProductsQueryValidator()
|
||||
{
|
||||
RuleFor(x => x.Threshold)
|
||||
.GreaterThanOrEqualTo(0).WithMessage("آستانه موجودی نمیتواند منفی باشد");
|
||||
|
||||
RuleFor(x => x.PageIndex)
|
||||
.GreaterThan(0).WithMessage("شماره صفحه باید بزرگتر از 0 باشد");
|
||||
|
||||
RuleFor(x => x.PageSize)
|
||||
.InclusiveBetween(1, 100).WithMessage("تعداد در هر صفحه باید بین 1 تا 100 باشد");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProducts;
|
||||
public record GetProductsQuery : IRequest<GetProductsResponseDto>
|
||||
{
|
||||
//
|
||||
public long Id { get; init; }
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProducts;
|
||||
public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, GetProductsResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
|
||||
public GetProductsQueryHandler(IApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<GetProductsResponseDto> Handle(GetProductsQuery request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _context.Products
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Id == request.Id)
|
||||
.Select(x => new GetProductsResponseDto
|
||||
{
|
||||
Id = x.Id,
|
||||
Title = x.Title,
|
||||
Description = x.Description,
|
||||
ShortInfomation = x.ShortInfomation,
|
||||
FullInformation = x.FullInformation,
|
||||
Price = x.Price,
|
||||
Discount = x.Discount,
|
||||
Rate = x.Rate,
|
||||
ImagePath = x.ImagePath,
|
||||
ThumbnailPath = x.ThumbnailPath,
|
||||
SaleCount = x.SaleCount,
|
||||
ViewCount = x.ViewCount,
|
||||
RemainingCount = x.RemainingCount,
|
||||
CategoryIds = x.ProductCategories
|
||||
.Select(pc => pc.CategoryId)
|
||||
.ToList()
|
||||
})
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
return response ?? throw new NotFoundException(nameof(Product), request.Id);
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProducts;
|
||||
public class GetProductsQueryValidator : AbstractValidator<GetProductsQuery>
|
||||
{
|
||||
public GetProductsQueryValidator()
|
||||
{
|
||||
RuleFor(model => model.Id)
|
||||
.NotNull();
|
||||
}
|
||||
public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
|
||||
{
|
||||
var result = await ValidateAsync(ValidationContext<GetProductsQuery>.CreateWithOptions((GetProductsQuery)model, x => x.IncludeProperties(propertyName)));
|
||||
if (result.IsValid)
|
||||
return Array.Empty<string>();
|
||||
return result.Errors.Select(e => e.ErrorMessage);
|
||||
};
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProducts;
|
||||
public class GetProductsResponseDto
|
||||
{
|
||||
//
|
||||
public long Id { get; set; }
|
||||
//
|
||||
public string Title { get; set; }
|
||||
//
|
||||
public string Description { get; set; }
|
||||
//
|
||||
public string ShortInfomation { get; set; }
|
||||
//
|
||||
public string FullInformation { get; set; }
|
||||
//
|
||||
public long Price { get; set; }
|
||||
//
|
||||
public int Discount { get; set; }
|
||||
//
|
||||
public int Rate { get; set; }
|
||||
//
|
||||
public string ImagePath { get; set; }
|
||||
//
|
||||
public string ThumbnailPath { get; set; }
|
||||
//
|
||||
public int SaleCount { get; set; }
|
||||
//
|
||||
public int ViewCount { get; set; }
|
||||
//
|
||||
public int RemainingCount { get; set; }
|
||||
// لیست شناسه دستهبندیهای محصول
|
||||
public List<long> CategoryIds { get; set; } = new();
|
||||
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProductsByCategory;
|
||||
|
||||
/// <summary>
|
||||
/// کوئری دریافت محصولات بر اساس دستهبندی
|
||||
/// </summary>
|
||||
public class GetProductsByCategoryQuery : IRequest<GetProductsByCategoryResponseDto>
|
||||
{
|
||||
public long CategoryId { get; set; }
|
||||
public int PageNumber { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
public bool OnlyActive { get; set; } = true;
|
||||
public bool OnlyInStock { get; set; } = false;
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProductsByCategory;
|
||||
|
||||
public class GetProductsByCategoryQueryHandler : IRequestHandler<GetProductsByCategoryQuery, GetProductsByCategoryResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<GetProductsByCategoryQueryHandler> _logger;
|
||||
|
||||
public GetProductsByCategoryQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<GetProductsByCategoryQueryHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<GetProductsByCategoryResponseDto> Handle(GetProductsByCategoryQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Products
|
||||
.Where(x => !x.IsDeleted)
|
||||
.Where(x => x.ProductCategories.Any(pc => pc.CategoryId == request.CategoryId && !pc.IsDeleted));
|
||||
|
||||
if (request.OnlyInStock)
|
||||
{
|
||||
query = query.Where(x => x.RemainingCount > 0);
|
||||
}
|
||||
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
|
||||
var products = await query
|
||||
.OrderByDescending(x => x.Created)
|
||||
.Skip((request.PageNumber - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.Select(x => new ProductListDto
|
||||
{
|
||||
Id = x.Id,
|
||||
Name = x.Title,
|
||||
Description = x.Description,
|
||||
Price = x.Price,
|
||||
Stock = x.RemainingCount,
|
||||
IsActive = !x.IsDeleted,
|
||||
ImageUrl = x.ImagePath,
|
||||
Created = x.Created
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var metaData = new MetaData
|
||||
{
|
||||
TotalCount = totalCount,
|
||||
PageSize = request.PageSize,
|
||||
CurrentPage = request.PageNumber,
|
||||
TotalPage = (int)Math.Ceiling(totalCount / (double)request.PageSize),
|
||||
HasNext = request.PageNumber < (int)Math.Ceiling(totalCount / (double)request.PageSize),
|
||||
HasPrevious = request.PageNumber > 1
|
||||
};
|
||||
|
||||
_logger.LogInformation(
|
||||
"Retrieved {Count} products for category {CategoryId}",
|
||||
products.Count,
|
||||
request.CategoryId
|
||||
);
|
||||
|
||||
return new GetProductsByCategoryResponseDto
|
||||
{
|
||||
MetaData = metaData,
|
||||
Products = products
|
||||
};
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProductsByCategory;
|
||||
|
||||
public class GetProductsByCategoryResponseDto
|
||||
{
|
||||
public MetaData MetaData { get; set; } = new();
|
||||
public List<ProductListDto> Products { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ProductListDto
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public long Price { get; set; }
|
||||
public int Stock { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProductsByTag;
|
||||
|
||||
/// <summary>
|
||||
/// کوئری دریافت محصولات بر اساس تگ
|
||||
/// </summary>
|
||||
public class GetProductsByTagQuery : IRequest<GetProductsByTagResponseDto>
|
||||
{
|
||||
public long TagId { get; set; }
|
||||
public int PageNumber { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
public bool OnlyActive { get; set; } = true;
|
||||
public bool OnlyInStock { get; set; } = false;
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using CMSMicroservice.Application.ProductsCQ.Queries.GetProductsByCategory;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProductsByTag;
|
||||
|
||||
public class GetProductsByTagQueryHandler : IRequestHandler<GetProductsByTagQuery, GetProductsByTagResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<GetProductsByTagQueryHandler> _logger;
|
||||
|
||||
public GetProductsByTagQueryHandler(
|
||||
IApplicationDbContext context,
|
||||
ILogger<GetProductsByTagQueryHandler> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<GetProductsByTagResponseDto> Handle(GetProductsByTagQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Products
|
||||
.Where(x => !x.IsDeleted)
|
||||
.Where(x => x.ProductTags.Any(pt => pt.TagId == request.TagId && !pt.IsDeleted));
|
||||
|
||||
if (request.OnlyInStock)
|
||||
{
|
||||
query = query.Where(x => x.RemainingCount > 0);
|
||||
}
|
||||
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
|
||||
var products = await query
|
||||
.OrderByDescending(x => x.Created)
|
||||
.Skip((request.PageNumber - 1) * request.PageSize)
|
||||
.Take(request.PageSize)
|
||||
.Select(x => new ProductListDto
|
||||
{
|
||||
Id = x.Id,
|
||||
Name = x.Title,
|
||||
Description = x.Description,
|
||||
Price = x.Price,
|
||||
Stock = x.RemainingCount,
|
||||
IsActive = !x.IsDeleted,
|
||||
ImageUrl = x.ImagePath,
|
||||
Created = x.Created
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var metaData = new MetaData
|
||||
{
|
||||
TotalCount = totalCount,
|
||||
PageSize = request.PageSize,
|
||||
CurrentPage = request.PageNumber,
|
||||
TotalPage = (int)Math.Ceiling(totalCount / (double)request.PageSize),
|
||||
HasNext = request.PageNumber < (int)Math.Ceiling(totalCount / (double)request.PageSize),
|
||||
HasPrevious = request.PageNumber > 1
|
||||
};
|
||||
|
||||
_logger.LogInformation(
|
||||
"Retrieved {Count} products for tag {TagId}",
|
||||
products.Count,
|
||||
request.TagId
|
||||
);
|
||||
|
||||
return new GetProductsByTagResponseDto
|
||||
{
|
||||
MetaData = metaData,
|
||||
Products = products
|
||||
};
|
||||
}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
using CMSMicroservice.Application.Common.Models;
|
||||
using CMSMicroservice.Application.ProductsCQ.Queries.GetProductsByCategory;
|
||||
|
||||
namespace CMSMicroservice.Application.ProductsCQ.Queries.GetProductsByTag;
|
||||
|
||||
public class GetProductsByTagResponseDto
|
||||
{
|
||||
public MetaData MetaData { get; set; } = new();
|
||||
public List<ProductListDto> Products { get; set; } = new();
|
||||
}
|
||||
Reference in New Issue
Block a user