feat(dal,templateMatcher): UnitFilterService переписан, теперь при подсчете количества связей для групп работ зонтик учитываются relationshipFilter. В TemplateMatcher добавлено изменение имени Unused шаблонов в конце имени добавляется DateTimeOffset.UtcNow.ToUnixTimeSeconds() для уникальности неиспользуемых шаблонов. Актуализированы unit-тесты
This commit is contained in:
@@ -0,0 +1,52 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MockQueryable;
|
||||||
|
using Moq;
|
||||||
|
using PARR.DAL.Contracts;
|
||||||
|
using PARR.DAL.DomainServices.Implementations;
|
||||||
|
using PARR.DAL.DomainServices.Interfaces;
|
||||||
|
using PARR.DAL.Models.Job;
|
||||||
|
using PARR.DAL.Models.Unit;
|
||||||
|
using PARR.DAL.Services.Interfaces.Job;
|
||||||
|
using PARR.DAL.Services.Interfaces.Unit;
|
||||||
|
|
||||||
|
namespace PARR.DAL.Tests.DomainServices.Implementations
|
||||||
|
{
|
||||||
|
public class ShortcodesServiceTests
|
||||||
|
{
|
||||||
|
private readonly Mock<ILogger<ShortcodesService>> loggerMock = new();
|
||||||
|
private readonly Mock<SettingsFromDb> settingsMock = new();
|
||||||
|
private readonly Mock<IJobService> jobServiceMock = new();
|
||||||
|
private readonly Mock<IUnitService> unitServiceMock = new();
|
||||||
|
private readonly Mock<IUnitInValueService> unitInValueServiceMock = new();
|
||||||
|
private readonly Mock<IUnitFieldService> unitFieldServiceMock = new();
|
||||||
|
private readonly Mock<IUnitFilterService> unitFilterServiceMock = new();
|
||||||
|
|
||||||
|
private ShortcodesService CreateService()
|
||||||
|
=> new(loggerMock.Object, settingsMock.Object, jobServiceMock.Object,
|
||||||
|
unitServiceMock.Object, unitFilterServiceMock.Object,
|
||||||
|
unitInValueServiceMock.Object, unitFieldServiceMock.Object);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApplyShortcodesAsync_WithEc_ShouldReplaceEc()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var unitId = Guid.NewGuid();
|
||||||
|
var jobId = Guid.NewGuid();
|
||||||
|
|
||||||
|
var unit = new Unit { Id = unitId, Name = "Сервер_01" };
|
||||||
|
var job = new Job { Id = jobId, Name = "SAN ТО-1 Проверка работы фабрики SAN", TemplateNameMask = "%ЭК%", WorkGroupMask = "", WorkName = "Аудит" };
|
||||||
|
|
||||||
|
// ✅ БЕЗ .Object — ключевое!
|
||||||
|
unitServiceMock.Setup(x => x.Get()).Returns(new[] { unit }.BuildMock());
|
||||||
|
jobServiceMock.Setup(x => x.Get()).Returns(new[] { job }.BuildMock());
|
||||||
|
|
||||||
|
var service = CreateService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await service.ApplyShortcodesAsync("%ЭК%", unitId, jobId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Сервер_01", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,8 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Moq;
|
using Moq;
|
||||||
using PARR.DAL.Context;
|
using PARR.DAL.Context;
|
||||||
using PARR.DAL.DomainServices.Implementations;
|
using PARR.DAL.DomainServices.Implementations;
|
||||||
using PARR.DAL.Services.Interfaces.Job;
|
using PARR.DAL.Services.Implementations.Job;
|
||||||
using PARR.DAL.Services.Interfaces.Unit;
|
using PARR.DAL.Services.Implementations.Unit;
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace PARR.DAL.Tests
|
namespace PARR.DAL.Tests
|
||||||
{
|
{
|
||||||
@@ -27,16 +26,26 @@ namespace PARR.DAL.Tests
|
|||||||
_context.Database.EnsureCreated();
|
_context.Database.EnsureCreated();
|
||||||
TestDataGenerator.Seed(_context);
|
TestDataGenerator.Seed(_context);
|
||||||
|
|
||||||
|
// Логгеры
|
||||||
var logger = Mock.Of<ILogger<UnitFilterService>>();
|
var logger = Mock.Of<ILogger<UnitFilterService>>();
|
||||||
|
var jobServiceLogger = Mock.Of<ILogger<JobService>>();
|
||||||
|
var unitServiceLogger = Mock.Of<ILogger<UnitService>>();
|
||||||
|
var unitInUnitServiceLogger = Mock.Of<ILogger<UnitInUnitService>>();
|
||||||
|
var unitInValueServiceLogger = Mock.Of<ILogger<UnitInValueService>>();
|
||||||
|
|
||||||
// Используем реальные сервисы, работающие с _context
|
// Реальные сервисы с правильными конструкторами
|
||||||
var jobServiceMock = new Mock<IJobService>();
|
var jobService = new JobService(_context, jobServiceLogger);
|
||||||
jobServiceMock.Setup(x => x.Get()).Returns(() => _context.Jobs);
|
var unitService = new UnitService(_context, unitServiceLogger);
|
||||||
|
var unitInUnitService = new UnitInUnitService(_context, unitInUnitServiceLogger);
|
||||||
|
var unitInValueService = new UnitInValueService(unitInValueServiceLogger, _context); // ← logger first!
|
||||||
|
|
||||||
var unitServiceMock = new Mock<IUnitService>();
|
_service = new UnitFilterService(
|
||||||
unitServiceMock.Setup(x => x.Get()).Returns(() => _context.Units);
|
logger,
|
||||||
|
jobService,
|
||||||
_service = new UnitFilterService(logger, jobServiceMock.Object, unitServiceMock.Object);
|
unitService,
|
||||||
|
unitInUnitService,
|
||||||
|
unitInValueService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() => _context.Dispose();
|
public void Dispose() => _context.Dispose();
|
||||||
@@ -75,46 +84,5 @@ namespace PARR.DAL.Tests
|
|||||||
// Assert
|
// Assert
|
||||||
result.Should().Contain(new[] { "СХД-КМТ-CISCO-MDS9148-1-ДВС", "СХД-КМТ-CISCO-MDS9148-3-ДВС" });
|
result.Should().Contain(new[] { "СХД-КМТ-CISCO-MDS9148-1-ДВС", "СХД-КМТ-CISCO-MDS9148-3-ДВС" });
|
||||||
}
|
}
|
||||||
//[Fact]
|
|
||||||
//public async Task GetRelatedUnitNamesAsync_WithChildRelationshipFilter_ShouldReturnExpectedNames()
|
|
||||||
//{
|
|
||||||
// // Arrange
|
|
||||||
// var jobId = _context.Jobs.First(j => j.Name == "TestJob_2").Id;
|
|
||||||
// var unitId = TestDataGenerator.UnitWId;
|
|
||||||
|
|
||||||
// // Проверим связи вручную
|
|
||||||
// var childLinks = await _context.UnitInUnits
|
|
||||||
// .Where(u => u.ParentUnitId == unitId)
|
|
||||||
// .ToListAsync();
|
|
||||||
|
|
||||||
// var childIds = childLinks.Select(l => l.ChildUnitId).ToList();
|
|
||||||
// var childUnits = await _context.Units
|
|
||||||
// .Where(u => childIds.Contains(u.Id))
|
|
||||||
// .ToListAsync();
|
|
||||||
|
|
||||||
// Console.WriteLine($"Child units of {unitId}:");
|
|
||||||
// foreach (var u in childUnits)
|
|
||||||
// {
|
|
||||||
// Console.WriteLine($" - {u.Id} ({u.Name})");
|
|
||||||
|
|
||||||
// var values = await _context.UnitInValues
|
|
||||||
// .Where(v => v.UnitId == u.Id && v.FieldId == TestDataGenerator.FieldId1)
|
|
||||||
// .ToListAsync();
|
|
||||||
|
|
||||||
// foreach (var v in values)
|
|
||||||
// {
|
|
||||||
// var val = await _context.UnitFieldValues.FindAsync(v.ValueId);
|
|
||||||
// Console.WriteLine($" FieldId1 = {val?.Value}");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Act
|
|
||||||
// var result = await _service.GetRelatedUnitNamesAsync(jobId, unitId);
|
|
||||||
|
|
||||||
// Console.WriteLine($"Result: [{string.Join(", ", result)}]");
|
|
||||||
|
|
||||||
// // Assert
|
|
||||||
// result.Should().Contain(new[] { "СХД-КМТ-CISCO-MDS9148-1-ДВС", "СХД-КМТ-CISCO-MDS9148-3-ДВС" });
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<PackageReference Include="FluentAssertions" Version="8.8.0" />
|
<PackageReference Include="FluentAssertions" Version="8.8.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.20" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.20" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||||
|
<PackageReference Include="MockQueryable.Moq" Version="7.0.3" />
|
||||||
<PackageReference Include="Moq" Version="4.20.72" />
|
<PackageReference Include="Moq" Version="4.20.72" />
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
|
|||||||
@@ -31,13 +31,45 @@ namespace PARR.DAL.DomainServices.Implementations
|
|||||||
this.unitInValueService = unitInValueService;
|
this.unitInValueService = unitInValueService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region вложенные классы
|
||||||
|
|
||||||
|
private class UnitInfoDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public required string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UnitValueDto
|
||||||
|
{
|
||||||
|
public Guid FieldId { get; set; }
|
||||||
|
public string? Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RelatedUnitDto
|
||||||
|
{
|
||||||
|
public Guid UnitId { get; set; }
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
public List<UnitValueDto> Values { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UnitDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
public List<UnitValueDto> Values { get; set; } = new();
|
||||||
|
public List<RelatedUnitDto> Parents { get; set; } = new();
|
||||||
|
public List<RelatedUnitDto> Children { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public async Task<IEnumerable<Guid>?> GetUnitsIdByJobFilterAsync(Guid jobId, int? takeCount = null)
|
public async Task<IEnumerable<Guid>?> GetUnitsIdByJobFilterAsync(Guid jobId, int? takeCount = null)
|
||||||
{
|
{
|
||||||
var job = await jobService
|
var job = await jobService
|
||||||
.Get().AsNoTracking()
|
.Get().AsNoTracking()
|
||||||
.Include(j => j.UnitFilters).ThenInclude(uf => uf.FieldFilters)
|
.Include(j => j.UnitFilters).ThenInclude(uf => uf.FieldFilters)
|
||||||
.Include(j => j.UnitFilters).ThenInclude(uf => uf.RelationshipFilters)
|
.Include(j => j.UnitFilters).ThenInclude(uf => uf.RelationshipFilters)
|
||||||
.Include(j => j.Group).ThenInclude(g => g.GroupType)
|
.Include(j => j.Group).ThenInclude(g => g!.GroupType)
|
||||||
.FirstOrDefaultAsync(j => j.Id == jobId);
|
.FirstOrDefaultAsync(j => j.Id == jobId);
|
||||||
|
|
||||||
return job == null ? null : await GetUnitsIdByJobFilterAsync(job, takeCount);
|
return job == null ? null : await GetUnitsIdByJobFilterAsync(job, takeCount);
|
||||||
@@ -45,14 +77,12 @@ namespace PARR.DAL.DomainServices.Implementations
|
|||||||
|
|
||||||
public async Task<IEnumerable<Guid>?> GetUnitsIdByJobFilterAsync(Job job, int? takeCount = null)
|
public async Task<IEnumerable<Guid>?> GetUnitsIdByJobFilterAsync(Job job, int? takeCount = null)
|
||||||
{
|
{
|
||||||
#region Проверка обязательных зависимостей
|
|
||||||
if (job.Group == null)
|
if (job.Group == null)
|
||||||
throw new ArgumentNullException(nameof(job.Group), $"Job {job.Id} не содержит Group");
|
throw new ArgumentNullException(nameof(job.Group), $"Job {job.Id} не содержит Group");
|
||||||
if (job.Group.GroupType == null)
|
if (job.Group.GroupType == null)
|
||||||
throw new ArgumentNullException(nameof(job.Group.GroupType), $"Job {job.Id} не содержит GroupType");
|
throw new ArgumentNullException(nameof(job.Group.GroupType), $"Job {job.Id} не содержит GroupType");
|
||||||
if (job.UnitFilters == null || !job.UnitFilters.Any())
|
if (job.UnitFilters == null || !job.UnitFilters.Any())
|
||||||
throw new ArgumentNullException(nameof(job.UnitFilters), $"Job {job.Id} не содержит UnitFilters");
|
throw new ArgumentNullException(nameof(job.UnitFilters), $"Job {job.Id} не содержит UnitFilters");
|
||||||
#endregion
|
|
||||||
|
|
||||||
var maxCount = takeCount ?? int.MaxValue;
|
var maxCount = takeCount ?? int.MaxValue;
|
||||||
var collectedIds = new HashSet<Guid>();
|
var collectedIds = new HashSet<Guid>();
|
||||||
@@ -69,30 +99,171 @@ namespace PARR.DAL.DomainServices.Implementations
|
|||||||
{
|
{
|
||||||
logger.LogDebug("Применяем фильтр #{Index} (Id={FilterId})", filterNumber, filter.Id);
|
logger.LogDebug("Применяем фильтр #{Index} (Id={FilterId})", filterNumber, filter.Id);
|
||||||
|
|
||||||
var query = unitService.Get().AsNoTracking()
|
// 1️ Найти ID юнитов по UnitFilter (Name LIKE)
|
||||||
.Where(unit => EF.Functions.Like(unit.Name, filter.UnitFilter));
|
var initialUnitIds = await unitService.Get().AsNoTracking()
|
||||||
|
.Where(unit => EF.Functions.Like(unit.Name, filter.UnitFilter))
|
||||||
|
.Select(u => u.Id)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
logger.LogDebug("Базовый фильтр по Name: {NameFilter}", filter.UnitFilter);
|
logger.LogDebug("Базовый фильтр по Name '{NameFilter}' дал {Count} юнитов", filter.UnitFilter, initialUnitIds.Count);
|
||||||
|
|
||||||
|
if (!initialUnitIds.Any())
|
||||||
|
{
|
||||||
|
logger.LogDebug("Фильтр #{Index}: 0 юнитов после UnitFilter. Пропускаем.", filterNumber);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2️ Загрузить UnitValues для этих юнитов (только FieldId из FieldFilter)
|
||||||
|
var fieldFilterFieldIds = filter.FieldFilters.Select(f => f.FieldId).ToHashSet();
|
||||||
|
var unitValues = fieldFilterFieldIds.Any()
|
||||||
|
? await unitInValueService.GetByUnitIdsAndFieldIdsAsync(initialUnitIds, fieldFilterFieldIds)
|
||||||
|
: new List<UnitInValue>();
|
||||||
|
|
||||||
|
// 3️ Загрузить связи
|
||||||
|
var parentRelFilters = filter.RelationshipFilters.Where(rf => rf.IsParent).ToList();
|
||||||
|
var childRelFilters = filter.RelationshipFilters.Where(rf => !rf.IsParent).ToList();
|
||||||
|
|
||||||
|
var parentLinks = parentRelFilters.Any()
|
||||||
|
? await unitInUnitService.GetParentLinksByChildIdsAsync(initialUnitIds)
|
||||||
|
: new List<UnitInUnit>();
|
||||||
|
|
||||||
|
var childLinks = childRelFilters.Any()
|
||||||
|
? await unitInUnitService.GetChildLinksByParentIdsAsync(initialUnitIds)
|
||||||
|
: new List<UnitInUnit>();
|
||||||
|
|
||||||
|
// 4️ ID родителей и детей
|
||||||
|
var parentUnitIds = parentLinks.Select(l => l.ParentUnitId).ToHashSet();
|
||||||
|
var childUnitIds = childLinks.Select(l => l.ChildUnitId).ToHashSet();
|
||||||
|
|
||||||
|
// 5️ Загрузить родительские и дочерние юниты (Id + Name)
|
||||||
|
var parentUnits = parentUnitIds.Any()
|
||||||
|
? await unitService.Get().AsNoTracking()
|
||||||
|
.Where(u => parentUnitIds.Contains(u.Id))
|
||||||
|
.Select(u => new UnitInfoDto { Id = u.Id, Name = u.Name })
|
||||||
|
.ToListAsync()
|
||||||
|
: new List<UnitInfoDto>();
|
||||||
|
|
||||||
|
var childUnits = childUnitIds.Any()
|
||||||
|
? await unitService.Get().AsNoTracking()
|
||||||
|
.Where(u => childUnitIds.Contains(u.Id))
|
||||||
|
.Select(u => new UnitInfoDto { Id = u.Id, Name = u.Name })
|
||||||
|
.ToListAsync()
|
||||||
|
: new List<UnitInfoDto>();
|
||||||
|
|
||||||
|
// 6️ UnitValues для родителей и детей (только FieldId из RelationshipFilter)
|
||||||
|
var relFilterFieldIds = filter.RelationshipFilters.Select(f => f.FieldId).ToHashSet();
|
||||||
|
|
||||||
|
var parentUnitValues = relFilterFieldIds.Any() && parentUnitIds.Any()
|
||||||
|
? await unitInValueService.GetByUnitIdsAndFieldIdsAsync(parentUnitIds, relFilterFieldIds)
|
||||||
|
: new List<UnitInValue>();
|
||||||
|
|
||||||
|
var childUnitValues = relFilterFieldIds.Any() && childUnitIds.Any()
|
||||||
|
? await unitInValueService.GetByUnitIdsAndFieldIdsAsync(childUnitIds, relFilterFieldIds)
|
||||||
|
: new List<UnitInValue>();
|
||||||
|
|
||||||
|
// 7️ Сборка DTO
|
||||||
|
var unitMap = initialUnitIds.ToDictionary(id => id, id => new UnitDto { Id = id });
|
||||||
|
|
||||||
|
// UnitValues → Unit
|
||||||
|
foreach (var uv in unitValues)
|
||||||
|
{
|
||||||
|
if (unitMap.TryGetValue(uv.UnitId, out var dto))
|
||||||
|
{
|
||||||
|
dto.Values.Add(new UnitValueDto { FieldId = uv.FieldId, Value = uv.Value?.Value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentIdMap = parentUnits.ToDictionary(u => u.Id, u => u.Name);
|
||||||
|
var childIdMap = childUnits.ToDictionary(u => u.Id, u => u.Name);
|
||||||
|
|
||||||
|
// ParentLinks → Parent
|
||||||
|
foreach (var link in parentLinks)
|
||||||
|
{
|
||||||
|
if (unitMap.TryGetValue(link.ChildUnitId, out var dto))
|
||||||
|
{
|
||||||
|
dto.Parents.Add(new RelatedUnitDto
|
||||||
|
{
|
||||||
|
UnitId = link.ParentUnitId,
|
||||||
|
Name = parentIdMap.GetValueOrDefault(link.ParentUnitId, "")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChildLinks → Child
|
||||||
|
foreach (var link in childLinks)
|
||||||
|
{
|
||||||
|
if (unitMap.TryGetValue(link.ParentUnitId, out var dto))
|
||||||
|
{
|
||||||
|
dto.Children.Add(new RelatedUnitDto
|
||||||
|
{
|
||||||
|
UnitId = link.ChildUnitId,
|
||||||
|
Name = childIdMap.GetValueOrDefault(link.ChildUnitId, "")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка значений к родителям
|
||||||
|
var parentValuesMap = parentUnitValues
|
||||||
|
.GroupBy(v => v.UnitId)
|
||||||
|
.ToDictionary(g => g.Key, g => g.Select(v => new UnitValueDto { FieldId = v.FieldId, Value = v.Value?.Value }).ToList());
|
||||||
|
|
||||||
|
foreach (var dto in unitMap.Values)
|
||||||
|
{
|
||||||
|
foreach (var parent in dto.Parents)
|
||||||
|
{
|
||||||
|
if (parentValuesMap.TryGetValue(parent.UnitId, out var values))
|
||||||
|
{
|
||||||
|
parent.Values.AddRange(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Привязка значений к детям
|
||||||
|
var childValuesMap = childUnitValues
|
||||||
|
.GroupBy(v => v.UnitId)
|
||||||
|
.ToDictionary(g => g.Key, g => g.Select(v => new UnitValueDto { FieldId = v.FieldId, Value = v.Value?.Value }).ToList());
|
||||||
|
|
||||||
|
foreach (var dto in unitMap.Values)
|
||||||
|
{
|
||||||
|
foreach (var child in dto.Children)
|
||||||
|
{
|
||||||
|
if (childValuesMap.TryGetValue(child.UnitId, out var values))
|
||||||
|
{
|
||||||
|
child.Values.AddRange(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8️ Применение фильтров
|
||||||
|
var candidateUnits = unitMap.Values.AsQueryable();
|
||||||
|
|
||||||
foreach (var fieldFilter in filter.FieldFilters)
|
foreach (var fieldFilter in filter.FieldFilters)
|
||||||
{
|
{
|
||||||
query = ApplyFieldFilter(query, fieldFilter);
|
candidateUnits = ApplyFieldFilter(candidateUnits, fieldFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("После FieldFilter осталось {Count} юнитов", candidateUnits.Count());
|
||||||
|
|
||||||
foreach (var relFilter in filter.RelationshipFilters)
|
foreach (var relFilter in filter.RelationshipFilters)
|
||||||
{
|
{
|
||||||
query = ApplyRelationshipFilterToQuery(query, relFilter);
|
candidateUnits = ApplyRelationshipFilterToQuery(candidateUnits, relFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("После RelationshipFilter осталось {Count} юнитов", candidateUnits.Count());
|
||||||
|
|
||||||
|
// 9️⃣ Umbrella-фильтр
|
||||||
|
var finalUnits = candidateUnits.AsEnumerable();
|
||||||
if (job.Group.GroupType.Code == JobGroupTypesEnum.Umbrella)
|
if (job.Group.GroupType.Code == JobGroupTypesEnum.Umbrella)
|
||||||
{
|
{
|
||||||
query = ApplyRelationshipCountFilter(query, job);
|
finalUnits = ApplyRelationshipCountFilter(finalUnits, job, filter.RelationshipFilters);
|
||||||
|
logger.LogDebug("После Umbrella-фильтра осталось {Count} юнитов", finalUnits.Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
var newIds = await query
|
// 10 Взять ID
|
||||||
|
var newIds = finalUnits
|
||||||
.Select(u => u.Id)
|
.Select(u => u.Id)
|
||||||
.Take(remaining)
|
.Take(remaining)
|
||||||
.ToListAsync();
|
.ToList();
|
||||||
|
|
||||||
collectedIds.UnionWith(newIds);
|
collectedIds.UnionWith(newIds);
|
||||||
logger.LogDebug("Фильтр #{Index}: найдено {Count} Unit'ов. Всего: {Total}",
|
logger.LogDebug("Фильтр #{Index}: найдено {Count} Unit'ов. Всего: {Total}",
|
||||||
@@ -133,11 +304,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
|||||||
|
|
||||||
foreach (var filter in job.UnitFilters)
|
foreach (var filter in job.UnitFilters)
|
||||||
{
|
{
|
||||||
if (!filter.RelationshipFilters.Any())
|
if (!filter.RelationshipFilters.Any()) continue;
|
||||||
{
|
|
||||||
logger.LogDebug("UnitFilter.Id {FilterId} не содержит RelationshipFilters. Пропускаем.", filter.Id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.LogDebug("Обработка UnitFilter.Id {FilterId}. Количество RelationshipFilters: {RelFilterCount}", filter.Id, filter.RelationshipFilters.Count());
|
logger.LogDebug("Обработка UnitFilter.Id {FilterId}. Количество RelationshipFilters: {RelFilterCount}", filter.Id, filter.RelationshipFilters.Count());
|
||||||
|
|
||||||
@@ -147,11 +314,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
|||||||
rf.UnitFilterId, rf.IsParent, rf.FieldId, rf.ValueMask);
|
rf.UnitFilterId, rf.IsParent, rf.FieldId, rf.ValueMask);
|
||||||
|
|
||||||
var valueMask = rf.ValueMask?.Trim();
|
var valueMask = rf.ValueMask?.Trim();
|
||||||
if (string.IsNullOrEmpty(valueMask))
|
if (string.IsNullOrEmpty(valueMask)) continue;
|
||||||
{
|
|
||||||
logger.LogDebug("ValueMask пуст. Пропускаем фильтр (UnitFilterId={UnitFilterId}).", rf.UnitFilterId);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<UnitInUnit> relevantLinks;
|
List<UnitInUnit> relevantLinks;
|
||||||
if (rf.IsParent)
|
if (rf.IsParent)
|
||||||
@@ -159,207 +322,176 @@ namespace PARR.DAL.DomainServices.Implementations
|
|||||||
else
|
else
|
||||||
relevantLinks = await unitInUnitService.GetByParentIdAsync(unitId);
|
relevantLinks = await unitInUnitService.GetByParentIdAsync(unitId);
|
||||||
|
|
||||||
logger.LogDebug("Найдено {Count} связей через UnitInUnitService.", relevantLinks.Count);
|
|
||||||
|
|
||||||
// Получаем Id юнитов, которые участвуют в связях (дети или родители)
|
|
||||||
var unitIdsToCheck = rf.IsParent
|
var unitIdsToCheck = rf.IsParent
|
||||||
? relevantLinks.Select(l => l.ParentUnitId).ToList()
|
? relevantLinks.Select(l => l.ParentUnitId).ToList()
|
||||||
: relevantLinks.Select(l => l.ChildUnitId).ToList();
|
: relevantLinks.Select(l => l.ChildUnitId).ToList();
|
||||||
|
|
||||||
if (!unitIdsToCheck.Any())
|
if (!unitIdsToCheck.Any()) continue;
|
||||||
{
|
|
||||||
logger.LogDebug("Нет юнитов для проверки значений.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем значения полей для этих юнитов
|
|
||||||
var unitValues = await unitInValueService.GetByUnitIdsAsync(unitIdsToCheck);
|
var unitValues = await unitInValueService.GetByUnitIdsAsync(unitIdsToCheck);
|
||||||
|
|
||||||
logger.LogDebug("Получено {Count} значений полей через UnitInValueService.", unitValues.Count);
|
|
||||||
|
|
||||||
// Фильтруем юниты, у которых есть нужное значение
|
|
||||||
var matchingUnitIds = unitValues
|
var matchingUnitIds = unitValues
|
||||||
.Where(uv => uv.FieldId == rf.FieldId
|
.Where(uv => uv.FieldId == rf.FieldId
|
||||||
&& uv.Value != null
|
&& uv.Value?.Value != null
|
||||||
&& uv.Value.Value != null
|
&& uv.Value.Value.Contains(valueMask, StringComparison.OrdinalIgnoreCase))
|
||||||
&& uv.Value.Value.ToString()!.Contains(valueMask, StringComparison.OrdinalIgnoreCase))
|
|
||||||
.Select(uv => uv.UnitId)
|
.Select(uv => uv.UnitId)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
logger.LogDebug("Найдено {Count} юнитов с подходящим значением поля.", matchingUnitIds.Count);
|
|
||||||
|
|
||||||
// Получаем имена этих юнитов через IUnitService
|
|
||||||
if (matchingUnitIds.Any())
|
if (matchingUnitIds.Any())
|
||||||
{
|
{
|
||||||
var matchingUnitNames = await unitService.Get().AsNoTracking()
|
var names = await unitService.Get().AsNoTracking()
|
||||||
.Where(u => matchingUnitIds.Contains(u.Id))
|
.Where(u => matchingUnitIds.Contains(u.Id))
|
||||||
.Select(u => u.Name)
|
.Select(u => u.Name)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
result.UnionWith(names);
|
||||||
logger.LogDebug("Найдены имена: [{Names}]", string.Join(", ", matchingUnitNames));
|
|
||||||
result.UnionWith(matchingUnitNames);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogDebug("Итоговый результат: [{Result}]", string.Join(", ", result));
|
|
||||||
|
|
||||||
return result.ToList();
|
return result.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Вспомогательные методы фильтрации
|
#region вспомогательные методы
|
||||||
|
|
||||||
private IQueryable<Unit> ApplyFieldFilter(IQueryable<Unit> query, JobFieldFilter fieldFilter)
|
private IQueryable<UnitDto> ApplyFieldFilter(IQueryable<UnitDto> query, JobFieldFilter fieldFilter)
|
||||||
{
|
{
|
||||||
var fieldId = fieldFilter.FieldId;
|
|
||||||
var valueMask = fieldFilter.ValueMask?.Trim();
|
var valueMask = fieldFilter.ValueMask?.Trim();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(valueMask))
|
if (string.IsNullOrEmpty(valueMask))
|
||||||
return query;
|
return query;
|
||||||
|
|
||||||
logger.LogDebug("Фильтр по полю: FieldId={FieldId}, ValueMask={ValueMask}", fieldId, valueMask);
|
logger.LogDebug("Применяем FieldFilterDto: FieldId={FieldId}, ValueMask={ValueMask}", fieldFilter.FieldId, valueMask);
|
||||||
|
|
||||||
return query.Where(unit =>
|
bool isStartsWith = valueMask.EndsWith("%") && !valueMask.EndsWith("%%");
|
||||||
unit.UnitValues.Any(v =>
|
bool isEndsWith = valueMask.StartsWith("%") && !valueMask.StartsWith("%%");
|
||||||
v.FieldId == fieldId &&
|
|
||||||
|
return query.AsEnumerable().Where(dto =>
|
||||||
|
dto.Values.Any(v =>
|
||||||
|
v.FieldId == fieldFilter.FieldId &&
|
||||||
v.Value != null &&
|
v.Value != null &&
|
||||||
v.Value.Value != null &&
|
(isStartsWith && isEndsWith ? v.Value.Contains(valueMask.Trim('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
EF.Functions.Like(v.Value.Value, valueMask)));
|
isStartsWith ? v.Value.StartsWith(valueMask.TrimEnd('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
|
isEndsWith ? v.Value.EndsWith(valueMask.TrimStart('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
|
v.Value.Contains(valueMask, StringComparison.OrdinalIgnoreCase))
|
||||||
|
)
|
||||||
|
).AsQueryable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IQueryable<Unit> ApplyRelationshipFilterToQuery(IQueryable<Unit> query, JobRelationshipFilter relFilter)
|
private IQueryable<UnitDto> ApplyRelationshipFilterToQuery(IQueryable<UnitDto> query, JobRelationshipFilter relFilter)
|
||||||
{
|
{
|
||||||
var fieldId = relFilter.FieldId;
|
var valueMask = relFilter.ValueMask?.Trim();
|
||||||
var valueMask = relFilter.ValueMask ?? "";
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(valueMask))
|
if (string.IsNullOrWhiteSpace(valueMask))
|
||||||
return query;
|
return query;
|
||||||
|
|
||||||
logger.LogDebug("Фильтр по связи: IsParent={IsParent}, IsInverse={IsInverse}, IsFullMatch={IsFullMatch}, FieldId={FieldId}, ValueMask={ValueMask}",
|
logger.LogDebug("Применяем RelationshipFilterDto: IsParent={IsParent}, IsInverse={IsInverse}, IsFullMatch={IsFullMatch}, FieldId={FieldId}, ValueMask={ValueMask}",
|
||||||
relFilter.IsParent, relFilter.IsInverse, relFilter.IsFullMatch, fieldId, valueMask);
|
relFilter.IsParent, relFilter.IsInverse, relFilter.IsFullMatch, relFilter.FieldId, valueMask);
|
||||||
|
|
||||||
if (relFilter.IsParent)
|
var isParent = relFilter.IsParent;
|
||||||
|
var isInverse = relFilter.IsInverse;
|
||||||
|
var isFullMatch = relFilter.IsFullMatch;
|
||||||
|
var fieldId = relFilter.FieldId;
|
||||||
|
|
||||||
|
bool isStartsWith = valueMask.EndsWith("%") && !valueMask.EndsWith("%%");
|
||||||
|
bool isEndsWith = valueMask.StartsWith("%") && !valueMask.StartsWith("%%");
|
||||||
|
|
||||||
|
return query.AsEnumerable().Where(dto =>
|
||||||
{
|
{
|
||||||
if (relFilter.IsInverse)
|
var links = isParent ? dto.Parents : dto.Children;
|
||||||
|
|
||||||
|
if (links == null || !links.Any())
|
||||||
{
|
{
|
||||||
if (relFilter.IsFullMatch)
|
// Для IsFullMatch/IsInverse — "все подходят", т.е. true
|
||||||
{
|
// Для остальных — false
|
||||||
return query.Where(unit =>
|
return isFullMatch && isInverse;
|
||||||
!unit.ParentUnits.Any() ||
|
|
||||||
unit.ParentUnits.All(link =>
|
|
||||||
!link.ParentUnit!.UnitValues.Any(v =>
|
|
||||||
v.FieldId == fieldId &&
|
|
||||||
v.Value != null &&
|
|
||||||
v.Value.Value != null &&
|
|
||||||
EF.Functions.Like(v.Value.Value, valueMask))));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return query.Where(unit =>
|
|
||||||
!unit.ParentUnits.Any() ||
|
|
||||||
unit.ParentUnits.Any(link =>
|
|
||||||
!link.ParentUnit!.UnitValues.Any(v =>
|
|
||||||
v.FieldId == fieldId &&
|
|
||||||
v.Value != null &&
|
|
||||||
v.Value.Value != null &&
|
|
||||||
EF.Functions.Like(v.Value.Value, valueMask))));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
var hasMatchingLinks = links.Any(link =>
|
||||||
{
|
{
|
||||||
if (relFilter.IsFullMatch)
|
var hasMatch = link.Values.Any(v =>
|
||||||
{
|
v.FieldId == fieldId &&
|
||||||
return query.Where(unit =>
|
v.Value != null &&
|
||||||
!unit.ParentUnits.Any() ||
|
(isStartsWith && isEndsWith ? v.Value.Contains(valueMask.Trim('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
unit.ParentUnits.All(link =>
|
isStartsWith ? v.Value.StartsWith(valueMask.TrimEnd('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
link.ParentUnit!.UnitValues.Any(v =>
|
isEndsWith ? v.Value.EndsWith(valueMask.TrimStart('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
v.FieldId == fieldId &&
|
v.Value.Contains(valueMask, StringComparison.OrdinalIgnoreCase))
|
||||||
v.Value != null &&
|
);
|
||||||
v.Value.Value != null &&
|
|
||||||
EF.Functions.Like(v.Value.Value, valueMask))));
|
return isInverse ? !hasMatch : hasMatch;
|
||||||
}
|
});
|
||||||
else
|
|
||||||
{
|
if (isFullMatch)
|
||||||
return query.Where(unit =>
|
|
||||||
unit.ParentUnits.Any(link =>
|
|
||||||
link.ParentUnit!.UnitValues.Any(v =>
|
|
||||||
v.FieldId == fieldId &&
|
|
||||||
v.Value != null &&
|
|
||||||
v.Value.Value != null &&
|
|
||||||
EF.Functions.Like(v.Value.Value, valueMask))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (relFilter.IsInverse)
|
|
||||||
{
|
{
|
||||||
if (relFilter.IsFullMatch)
|
var allMatch = links.All(link =>
|
||||||
{
|
{
|
||||||
return query.Where(unit =>
|
var hasMatch = link.Values.Any(v =>
|
||||||
!unit.ChildUnits.Any() ||
|
v.FieldId == fieldId &&
|
||||||
unit.ChildUnits.All(link =>
|
v.Value != null &&
|
||||||
!link.ChildUnit!.UnitValues.Any(v =>
|
(isStartsWith && isEndsWith ? v.Value.Contains(valueMask.Trim('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
v.FieldId == fieldId &&
|
isStartsWith ? v.Value.StartsWith(valueMask.TrimEnd('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
v.Value != null &&
|
isEndsWith ? v.Value.EndsWith(valueMask.TrimStart('%'), StringComparison.OrdinalIgnoreCase) :
|
||||||
v.Value.Value != null &&
|
v.Value.Contains(valueMask, StringComparison.OrdinalIgnoreCase))
|
||||||
EF.Functions.Like(v.Value.Value, valueMask))));
|
);
|
||||||
}
|
|
||||||
else
|
return isInverse ? !hasMatch : hasMatch;
|
||||||
{
|
});
|
||||||
return query.Where(unit =>
|
|
||||||
!unit.ChildUnits.Any() ||
|
return allMatch;
|
||||||
unit.ChildUnits.Any(link =>
|
|
||||||
!link.ChildUnit!.UnitValues.Any(v =>
|
|
||||||
v.FieldId == fieldId &&
|
|
||||||
v.Value != null &&
|
|
||||||
v.Value.Value != null &&
|
|
||||||
EF.Functions.Like(v.Value.Value, valueMask))));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return hasMatchingLinks;
|
||||||
if (relFilter.IsFullMatch)
|
}).AsQueryable();
|
||||||
{
|
|
||||||
return query.Where(unit =>
|
|
||||||
!unit.ChildUnits.Any() ||
|
|
||||||
unit.ChildUnits.All(link =>
|
|
||||||
link.ChildUnit!.UnitValues.Any(v =>
|
|
||||||
v.FieldId == fieldId &&
|
|
||||||
v.Value != null &&
|
|
||||||
v.Value.Value != null &&
|
|
||||||
EF.Functions.Like(v.Value.Value, valueMask))));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return query.Where(unit =>
|
|
||||||
unit.ChildUnits.Any(link =>
|
|
||||||
link.ChildUnit!.UnitValues.Any(v =>
|
|
||||||
v.FieldId == fieldId &&
|
|
||||||
v.Value != null &&
|
|
||||||
v.Value.Value != null &&
|
|
||||||
EF.Functions.Like(v.Value.Value, valueMask))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IQueryable<Unit> ApplyRelationshipCountFilter(IQueryable<Unit> query, Job job)
|
private IEnumerable<UnitDto> ApplyRelationshipCountFilter(
|
||||||
|
IEnumerable<UnitDto> units,
|
||||||
|
Job job,
|
||||||
|
IEnumerable<JobRelationshipFilter> relationshipFilters)
|
||||||
{
|
{
|
||||||
int min = job.MinValueRelationships.GetValueOrDefault(0);
|
var min = job.MinValueRelationships ?? 0;
|
||||||
int max = job.MaxValueRelationships.GetValueOrDefault(int.MaxValue);
|
var max = job.MaxValueRelationships ?? int.MaxValue;
|
||||||
|
var isParentDirection = job.IsParentRelationships == true;
|
||||||
|
|
||||||
logger.LogDebug("Фильтр по количеству связей: Min={Min}, Max={Max}, IsParent={IsParent}", min, max, job.IsParentRelationships);
|
if (min == 0 && max == int.MaxValue)
|
||||||
|
return units;
|
||||||
|
|
||||||
if (job.IsParentRelationships == true)
|
var activeFilters = relationshipFilters
|
||||||
|
.Where(rf => rf.IsParent == isParentDirection && !string.IsNullOrWhiteSpace(rf.ValueMask))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (activeFilters.Count == 0)
|
||||||
|
return units;
|
||||||
|
|
||||||
|
logger.LogDebug("RelationshipFilter: Min={Min}, Max={Max}, IsParent={IsParent}, Filters={Count}",
|
||||||
|
min, max, isParentDirection, activeFilters.Count);
|
||||||
|
|
||||||
|
return units.Where(dto =>
|
||||||
{
|
{
|
||||||
return query.Where(unit => unit.ParentUnits.Count >= min && unit.ParentUnits.Count <= max);
|
var links = isParentDirection ? dto.Parents : dto.Children;
|
||||||
}
|
|
||||||
else
|
if (links == null || !links.Any())
|
||||||
{
|
return min == 0;
|
||||||
return query.Where(unit => unit.ChildUnits.Count >= min && unit.ChildUnits.Count <= max);
|
|
||||||
}
|
int matchingCount = 0;
|
||||||
|
|
||||||
|
foreach (var link in links)
|
||||||
|
{
|
||||||
|
bool hasMatch = link.Values.Any(v =>
|
||||||
|
activeFilters.Any(f =>
|
||||||
|
v.FieldId == f.FieldId &&
|
||||||
|
v.Value != null &&
|
||||||
|
v.Value.Contains(f.ValueMask, StringComparison.OrdinalIgnoreCase)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasMatch)
|
||||||
|
matchingCount++;
|
||||||
|
|
||||||
|
if (matchingCount > max)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingCount >= min && matchingCount <= max;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using PARR.DAL.Context;
|
using PARR.DAL.Context;
|
||||||
using PARR.DAL.Models.Unit;
|
using PARR.DAL.Models.Unit;
|
||||||
using PARR.DAL.Services.Abstracts;
|
|
||||||
using PARR.DAL.Services.Interfaces.Unit;
|
using PARR.DAL.Services.Interfaces.Unit;
|
||||||
|
|
||||||
namespace PARR.DAL.Services.Implementations.Unit
|
namespace PARR.DAL.Services.Implementations.Unit
|
||||||
@@ -36,5 +35,24 @@ namespace PARR.DAL.Services.Implementations.Unit
|
|||||||
.Where(u => u.ChildUnitId == childId)
|
.Where(u => u.ChildUnitId == childId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<List<UnitInUnit>> GetParentLinksByChildIdsAsync(IEnumerable<Guid> childUnitIds)
|
||||||
|
{
|
||||||
|
var set = childUnitIds.ToHashSet();
|
||||||
|
return await dataContext.UnitInUnits
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(uinu => set.Contains(uinu.ChildUnitId))
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UnitInUnit>> GetChildLinksByParentIdsAsync(IEnumerable<Guid> parentUnitIds)
|
||||||
|
{
|
||||||
|
var set = parentUnitIds.ToHashSet();
|
||||||
|
return await dataContext.UnitInUnits
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(uinu => set.Contains(uinu.ParentUnitId))
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,5 +67,18 @@ namespace PARR.DAL.Services.Implementations.Unit
|
|||||||
|
|
||||||
return keyValuePairs.Select(kvp => (kvp.Key, kvp.Value)).ToList();
|
return keyValuePairs.Select(kvp => (kvp.Key, kvp.Value)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<List<UnitInValue>> GetByUnitIdsAndFieldIdsAsync(IEnumerable<Guid> unitIds, IEnumerable<Guid> fieldIds)
|
||||||
|
{
|
||||||
|
var unitIdSet = unitIds.ToHashSet();
|
||||||
|
var fieldIdSet = fieldIds.ToHashSet();
|
||||||
|
|
||||||
|
return await dataContext.UnitInValues
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(uv => uv.Value) // UnitFieldValue
|
||||||
|
.Where(uv => unitIdSet.Contains(uv.UnitId) && fieldIdSet.Contains(uv.FieldId))
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using PARR.DAL.Models.Unit;
|
using PARR.DAL.Models.Unit;
|
||||||
using PARR.DAL.Services.Interfaces.Base;
|
|
||||||
|
|
||||||
namespace PARR.DAL.Services.Interfaces.Unit
|
namespace PARR.DAL.Services.Interfaces.Unit
|
||||||
{
|
{
|
||||||
@@ -7,5 +6,15 @@ namespace PARR.DAL.Services.Interfaces.Unit
|
|||||||
{
|
{
|
||||||
Task<List<UnitInUnit>> GetByParentIdAsync(Guid parentId);
|
Task<List<UnitInUnit>> GetByParentIdAsync(Guid parentId);
|
||||||
Task<List<UnitInUnit>> GetByChildIdAsync(Guid childId);
|
Task<List<UnitInUnit>> GetByChildIdAsync(Guid childId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получает связи, где ChildUnitId unitIds (для IsParent=True).
|
||||||
|
/// </summary>
|
||||||
|
Task<List<UnitInUnit>> GetParentLinksByChildIdsAsync(IEnumerable<Guid> childUnitIds);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получает связи, где ParentUnitId unitIds (для IsParent=False).
|
||||||
|
/// </summary>
|
||||||
|
Task<List<UnitInUnit>> GetChildLinksByParentIdsAsync(IEnumerable<Guid> parentUnitIds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,11 @@ namespace PARR.DAL.Services.Interfaces.Unit
|
|||||||
Task<List<UnitInValue>> GetByUnitIdsAsync(IEnumerable<Guid> unitIds);
|
Task<List<UnitInValue>> GetByUnitIdsAsync(IEnumerable<Guid> unitIds);
|
||||||
Task<List<UnitInValue>> GetByUnitIdAsync(Guid unitId);
|
Task<List<UnitInValue>> GetByUnitIdAsync(Guid unitId);
|
||||||
Task<List<(string FieldName, string? Value)>> GetFieldValuesAsync(Guid unitId, IReadOnlyCollection<string> aihitNames);
|
Task<List<(string FieldName, string? Value)>> GetFieldValuesAsync(Guid unitId, IReadOnlyCollection<string> aihitNames);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Получает UnitInValue (с Value) для заданных UnitId и FieldId.
|
||||||
|
/// </summary>
|
||||||
|
Task<List<UnitInValue>> GetByUnitIdsAndFieldIdsAsync(IEnumerable<Guid> unitIds, IEnumerable<Guid> fieldIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ namespace PARR.TemplateMatcher
|
|||||||
TemplateId = template.Id,
|
TemplateId = template.Id,
|
||||||
JobId = jobId,
|
JobId = jobId,
|
||||||
UnitId = template.UnitId,
|
UnitId = template.UnitId,
|
||||||
Name = template.Name,
|
Name = GetTemplateNameForUnsed(template.Name),
|
||||||
IsActiveTemplate = DefaultUnusedTemplateState,
|
IsActiveTemplate = DefaultUnusedTemplateState,
|
||||||
IsActiveSchedule = DefaultUnusedScheduleState,
|
IsActiveSchedule = DefaultUnusedScheduleState,
|
||||||
LastRun = template.LastRun,
|
LastRun = template.LastRun,
|
||||||
@@ -232,6 +232,7 @@ namespace PARR.TemplateMatcher
|
|||||||
logger.LogInformation("Синхронизация завершена для JobId {JobId}.", jobId);
|
logger.LogInformation("Синхронизация завершена для JobId {JobId}.", jobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task UpdateTemplatesForJob(Guid jobId, HistoryInitiator initiator)
|
public async Task UpdateTemplatesForJob(Guid jobId, HistoryInitiator initiator)
|
||||||
{
|
{
|
||||||
logger.LogDebug("Начало обновления шаблонов для JobId {JobId}", jobId);
|
logger.LogDebug("Начало обновления шаблонов для JobId {JobId}", jobId);
|
||||||
@@ -271,6 +272,7 @@ namespace PARR.TemplateMatcher
|
|||||||
targetStatus = TemplateStatusTypeEnum.Unused;
|
targetStatus = TemplateStatusTypeEnum.Unused;
|
||||||
targetIsActiveTemplate = DefaultUnusedTemplateState;
|
targetIsActiveTemplate = DefaultUnusedTemplateState;
|
||||||
targetIsActiveSchedule = DefaultUnusedScheduleState;
|
targetIsActiveSchedule = DefaultUnusedScheduleState;
|
||||||
|
expectedName = GetTemplateNameForUnsed(expectedName);
|
||||||
logger.LogInformation("Шаблон {TemplateId} (UnitId {UnitId}) → деактивация.", template.Id, template.UnitId);
|
logger.LogInformation("Шаблон {TemplateId} (UnitId {UnitId}) → деактивация.", template.Id, template.UnitId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,6 +405,11 @@ namespace PARR.TemplateMatcher
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetTemplateNameForUnsed(string templateName)
|
||||||
|
{
|
||||||
|
return templateName + "_" + DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<Job?> GetJobWithGroupAndAutoControlAsync(Guid jobId)
|
private async Task<Job?> GetJobWithGroupAndAutoControlAsync(Guid jobId)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user