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

209 lines
7.6 KiB
C#
Raw Permalink Normal View History

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;
}
}
}
}