2026-10-12 14:14:30 +10:00
|
|
|
|
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<HelpDocumentService> _logger;
|
|
|
|
|
|
|
|
|
|
|
|
public HelpDocumentService(
|
|
|
|
|
|
IHelpDocumentProvider helpProvider,
|
|
|
|
|
|
IRedisCacheService redisCacheService,
|
|
|
|
|
|
HelpDocumentSettings helpDocumentSettings,
|
|
|
|
|
|
ILogger<HelpDocumentService> logger
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
_helpProvider = helpProvider;
|
|
|
|
|
|
_redisCacheService = redisCacheService;
|
|
|
|
|
|
_helpDocumentSettings = helpDocumentSettings;
|
|
|
|
|
|
_logger = logger;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<HelpDocumentResult?> 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<HelpDocumentResult?>(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);
|
2026-10-12 15:13:14 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Создаем понятный для пользователя текст-заглушку если нет документации
|
|
|
|
|
|
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;
|
2026-10-12 14:14:30 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|