using System.Collections.Concurrent; using FrontOffice.BFF.Products.Protobuf.Protos.Products; namespace FrontOffice.Main.Utilities; public record Product( long Id, string Title, string Description, string ImageUrl, long Price, int Discount = 0, int Rate = 0, int RemainingCount = 0) { public IReadOnlyList Gallery { get; init; } = Array.Empty(); } public record ProductGalleryImage( long ProductGalleryId, long ProductImageId, string Title, string ImageUrl, string ThumbnailUrl); public class ProductService { private readonly ConcurrentDictionary _cache = new(); private readonly ProductsContract.ProductsContractClient _client; public ProductService(ProductsContract.ProductsContractClient client) { _client = client; } public async Task> GetProductsAsync(string? query = null) { try { var resp = await _client.GetAllProductsByFilterAsync(new GetAllProductsByFilterRequest { Filter = new GetAllProductsByFilterFilter { Title = query ?? string.Empty, Description = query ?? string.Empty, ShortInfomation = query ?? string.Empty, FullInformation = query ?? string.Empty } }); return MapAndCache(resp.Models); } catch { IEnumerable list = _cache.Values; if (!string.IsNullOrWhiteSpace(query)) { var q = query.Trim(); list = list.Where(p => p.Title.Contains(q, StringComparison.OrdinalIgnoreCase) || p.Description.Contains(q, StringComparison.OrdinalIgnoreCase)); } return list.OrderBy(p => p.Id).ToList(); } } public async Task GetByIdAsync(long id) { if (_cache.TryGetValue(id, out var cached)) return cached; try { var resp = await _client.GetProductsAsync(new GetProductsRequest { Id = id }); if (resp == null) { return null; } return MapAndCache(resp); } catch { _cache.TryGetValue(id, out var result); return result; } } private List MapAndCache(Google.Protobuf.Collections.RepeatedField models) { var list = new List(); foreach (var m in models) { var p = new Product( Id: m.Id, Title: m.Title ?? string.Empty, Description: m.Description ?? string.Empty, ImageUrl: string.IsNullOrWhiteSpace(m.ImagePath) ? string.Empty : UrlUtility.DownloadUrl + m.ImagePath, Price: m.Price, Discount: m.Discount, Rate: m.Rate, RemainingCount: m.RemainingCount ); _cache[p.Id] = p; list.Add(p); } return list; } private Product MapAndCache(GetProductsResponse model) { var gallery = model.Gallery .Select(item => new ProductGalleryImage( ProductGalleryId: item.ProductGalleryId, ProductImageId: item.ProductImageId, Title: item.Title ?? string.Empty, ImageUrl: BuildUrl(item.ImagePath), ThumbnailUrl: BuildUrl(item.ImageThumbnailPath))) .ToList(); var product = new Product( Id: model.Id, Title: model.Title ?? string.Empty, Description: model.Description ?? string.Empty, ImageUrl: BuildUrl(model.ImagePath), Price: model.Price, Discount: model.Discount, Rate: model.Rate, RemainingCount: model.RemainingCount) { Gallery = gallery }; _cache[product.Id] = product; return product; } private static string BuildUrl(string? path) => string.IsNullOrWhiteSpace(path) ? string.Empty : UrlUtility.DownloadUrl + path; }