using System.Collections.Concurrent; using CMSMicroservice.Protobuf.Protos.ImageResolver; namespace FrontOffice.Main.Utilities; /// /// سرویس مرکزی resolve تصاویر — تمام تصاویر دینامیک FrontOffice از این سرویس عبور می‌کنند. /// مسیرهای نسبی از طریق gRPC به CMS ارسال شده و base64 data-URI دریافت می‌شود. /// نتایج در حافظه کش می‌شوند تا از فراخوانی مجدد جلوگیری شود. /// public class ImageCacheService { private readonly ImageResolverContract.ImageResolverContractClient _client; private readonly ConcurrentDictionary _cache = new(); public ImageCacheService(ImageResolverContract.ImageResolverContractClient client) { _client = client; } /// /// تبدیل یک مسیر تصویر به base64 data-URI. /// اگر مسیر خالی باشد → رشته خالی. /// اگر قبلاً data-URI باشد → بدون تغییر. /// اگر در کش باشد → از کش. /// در غیر این صورت → فراخوانی gRPC. /// public async Task ResolveAsync(string? path) { if (string.IsNullOrWhiteSpace(path)) return string.Empty; // اگر قبلاً base64 هست (مثلاً از interceptor آمده) if (path.StartsWith("data:", StringComparison.Ordinal)) return path; // مسیرهای استاتیک (wwwroot) یا لینک‌های خارجی نیاز به resolve ندارند if (path.StartsWith('/') || path.StartsWith("http://", StringComparison.Ordinal) || path.StartsWith("https://", StringComparison.Ordinal)) return path; // جستجو در کش if (_cache.TryGetValue(path, out var cached)) return cached; try { var response = await _client.ResolveImagesAsync( new ResolveImagesRequest { Paths = { path } }); var result = response?.Images?.FirstOrDefault()?.DataUri ?? string.Empty; _cache.TryAdd(path, result); return result; } catch { // در صورت خطا، رشته خالی برگردان تا UI خراب نشود return string.Empty; } } /// /// تبدیل دسته‌ای مسیرهای تصویر به base64 data-URI. /// مسیرهای کش‌شده و data-URI‌ها بدون فراخوانی gRPC برگردانده می‌شوند. /// public async Task> ResolveBatchAsync(IEnumerable paths) { var result = new Dictionary(); var toResolve = new List(); foreach (var path in paths) { if (string.IsNullOrWhiteSpace(path)) continue; if (path.StartsWith("data:", StringComparison.Ordinal) || path.StartsWith('/') || path.StartsWith("http://", StringComparison.Ordinal) || path.StartsWith("https://", StringComparison.Ordinal)) { result[path] = path; } else if (_cache.TryGetValue(path, out var cached)) { result[path] = cached; } else { toResolve.Add(path); } } if (toResolve.Count == 0) return result; try { var response = await _client.ResolveImagesAsync( new ResolveImagesRequest { Paths = { toResolve } }); if (response?.Images != null) { foreach (var img in response.Images) { _cache.TryAdd(img.OriginalPath, img.DataUri); result[img.OriginalPath] = img.DataUri; } } } catch { // در صورت خطا، مسیرهای resolve نشده خالی بمانند } return result; } /// /// پاکسازی تمام کش (مثلاً هنگام logout) /// public void ClearCache() => _cache.Clear(); }