2502cbbda2
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m44s
Payment Gateway: - Add PYMSPaymentService: IPaymentGatewayService via gRPC to PYMS microservice - Add ZarinPalPaymentService: direct ZarinPal integration (backup) - Register 'pyms' payment provider in DI ConfigureServices - Add PYMS proto files (pyms_transaction.proto, pyms_public_messages.proto) - Fix VerifyDiscountWalletCharge: pass 'OK' as status instead of Authority - Update appsettings: PaymentProvider=pyms, sandbox mode, merchant ID Blog System: - Add BlogCategory, BlogPost, BlogPostImage entities and CQRS - Add proto files and gRPC services for blog management - Add Mapster profiles for blog responses Content Management: - Add SitePage entity and CQRS for static pages - Add proto and gRPC service for site pages Image/File Management: - Add LocalFileManager with disk storage + base64 serving + FMS fallback - Add ImagePathResolverInterceptor for gRPC responses - Add ImageResolverService for explicit image resolution - Add UploadsController for public file serving with FMS fallback - Add PaymentCallbackController for discount order payment callbacks Database: - Add blog and content entity migrations - Remove ImagePath MaxLength constraints - Remove old FileManagementService (replaced by LocalFileManager)
692 lines
27 KiB
C#
692 lines
27 KiB
C#
using CMSMicroservice.Protobuf.Protos.Products;
|
|
using Grpc.Core;
|
|
using MediatR;
|
|
using CMSMicroservice.Application.ProductsCQ.Commands.CreateNewProducts;
|
|
using CMSMicroservice.Application.ProductsCQ.Commands.UpdateProducts;
|
|
using CMSMicroservice.Application.ProductsCQ.Commands.DeleteProducts;
|
|
using CMSMicroservice.Application.ProductsCQ.Commands.AddProductImage;
|
|
using CMSMicroservice.Application.ProductsCQ.Commands.RemoveProductImage;
|
|
using CMSMicroservice.Application.ProductsCQ.Queries.GetCustomerProducts;
|
|
using CMSMicroservice.Application.ProductsCQ.Queries.GetCustomerProductsByFilter;
|
|
using CMSMicroservice.Application.ProductsCQ.Queries.GetProductGallery;
|
|
using CMSMicroservice.Application.Common.Interfaces;
|
|
using Mapster;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using AppModels = CMSMicroservice.Application.Common.Models;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace CMSMicroservice.WebApi.Services;
|
|
|
|
public class ProductsService : ProductsContract.ProductsContractBase
|
|
{
|
|
private readonly ISender _sender;
|
|
private readonly IApplicationDbContext _context;
|
|
|
|
public ProductsService(ISender sender, IApplicationDbContext context)
|
|
{
|
|
_sender = sender;
|
|
_context = context;
|
|
}
|
|
public override async Task<CreateNewProductsResponse> CreateNewProducts(CreateNewProductsRequest request, ServerCallContext context)
|
|
{
|
|
var command = new CreateNewProductsCommand
|
|
{
|
|
Title = request.Title,
|
|
Description = request.Description,
|
|
ShortInfomation = request.ShortInfomation,
|
|
FullInformation = request.FullInformation,
|
|
Price = request.Price,
|
|
Discount = request.Discount,
|
|
Rate = request.Rate,
|
|
ImagePath = request.ImagePath,
|
|
ThumbnailPath = request.ThumbnailPath,
|
|
SaleCount = request.SaleCount,
|
|
ViewCount = request.ViewCount,
|
|
RemainingCount = request.RemainingCount,
|
|
CategoryIds = request.CategoryIds?.ToList() ?? new List<long>(),
|
|
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
|
ImageFileMime = request.ImageFile?.Mime,
|
|
ImageFileName = request.ImageFile?.FileName,
|
|
ThumbnailFileBytes = request.ThumbnailFile?.File?.ToByteArray(),
|
|
ThumbnailFileMime = request.ThumbnailFile?.Mime,
|
|
ThumbnailFileName = request.ThumbnailFile?.FileName
|
|
};
|
|
|
|
var result = await _sender.Send(command, context.CancellationToken);
|
|
return new CreateNewProductsResponse { Id = result.Id };
|
|
}
|
|
|
|
public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateProducts(UpdateProductsRequest request, ServerCallContext context)
|
|
{
|
|
var command = new UpdateProductsCommand
|
|
{
|
|
Id = request.Id,
|
|
Title = request.Title,
|
|
Description = request.Description,
|
|
ShortInfomation = request.ShortInfomation,
|
|
FullInformation = request.FullInformation,
|
|
Price = request.Price,
|
|
Discount = request.Discount,
|
|
Rate = request.Rate,
|
|
ImagePath = request.ImagePath,
|
|
ThumbnailPath = request.ThumbnailPath,
|
|
SaleCount = request.SaleCount,
|
|
ViewCount = request.ViewCount,
|
|
RemainingCount = request.RemainingCount,
|
|
CategoryIds = request.CategoryIds?.ToList() ?? new List<long>(),
|
|
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
|
ImageFileMime = request.ImageFile?.Mime,
|
|
ImageFileName = request.ImageFile?.FileName,
|
|
ThumbnailFileBytes = request.ThumbnailFile?.File?.ToByteArray(),
|
|
ThumbnailFileMime = request.ThumbnailFile?.Mime,
|
|
ThumbnailFileName = request.ThumbnailFile?.FileName
|
|
};
|
|
|
|
await _sender.Send(command, context.CancellationToken);
|
|
return new Google.Protobuf.WellKnownTypes.Empty();
|
|
}
|
|
|
|
public override async Task<Google.Protobuf.WellKnownTypes.Empty> DeleteProducts(DeleteProductsRequest request, ServerCallContext context)
|
|
{
|
|
var command = new DeleteProductsCommand { Id = request.Id };
|
|
await _sender.Send(command, context.CancellationToken);
|
|
return new Google.Protobuf.WellKnownTypes.Empty();
|
|
}
|
|
|
|
public override async Task<GetProductsResponse> GetProducts(GetProductsRequest request, ServerCallContext context)
|
|
{
|
|
var query = new GetCustomerProductsQuery { Id = request.Id };
|
|
var result = await _sender.Send(query, context.CancellationToken);
|
|
|
|
var response = new GetProductsResponse
|
|
{
|
|
Id = result.Id,
|
|
Title = result.Title,
|
|
Description = result.Description,
|
|
ShortInfomation = result.ShortInfomation,
|
|
FullInformation = result.FullInformation,
|
|
Price = result.Price,
|
|
Discount = result.Discount,
|
|
Rate = result.Rate,
|
|
ImagePath = result.ImagePath,
|
|
ThumbnailPath = result.ThumbnailPath,
|
|
SaleCount = result.SaleCount,
|
|
ViewCount = result.ViewCount,
|
|
RemainingCount = result.RemainingCount
|
|
};
|
|
|
|
if (result.Gallery != null)
|
|
{
|
|
foreach (var item in result.Gallery)
|
|
{
|
|
response.Gallery.Add(new ProductGalleryItem
|
|
{
|
|
ProductGalleryId = item.ProductGalleryId,
|
|
ProductImageId = item.ProductImageId,
|
|
Title = item.Title,
|
|
ImagePath = item.ImagePath,
|
|
ImageThumbnailPath = item.ImageThumbnailPath
|
|
});
|
|
}
|
|
}
|
|
|
|
if (result.Categories != null)
|
|
{
|
|
foreach (var cat in result.Categories)
|
|
{
|
|
var categoryPath = new ProductCategoryPath
|
|
{
|
|
CategoryId = cat.CategoryId,
|
|
Title = cat.Title
|
|
};
|
|
if (cat.Path != null)
|
|
{
|
|
foreach (var node in cat.Path)
|
|
{
|
|
categoryPath.Path.Add(new CategoryNode
|
|
{
|
|
Id = node.Id,
|
|
Title = node.Title,
|
|
ParentId = node.ParentId
|
|
});
|
|
}
|
|
}
|
|
response.Categories.Add(categoryPath);
|
|
}
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
public override async Task<GetAllProductsByFilterResponse> GetAllProductsByFilter(GetAllProductsByFilterRequest request, ServerCallContext context)
|
|
{
|
|
// Map to GetCustomerProductsByFilter query (public products API)
|
|
var query = new GetCustomerProductsByFilterQuery
|
|
{
|
|
Id = request.Filter?.Id,
|
|
Title = request.Filter?.Title ?? string.Empty,
|
|
Description = request.Filter?.Description ?? string.Empty,
|
|
ShortInfomation = request.Filter?.ShortInfomation ?? string.Empty,
|
|
FullInformation = request.Filter?.FullInformation ?? string.Empty,
|
|
Price = request.Filter?.Price,
|
|
Discount = request.Filter?.Discount,
|
|
Rate = request.Filter?.Rate,
|
|
SaleCount = request.Filter?.SaleCount,
|
|
ViewCount = request.Filter?.ViewCount,
|
|
RemainingCount = request.Filter?.RemainingCount,
|
|
CategoryIds = request.Filter?.CategoryId.HasValue == true
|
|
? new List<long> { request.Filter.CategoryId.Value }
|
|
: new List<long>(),
|
|
IsActive = request.Filter?.IsActive,
|
|
SortBy = request.SortBy ?? string.Empty,
|
|
PaginationState = request.PaginationState != null
|
|
? new AppModels.PaginationState
|
|
{
|
|
PageNumber = request.PaginationState.PageNumber,
|
|
PageSize = request.PaginationState.PageSize
|
|
}
|
|
: new AppModels.PaginationState { PageNumber = 1, PageSize = 20 }
|
|
};
|
|
|
|
var result = await _sender.Send(query, context.CancellationToken);
|
|
|
|
return new GetAllProductsByFilterResponse
|
|
{
|
|
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
|
|
{
|
|
CurrentPage = result.MetaData.CurrentPage,
|
|
TotalPage = result.MetaData.TotalPage,
|
|
PageSize = result.MetaData.PageSize,
|
|
TotalCount = result.MetaData.TotalCount,
|
|
HasPrevious = result.MetaData.HasPrevious,
|
|
HasNext = result.MetaData.HasNext
|
|
},
|
|
Models = { result.Models.Select(m => new GetAllProductsByFilterResponseModel
|
|
{
|
|
Id = m.Id,
|
|
Title = m.Title,
|
|
Description = m.Description,
|
|
ShortInfomation = m.ShortInfomation,
|
|
FullInformation = m.FullInformation,
|
|
Price = m.Price,
|
|
Discount = m.Discount,
|
|
Rate = m.Rate,
|
|
ImagePath = m.ImagePath,
|
|
ThumbnailPath = m.ThumbnailPath,
|
|
SaleCount = m.SaleCount,
|
|
ViewCount = m.ViewCount,
|
|
RemainingCount = m.RemainingCount,
|
|
CategoryIds = { m.Categories?.Select(c => c.CategoryId) ?? Enumerable.Empty<long>() },
|
|
IsActive = m.IsActive
|
|
}) }
|
|
};
|
|
}
|
|
|
|
public override async Task<BulkUpdateProductPricesResponse> BulkUpdateProductPrices(BulkUpdateProductPricesRequest request, ServerCallContext context)
|
|
{
|
|
var response = new BulkUpdateProductPricesResponse { Total = request.Products.Count };
|
|
|
|
foreach (var item in request.Products)
|
|
{
|
|
try
|
|
{
|
|
var product = await _context.Products.FindAsync(new object[] { item.ProductId }, context.CancellationToken);
|
|
if (product == null)
|
|
{
|
|
response.Failed++;
|
|
response.Errors.Add(new BulkOperationError { ProductId = item.ProductId, ErrorMessage = "محصول یافت نشد" });
|
|
continue;
|
|
}
|
|
|
|
product.Price = item.NewPrice;
|
|
if (item.NewDiscount != null) product.Discount = item.NewDiscount.Value;
|
|
if (item.NewClubDiscountPercent != null) product.ClubDiscountPercent = item.NewClubDiscountPercent.Value;
|
|
|
|
response.Succeeded++;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
response.Failed++;
|
|
response.Errors.Add(new BulkOperationError { ProductId = item.ProductId, ErrorMessage = ex.Message });
|
|
}
|
|
}
|
|
|
|
await _context.SaveChangesAsync(context.CancellationToken);
|
|
return response;
|
|
}
|
|
|
|
public override async Task<BulkUpdateProductStockResponse> BulkUpdateProductStock(BulkUpdateProductStockRequest request, ServerCallContext context)
|
|
{
|
|
var response = new BulkUpdateProductStockResponse { Total = request.Products.Count };
|
|
|
|
foreach (var item in request.Products)
|
|
{
|
|
try
|
|
{
|
|
var product = await _context.Products.FindAsync(new object[] { item.ProductId }, context.CancellationToken);
|
|
if (product == null)
|
|
{
|
|
response.Failed++;
|
|
response.Errors.Add(new BulkOperationError { ProductId = item.ProductId, ErrorMessage = "محصول یافت نشد" });
|
|
continue;
|
|
}
|
|
|
|
product.RemainingCount = request.UpdateType switch
|
|
{
|
|
StockUpdateType.Set => item.Quantity,
|
|
StockUpdateType.Add => product.RemainingCount + item.Quantity,
|
|
StockUpdateType.Subtract => Math.Max(0, product.RemainingCount - item.Quantity),
|
|
_ => item.Quantity
|
|
};
|
|
|
|
response.Succeeded++;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
response.Failed++;
|
|
response.Errors.Add(new BulkOperationError { ProductId = item.ProductId, ErrorMessage = ex.Message });
|
|
}
|
|
}
|
|
|
|
await _context.SaveChangesAsync(context.CancellationToken);
|
|
return response;
|
|
}
|
|
|
|
public override async Task<GetLowStockProductsResponse> GetLowStockProducts(GetLowStockProductsRequest request, ServerCallContext context)
|
|
{
|
|
var threshold = request.Threshold > 0 ? request.Threshold : 10;
|
|
var pageIndex = request.PageIndex > 0 ? request.PageIndex : 1;
|
|
var pageSize = request.PageSize > 0 ? request.PageSize : 20;
|
|
|
|
var query = _context.Products
|
|
.Where(p => !p.IsDeleted && p.RemainingCount <= threshold);
|
|
|
|
if (request.IsClubExclusive != null)
|
|
query = query.Where(p => p.IsClubExclusive == request.IsClubExclusive.Value);
|
|
|
|
var totalCount = await query.CountAsync(context.CancellationToken);
|
|
|
|
var products = await query
|
|
.OrderBy(p => p.RemainingCount)
|
|
.Skip((pageIndex - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.Select(p => new LowStockProduct
|
|
{
|
|
Id = p.Id,
|
|
Title = p.Title,
|
|
RemainingCount = p.RemainingCount,
|
|
Price = p.Price,
|
|
IsClubExclusive = p.IsClubExclusive
|
|
})
|
|
.ToListAsync(context.CancellationToken);
|
|
|
|
return new GetLowStockProductsResponse
|
|
{
|
|
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
|
|
{
|
|
CurrentPage = pageIndex,
|
|
PageSize = pageSize,
|
|
TotalCount = totalCount,
|
|
TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize),
|
|
HasPrevious = pageIndex > 1,
|
|
HasNext = pageIndex * pageSize < totalCount
|
|
},
|
|
Products = { products }
|
|
};
|
|
}
|
|
|
|
public override async Task<ToggleProductStatusResponse> ToggleProductStatus(ToggleProductStatusRequest request, ServerCallContext context)
|
|
{
|
|
var response = new ToggleProductStatusResponse { Total = request.ProductIds.Count };
|
|
|
|
foreach (var productId in request.ProductIds)
|
|
{
|
|
try
|
|
{
|
|
var product = await _context.Products.FindAsync(new object[] { productId }, context.CancellationToken);
|
|
if (product == null)
|
|
{
|
|
response.Failed++;
|
|
response.Errors.Add(new BulkOperationError { ProductId = productId, ErrorMessage = "محصول یافت نشد" });
|
|
continue;
|
|
}
|
|
|
|
product.IsDeleted = !request.Enable;
|
|
if (request.Enable && request.DefaultStock > 0 && product.RemainingCount == 0)
|
|
product.RemainingCount = request.DefaultStock;
|
|
|
|
response.Succeeded++;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
response.Failed++;
|
|
response.Errors.Add(new BulkOperationError { ProductId = productId, ErrorMessage = ex.Message });
|
|
}
|
|
}
|
|
|
|
await _context.SaveChangesAsync(context.CancellationToken);
|
|
return response;
|
|
}
|
|
|
|
// ============= Category-Product DragDrop Methods =============
|
|
|
|
public override async Task<GetProductsForCategoryResponse> GetProductsForCategory(GetProductsForCategoryRequest request, ServerCallContext context)
|
|
{
|
|
var assignedProductIds = await _context.ProductCategories
|
|
.Where(pc => pc.CategoryId == request.CategoryId && !pc.IsDeleted)
|
|
.Select(pc => pc.ProductId)
|
|
.ToListAsync(context.CancellationToken);
|
|
|
|
var allProducts = await _context.Products
|
|
.Where(p => !p.IsDeleted)
|
|
.OrderBy(p => p.Title)
|
|
.Select(p => new CategoryProductItem
|
|
{
|
|
Id = p.Id,
|
|
Title = p.Title,
|
|
Selected = assignedProductIds.Contains(p.Id)
|
|
})
|
|
.ToListAsync(context.CancellationToken);
|
|
|
|
return new GetProductsForCategoryResponse { Items = { allProducts } };
|
|
}
|
|
|
|
public override async Task<GetCategoriesResponse> GetCategories(GetCategoriesRequest request, ServerCallContext context)
|
|
{
|
|
var assignedCategoryIds = await _context.ProductCategories
|
|
.Where(pc => pc.ProductId == request.ProductId && !pc.IsDeleted)
|
|
.Select(pc => pc.CategoryId)
|
|
.ToListAsync(context.CancellationToken);
|
|
|
|
var allCategories = await _context.Categories
|
|
.Where(c => !c.IsDeleted && c.IsActive)
|
|
.OrderBy(c => c.SortOrder)
|
|
.Select(c => new CategoryItem
|
|
{
|
|
Id = c.Id,
|
|
Title = c.Title,
|
|
Selected = assignedCategoryIds.Contains(c.Id)
|
|
})
|
|
.ToListAsync(context.CancellationToken);
|
|
|
|
return new GetCategoriesResponse { Items = { allCategories } };
|
|
}
|
|
|
|
public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateProductCategories(UpdateProductCategoriesRequest request, ServerCallContext context)
|
|
{
|
|
var existingLinks = await _context.ProductCategories
|
|
.Where(pc => pc.ProductId == request.ProductId)
|
|
.ToListAsync(context.CancellationToken);
|
|
|
|
// حذف لینکهای قبلی
|
|
foreach (var link in existingLinks)
|
|
link.IsDeleted = true;
|
|
|
|
// ایجاد لینکهای جدید
|
|
foreach (var categoryId in request.CategoryIds)
|
|
{
|
|
var existing = existingLinks.FirstOrDefault(l => l.CategoryId == categoryId);
|
|
if (existing != null)
|
|
{
|
|
existing.IsDeleted = false;
|
|
}
|
|
else
|
|
{
|
|
_context.ProductCategories.Add(new Domain.Entities.ProductCategory
|
|
{
|
|
ProductId = request.ProductId,
|
|
CategoryId = categoryId
|
|
});
|
|
}
|
|
}
|
|
|
|
await _context.SaveChangesAsync(context.CancellationToken);
|
|
return new Google.Protobuf.WellKnownTypes.Empty();
|
|
}
|
|
|
|
public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateCategoryProducts(UpdateCategoryProductsRequest request, ServerCallContext context)
|
|
{
|
|
var existingLinks = await _context.ProductCategories
|
|
.Where(pc => pc.CategoryId == request.CategoryId)
|
|
.ToListAsync(context.CancellationToken);
|
|
|
|
// حذف لینکهای قبلی
|
|
foreach (var link in existingLinks)
|
|
link.IsDeleted = true;
|
|
|
|
// ایجاد لینکهای جدید
|
|
foreach (var productId in request.ProductIds)
|
|
{
|
|
var existing = existingLinks.FirstOrDefault(l => l.ProductId == productId);
|
|
if (existing != null)
|
|
{
|
|
existing.IsDeleted = false;
|
|
}
|
|
else
|
|
{
|
|
_context.ProductCategories.Add(new Domain.Entities.ProductCategory
|
|
{
|
|
ProductId = productId,
|
|
CategoryId = request.CategoryId
|
|
});
|
|
}
|
|
}
|
|
|
|
await _context.SaveChangesAsync(context.CancellationToken);
|
|
return new Google.Protobuf.WellKnownTypes.Empty();
|
|
}
|
|
|
|
// ============= Customer-specific Methods =============
|
|
|
|
public override async Task<GetProductsResponse> GetCustomerProducts(GetProductsRequest request, ServerCallContext context)
|
|
{
|
|
var query = new GetCustomerProductsQuery { Id = request.Id };
|
|
var result = await _sender.Send(query, context.CancellationToken);
|
|
|
|
var response = new GetProductsResponse
|
|
{
|
|
Id = result.Id,
|
|
Title = result.Title,
|
|
Description = result.Description,
|
|
ShortInfomation = result.ShortInfomation,
|
|
FullInformation = result.FullInformation,
|
|
Price = result.Price,
|
|
Discount = result.Discount,
|
|
Rate = result.Rate,
|
|
ImagePath = result.ImagePath,
|
|
ThumbnailPath = result.ThumbnailPath,
|
|
SaleCount = result.SaleCount,
|
|
ViewCount = result.ViewCount,
|
|
RemainingCount = result.RemainingCount
|
|
};
|
|
|
|
// Add gallery items
|
|
if (result.Gallery != null)
|
|
{
|
|
foreach (var item in result.Gallery)
|
|
{
|
|
response.Gallery.Add(new ProductGalleryItem
|
|
{
|
|
ProductGalleryId = item.ProductGalleryId,
|
|
ProductImageId = item.ProductImageId,
|
|
Title = item.Title,
|
|
ImagePath = item.ImagePath,
|
|
ImageThumbnailPath = item.ImageThumbnailPath
|
|
});
|
|
}
|
|
}
|
|
|
|
// Add categories
|
|
if (result.Categories != null)
|
|
{
|
|
foreach (var cat in result.Categories)
|
|
{
|
|
var categoryPath = new ProductCategoryPath
|
|
{
|
|
CategoryId = cat.CategoryId,
|
|
Title = cat.Title
|
|
};
|
|
|
|
if (cat.Path != null)
|
|
{
|
|
foreach (var node in cat.Path)
|
|
{
|
|
categoryPath.Path.Add(new CategoryNode
|
|
{
|
|
Id = node.Id,
|
|
Title = node.Title,
|
|
ParentId = node.ParentId
|
|
});
|
|
}
|
|
}
|
|
|
|
response.Categories.Add(categoryPath);
|
|
}
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
public override async Task<GetCustomerProductsByFilterResponse> GetCustomerProductsByFilter(GetAllProductsByFilterRequest request, ServerCallContext context)
|
|
{
|
|
var query = new GetCustomerProductsByFilterQuery
|
|
{
|
|
PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
|
|
SortBy = request.SortBy,
|
|
Id = request.Filter?.Id,
|
|
Title = request.Filter?.Title,
|
|
Description = request.Filter?.Description,
|
|
ShortInfomation = request.Filter?.ShortInfomation,
|
|
FullInformation = request.Filter?.FullInformation,
|
|
Price = request.Filter?.Price,
|
|
Discount = request.Filter?.Discount,
|
|
Rate = request.Filter?.Rate,
|
|
ImagePath = request.Filter?.ImagePath,
|
|
ThumbnailPath = request.Filter?.ThumbnailPath,
|
|
SaleCount = request.Filter?.SaleCount,
|
|
ViewCount = request.Filter?.ViewCount,
|
|
RemainingCount = request.Filter?.RemainingCount,
|
|
CategoryIds = request.Filter?.CategoryId != null ? new List<long> { request.Filter.CategoryId.Value } : null
|
|
};
|
|
|
|
var result = await _sender.Send(query, context.CancellationToken);
|
|
|
|
var response = new GetCustomerProductsByFilterResponse
|
|
{
|
|
MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
|
|
{
|
|
CurrentPage = result.MetaData.CurrentPage,
|
|
TotalPage = result.MetaData.TotalPage,
|
|
PageSize = result.MetaData.PageSize,
|
|
TotalCount = result.MetaData.TotalCount,
|
|
HasPrevious = result.MetaData.HasPrevious,
|
|
HasNext = result.MetaData.HasNext
|
|
}
|
|
};
|
|
|
|
foreach (var model in result.Models)
|
|
{
|
|
var productModel = new GetCustomerProductsByFilterResponseModel
|
|
{
|
|
Id = model.Id,
|
|
Title = model.Title,
|
|
Description = model.Description,
|
|
ShortInfomation = model.ShortInfomation,
|
|
FullInformation = model.FullInformation,
|
|
Price = model.Price,
|
|
Discount = model.Discount,
|
|
Rate = model.Rate,
|
|
ImagePath = model.ImagePath,
|
|
ThumbnailPath = model.ThumbnailPath,
|
|
SaleCount = model.SaleCount,
|
|
ViewCount = model.ViewCount,
|
|
RemainingCount = model.RemainingCount
|
|
};
|
|
|
|
if (model.Categories != null)
|
|
{
|
|
foreach (var cat in model.Categories)
|
|
{
|
|
var categoryPath = new ProductCategoryPath
|
|
{
|
|
CategoryId = cat.CategoryId,
|
|
Title = cat.Title
|
|
};
|
|
|
|
if (cat.Path != null)
|
|
{
|
|
foreach (var node in cat.Path)
|
|
{
|
|
categoryPath.Path.Add(new CategoryNode
|
|
{
|
|
Id = node.Id,
|
|
Title = node.Title,
|
|
ParentId = node.ParentId
|
|
});
|
|
}
|
|
}
|
|
|
|
productModel.Categories.Add(categoryPath);
|
|
}
|
|
}
|
|
|
|
response.Models.Add(productModel);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
// ============= Product Image Management =============
|
|
|
|
public override async Task<AddProductImageResponse> AddProductImage(AddProductImageRequest request, ServerCallContext context)
|
|
{
|
|
var command = new AddProductImageCommand
|
|
{
|
|
ProductId = request.ProductId,
|
|
Title = request.Title,
|
|
ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
|
|
ImageFileMime = request.ImageFile?.Mime,
|
|
ImageFileName = request.ImageFile?.FileName
|
|
};
|
|
|
|
var result = await _sender.Send(command, context.CancellationToken);
|
|
|
|
return new AddProductImageResponse
|
|
{
|
|
ProductGalleryId = result.ProductGalleryId,
|
|
ProductImageId = result.ProductImageId,
|
|
Title = result.Title,
|
|
ImagePath = result.ImagePath,
|
|
ImageThumbnailPath = result.ImageThumbnailPath
|
|
};
|
|
}
|
|
|
|
public override async Task<Google.Protobuf.WellKnownTypes.Empty> RemoveProductImage(RemoveProductImageRequest request, ServerCallContext context)
|
|
{
|
|
var command = new RemoveProductImageCommand { ProductGalleryId = request.ProductGalleryId };
|
|
await _sender.Send(command, context.CancellationToken);
|
|
return new Google.Protobuf.WellKnownTypes.Empty();
|
|
}
|
|
|
|
public override async Task<GetProductGalleryResponse> GetProductGallery(GetProductGalleryRequest request, ServerCallContext context)
|
|
{
|
|
var query = new GetProductGalleryQuery { ProductId = request.ProductId };
|
|
var result = await _sender.Send(query, context.CancellationToken);
|
|
|
|
var response = new GetProductGalleryResponse();
|
|
foreach (var item in result.Items)
|
|
{
|
|
response.Items.Add(new ProductGalleryItem
|
|
{
|
|
ProductGalleryId = item.ProductGalleryId,
|
|
ProductImageId = item.ProductImageId,
|
|
Title = item.Title,
|
|
ImagePath = item.ImagePath,
|
|
ImageThumbnailPath = item.ImageThumbnailPath
|
|
});
|
|
}
|
|
return response;
|
|
}
|
|
}
|