using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.Core.Repositories.Interfaces; using PARR.Core.Repositories.Interfaces.RobotRepositories; using PARR.Core.Services.Snapshots.Interfaces; using PARR.Domain.Entities.RobotEntities; using PARR.Domain.Exceptions; namespace PARR.Core.Services.Snapshots.Implementations { /// /// Снапшоты для таблицы RobotConfiguration /// internal class RobotConfigurationSnapshotService : ISnapshotProvider { private readonly IRobotConfigurationRepository _robotConfigurationRepository; private readonly IRobotConfigurationSnapshotRepository _robotConfigurationSnapshotRepository; private readonly ILogger _logger; private readonly ISnapshotSettings _snapshotSettings; public TimeSpan Interval => _snapshotSettings.RobotConfigurationSnapshotInterval;// TimeSpan.FromMinutes(2); public TimeSpan RetentionPeriod => _snapshotSettings.RobotConfigurationSnapshotRetentionPeriod;//TimeSpan.FromDays(60); public RobotConfigurationSnapshotService( IRobotConfigurationRepository robotConfigurationRepository, IRobotConfigurationSnapshotRepository robotConfigurationSnapshotRepository, ILogger logger, ISnapshotSettings snapshotSettings ) { _robotConfigurationRepository = robotConfigurationRepository; _robotConfigurationSnapshotRepository = robotConfigurationSnapshotRepository; _logger = logger; _snapshotSettings = snapshotSettings; } public async Task TakeSnapshotAsync(CancellationToken cancellationToken) { var now = DateTimeOffset.UtcNow; _logger.LogDebug("Сбор метрик для снапшота RobotConfigurations..."); var stats = await _robotConfigurationRepository.Get() .GroupBy(t => new { t.RobotCode, t.RobotStatusCode, t.TaskStatusCode }) .Select(g => new { g.Key.RobotCode, g.Key.RobotStatusCode, g.Key.TaskStatusCode, Count = g.Count() }) .ToListAsync(cancellationToken); if (!stats.Any()) { _logger.LogDebug("Нет активных заданий роботов для создания снапшота."); return; } var snapshots = stats.Select(s => new RobotConfigurationSnapshot { Id = Guid.NewGuid(), DateCreated = now, RobotCode = s.RobotCode, RobotStatusCode = s.RobotStatusCode, TaskStatusCode = s.TaskStatusCode, Count = s.Count }).ToList(); if (!await _robotConfigurationSnapshotRepository.AddRangeAsync(snapshots) || !await _robotConfigurationSnapshotRepository.CommitAsync()) throw new DbErrorException("Ошибка при сохранении в БД"); _logger.LogInformation("Успешно сохранен снапшот RobotConfigurations. Записано строк: {Count}. Периодичность: {Interval}", snapshots.Count, Interval); } public async Task CleanUpOldSnapshotsAsync(CancellationToken cancellationToken) { // Вычисляем граничную дату (всё, что было ДО нее — удаляем) var thresholdDate = DateTimeOffset.UtcNow.Subtract(RetentionPeriod); _logger.LogInformation("[{ServiceName}] Запуск очистки старых снапшотов. Удаление данных старше {ThresholdDate}", GetType().Name, thresholdDate); // Фильтруем старые записи и вызываем ExecuteDeleteAsync для удаления прямо в базе данных var deletedCount = await _robotConfigurationSnapshotRepository.Get() .Where(s => s.DateCreated < thresholdDate) .ExecuteDeleteAsync(cancellationToken); _logger.LogInformation("[{ServiceName}] Очистка завершена. Удалено устаревших строк снапшотов: {Count}", GetType().Name, deletedCount); } } }