using Microsoft.Extensions.Logging; using PARR.DAL.CacheServices; using PARR.DAL.DomainServices.Interfaces; using PARR.DAL.Settings; using System.Security.Cryptography; using System.Text; namespace PARR.DAL.DomainServices.Implementations { public class GroupedShortcodesCacheService : IGroupedShortcodesCacheService { private readonly IRedisCacheService cacheService; private readonly GroupedShortcodesCacheSettings settings; private readonly ILogger logger; public GroupedShortcodesCacheService( IRedisCacheService cacheService, GroupedShortcodesCacheSettings settings, ILogger logger) { this.cacheService = cacheService; this.settings = settings; this.logger = logger; } public async Task GetAggregatedValueAsync( Guid jobGroupId, Guid unitId, string shortcode, Func> computeIfMissing) { if (string.IsNullOrEmpty(shortcode)) throw new ArgumentException("Ключ шорткода должен быть указан.", nameof(shortcode)); var cacheKey = GetCacheKey(jobGroupId, unitId, shortcode); try { var cachedValue = await cacheService.GetCachedDataAsync(cacheKey); if (cachedValue != null) { logger.LogDebug( "Попадание в кэш для шорткода '{ShortcodeKey}': unit={UnitId} → '{Value}'", shortcode, unitId, cachedValue); return cachedValue; } logger.LogDebug( "Промах кэша для шорткода '{ShortcodeKey}': unit={UnitId}. Вычисление...", shortcode, unitId); var computedValue = await computeIfMissing(); await cacheService.SetCachedDataAsync(cacheKey, computedValue, settings.ValueTtl); logger.LogDebug( "Вычислено и сохранено значение для '{ShortcodeKey}': unit={UnitId} → '{Value}' (срок хранения={Ttl})", shortcode, unitId, computedValue, settings.ValueTtl); return computedValue; } catch (Exception ex) { logger.LogWarning( ex, "Ошибка при получении или вычислении значения для шорткода '{ShortcodeKey}' (unit={UnitId}). Возвращена пустая строка.", shortcode, unitId); return string.Empty; } } private static string GetCacheKey(Guid jobGroupId, Guid unitId, string shortcodeKey) { var safeKey = shortcodeKey .Trim() .Replace(":", "_") .Replace(" ", "_") .Replace(".", "_") .Replace("%", "") .Replace("[", "_") .Replace("]", "_") .Replace("/", "_") .Replace("\\", "_"); // ✅ SHA256 от safeKey using var sha256 = SHA256.Create(); var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(safeKey)); var hashHex = BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); // ✅ Новый формат ключа return $"gr_shcd_{jobGroupId:N}{unitId:N}_{hashHex}"; } } }