Files
parr_api/PARR.TemplateMatcher/Services/Implementations/UnusedTemplateTargetUnitResolver.cs
Mikhail Kuznetsov f9f234d119 refactor(templateMatcher):
- унификация деактивации шаблонов между разными синхронизаторами
- поиск ЭК выделен в отдельную сущность
2026-10-14 14:28:58 +10:00

209 lines
7.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces.Unit;
using PARR.Domain.Entities;
using PARR.Domain.Entities.Unit;
using PARR.TemplateMatcher.Constants;
using PARR.TemplateMatcher.Services.Interfaces;
namespace PARR.TemplateMatcher.Services.Implementations;
internal class UnusedTemplateTargetUnitResolver : IUnusedTemplateTargetUnitResolver
{
private readonly IUnitFieldRepository _unitFieldRepository;
private readonly IUnitInValueRepository _unitInValueRepository;
private readonly IUnitRepository _unitRepository;
private readonly ILogger<UnusedTemplateTargetUnitResolver> _logger;
public UnusedTemplateTargetUnitResolver(
IUnitFieldRepository unitFieldRepository,
IUnitInValueRepository unitInValueRepository,
IUnitRepository unitRepository,
ILogger<UnusedTemplateTargetUnitResolver> logger)
{
_unitFieldRepository = unitFieldRepository;
_unitInValueRepository = unitInValueRepository;
_unitRepository = unitRepository;
_logger = logger;
}
public async Task<IReadOnlyDictionary<Guid, Unit>> ResolveAsync(
IReadOnlyCollection<Template> templates,
CancellationToken ct = default)
{
var result = new Dictionary<Guid, Unit>();
foreach (var template in templates.Where(t => t.Unit != null))
{
result[template.Id] = template.Unit!;
}
if (templates.Count == 0)
return result;
var responsableAreaField = await _unitFieldRepository.GetByAihitNameAsync(
UnusedTemplateConstants.ResponsibilityAreaFieldName,
ct);
var tagField = await _unitFieldRepository.GetByAihitNameAsync(
UnusedTemplateConstants.ParrTagFieldName,
ct);
if (responsableAreaField == null || tagField == null)
{
_logger.LogWarning(
"Не найдены поля '{ResponsibilityAreaField}' или '{TagField}'. Целевой Unit для неиспользуемых шаблонов будет выбран как текущий.",
UnusedTemplateConstants.ResponsibilityAreaFieldName,
UnusedTemplateConstants.ParrTagFieldName);
await AddFallbackUnitsAsync(templates, result, ct);
return result;
}
var targetTagValueId = await _unitInValueRepository.Get()
.AsNoTracking()
.Where(uiv => uiv.FieldId == tagField.Id
&& uiv.Value != null
&& uiv.Value.Value == UnusedTemplateConstants.NotUsedTagValue)
.Select(uiv => uiv.ValueId)
.FirstOrDefaultAsync(ct);
if (targetTagValueId == Guid.Empty)
{
_logger.LogWarning(
"Значение '{TagValue}' для поля '{FieldName}' не найдено. Целевой Unit для неиспользуемых шаблонов будет выбран как текущий.",
UnusedTemplateConstants.NotUsedTagValue,
UnusedTemplateConstants.ParrTagFieldName);
await AddFallbackUnitsAsync(templates, result, ct);
return result;
}
var unitIds = templates
.Select(t => t.UnitId)
.Distinct()
.ToList();
var unitResponsableAreaValues = await _unitInValueRepository.GetByUnitIdsAndFieldIdsAsync(
unitIds,
new List<Guid> { responsableAreaField.Id },
ct);
var unitToResponsableAreaValueMap = unitResponsableAreaValues
.Where(uiv => uiv.ValueId != Guid.Empty)
.ToDictionary(uiv => uiv.UnitId, uiv => uiv.ValueId);
if (unitToResponsableAreaValueMap.Count == 0)
{
_logger.LogDebug(
"Для юнитов не найдены значения поля '{FieldName}'. Целевой Unit для неиспользуемых шаблонов будет выбран как текущий.",
UnusedTemplateConstants.ResponsibilityAreaFieldName);
await AddFallbackUnitsAsync(templates, result, ct);
return result;
}
var distinctResponsableAreaValues = unitToResponsableAreaValueMap.Values
.Distinct()
.ToList();
var matches = await _unitInValueRepository.Get()
.AsNoTracking()
.Where(uiv => uiv.FieldId == responsableAreaField.Id
&& distinctResponsableAreaValues.Contains(uiv.ValueId))
.Join(
_unitInValueRepository.Get()
.AsNoTracking()
.Where(t => t.FieldId == tagField.Id && t.ValueId == targetTagValueId),
responsableArea => responsableArea.UnitId,
tag => tag.UnitId,
(responsableArea, tag) => new
{
responsableArea.ValueId,
responsableArea.UnitId
})
.ToListAsync(ct);
var responsableAreaToTargetUnitMap = matches
.GroupBy(x => x.ValueId)
.ToDictionary(g => g.Key, g => g.First().UnitId);
var templatesToReload = new Dictionary<Guid, Guid>();
foreach (var template in templates)
{
if (!unitToResponsableAreaValueMap.TryGetValue(template.UnitId, out var responsableAreaValueId))
continue;
if (!responsableAreaToTargetUnitMap.TryGetValue(responsableAreaValueId, out var targetUnitId))
continue;
if (targetUnitId == Guid.Empty || targetUnitId == template.UnitId)
continue;
templatesToReload[template.Id] = targetUnitId;
}
if (templatesToReload.Count > 0)
{
var targetUnitIds = templatesToReload.Values
.Distinct()
.ToList();
var targetUnits = await _unitRepository.Get()
.AsNoTracking()
.Where(u => targetUnitIds.Contains(u.Id))
.ToDictionaryAsync(u => u.Id, ct);
foreach (var kvp in templatesToReload)
{
if (targetUnits.TryGetValue(kvp.Value, out var targetUnit))
{
result[kvp.Key] = targetUnit;
}
else
{
_logger.LogWarning(
"Не удалось загрузить целевой Unit {TargetUnitId} для шаблона {TemplateId}.",
kvp.Value,
kvp.Key);
}
}
}
await AddFallbackUnitsAsync(templates, result, ct);
return result;
}
private async Task AddFallbackUnitsAsync(
IReadOnlyCollection<Template> templates,
Dictionary<Guid, Unit> result,
CancellationToken ct)
{
var missingUnitIds = templates
.Where(t => !result.ContainsKey(t.Id) && t.UnitId != Guid.Empty)
.Select(t => t.UnitId)
.Distinct()
.ToList();
if (missingUnitIds.Count == 0)
return;
var fallbackUnits = await _unitRepository.Get()
.AsNoTracking()
.Where(u => missingUnitIds.Contains(u.Id))
.ToDictionaryAsync(u => u.Id, ct);
foreach (var template in templates.Where(t => !result.ContainsKey(t.Id) && t.UnitId != Guid.Empty))
{
if (fallbackUnits.TryGetValue(template.UnitId, out var fallbackUnit))
{
result[template.Id] = fallbackUnit;
}
}
}
}