feat: Add bulk update functionalities for product prices and stock, along with low stock query support

This commit is contained in:
masoodafar-web
2025-12-04 02:56:07 +03:30
parent 4f400eabc5
commit 256b41fcb5
10 changed files with 431 additions and 225 deletions

View File

@@ -0,0 +1,28 @@
namespace BackOffice.BFF.Application.ProductsCQ.Commands.BulkUpdateProductPrices;
public record BulkUpdateProductPricesCommand : IRequest<BulkUpdateProductPricesResponseDto>
{
public List<ProductPriceUpdateDto> Products { get; init; } = new();
}
public class ProductPriceUpdateDto
{
public long ProductId { get; set; }
public long NewPrice { get; set; }
public int? NewDiscount { get; set; }
public int? NewClubDiscountPercent { get; set; }
}
public class BulkUpdateProductPricesResponseDto
{
public int Total { get; set; }
public int Succeeded { get; set; }
public int Failed { get; set; }
public List<BulkOperationError> Errors { get; set; } = new();
}
public class BulkOperationError
{
public long ProductId { get; set; }
public string ErrorMessage { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,34 @@
using BackOffice.BFF.Application.Common.Interfaces;
using CMSMicroservice.Protobuf.Protos.Products;
namespace BackOffice.BFF.Application.ProductsCQ.Commands.BulkUpdateProductPrices;
public class BulkUpdateProductPricesCommandHandler : IRequestHandler<BulkUpdateProductPricesCommand, BulkUpdateProductPricesResponseDto>
{
private readonly IApplicationContractContext _context;
public BulkUpdateProductPricesCommandHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<BulkUpdateProductPricesResponseDto> Handle(BulkUpdateProductPricesCommand request, CancellationToken cancellationToken)
{
var protoRequest = new BulkUpdateProductPricesRequest();
foreach (var product in request.Products)
{
protoRequest.Products.Add(new ProductPriceUpdate
{
ProductId = product.ProductId,
NewPrice = product.NewPrice,
NewDiscount = product.NewDiscount,
NewClubDiscountPercent = product.NewClubDiscountPercent
});
}
var response = await _context.Products.BulkUpdateProductPricesAsync(protoRequest, cancellationToken: cancellationToken);
return response.Adapt<BulkUpdateProductPricesResponseDto>();
}
}

View File

@@ -0,0 +1,34 @@
namespace BackOffice.BFF.Application.ProductsCQ.Commands.BulkUpdateProductStock;
public record BulkUpdateProductStockCommand : IRequest<BulkUpdateProductStockResponseDto>
{
public List<ProductStockUpdateDto> Products { get; init; } = new();
public StockUpdateType UpdateType { get; init; }
}
public class ProductStockUpdateDto
{
public long ProductId { get; set; }
public int Quantity { get; set; }
}
public enum StockUpdateType
{
Set = 0,
Add = 1,
Subtract = 2
}
public class BulkUpdateProductStockResponseDto
{
public int Total { get; set; }
public int Succeeded { get; set; }
public int Failed { get; set; }
public List<BulkOperationError> Errors { get; set; } = new();
}
public class BulkOperationError
{
public long ProductId { get; set; }
public string ErrorMessage { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,35 @@
using BackOffice.BFF.Application.Common.Interfaces;
using CMSMicroservice.Protobuf.Protos.Products;
namespace BackOffice.BFF.Application.ProductsCQ.Commands.BulkUpdateProductStock;
public class BulkUpdateProductStockCommandHandler : IRequestHandler<BulkUpdateProductStockCommand, BulkUpdateProductStockResponseDto>
{
private readonly IApplicationContractContext _context;
public BulkUpdateProductStockCommandHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<BulkUpdateProductStockResponseDto> Handle(BulkUpdateProductStockCommand request, CancellationToken cancellationToken)
{
var protoRequest = new BulkUpdateProductStockRequest
{
UpdateType = (CMSMicroservice.Protobuf.Protos.Products.StockUpdateType)request.UpdateType
};
foreach (var product in request.Products)
{
protoRequest.Products.Add(new ProductStockUpdate
{
ProductId = product.ProductId,
Quantity = product.Quantity
});
}
var response = await _context.Products.BulkUpdateProductStockAsync(protoRequest, cancellationToken: cancellationToken);
return response.Adapt<BulkUpdateProductStockResponseDto>();
}
}

View File

@@ -0,0 +1,22 @@
namespace BackOffice.BFF.Application.ProductsCQ.Commands.ToggleProductStatus;
public record ToggleProductStatusCommand : IRequest<ToggleProductStatusResponseDto>
{
public List<long> ProductIds { get; init; } = new();
public bool Enable { get; init; }
public int DefaultStock { get; init; } = 1;
}
public class ToggleProductStatusResponseDto
{
public int Total { get; set; }
public int Succeeded { get; set; }
public int Failed { get; set; }
public List<BulkOperationError> Errors { get; set; } = new();
}
public class BulkOperationError
{
public long ProductId { get; set; }
public string ErrorMessage { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,29 @@
using BackOffice.BFF.Application.Common.Interfaces;
using CMSMicroservice.Protobuf.Protos.Products;
namespace BackOffice.BFF.Application.ProductsCQ.Commands.ToggleProductStatus;
public class ToggleProductStatusCommandHandler : IRequestHandler<ToggleProductStatusCommand, ToggleProductStatusResponseDto>
{
private readonly IApplicationContractContext _context;
public ToggleProductStatusCommandHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<ToggleProductStatusResponseDto> Handle(ToggleProductStatusCommand request, CancellationToken cancellationToken)
{
var protoRequest = new ToggleProductStatusRequest
{
Enable = request.Enable,
DefaultStock = request.DefaultStock
};
protoRequest.ProductIds.AddRange(request.ProductIds);
var response = await _context.Products.ToggleProductStatusAsync(protoRequest, cancellationToken: cancellationToken);
return response.Adapt<ToggleProductStatusResponseDto>();
}
}

View File

@@ -0,0 +1,26 @@
using BackOffice.BFF.Application.Common.Models;
namespace BackOffice.BFF.Application.ProductsCQ.Queries.GetLowStockProducts;
public record GetLowStockProductsQuery : IRequest<GetLowStockProductsResponseDto>
{
public int Threshold { get; init; } = 10;
public int PageIndex { get; init; } = 1;
public int PageSize { get; init; } = 20;
public bool? IsClubExclusive { get; init; }
}
public class GetLowStockProductsResponseDto
{
public MetaData MetaData { get; set; } = new();
public List<LowStockProductDto> Products { get; set; } = new();
}
public class LowStockProductDto
{
public long Id { get; set; }
public string Title { get; set; } = string.Empty;
public int RemainingCount { get; set; }
public long Price { get; set; }
public bool IsClubExclusive { get; set; }
}

View File

@@ -0,0 +1,33 @@
using BackOffice.BFF.Application.Common.Interfaces;
using CMSMicroservice.Protobuf.Protos.Products;
namespace BackOffice.BFF.Application.ProductsCQ.Queries.GetLowStockProducts;
public class GetLowStockProductsQueryHandler : IRequestHandler<GetLowStockProductsQuery, GetLowStockProductsResponseDto>
{
private readonly IApplicationContractContext _context;
public GetLowStockProductsQueryHandler(IApplicationContractContext context)
{
_context = context;
}
public async Task<GetLowStockProductsResponseDto> Handle(GetLowStockProductsQuery request, CancellationToken cancellationToken)
{
var protoRequest = new GetLowStockProductsRequest
{
Threshold = request.Threshold,
PageIndex = request.PageIndex,
PageSize = request.PageSize
};
if (request.IsClubExclusive.HasValue)
{
protoRequest.IsClubExclusive = request.IsClubExclusive.Value;
}
var response = await _context.Products.GetLowStockProductsAsync(protoRequest, cancellationToken: cancellationToken);
return response.Adapt<GetLowStockProductsResponseDto>();
}
}