update 2
This commit is contained in:
@@ -5,6 +5,8 @@ using CMSMicroservice.Protobuf.Protos.UserAddress;
|
|||||||
using CMSMicroservice.Protobuf.Protos.UserOrder;
|
using CMSMicroservice.Protobuf.Protos.UserOrder;
|
||||||
using CMSMicroservice.Protobuf.Protos.UserRole;
|
using CMSMicroservice.Protobuf.Protos.UserRole;
|
||||||
using CMSMicroservice.Protobuf.Protos.Products;
|
using CMSMicroservice.Protobuf.Protos.Products;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductImages;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
||||||
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.Common.Interfaces;
|
namespace BackOffice.BFF.Application.Common.Interfaces;
|
||||||
@@ -17,6 +19,8 @@ public interface IApplicationContractContext
|
|||||||
#region CMS
|
#region CMS
|
||||||
PackageContract.PackageContractClient Packages { get; }
|
PackageContract.PackageContractClient Packages { get; }
|
||||||
ProductsContract.ProductsContractClient Products { get; }
|
ProductsContract.ProductsContractClient Products { get; }
|
||||||
|
ProductImagesContract.ProductImagesContractClient ProductImages { get; }
|
||||||
|
ProductGallerysContract.ProductGallerysContractClient ProductGallerys { get; }
|
||||||
RoleContract.RoleContractClient Roles { get; }
|
RoleContract.RoleContractClient Roles { get; }
|
||||||
UserAddressContract.UserAddressContractClient UserAddress { get; }
|
UserAddressContract.UserAddressContractClient UserAddress { get; }
|
||||||
UserContract.UserContractClient Users { get; }
|
UserContract.UserContractClient Users { get; }
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ProductsCQ.Commands.AddProductImage;
|
||||||
|
|
||||||
|
public record AddProductImageCommand : IRequest<AddProductImageResponseDto>
|
||||||
|
{
|
||||||
|
public long ProductId { get; init; }
|
||||||
|
public string Title { get; init; }
|
||||||
|
public ImageFileModel ImageFile { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ImageFileModel
|
||||||
|
{
|
||||||
|
public byte[] File { get; set; }
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public string Mime { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
using System.IO;
|
||||||
|
using BackOffice.BFF.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductImages;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.ProductsCQ.Commands.AddProductImage;
|
||||||
|
|
||||||
|
public class AddProductImageCommandHandler : IRequestHandler<AddProductImageCommand, AddProductImageResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public AddProductImageCommandHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AddProductImageResponseDto> Handle(AddProductImageCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (request.ImageFile == null || request.ImageFile.File == null || request.ImageFile.File.Length == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Image file is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var optimizedMainImage = OptimizeImage(request.ImageFile.File, 1200, 1200, 80);
|
||||||
|
var thumbnailImage = OptimizeImage(request.ImageFile.File, 300, 300, 75);
|
||||||
|
|
||||||
|
var mainFileInfo = await _context.FileInfos.CreateNewFileInfoAsync(new()
|
||||||
|
{
|
||||||
|
Directory = "Images/Products/Gallery",
|
||||||
|
IsBase64 = false,
|
||||||
|
MIME = request.ImageFile.Mime,
|
||||||
|
FileName = request.ImageFile.FileName,
|
||||||
|
File = ByteString.CopyFrom(optimizedMainImage)
|
||||||
|
}, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
var thumbFileInfo = await _context.FileInfos.CreateNewFileInfoAsync(new()
|
||||||
|
{
|
||||||
|
Directory = "Images/Products/Gallery/Thumbnail",
|
||||||
|
IsBase64 = false,
|
||||||
|
MIME = request.ImageFile.Mime,
|
||||||
|
FileName = request.ImageFile.FileName,
|
||||||
|
File = ByteString.CopyFrom(thumbnailImage)
|
||||||
|
}, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
if (mainFileInfo == null || string.IsNullOrWhiteSpace(mainFileInfo.File) ||
|
||||||
|
thumbFileInfo == null || string.IsNullOrWhiteSpace(thumbFileInfo.File))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Error while uploading gallery image.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var createImageResponse = await _context.ProductImages.CreateNewProductImagesAsync(new CreateNewProductImagesRequest
|
||||||
|
{
|
||||||
|
Title = request.Title ?? string.Empty,
|
||||||
|
ImagePath = mainFileInfo.File,
|
||||||
|
ImageThumbnailPath = thumbFileInfo.File
|
||||||
|
}, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
var createGalleryResponse = await _context.ProductGallerys.CreateNewProductGallerysAsync(new CreateNewProductGallerysRequest
|
||||||
|
{
|
||||||
|
ProductId = request.ProductId,
|
||||||
|
ProductImageId = createImageResponse.Id
|
||||||
|
}, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
return new AddProductImageResponseDto
|
||||||
|
{
|
||||||
|
ProductGalleryId = createGalleryResponse.Id,
|
||||||
|
ProductImageId = createImageResponse.Id,
|
||||||
|
Title = request.Title ?? string.Empty,
|
||||||
|
ImagePath = mainFileInfo.File,
|
||||||
|
ImageThumbnailPath = thumbFileInfo.File
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] OptimizeImage(byte[] original, int maxWidth, int maxHeight, int quality)
|
||||||
|
{
|
||||||
|
using var image = Image.Load(original);
|
||||||
|
|
||||||
|
var ratio = Math.Min(maxWidth / (float)image.Width, maxHeight / (float)image.Height);
|
||||||
|
if (ratio < 1f)
|
||||||
|
{
|
||||||
|
var width = (int)(image.Width * ratio);
|
||||||
|
var height = (int)(image.Height * ratio);
|
||||||
|
image.Mutate(x => x.Resize(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
image.Save(ms, new JpegEncoder { Quality = quality });
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ProductsCQ.Commands.AddProductImage;
|
||||||
|
|
||||||
|
public class AddProductImageResponseDto
|
||||||
|
{
|
||||||
|
public long ProductGalleryId { get; set; }
|
||||||
|
public long ProductImageId { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string ImagePath { get; set; }
|
||||||
|
public string ImageThumbnailPath { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ProductsCQ.Commands.RemoveProductImage;
|
||||||
|
|
||||||
|
public record RemoveProductImageCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public long ProductGalleryId { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using BackOffice.BFF.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.ProductsCQ.Commands.RemoveProductImage;
|
||||||
|
|
||||||
|
public class RemoveProductImageCommandHandler : IRequestHandler<RemoveProductImageCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public RemoveProductImageCommandHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Unit> Handle(RemoveProductImageCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _context.ProductGallerys.DeleteProductGallerysAsync(new DeleteProductGallerysRequest
|
||||||
|
{
|
||||||
|
Id = request.ProductGalleryId
|
||||||
|
}, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
return Unit.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ProductsCQ.Queries.GetProductGallery;
|
||||||
|
|
||||||
|
public record GetProductGalleryQuery : IRequest<GetProductGalleryResponseDto>
|
||||||
|
{
|
||||||
|
public long ProductId { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using BackOffice.BFF.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductImages;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.ProductsCQ.Queries.GetProductGallery;
|
||||||
|
|
||||||
|
public class GetProductGalleryQueryHandler : IRequestHandler<GetProductGalleryQuery, GetProductGalleryResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public GetProductGalleryQueryHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetProductGalleryResponseDto> Handle(GetProductGalleryQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var galleryRequest = new GetAllProductGallerysByFilterRequest
|
||||||
|
{
|
||||||
|
Filter = new GetAllProductGallerysByFilterFilter()
|
||||||
|
};
|
||||||
|
|
||||||
|
var galleryResponse = await _context.ProductGallerys.GetAllProductGallerysByFilterAsync(galleryRequest, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
// Filter by product id on client side because generated type may not support assigning Int64Value directly
|
||||||
|
var filteredModels = galleryResponse?.Models?.Where(x => x.ProductId == request.ProductId).ToList();
|
||||||
|
|
||||||
|
var result = new GetProductGalleryResponseDto();
|
||||||
|
|
||||||
|
if (filteredModels == null || filteredModels.Count == 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
foreach (var item in filteredModels)
|
||||||
|
{
|
||||||
|
var image = await _context.ProductImages.GetProductImagesAsync(new GetProductImagesRequest
|
||||||
|
{
|
||||||
|
Id = item.ProductImageId
|
||||||
|
}, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
if (image == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
result.Items.Add(new ProductGalleryItemDto
|
||||||
|
{
|
||||||
|
ProductGalleryId = item.Id,
|
||||||
|
ProductImageId = item.ProductImageId,
|
||||||
|
Title = image.Title,
|
||||||
|
ImagePath = image.ImagePath,
|
||||||
|
ImageThumbnailPath = image.ImageThumbnailPath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ProductsCQ.Queries.GetProductGallery;
|
||||||
|
|
||||||
|
public class GetProductGalleryResponseDto
|
||||||
|
{
|
||||||
|
public List<ProductGalleryItemDto> Items { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProductGalleryItemDto
|
||||||
|
{
|
||||||
|
public long ProductGalleryId { get; set; }
|
||||||
|
public long ProductImageId { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string ImagePath { get; set; }
|
||||||
|
public string ImageThumbnailPath { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -6,6 +6,8 @@ using CMSMicroservice.Protobuf.Protos.UserAddress;
|
|||||||
using CMSMicroservice.Protobuf.Protos.UserOrder;
|
using CMSMicroservice.Protobuf.Protos.UserOrder;
|
||||||
using CMSMicroservice.Protobuf.Protos.UserRole;
|
using CMSMicroservice.Protobuf.Protos.UserRole;
|
||||||
using CMSMicroservice.Protobuf.Protos.Products;
|
using CMSMicroservice.Protobuf.Protos.Products;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductImages;
|
||||||
|
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
||||||
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@@ -41,6 +43,8 @@ public class ApplicationContractContext : IApplicationContractContext
|
|||||||
#region CMS
|
#region CMS
|
||||||
public PackageContract.PackageContractClient Packages => GetService<PackageContract.PackageContractClient>();
|
public PackageContract.PackageContractClient Packages => GetService<PackageContract.PackageContractClient>();
|
||||||
public ProductsContract.ProductsContractClient Products => GetService<ProductsContract.ProductsContractClient>();
|
public ProductsContract.ProductsContractClient Products => GetService<ProductsContract.ProductsContractClient>();
|
||||||
|
public ProductImagesContract.ProductImagesContractClient ProductImages => GetService<ProductImagesContract.ProductImagesContractClient>();
|
||||||
|
public ProductGallerysContract.ProductGallerysContractClient ProductGallerys => GetService<ProductGallerysContract.ProductGallerysContractClient>();
|
||||||
public RoleContract.RoleContractClient Roles => GetService<RoleContract.RoleContractClient>();
|
public RoleContract.RoleContractClient Roles => GetService<RoleContract.RoleContractClient>();
|
||||||
public UserAddressContract.UserAddressContractClient UserAddress => GetService<UserAddressContract.UserAddressContractClient>();
|
public UserAddressContract.UserAddressContractClient UserAddress => GetService<UserAddressContract.UserAddressContractClient>();
|
||||||
public UserContract.UserContractClient Users => GetService<UserContract.UserContractClient>();
|
public UserContract.UserContractClient Users => GetService<UserContract.UserContractClient>();
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using BackOffice.BFF.Application.ProductsCQ.Commands.UpdateProducts;
|
|||||||
using BackOffice.BFF.Application.ProductsCQ.Commands.DeleteProducts;
|
using BackOffice.BFF.Application.ProductsCQ.Commands.DeleteProducts;
|
||||||
using BackOffice.BFF.Application.ProductsCQ.Queries.GetProducts;
|
using BackOffice.BFF.Application.ProductsCQ.Queries.GetProducts;
|
||||||
using BackOffice.BFF.Application.ProductsCQ.Queries.GetAllProductsByFilter;
|
using BackOffice.BFF.Application.ProductsCQ.Queries.GetAllProductsByFilter;
|
||||||
|
using BackOffice.BFF.Application.ProductsCQ.Commands.AddProductImage;
|
||||||
|
using BackOffice.BFF.Application.ProductsCQ.Queries.GetProductGallery;
|
||||||
|
using BackOffice.BFF.Application.ProductsCQ.Commands.RemoveProductImage;
|
||||||
|
|
||||||
namespace BackOffice.BFF.WebApi.Services;
|
namespace BackOffice.BFF.WebApi.Services;
|
||||||
|
|
||||||
@@ -41,5 +44,19 @@ public class ProductsService : ProductsContract.ProductsContractBase
|
|||||||
{
|
{
|
||||||
return await _dispatchRequestToCQRS.Handle<GetAllProductsByFilterRequest, GetAllProductsByFilterQuery, GetAllProductsByFilterResponse>(request, context);
|
return await _dispatchRequestToCQRS.Handle<GetAllProductsByFilterRequest, GetAllProductsByFilterQuery, GetAllProductsByFilterResponse>(request, context);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public override async Task<AddProductImageResponse> AddProductImage(AddProductImageRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<AddProductImageRequest, AddProductImageCommand, AddProductImageResponse>(request, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<GetProductGalleryResponse> GetProductGallery(GetProductGalleryRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<GetProductGalleryRequest, GetProductGalleryQuery, GetProductGalleryResponse>(request, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Empty> RemoveProductImage(RemoveProductImageRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<RemoveProductImageRequest, RemoveProductImageCommand>(request, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>0.0.1</Version>
|
<Version>0.0.2</Version>
|
||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<DebugSymbols>False</DebugSymbols>
|
<DebugSymbols>False</DebugSymbols>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
|
|||||||
@@ -40,6 +40,26 @@ service ProductsContract
|
|||||||
get: "/GetAllProductsByFilter"
|
get: "/GetAllProductsByFilter"
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rpc AddProductImage(AddProductImageRequest) returns (AddProductImageResponse){
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/AddProductImage"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
rpc GetProductGallery(GetProductGalleryRequest) returns (GetProductGalleryResponse){
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/GetProductGallery"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
rpc RemoveProductImage(RemoveProductImageRequest) returns (google.protobuf.Empty){
|
||||||
|
option (google.api.http) = {
|
||||||
|
delete: "/RemoveProductImage"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
message ImageFileModel
|
message ImageFileModel
|
||||||
@@ -174,3 +194,42 @@ message DecimalValue
|
|||||||
sfixed32 nanos = 2;
|
sfixed32 nanos = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message AddProductImageRequest
|
||||||
|
{
|
||||||
|
int64 product_id = 1;
|
||||||
|
string title = 2;
|
||||||
|
ImageFileModel image_file = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddProductImageResponse
|
||||||
|
{
|
||||||
|
int64 product_gallery_id = 1;
|
||||||
|
int64 product_image_id = 2;
|
||||||
|
string title = 3;
|
||||||
|
string image_path = 4;
|
||||||
|
string image_thumbnail_path = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetProductGalleryRequest
|
||||||
|
{
|
||||||
|
int64 product_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ProductGalleryItem
|
||||||
|
{
|
||||||
|
int64 product_gallery_id = 1;
|
||||||
|
int64 product_image_id = 2;
|
||||||
|
string title = 3;
|
||||||
|
string image_path = 4;
|
||||||
|
string image_thumbnail_path = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetProductGalleryResponse
|
||||||
|
{
|
||||||
|
repeated ProductGalleryItem items = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RemoveProductImageRequest
|
||||||
|
{
|
||||||
|
int64 product_gallery_id = 1;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user