Files
parr_api/PARR.TemplateMatcher/Services/Implementations/TemplateReuser.cs

118 lines
4.6 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces;
using PARR.Domain.Common.Rabbit.Messages.TemplateMatching;
using PARR.Domain.Entities;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Entities.JobEntities;
using PARR.Domain.Entities.Unit;
using PARR.Domain.Enums;
using PARR.TemplateMatcher.Services.Interfaces;
namespace PARR.TemplateMatcher.Services.Implementations;
internal class TemplateReuser : ITemplateReuser
{
private readonly ILogger<TemplateReuser> logger;
private readonly ITemplateRepository templateRepository;
private readonly ITemplateNameNormalizer nameNormalizer;
private readonly ITemplateMqPublisher mqPublisher;
public TemplateReuser(
ILogger<TemplateReuser> logger,
ITemplateRepository templateRepository,
ITemplateNameNormalizer nameNormalizer,
ITemplateMqPublisher mqPublisher)
{
this.logger = logger;
this.templateRepository = templateRepository;
this.nameNormalizer = nameNormalizer;
this.mqPublisher = mqPublisher;
}
public async Task<bool> TryReuseAsync(
Job targetJob,
Guid targetUnitId,
Unit? targetUnit,
int? index,
List<UnitInTemplateMessage> unitsInTemplate,
bool isActiveTemplate,
bool isActiveSchedule,
HistoryInitiator initiator,
CancellationToken ct = default)
{
const int maxAttempts = 3;
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
ct.ThrowIfCancellationRequested();
try
{
// 1. Атомарный захват шаблона
var templateId = await templateRepository.ReserveUnusedTemplateAsync(targetUnitId, initiator);
if (templateId == null)
{
logger.LogDebug("Нет доступных Unused-шаблонов (попытка {Attempt}).", attempt);
return false; // Шаблоны закончились
}
// Загружаем захваченный шаблон
var template = await templateRepository.Get()
.AsNoTracking()
.Include(t => t.Unit)
.FirstOrDefaultAsync(t => t.Id == templateId, ct);
if (template == null)
{
logger.LogWarning("Зарезервированный шаблон {TemplateId} не найден.", templateId);
continue; // Попробовать еще раз
}
logger.LogInformation("Шаблон {TemplateId} захвачен для переиспользования.", templateId);
// 2. Нормализация имени
var templateForName = new Template
{
Id = template.Id,
Name = template.Name,
JobId = targetJob.Id,
UnitId = targetUnitId,
Index = index,
Job = targetJob,
Unit = targetUnit,
UnitsInTemplate = unitsInTemplate.Select(m => new UnitsInTemplate { UnitId = m.UnitId, UnitFieldValueId = m.UnitFieldValueId }).ToList()
};
var expectedName = await nameNormalizer.GetNormalizedTemplateNameAsync(templateForName);
// 3. Отправка команды в MQ
var message = new TemplateUpdaterMessage
{
TemplateId = template.Id,
JobId = targetJob.Id,
UnitId = targetUnitId,
Name = expectedName,
IsActiveTemplate = isActiveTemplate,
IsActiveSchedule = isActiveSchedule,
StatusTypeId = TemplateStatusTypeEnum.Used,
Initiator = initiator,
IsNew = true,
Index = index ?? template.Index,
UnitsInTemplate = unitsInTemplate
};
await mqPublisher.PublishUpdateAsync(message, ct);
return true; // Успех
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при переиспользовании (попытка {Attempt}).", attempt);
if (attempt == maxAttempts) return false; // После 3 попыток сдаемся
await Task.Delay(Random.Shared.Next(10, 50), ct);
}
}
return false;
}
}