using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using PARR.Common.Domain; using PARR.DAL.Models; using PARR.DAL.Services.Interfaces; using PARR.TemplateMatcher.Services.Interfaces; namespace PARR.TemplateMatcher.Services.Implementations; internal class TemplateReuser : ITemplateReuser { private readonly ILogger logger; private readonly ITemplateService templateService; public TemplateReuser( ILogger logger, ITemplateService templateService) { this.logger = logger; this.templateService = templateService; } public async Task TryReuseOneUnusedTemplateAsync( Guid jobId, Guid unitId, HistoryInitiator initiator, int maxAttempts = 3) { for (int attempt = 1; attempt <= maxAttempts; attempt++) { try { // Атомарно резервируем один шаблон через DAL var templateId = await templateService.ReserveUnusedTemplateAsync(unitId, initiator); if (templateId == null) { logger.LogDebug("Нет доступных Unused-шаблонов для переиспользования (попытка {Attempt}).", attempt); return null; } // Загружаем зарезервированный шаблон var template = await templateService.Get() .AsNoTracking() .Include(t=>t.Unit) .Include(t=>t.Job) .ThenInclude(t=>t!.Tnk) .Include(t => t.Job) .ThenInclude(t => t!.Group) .ThenInclude(t=>t!.GroupType) .FirstOrDefaultAsync(t => t.Id == templateId); if (template == null) { logger.LogWarning("Зарезервированный шаблон {TemplateId} не найден при загрузке.", templateId); continue; } logger.LogInformation( "Успешно захвачен шаблон {TemplateId} (старый Job {OldJobId}) для нового Job {NewJobId}, Unit {UnitId} (попытка {Attempt}).", template.Id, template.JobId, jobId, unitId, attempt); return template; } catch (Exception ex) { logger.LogError(ex, "Ошибка при попытке захвата шаблона (попытка {Attempt}).", attempt); if (attempt == maxAttempts) throw; // Небольшая задержка перед повтором await Task.Delay(Random.Shared.Next(10, 50)); } } return null; } }