using Microsoft.Extensions.Logging; using PARR.Core.Common.Interfaces; using PARR.Core.Services.HelpDocument.Interfaces; using PARR.Domain.Cache; using PARR.Domain.DTOs.HelpDocument; using PARR.Domain.Settings; namespace PARR.Core.Services.HelpDocument.Implementations { internal class HelpDocumentService : IHelpDocumentService { private readonly IHelpDocumentProvider _helpProvider; private readonly IRedisCacheService _redisCacheService; private readonly HelpDocumentSettings _helpDocumentSettings; private readonly ILogger _logger; public HelpDocumentService( IHelpDocumentProvider helpProvider, IRedisCacheService redisCacheService, HelpDocumentSettings helpDocumentSettings, ILogger logger ) { _helpProvider = helpProvider; _redisCacheService = redisCacheService; _helpDocumentSettings = helpDocumentSettings; _logger = logger; } public async Task GetHelpDocumentAsync(string helpId) { if (string.IsNullOrEmpty(helpId)) { _logger.LogWarning("Значение helpId: '{helpId}' пустое.", helpId); return null; } var cacheKey = _redisCacheService.GetKey(CacheKeys.HelpDocument.HelpDocumentItem(helpId)); _logger.LogDebug("Запрос справки. Проверка наличия в Redis по ключу: {CacheKey}", cacheKey); // 1. Попытка получить данные из Redis var cachedData = await _redisCacheService.GetCachedDataAsync(cacheKey, useCompression: true); if (cachedData != null) { _logger.LogInformation("Документ справки успешно извлечен из кэша Redis. HelpId: {HelpId}", helpId); return cachedData; } _logger.LogDebug("Кэш пуст или недоступен. Запрос документа напрямую через провайдер для HelpId: {HelpId}", helpId); // 2. Если в Redis пусто, идем в GitLab через наш провайдер var remoteDocument = await _helpProvider.GetDocumentAsync(helpId); if (remoteDocument == null) { _logger.LogWarning("Документ не найден ни в Redis, ни в GitLab. HelpId: {HelpId}", helpId); // Создаем понятный для пользователя текст-заглушку если нет документации remoteDocument = new HelpDocumentResult { HelpId = helpId, Title = "Раздел в разработке", Content = "### 📚 Отсутствует документация по данному разделу.\n\n" + "Если у вас возникли срочные вопросы, свяжитесь с теми, кто знает на них ответы :)." }; // Кэшируем заглушку на короткий срок (например, на 5-10 минут), а не на 24 часа. // Это нужно, чтобы как только аналитик создаст файл и прилетит хук, кэш обновился, // но при этом частые клики пользователей по сломанному вопросику не спамили GitLab запросами 404. await _redisCacheService.SetCachedDataAsync(cacheKey, remoteDocument, _helpDocumentSettings.ShortTTL, useCompression: true); return remoteDocument; } // 3. Сохраняем полученный документ в Redis для последующих запросов await _redisCacheService.SetCachedDataAsync(cacheKey, remoteDocument, _helpDocumentSettings.CacheTTL, useCompression: true); _logger.LogDebug("Вернул документацию из провайдера для HelpId: {HelpId}", helpId); return remoteDocument; } public async Task ClearCacheAsync(string helpId) { if (string.IsNullOrEmpty(helpId)) { _logger.LogWarning("Попытка очистки кэша: передан пустой helpId."); return; } var cacheKey = _redisCacheService.GetKey(CacheKeys.HelpDocument.HelpDocumentItem(helpId)); _logger.LogInformation("Удаление документа справки из кэша Redis по вебхуку. Ключ: {CacheKey}", cacheKey); await _redisCacheService.DeleteCachedDataAsync(cacheKey); } } }