feat(dal,templateMatcher): Shortcodes добавлены %МАКС:ИМЯ АТРИБУТА%, %ГР_ПОЛЕ-ПН%, %БУКВЫ:ИМЯ АТРИБУТА%, исправлена фильтрация в UnitFilter, TemplateMatcher отдельные классы для типов работ, Shortcodes теперь работает по своим моделям Dto
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PARR.Common.Domain;
|
||||
using PARR.Constants;
|
||||
using PARR.DAL.Models;
|
||||
using PARR.DAL.Services.Interfaces;
|
||||
using PARR.TemplateMatcher.Services.Interfaces;
|
||||
|
||||
namespace PARR.TemplateMatcher.Services.Implementations;
|
||||
|
||||
internal class TemplateReuser : ITemplateReuser
|
||||
{
|
||||
private const int UnusedCandidateBatchSize = 10;
|
||||
|
||||
private readonly ILogger<TemplateReuser> logger;
|
||||
private readonly ITemplateService templateService;
|
||||
|
||||
public TemplateReuser(
|
||||
ILogger<TemplateReuser> logger,
|
||||
ITemplateService templateService)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.templateService = templateService;
|
||||
}
|
||||
|
||||
public async Task<Template?> TryReuseOneUnusedTemplateAsync(
|
||||
Guid jobId,
|
||||
Guid unitId,
|
||||
HistoryInitiator initiator,
|
||||
int maxAttempts = 3)
|
||||
{
|
||||
for (int attempt = 1; attempt <= maxAttempts; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var unusedCandidates = await templateService.Get()
|
||||
.AsNoTracking()
|
||||
.Where(t => t.StatusTypeId == TemplateStatusTypeEnum.Unused)
|
||||
.OrderBy(t => t.DateModified ?? t.DateCreated)
|
||||
.Take(UnusedCandidateBatchSize)
|
||||
.ToListAsync();
|
||||
|
||||
if (!unusedCandidates.Any())
|
||||
{
|
||||
logger.LogDebug("Нет Unused-шаблонов (попытка {Attempt}).", attempt);
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var candidate in unusedCandidates)
|
||||
{
|
||||
var originalStatus = candidate.StatusTypeId;
|
||||
var originalModified = candidate.DateModified;
|
||||
|
||||
try
|
||||
{
|
||||
candidate.StatusTypeId = TemplateStatusTypeEnum.Updating;
|
||||
candidate.DateModified = DateTimeOffset.UtcNow;
|
||||
|
||||
if (await templateService.CommitAsync(initiator))
|
||||
{
|
||||
logger.LogInformation("Успешно захвачен шаблон {TemplateId} для переиспользования (попытка {Attempt}).",
|
||||
candidate.Id, attempt);
|
||||
return candidate; // Возвращаем захваченный шаблон
|
||||
}
|
||||
|
||||
// Откат при неудаче
|
||||
candidate.StatusTypeId = originalStatus;
|
||||
candidate.DateModified = originalModified;
|
||||
}
|
||||
catch (Exception ex) when (
|
||||
ex is DbUpdateException ||
|
||||
ex.InnerException?.Message.Contains("deadlock", StringComparison.OrdinalIgnoreCase) == true ||
|
||||
ex.InnerException?.Message.Contains("timeout", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
logger.LogWarning(ex, "Конфликт при захвате шаблона {TemplateId} (попытка {Attempt}).", candidate.Id, attempt);
|
||||
candidate.StatusTypeId = originalStatus;
|
||||
candidate.DateModified = originalModified;
|
||||
}
|
||||
}
|
||||
|
||||
if (attempt < maxAttempts)
|
||||
await Task.Delay(Random.Shared.Next(5, 15) * attempt);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Ошибка в попытке захвата (попытка {Attempt}).", attempt);
|
||||
if (attempt == maxAttempts) throw;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user