diff --git a/src/CMSMicroservice.Application/ProductsCQ/Queries/GetLowStockProducts/GetLowStockProductsQueryHandler.cs b/src/CMSMicroservice.Application/ProductsCQ/Queries/GetLowStockProducts/GetLowStockProductsQueryHandler.cs index 8d768ff..3109c71 100644 --- a/src/CMSMicroservice.Application/ProductsCQ/Queries/GetLowStockProducts/GetLowStockProductsQueryHandler.cs +++ b/src/CMSMicroservice.Application/ProductsCQ/Queries/GetLowStockProducts/GetLowStockProductsQueryHandler.cs @@ -56,13 +56,18 @@ public class GetLowStockProductsQueryHandler : IRequestHandler 1 }, Products = products }; diff --git a/src/CMSMicroservice.Protobuf/Protos/products.proto b/src/CMSMicroservice.Protobuf/Protos/products.proto index 7359f2b..0c1f73c 100644 --- a/src/CMSMicroservice.Protobuf/Protos/products.proto +++ b/src/CMSMicroservice.Protobuf/Protos/products.proto @@ -43,6 +43,29 @@ service ProductsContract }; }; + rpc BulkUpdateProductPrices(BulkUpdateProductPricesRequest) returns (BulkUpdateProductPricesResponse){ + option (google.api.http) = { + post: "/BulkUpdateProductPrices" + body: "*" + }; + }; + rpc BulkUpdateProductStock(BulkUpdateProductStockRequest) returns (BulkUpdateProductStockResponse){ + option (google.api.http) = { + post: "/BulkUpdateProductStock" + body: "*" + }; + }; + rpc GetLowStockProducts(GetLowStockProductsRequest) returns (GetLowStockProductsResponse){ + option (google.api.http) = { + get: "/GetLowStockProducts" + }; + }; + rpc ToggleProductStatus(ToggleProductStatusRequest) returns (ToggleProductStatusResponse){ + option (google.api.http) = { + post: "/ToggleProductStatus" + body: "*" + }; + }; } message CreateNewProductsRequest { @@ -155,3 +178,99 @@ message GetAllProductsByFilterResponseModel // لیست شناسه دسته‌بندی‌های محصول repeated int64 category_ids = 14; } + +// Bulk Update Product Prices +message BulkUpdateProductPricesRequest +{ + repeated ProductPriceUpdate products = 1; +} + +message ProductPriceUpdate +{ + int64 product_id = 1; + int64 new_price = 2; + google.protobuf.Int32Value new_discount = 3; + google.protobuf.Int32Value new_club_discount_percent = 4; +} + +message BulkUpdateProductPricesResponse +{ + int32 total = 1; + int32 succeeded = 2; + int32 failed = 3; + repeated BulkOperationError errors = 4; +} + +message BulkOperationError +{ + int64 product_id = 1; + string error_message = 2; +} + +// Bulk Update Product Stock +message BulkUpdateProductStockRequest +{ + repeated ProductStockUpdate products = 1; + StockUpdateType update_type = 2; +} + +message ProductStockUpdate +{ + int64 product_id = 1; + int32 quantity = 2; +} + +enum StockUpdateType +{ + SET = 0; + ADD = 1; + SUBTRACT = 2; +} + +message BulkUpdateProductStockResponse +{ + int32 total = 1; + int32 succeeded = 2; + int32 failed = 3; + repeated BulkOperationError errors = 4; +} + +// Get Low Stock Products +message GetLowStockProductsRequest +{ + int32 threshold = 1; + int32 page_index = 2; + int32 page_size = 3; + google.protobuf.BoolValue is_club_exclusive = 4; +} + +message GetLowStockProductsResponse +{ + messages.MetaData meta_data = 1; + repeated LowStockProduct products = 2; +} + +message LowStockProduct +{ + int64 id = 1; + string title = 2; + int32 remaining_count = 3; + int64 price = 4; + bool is_club_exclusive = 5; +} + +// Toggle Product Status +message ToggleProductStatusRequest +{ + repeated int64 product_ids = 1; + bool enable = 2; + int32 default_stock = 3; +} + +message ToggleProductStatusResponse +{ + int32 total = 1; + int32 succeeded = 2; + int32 failed = 3; + repeated BulkOperationError errors = 4; +} diff --git a/src/CMSMicroservice.WebApi/Common/Mappings/ProductsProfile.cs b/src/CMSMicroservice.WebApi/Common/Mappings/ProductsProfile.cs index b8e0e09..4f71145 100644 --- a/src/CMSMicroservice.WebApi/Common/Mappings/ProductsProfile.cs +++ b/src/CMSMicroservice.WebApi/Common/Mappings/ProductsProfile.cs @@ -1,10 +1,93 @@ +using CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices; +using CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock; +using CMSMicroservice.Application.ProductsCQ.Commands.ToggleProductStatus; +using CMSMicroservice.Application.ProductsCQ.Queries.GetLowStockProducts; +using CMSMicroservice.Protobuf.Protos.Products; +using ProtoProductPriceUpdate = CMSMicroservice.Protobuf.Protos.Products.ProductPriceUpdate; +using AppProductPriceUpdate = CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices.ProductPriceUpdate; +using ProtoProductStockUpdate = CMSMicroservice.Protobuf.Protos.Products.ProductStockUpdate; +using AppProductStockUpdate = CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock.ProductStockUpdate; +using ProtoStockUpdateType = CMSMicroservice.Protobuf.Protos.Products.StockUpdateType; +using AppStockUpdateType = CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock.StockUpdateType; +using System.Linq; + namespace CMSMicroservice.WebApi.Common.Mappings; public class ProductsProfile : IRegister { void IRegister.Register(TypeAdapterConfig config) { - //config.NewConfig() - // .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}"); + // BulkUpdateProductPrices mappings + config.NewConfig() + .Map(dest => dest.Products, src => src.Products); + + config.NewConfig() + .Map(dest => dest.ProductId, src => src.ProductId) + .Map(dest => dest.NewPrice, src => src.NewPrice) + .Map(dest => dest.NewDiscount, src => src.NewDiscount != null ? (int?)src.NewDiscount.Value : null) + .Map(dest => dest.NewClubDiscountPercent, src => src.NewClubDiscountPercent != null ? (int?)src.NewClubDiscountPercent.Value : null); + + config.NewConfig() + .Map(dest => dest.Total, src => src.UpdatedCount + src.FailedCount) + .Map(dest => dest.Succeeded, src => src.UpdatedCount) + .Map(dest => dest.Failed, src => src.FailedCount) + .Map(dest => dest.Errors, src => src.Errors.Select((msg, idx) => new BulkOperationError + { + ProductId = 0, // We don't have the ID in the error message + ErrorMessage = msg + }).ToList()); + + // BulkUpdateProductStock mappings + config.NewConfig() + .Map(dest => dest.Products, src => src.Products) + .Map(dest => dest.UpdateType, src => (AppStockUpdateType)src.UpdateType); + + config.NewConfig() + .Map(dest => dest.ProductId, src => src.ProductId) + .Map(dest => dest.Quantity, src => src.Quantity); + + config.NewConfig() + .Map(dest => dest.Total, src => src.UpdatedCount + src.FailedCount) + .Map(dest => dest.Succeeded, src => src.UpdatedCount) + .Map(dest => dest.Failed, src => src.FailedCount) + .Map(dest => dest.Errors, src => src.Errors.Select(msg => new BulkOperationError + { + ProductId = 0, + ErrorMessage = msg + }).ToList()); + + // GetLowStockProducts mappings + config.NewConfig() + .Map(dest => dest.Threshold, src => src.Threshold) + .Map(dest => dest.PageIndex, src => src.PageIndex) + .Map(dest => dest.PageSize, src => src.PageSize) + .Map(dest => dest.IsClubExclusive, src => src.IsClubExclusive != null ? (bool?)src.IsClubExclusive.Value : null); + + config.NewConfig() + .Map(dest => dest.MetaData, src => src.MetaData) + .Map(dest => dest.Products, src => src.Products); + + config.NewConfig() + .Map(dest => dest.Id, src => src.Id) + .Map(dest => dest.Title, src => src.Title) + .Map(dest => dest.RemainingCount, src => src.RemainingCount) + .Map(dest => dest.Price, src => src.Price) + .Map(dest => dest.IsClubExclusive, src => src.IsClubExclusive); + + // ToggleProductStatus mappings + config.NewConfig() + .Map(dest => dest.ProductIds, src => src.ProductIds) + .Map(dest => dest.Enable, src => src.Enable) + .Map(dest => dest.DefaultStock, src => src.DefaultStock); + + config.NewConfig() + .Map(dest => dest.Total, src => src.UpdatedCount + src.FailedCount) + .Map(dest => dest.Succeeded, src => src.UpdatedCount) + .Map(dest => dest.Failed, src => src.FailedCount) + .Map(dest => dest.Errors, src => src.Errors.Select(msg => new BulkOperationError + { + ProductId = 0, + ErrorMessage = msg + }).ToList()); } } diff --git a/src/CMSMicroservice.WebApi/Services/ProductsService.cs b/src/CMSMicroservice.WebApi/Services/ProductsService.cs index 2dfb1f0..97e8cd4 100644 --- a/src/CMSMicroservice.WebApi/Services/ProductsService.cs +++ b/src/CMSMicroservice.WebApi/Services/ProductsService.cs @@ -5,6 +5,10 @@ using CMSMicroservice.Application.ProductsCQ.Commands.UpdateProducts; using CMSMicroservice.Application.ProductsCQ.Commands.DeleteProducts; using CMSMicroservice.Application.ProductsCQ.Queries.GetProducts; using CMSMicroservice.Application.ProductsCQ.Queries.GetAllProductsByFilter; +using CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductPrices; +using CMSMicroservice.Application.ProductsCQ.Commands.BulkUpdateProductStock; +using CMSMicroservice.Application.ProductsCQ.Queries.GetLowStockProducts; +using CMSMicroservice.Application.ProductsCQ.Commands.ToggleProductStatus; namespace CMSMicroservice.WebApi.Services; public class ProductsService : ProductsContract.ProductsContractBase { @@ -34,4 +38,24 @@ public class ProductsService : ProductsContract.ProductsContractBase { return await _dispatchRequestToCQRS.Handle(request, context); } + + public override async Task BulkUpdateProductPrices(BulkUpdateProductPricesRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + + public override async Task BulkUpdateProductStock(BulkUpdateProductStockRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + + public override async Task GetLowStockProducts(GetLowStockProductsRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + + public override async Task ToggleProductStatus(ToggleProductStatusRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } }