From d19aa77ec4f16ef45c94fdd1a252a4c3013d3a70 Mon Sep 17 00:00:00 2001 From: Mikhail Kuznetsov Date: Tue, 9 Dec 2025 16:50:35 +1000 Subject: [PATCH] =?UTF-8?q?feat(dal):=20ShortcodesService=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20=D0=B4=D0=B8=D0=BD=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8=D1=87=D0=B5=D1=81=D0=B8=D1=85=20=D1=81=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=B2=D0=BB=D1=8F=D1=8E=D1=89=D0=B8=D1=85=20"%?= =?UTF-8?q?=D0=A1=D0=92=D0=AF=D0=97=D0=98%",=20"%=D0=A2=D0=9D=D0=9A-=D0=9A?= =?UTF-8?q?=D0=A0=D0=90=D0=A2=D0=9A=D0=9E%"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PARR.API.sln | 6 + .../V1/JobUnitPreviewController.cs | 6 +- .../UnitFilterServiceIntegrationTests.cs | 104 ++++ .../PARR.DAL.IntegrationTests.csproj | 33 ++ .../TestDataGenerator.cs | 254 +++++++++ .../Implementations/UnitFilterServiceTests.cs | 120 +++++ PARR.DAL.Tests/PARR.DAL.Tests.csproj | 2 + PARR.DAL.Tests/TestDataGenerator.cs | 255 +++++++++ .../Implementations/ShortcodesService.cs | 125 ++--- .../Implementations/UnitFilterService.cs | 488 +++++++++++------- .../Interfaces/IUnitFilterService.cs | 35 +- PARR.DAL/PARR.DAL.csproj | 7 - PARR.DAL/ParrDalInstaller.cs | 2 + PARR.DAL/Properties/AssemblyInfo.cs | 5 + .../Implementations/Unit/UnitInUnitService.cs | 40 ++ .../Unit/UnitInValueService.cs | 37 ++ .../Interfaces/Unit/IUnitInUnitService.cs | 11 + .../Interfaces/Unit/IUnitInValueService.cs | 15 + 18 files changed, 1265 insertions(+), 280 deletions(-) create mode 100644 PARR.DAL.IntegrationTests/Integration/UnitFilterServiceIntegrationTests.cs create mode 100644 PARR.DAL.IntegrationTests/PARR.DAL.IntegrationTests.csproj create mode 100644 PARR.DAL.IntegrationTests/TestDataGenerator.cs create mode 100644 PARR.DAL.Tests/DomainServices/Implementations/UnitFilterServiceTests.cs create mode 100644 PARR.DAL.Tests/TestDataGenerator.cs create mode 100644 PARR.DAL/Properties/AssemblyInfo.cs create mode 100644 PARR.DAL/Services/Implementations/Unit/UnitInUnitService.cs create mode 100644 PARR.DAL/Services/Implementations/Unit/UnitInValueService.cs create mode 100644 PARR.DAL/Services/Interfaces/Unit/IUnitInUnitService.cs create mode 100644 PARR.DAL/Services/Interfaces/Unit/IUnitInValueService.cs diff --git a/PARR.API.sln b/PARR.API.sln index 1b9bfec1..393cb427 100644 --- a/PARR.API.sln +++ b/PARR.API.sln @@ -106,6 +106,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.TemplateUpdaterWorker" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.TemplateUpdater", "PARR.TemplateUpdater\PARR.TemplateUpdater.csproj", "{10905C04-A6E5-42BC-9804-8671D22C5D6E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PARR.DAL.IntegrationTests", "PARR.DAL.IntegrationTests\PARR.DAL.IntegrationTests.csproj", "{D1C65DB8-77BC-00FD-5BA1-6C7303FD093C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -298,6 +300,10 @@ Global {10905C04-A6E5-42BC-9804-8671D22C5D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU {10905C04-A6E5-42BC-9804-8671D22C5D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU {10905C04-A6E5-42BC-9804-8671D22C5D6E}.Release|Any CPU.Build.0 = Release|Any CPU + {D1C65DB8-77BC-00FD-5BA1-6C7303FD093C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1C65DB8-77BC-00FD-5BA1-6C7303FD093C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1C65DB8-77BC-00FD-5BA1-6C7303FD093C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1C65DB8-77BC-00FD-5BA1-6C7303FD093C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PARR.API/Controllers/V1/JobUnitPreviewController.cs b/PARR.API/Controllers/V1/JobUnitPreviewController.cs index 3a6debd5..c442dac1 100644 --- a/PARR.API/Controllers/V1/JobUnitPreviewController.cs +++ b/PARR.API/Controllers/V1/JobUnitPreviewController.cs @@ -10,10 +10,8 @@ using PARR.API.Contracts.V1.Responses.Base; using PARR.API.Controllers.V1.Base; using PARR.Constants; using PARR.DAL.DomainServices.Interfaces; -using PARR.DAL.Models.Job; using PARR.DAL.Services.Interfaces.Job; using PARR.DAL.Services.Interfaces.Unit; -using static PARR.API.Contracts.V1.ApiRoutes; using Job = PARR.DAL.Models.Job.Job; namespace PARR.API.Controllers.V1 @@ -66,8 +64,8 @@ namespace PARR.API.Controllers.V1 var job = mapper.Map(request); var jobGroup = await jobGroupService.Get().AsNoTracking() - .Include(t=>t.GroupType) - .FirstAsync(t=>t.Id == job.GroupId); + .Include(t => t.GroupType) + .FirstAsync(t => t.Id == job.GroupId); job.Group = jobGroup; var unitIds = await unitFilterService.GetUnitsIdByJobFilterAsync(job, 100); diff --git a/PARR.DAL.IntegrationTests/Integration/UnitFilterServiceIntegrationTests.cs b/PARR.DAL.IntegrationTests/Integration/UnitFilterServiceIntegrationTests.cs new file mode 100644 index 00000000..3e08053d --- /dev/null +++ b/PARR.DAL.IntegrationTests/Integration/UnitFilterServiceIntegrationTests.cs @@ -0,0 +1,104 @@ +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using PARR.DAL.Context; +using PARR.DAL.DomainServices.Implementations; +// Предположим, что у вас есть реализации IJobService и IUnitService +// Если нет, можно создать моки, но лучше использовать реальные, если они просты +using PARR.DAL.Services.Implementations.Job; // Пример: JobService +using PARR.DAL.Services.Implementations.Unit; // Пример: UnitService +using PARR.DAL.Services.Interfaces.Job; +using PARR.DAL.Services.Interfaces.Unit; +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace PARR.DAL.IntegrationTests.Integration +{ + public class UnitFilterServiceIntegrationTests : IAsyncLifetime + { + private DbContextOptions _options; + private DataContext _context; + private UnitFilterService _service; + + public UnitFilterServiceIntegrationTests() + { + _options = new DbContextOptionsBuilder() + .UseInMemoryDatabase($"IntegrationTestDb_{Guid.NewGuid()}") + .EnableSensitiveDataLogging() + .Options; + } + + public async Task InitializeAsync() + { + // Создаём контекст + _context = new DataContext(_options); + await _context.Database.EnsureCreatedAsync(); + + // Заполняем тестовые данные (убедитесь, что TestDataGenerator доступен) + TestDataGenerator.Seed(_context); // или async, если вы его переделаете + + // Создаём ServiceProvider с реальными сервисами + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(builder => builder.AddConsole()); // или Mock.Of + + // Регистрируем контекст + serviceCollection.AddScoped(provider => _context); + + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + // Добавляем новые сервисы + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + // Регистрируем тестируемый сервис + serviceCollection.AddScoped(); + + var serviceProvider = serviceCollection.BuildServiceProvider(); + + // Получаем сервис + _service = serviceProvider.GetRequiredService(); + } + + public async Task DisposeAsync() + { + if (_context != null) + { + await _context.DisposeAsync(); + } + } + + [Fact] + public async Task GetRelatedUnitNamesAsync_WithChildRelationshipFilter_ShouldReturnExpectedNames() + { + // Arrange + var jobId = _context.Jobs.First(j => j.Name == "TestJob_2").Id; + var unitId = TestDataGenerator.UnitWId; + + // Act + var result = await _service.GetRelatedUnitNamesAsync(jobId, unitId); + + // Assert + result.Should().Contain(new[] { "СХД-КМТ-CISCO-MDS9148-1-ДВС", "СХД-КМТ-CISCO-MDS9148-3-ДВС" }); + } + + [Fact] + public async Task GetUnitsIdByJobFilterAsync_WithExistingJobId_ShouldReturnExpectedUnits() + { + // Arrange + var jobId = _context.Jobs.First(j => j.Name == "TestJob_1").Id; + var expectedUnitIds = new[] { TestDataGenerator.UnitAId, TestDataGenerator.UnitBId }; + + // Act + var result = await _service.GetUnitsIdByJobFilterAsync(jobId, takeCount: 10); + + // Assert + result.Should().NotBeNull() + .And.HaveCount(2) + .And.Contain(expectedUnitIds); + } + } +} \ No newline at end of file diff --git a/PARR.DAL.IntegrationTests/PARR.DAL.IntegrationTests.csproj b/PARR.DAL.IntegrationTests/PARR.DAL.IntegrationTests.csproj new file mode 100644 index 00000000..d2e9911e --- /dev/null +++ b/PARR.DAL.IntegrationTests/PARR.DAL.IntegrationTests.csproj @@ -0,0 +1,33 @@ + + + + net10.0 + enable + enable + false + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + \ No newline at end of file diff --git a/PARR.DAL.IntegrationTests/TestDataGenerator.cs b/PARR.DAL.IntegrationTests/TestDataGenerator.cs new file mode 100644 index 00000000..a18935a1 --- /dev/null +++ b/PARR.DAL.IntegrationTests/TestDataGenerator.cs @@ -0,0 +1,254 @@ +using PARR.DAL.Context; +using PARR.DAL.Contracts; +using PARR.DAL.Models.Job; +using PARR.DAL.Models.Unit; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PARR.DAL.IntegrationTests +{ + public static class TestDataGenerator + { + // --- Unit IDs --- + public static readonly Guid UnitAId = Guid.Parse("11111111-1111-1111-1111-111111111111"); + public static readonly Guid UnitBId = Guid.Parse("00000000-0000-1111-1111-111111111111"); + public static readonly Guid UnitCId = Guid.Parse("22222222-2222-2222-2222-222222222222"); + public static readonly Guid UnitDId = Guid.Parse("33333333-3333-3333-3333-333333333333"); + public static readonly Guid UnitWId = Guid.Parse("44444444-4444-4444-4444-444444444444"); + public static readonly Guid UnitXId = Guid.Parse("55555555-5555-5555-5555-555555555555"); + public static readonly Guid UnitYId = Guid.Parse("66666666-6666-6666-6666-666666666666"); + public static readonly Guid UnitZId = Guid.Parse("77777777-7777-7777-7777-777777777777"); + + // --- Field IDs (для обхода составного ключа UnitFilterId+FieldId) --- + public static readonly Guid FieldId1 = Guid.Parse("08b42860-09a7-4a1f-aa4f-0290e86df883");//ТИП_ЭК + public static readonly Guid FieldId2 = Guid.Parse("5b2d3f5b-a9f3-4a6b-9142-70ef4357ac37"); //неуникальный + public static readonly Guid FieldId3 = Guid.Parse("5f3c7b8b-583d-411b-b9bd-1e045ddc5a8e");//ПОДКАТЕГОРИЯ_ЭК + public static readonly Guid FieldId4 = Guid.Parse("3e9ac5aa-531b-4ffc-8f93-694d00aa92a0");//активный + + // --- Field Value IDs --- + public static readonly Guid FieldValueId1 = Guid.Parse("fe18edcd-ab6a-4f3e-b7bc-e54aad9ecc0d"); // система хранения данных (схд) field3 + public static readonly Guid FieldValueId2 = Guid.Parse("f385b22c-42c3-4db9-bd41-b829603cc2d9"); // 1 field2+field4 + public static readonly Guid FieldValueId3 = Guid.Parse("2badc139-69cb-4d52-830a-1b3c6a9faf9d"); // 0 field2+field4 + public static readonly Guid FieldValueId4 = Guid.Parse("fde68ef7-a07e-4c10-872c-cd98c3d1c30b"); // коммутатор + public static readonly Guid FieldValueId5 = Guid.Parse("9f169862-3f78-49f2-b967-13b5b2f56231"); // серверное оборудование + public static readonly Guid FieldValueId6 = Guid.Parse("075b6fad-d24f-40f4-aa0e-83a4cfc46bbf"); // стойка цвк + public static readonly Guid FieldValueId7 = Guid.Parse("9679afd8-8004-4ce2-bd6f-9005b037ef96"); // схд + + internal static void Seed(DataContext context) + { + if (context.Units.Any()) return; + + var now = DateTimeOffset.UtcNow; + + // 1. Значения полей (остаются те же) + var fieldValues = new[] + { + new UnitFieldValue { Id = FieldValueId1, Value = "система хранения данных (схд)", DateCreated = now }, + new UnitFieldValue { Id = FieldValueId2, Value = "1", DateCreated = now }, + new UnitFieldValue { Id = FieldValueId3, Value = "0", DateCreated = now }, + new UnitFieldValue { Id = FieldValueId4, Value = "коммутатор", DateCreated = now }, + }; + context.UnitFieldValues.AddRange(fieldValues); + + // 2. Unit'ы + var units = new[] + { + new Unit { Id = UnitAId, Name = "СХД-КМТ-DVGD-MDS9148-1-ДВС", DateCreated = now }, + new Unit { Id = UnitBId, Name = "СХД-КМТ-DVGD-MDS9148-2-ДВС", DateCreated = now }, + new Unit { Id = UnitCId, Name = "СРВ-СТОЙКА-СТОЙКА-ХИВЦ-4.0-ДВС", DateCreated = now }, + new Unit { Id = UnitDId, Name = "СХД-VSP-E590-01-ДВС", DateCreated = now }, + new Unit { Id = UnitWId, Name = "СХД-КМТ-SAN1_FAB_1-ДВС", DateCreated = now }, + new Unit { Id = UnitXId, Name = "СХД-КМТ-CISCO-MDS9148-1-ДВС", DateCreated = now }, + new Unit { Id = UnitYId, Name = "СХД-КМТ-CISCO-MDS9148-3-ДВС", DateCreated = now }, + }; + context.Units.AddRange(units); + + // 3. UnitInUnit (связи) + var unitInUnits = new[] + { + new UnitInUnit { ParentUnitId = UnitAId, ChildUnitId = UnitCId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitBId, ChildUnitId = UnitCId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitDId, ChildUnitId = UnitAId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitDId, ChildUnitId = UnitBId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitWId, ChildUnitId = UnitXId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitWId, ChildUnitId = UnitYId, DateCreated = now } + }; + context.UnitInUnits.AddRange(unitInUnits); + + // 4. UnitInValue + var unitInValues = new List + { + // Основные (FieldId) + new UnitInValue { UnitId = UnitAId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitAId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitAId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitAId, FieldId = FieldId2, ValueId = FieldValueId3, DateCreated = now }, + new UnitInValue { UnitId = UnitBId, FieldId = FieldId2, ValueId = FieldValueId3, DateCreated = now }, + new UnitInValue { UnitId = UnitBId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitBId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitBId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitCId, FieldId = FieldId3, ValueId = FieldValueId5, DateCreated = now }, + new UnitInValue { UnitId = UnitCId, FieldId = FieldId1, ValueId = FieldValueId6, DateCreated = now }, + new UnitInValue { UnitId = UnitCId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitDId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitDId, FieldId = FieldId1, ValueId = FieldValueId7, DateCreated = now }, + new UnitInValue { UnitId = UnitDId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitWId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitWId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitWId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitWId, FieldId = FieldId2, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitXId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitXId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitXId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitYId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitYId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitYId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + + }; + context.UnitInValues.AddRange(unitInValues); + + // 5. JobGroupType и Group + var groupType = context.JobGroupTypes + .FirstOrDefault(t => t.Code == JobGroupTypesEnum.Simple) + ?? throw new InvalidOperationException("JobGroupType 'Simple' не найден в контексте."); + + var group = new JobGroup + { + Id = Guid.NewGuid(), + GroupTypeId = groupType.Id, + GroupType = groupType, + GroupName = "Группа", + ShortDescription = "%ТНК-КРАТКО% %ЭК%", + FullDescription = "Подробно:\n%СВЯЗИ%", + Solution = "Решение", + TemplateDuration = "540 00:00:00", + ReferenceDate = now, + IsAutoDistributionEnabled = false, + IsAgent = false, + DateCreated = now + }; + context.JobGroups.Add(group); + + // 6. Jobs + var jobs = new List(); + + // --- TestJob --- + var job1 = new Job + { + Id = Guid.NewGuid(), + Name = "TestJob_1", + WorkName = "Работа", + TemplateNameMask = "%ЭК%", + WorkGroupMask = "%ГРУППА_РАБОТ%", + GroupId = group.Id, + TnkId = Guid.NewGuid(), + Group = group, + DateCreated = now + }; + var unitFilter1Id = Guid.NewGuid(); + job1.UnitFilters = new List + { + new JobUnitFilter + { + Id = unitFilter1Id, + UnitFilter = "СХД-%", + JobId = job1.Id, + Job = job1, + DateCreated = now, + FieldFilters = new List + { + new JobFieldFilter + { + UnitFilterId = unitFilter1Id, + FieldId = FieldId1, + ValueMask="коммутатор" + }, + new JobFieldFilter + { + UnitFilterId = unitFilter1Id, + FieldId = FieldId4, + ValueMask="1" + }, + new JobFieldFilter + { + UnitFilterId = unitFilter1Id, + FieldId = FieldId2, + ValueMask="0" + }, + }, + RelationshipFilters = new List + { + new JobRelationshipFilter + { + IsParent = true, + IsInverse = true, + IsFullMatch = true, + FieldId = FieldId1, + ValueMask = "коммутатор", + UnitFilterId = unitFilter1Id + } + } + } + }; + jobs.Add(job1); + + // --- TestJob_2 (с IsParent = false для дочерних связей) --- + var job2 = new Job + { + Id = Guid.NewGuid(), + Name = "TestJob_2", + WorkName = "Работа2", + TemplateNameMask = "%ЭК%", + WorkGroupMask = "%ГРУППА_РАБОТ%", + GroupId = group.Id, + TnkId = Guid.NewGuid(), + Group = group, + DateCreated = now + }; + var unitFilter2Id = Guid.NewGuid(); + job2.UnitFilters = new List + { + new JobUnitFilter + { + Id = unitFilter2Id, + UnitFilter = "СХД-%", + JobId = job2.Id, + Job = job2, + DateCreated = now, + FieldFilters = new List + { + new JobFieldFilter + { + UnitFilterId = unitFilter2Id, + FieldId = FieldId1, + ValueMask="коммутатор" + }, + new JobFieldFilter + { + UnitFilterId = unitFilter2Id, + FieldId = FieldId4, + ValueMask="1" + }, + }, + RelationshipFilters = new List + { + new JobRelationshipFilter + { + IsParent = false, // <-- Новый фильтр: ищем дочерние юниты + IsInverse = false, // <-- Простой фильтр + IsFullMatch = false, // <-- Простой фильтр + FieldId = FieldId1, + ValueMask = "коммутатор", + UnitFilterId = unitFilter2Id + } + } + } + }; + jobs.Add(job2); + + context.Jobs.AddRange(jobs); + context.SaveChanges(); + } + } +} diff --git a/PARR.DAL.Tests/DomainServices/Implementations/UnitFilterServiceTests.cs b/PARR.DAL.Tests/DomainServices/Implementations/UnitFilterServiceTests.cs new file mode 100644 index 00000000..a278b40d --- /dev/null +++ b/PARR.DAL.Tests/DomainServices/Implementations/UnitFilterServiceTests.cs @@ -0,0 +1,120 @@ +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Moq; +using PARR.DAL.Context; +using PARR.DAL.DomainServices.Implementations; +using PARR.DAL.Services.Interfaces.Job; +using PARR.DAL.Services.Interfaces.Unit; +using Xunit; + +namespace PARR.DAL.Tests +{ + public class UnitFilterServiceTests : IDisposable + { + private readonly DbContextOptions _options; + private readonly DataContext _context; + private readonly UnitFilterService _service; + + public UnitFilterServiceTests() + { + _options = new DbContextOptionsBuilder() + .UseInMemoryDatabase($"TestDb_{Guid.NewGuid()}") + .EnableSensitiveDataLogging() + .Options; + + _context = new DataContext(_options); + _context.Database.EnsureCreated(); + TestDataGenerator.Seed(_context); + + var logger = Mock.Of>(); + + // Используем реальные сервисы, работающие с _context + var jobServiceMock = new Mock(); + jobServiceMock.Setup(x => x.Get()).Returns(() => _context.Jobs); + + var unitServiceMock = new Mock(); + unitServiceMock.Setup(x => x.Get()).Returns(() => _context.Units); + + _service = new UnitFilterService(logger, jobServiceMock.Object, unitServiceMock.Object); + } + + public void Dispose() => _context.Dispose(); + + [Fact] + public async Task GetUnitsIdByJobFilterAsync_WithExistingJobId_ShouldReturnExpectedUnits() + { + // Arrange + var jobId = _context.Jobs.First(j => j.Name == "TestJob_1").Id; + var expectedUnitIds = new[] { TestDataGenerator.UnitAId, TestDataGenerator.UnitBId }; + + // Act + var result = await _service.GetUnitsIdByJobFilterAsync(jobId, takeCount: 10); + + // Assert + result.Should().NotBeNull() + .And.HaveCount(2) + .And.Contain(expectedUnitIds); + } + + [Fact] + public async Task GetRelatedUnitNamesAsync_WithChildRelationshipFilter_ShouldReturnExpectedNames() + { + // Arrange + var jobId = _context.Jobs.First(j => j.Name == "TestJob_2").Id; + var unitId = TestDataGenerator.UnitWId; // Родитель + + // rf.IsParent = false + // Ищем юниты, у которых ParentUnitId = UnitWId (т.е. это дети UnitWId) + // и у этих юнитов (детей) есть FieldId1 = "коммутатор" + // Это UnitXId и UnitYId -> имена: "СХД-КМТ-CISCO-MDS9148-1-ДВС", "СХД-КМТ-CISCO-MDS9148-3-ДВС" + + // Act + var result = await _service.GetRelatedUnitNamesAsync(jobId, unitId); + + // Assert + 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-ДВС" }); + //} + } +} \ No newline at end of file diff --git a/PARR.DAL.Tests/PARR.DAL.Tests.csproj b/PARR.DAL.Tests/PARR.DAL.Tests.csproj index d0843bba..1f2d1df6 100644 --- a/PARR.DAL.Tests/PARR.DAL.Tests.csproj +++ b/PARR.DAL.Tests/PARR.DAL.Tests.csproj @@ -9,6 +9,8 @@ + + diff --git a/PARR.DAL.Tests/TestDataGenerator.cs b/PARR.DAL.Tests/TestDataGenerator.cs new file mode 100644 index 00000000..a0af3f00 --- /dev/null +++ b/PARR.DAL.Tests/TestDataGenerator.cs @@ -0,0 +1,255 @@ +using Microsoft.EntityFrameworkCore; +using PARR.DAL.Context; +using PARR.DAL.Contracts; +using PARR.DAL.Models.Job; +using PARR.DAL.Models.Unit; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PARR.DAL.Tests +{ + public static class TestDataGenerator + { + // --- Unit IDs --- + public static readonly Guid UnitAId = Guid.Parse("11111111-1111-1111-1111-111111111111"); + public static readonly Guid UnitBId = Guid.Parse("00000000-0000-1111-1111-111111111111"); + public static readonly Guid UnitCId = Guid.Parse("22222222-2222-2222-2222-222222222222"); + public static readonly Guid UnitDId = Guid.Parse("33333333-3333-3333-3333-333333333333"); + public static readonly Guid UnitWId = Guid.Parse("44444444-4444-4444-4444-444444444444"); + public static readonly Guid UnitXId = Guid.Parse("55555555-5555-5555-5555-555555555555"); + public static readonly Guid UnitYId = Guid.Parse("66666666-6666-6666-6666-666666666666"); + public static readonly Guid UnitZId = Guid.Parse("77777777-7777-7777-7777-777777777777"); + + // --- Field IDs (для обхода составного ключа UnitFilterId+FieldId) --- + public static readonly Guid FieldId1 = Guid.Parse("08b42860-09a7-4a1f-aa4f-0290e86df883");//ТИП_ЭК + public static readonly Guid FieldId2 = Guid.Parse("5b2d3f5b-a9f3-4a6b-9142-70ef4357ac37"); //неуникальный + public static readonly Guid FieldId3 = Guid.Parse("5f3c7b8b-583d-411b-b9bd-1e045ddc5a8e");//ПОДКАТЕГОРИЯ_ЭК + public static readonly Guid FieldId4 = Guid.Parse("3e9ac5aa-531b-4ffc-8f93-694d00aa92a0");//активный + + // --- Field Value IDs --- + public static readonly Guid FieldValueId1 = Guid.Parse("fe18edcd-ab6a-4f3e-b7bc-e54aad9ecc0d"); // система хранения данных (схд) field3 + public static readonly Guid FieldValueId2 = Guid.Parse("f385b22c-42c3-4db9-bd41-b829603cc2d9"); // 1 field2+field4 + public static readonly Guid FieldValueId3 = Guid.Parse("2badc139-69cb-4d52-830a-1b3c6a9faf9d"); // 0 field2+field4 + public static readonly Guid FieldValueId4 = Guid.Parse("fde68ef7-a07e-4c10-872c-cd98c3d1c30b"); // коммутатор + public static readonly Guid FieldValueId5 = Guid.Parse("9f169862-3f78-49f2-b967-13b5b2f56231"); // серверное оборудование + public static readonly Guid FieldValueId6 = Guid.Parse("075b6fad-d24f-40f4-aa0e-83a4cfc46bbf"); // стойка цвк + public static readonly Guid FieldValueId7 = Guid.Parse("9679afd8-8004-4ce2-bd6f-9005b037ef96"); // схд + + internal static void Seed(DataContext context) + { + if (context.Units.Any()) return; + + var now = DateTimeOffset.UtcNow; + + // 1. Значения полей (остаются те же) + var fieldValues = new[] + { + new UnitFieldValue { Id = FieldValueId1, Value = "система хранения данных (схд)", DateCreated = now }, + new UnitFieldValue { Id = FieldValueId2, Value = "1", DateCreated = now }, + new UnitFieldValue { Id = FieldValueId3, Value = "0", DateCreated = now }, + new UnitFieldValue { Id = FieldValueId4, Value = "коммутатор", DateCreated = now }, + }; + context.UnitFieldValues.AddRange(fieldValues); + + // 2. Unit'ы + var units = new[] + { + new Unit { Id = UnitAId, Name = "СХД-КМТ-DVGD-MDS9148-1-ДВС", DateCreated = now }, + new Unit { Id = UnitBId, Name = "СХД-КМТ-DVGD-MDS9148-2-ДВС", DateCreated = now }, + new Unit { Id = UnitCId, Name = "СРВ-СТОЙКА-СТОЙКА-ХИВЦ-4.0-ДВС", DateCreated = now }, + new Unit { Id = UnitDId, Name = "СХД-VSP-E590-01-ДВС", DateCreated = now }, + new Unit { Id = UnitWId, Name = "СХД-КМТ-SAN1_FAB_1-ДВС", DateCreated = now }, + new Unit { Id = UnitXId, Name = "СХД-КМТ-CISCO-MDS9148-1-ДВС", DateCreated = now }, + new Unit { Id = UnitYId, Name = "СХД-КМТ-CISCO-MDS9148-3-ДВС", DateCreated = now }, + }; + context.Units.AddRange(units); + + // 3. UnitInUnit (связи) + var unitInUnits = new[] + { + new UnitInUnit { ParentUnitId = UnitAId, ChildUnitId = UnitCId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitBId, ChildUnitId = UnitCId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitDId, ChildUnitId = UnitAId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitDId, ChildUnitId = UnitBId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitWId, ChildUnitId = UnitXId, DateCreated = now }, + new UnitInUnit { ParentUnitId = UnitWId, ChildUnitId = UnitYId, DateCreated = now } + }; + context.UnitInUnits.AddRange(unitInUnits); + + // 4. UnitInValue + var unitInValues = new List + { + // Основные (FieldId) + new UnitInValue { UnitId = UnitAId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitAId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitAId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitAId, FieldId = FieldId2, ValueId = FieldValueId3, DateCreated = now }, + new UnitInValue { UnitId = UnitBId, FieldId = FieldId2, ValueId = FieldValueId3, DateCreated = now }, + new UnitInValue { UnitId = UnitBId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitBId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitBId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitCId, FieldId = FieldId3, ValueId = FieldValueId5, DateCreated = now }, + new UnitInValue { UnitId = UnitCId, FieldId = FieldId1, ValueId = FieldValueId6, DateCreated = now }, + new UnitInValue { UnitId = UnitCId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitDId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitDId, FieldId = FieldId1, ValueId = FieldValueId7, DateCreated = now }, + new UnitInValue { UnitId = UnitDId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitWId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitWId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitWId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitWId, FieldId = FieldId2, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitXId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitXId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitXId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + new UnitInValue { UnitId = UnitYId, FieldId = FieldId3, ValueId = FieldValueId1, DateCreated = now }, + new UnitInValue { UnitId = UnitYId, FieldId = FieldId1, ValueId = FieldValueId4, DateCreated = now }, + new UnitInValue { UnitId = UnitYId, FieldId = FieldId4, ValueId = FieldValueId2, DateCreated = now }, + + }; + context.UnitInValues.AddRange(unitInValues); + + // 5. JobGroupType и Group + var groupType = context.JobGroupTypes + .FirstOrDefault(t => t.Code == JobGroupTypesEnum.Simple) + ?? throw new InvalidOperationException("JobGroupType 'Simple' не найден в контексте."); + + var group = new JobGroup + { + Id = Guid.NewGuid(), + GroupTypeId = groupType.Id, + GroupType = groupType, + GroupName = "Группа", + ShortDescription = "%ТНК-КРАТКО% %ЭК%", + FullDescription = "Подробно:\n%СВЯЗИ%", + Solution = "Решение", + TemplateDuration = "540 00:00:00", + ReferenceDate = now, + IsAutoDistributionEnabled = false, + IsAgent = false, + DateCreated = now + }; + context.JobGroups.Add(group); + + // 6. Jobs + var jobs = new List(); + + // --- TestJob --- + var job1 = new Job + { + Id = Guid.NewGuid(), + Name = "TestJob_1", + WorkName = "Работа", + TemplateNameMask = "%ЭК%", + WorkGroupMask = "%ГРУППА_РАБОТ%", + GroupId = group.Id, + TnkId = Guid.NewGuid(), + Group = group, + DateCreated = now + }; + var unitFilter1Id = Guid.NewGuid(); + job1.UnitFilters = new List + { + new JobUnitFilter + { + Id = unitFilter1Id, + UnitFilter = "СХД-%", + JobId = job1.Id, + Job = job1, + DateCreated = now, + FieldFilters = new List + { + new JobFieldFilter + { + UnitFilterId = unitFilter1Id, + FieldId = FieldId1, + ValueMask="коммутатор" + }, + new JobFieldFilter + { + UnitFilterId = unitFilter1Id, + FieldId = FieldId4, + ValueMask="1" + }, + new JobFieldFilter + { + UnitFilterId = unitFilter1Id, + FieldId = FieldId2, + ValueMask="0" + }, + }, + RelationshipFilters = new List + { + new JobRelationshipFilter + { + IsParent = true, + IsInverse = true, + IsFullMatch = true, + FieldId = FieldId1, + ValueMask = "коммутатор", + UnitFilterId = unitFilter1Id + } + } + } + }; + jobs.Add(job1); + + // --- TestJob_2 (с IsParent = false для дочерних связей) --- + var job2 = new Job + { + Id = Guid.NewGuid(), + Name = "TestJob_2", + WorkName = "Работа2", + TemplateNameMask = "%ЭК%", + WorkGroupMask = "%ГРУППА_РАБОТ%", + GroupId = group.Id, + TnkId = Guid.NewGuid(), + Group = group, + DateCreated = now + }; + var unitFilter2Id = Guid.NewGuid(); + job2.UnitFilters = new List + { + new JobUnitFilter + { + Id = unitFilter2Id, + UnitFilter = "СХД-%", + JobId = job2.Id, + Job = job2, + DateCreated = now, + FieldFilters = new List + { + new JobFieldFilter + { + UnitFilterId = unitFilter2Id, + FieldId = FieldId1, + ValueMask="коммутатор" + }, + new JobFieldFilter + { + UnitFilterId = unitFilter2Id, + FieldId = FieldId4, + ValueMask="1" + }, + }, + RelationshipFilters = new List + { + new JobRelationshipFilter + { + IsParent = false, // <-- Новый фильтр: ищем дочерние юниты + IsInverse = false, // <-- Простой фильтр + IsFullMatch = false, // <-- Простой фильтр + FieldId = FieldId1, + ValueMask = "коммутатор", + UnitFilterId = unitFilter2Id + } + } + } + }; + jobs.Add(job2); + + context.Jobs.AddRange(jobs); + context.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/PARR.DAL/DomainServices/Implementations/ShortcodesService.cs b/PARR.DAL/DomainServices/Implementations/ShortcodesService.cs index 7f43e916..24a45144 100644 --- a/PARR.DAL/DomainServices/Implementations/ShortcodesService.cs +++ b/PARR.DAL/DomainServices/Implementations/ShortcodesService.cs @@ -1,6 +1,9 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using PARR.DAL.Contracts; 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; using System.Text.RegularExpressions; @@ -10,65 +13,84 @@ namespace PARR.DAL.DomainServices.Implementations internal class ShortcodesService : IShortcodesService { private const string shortcodePattern = "%[^%\\s]+%"; + + private static readonly HashSet SupportedShortcodes = new(StringComparer.OrdinalIgnoreCase) + { + "%ЭК%", "%ГРУППА_РАБОТ%", "%РАБОТА%", "%ТНК%", "%СВЯЗИ%", "%ТНК-КРАТКО%" + }; + + private readonly ILogger logger; private readonly SettingsFromDb settingsFromDb; private readonly IJobService jobService; private readonly IUnitService unitService; + private readonly IUnitFilterService unitFilterService; public ShortcodesService( + ILogger logger, SettingsFromDb settingsFromDb, IJobService jobService, - IUnitService unitService + IUnitService unitService, + IUnitFilterService unitFilterService ) { + this.logger = logger; this.settingsFromDb = settingsFromDb; this.jobService = jobService; this.unitService = unitService; + this.unitFilterService = unitFilterService; } public async Task ApplyShortcodesAsync(string str, Guid unitId, Guid jobId) { - //TODO удалить старый в GeneralExtesions, связанные с ним Enum и написать метод. делов... - var nameConstants = settingsFromDb.TemplateNameConstantPartsList; var job = await jobService - .Get().AsNoTracking() - .Include(t=>t.Tnk) - .Include(t=>t.Group) - .FirstOrDefaultAsync(t => t.Id == jobId); + .Get().AsNoTracking() + .Include(j => j.Tnk) + .Include(j => j.Group) + .Include(j => j.UnitFilters) + .ThenInclude(uf => uf.RelationshipFilters) + .FirstOrDefaultAsync(j => j.Id == jobId); - var unit = await unitService.Get().AsNoTracking().FirstOrDefaultAsync(t => t.Id == unitId); + var unit = await unitService.Get().AsNoTracking().FirstOrDefaultAsync(u => u.Id == unitId); - if (job != null && unit != null && job.TemplateNameMask != null) + if (job == null || unit == null || string.IsNullOrEmpty(str)) { - var resultName = str; - - var shortcodesInMask = GetShortCodes(resultName); - - //Проверяем и меняем наличие статичных частей в маске имени шаблона - if (shortcodesInMask.Any(x => nameConstants.Select(x => "%" + x.Name + "%").ToList().Contains(x.Value))) - { - resultName = ReplaceConstants(nameConstants, resultName); - } - - //Проверяем и меняем Shortcodes в маске имени шаблона - var shortCodes = GetShortcodesNames(); - if (shortcodesInMask.Any(x => shortCodes.Select(x => x).ToList().Contains(x.Value))) - { - resultName = ReplaceShortcodes(job, unit, resultName, shortcodesInMask); - } - - //Если остались %переменные% проверяем совпадение по имени поля - shortcodesInMask = GetShortCodes(resultName); - if (shortcodesInMask.Count > 0) - { - resultName = await ReplaceFieldValues(unitId, resultName, shortcodesInMask); - } - - return resultName; + logger.LogError("Переданы некорректные данные для подстановки динамических записей"); + return str; } - return ""; + var resultName = str; + var shortcodesInMask = GetShortCodes(resultName); + + // 1. Статические константы + if (shortcodesInMask.Any(m => nameConstants.Any(c => $"%{c.Name}%".Equals(m.Value, StringComparison.OrdinalIgnoreCase)))) + { + resultName = ReplaceConstants(nameConstants, resultName); + } + + // 2. Стандартные шорткоды (%ЭК%, %РАБОТА% и т.д.) + if (shortcodesInMask.Any(m => SupportedShortcodes.Contains(m.Value))) + { + resultName = ReplaceStandardShortcodes(job, unit, resultName); + } + + // 3. %СВЯЗИ% — отдельная обработка + if (shortcodesInMask.Any(m => string.Equals(m.Value, "%СВЯЗИ%", StringComparison.OrdinalIgnoreCase))) + { + var relatedUnitNames = await unitFilterService.GetRelatedUnitNamesAsync(jobId, unitId);//GetRelatedUnitNamesAsync(job, unit); + var linksText = string.Join("\n", relatedUnitNames); + resultName = Regex.Replace(resultName, "%СВЯЗИ%", linksText, RegexOptions.IgnoreCase); + } + + // 4. Поля (оставшиеся %FIELD_NAME%) + shortcodesInMask = GetShortCodes(resultName); + if (shortcodesInMask.Count > 0) + { + resultName = await ReplaceFieldValues(unitId, resultName, shortcodesInMask); + } + + return resultName; } @@ -102,21 +124,14 @@ namespace PARR.DAL.DomainServices.Implementations } - private static string ReplaceShortcodes(Models.Job.Job job, Models.Unit.Unit unit, string resultName, List shortcodesInMask) + private static string ReplaceStandardShortcodes(Job job, Unit unit, string input) { - if (shortcodesInMask.Any(x => x.Value == "%ЭК%")) - resultName = resultName.Replace("%ЭК%", unit.Name); - - if (shortcodesInMask.Any(x => x.Value == "%ГРУППА_РАБОТ%")) - resultName = resultName.Replace("%ГРУППА_РАБОТ%", job.Group!.GroupName); - - if (shortcodesInMask.Any(x => x.Value == "%РАБОТА%")) - resultName = resultName.Replace("%РАБОТА%", job.WorkName); - - if (shortcodesInMask.Any(x => x.Value == "%ТНК%")) - resultName = resultName.Replace("%ТНК%", job.Tnk!.Name); - - return resultName; + return input + .Replace("%ЭК%", unit.Name, StringComparison.OrdinalIgnoreCase) + .Replace("%ГРУППА_РАБОТ%", job.Group?.GroupName ?? "", StringComparison.OrdinalIgnoreCase) + .Replace("%РАБОТА%", job.WorkName, StringComparison.OrdinalIgnoreCase) + .Replace("%ТНК%", job.Tnk?.Name ?? "", StringComparison.OrdinalIgnoreCase) + .Replace("%ТНК-КРАТКО%", job.Tnk?.ShortName ?? "", StringComparison.OrdinalIgnoreCase); } @@ -136,17 +151,5 @@ namespace PARR.DAL.DomainServices.Implementations var shortcodesInMask = Regex.Matches(resultName, shortcodePattern).ToList(); return shortcodesInMask; } - - private List GetShortcodesNames() - { - var result = new List { - "%ЭК%", - "%ГРУППА_РАБОТ%", - "%РАБОТА%", - "%ТНК%" - }; - - return result; - } } } diff --git a/PARR.DAL/DomainServices/Implementations/UnitFilterService.cs b/PARR.DAL/DomainServices/Implementations/UnitFilterService.cs index eeda69ee..d6c071e9 100644 --- a/PARR.DAL/DomainServices/Implementations/UnitFilterService.cs +++ b/PARR.DAL/DomainServices/Implementations/UnitFilterService.cs @@ -1,12 +1,12 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using PARR.DAL.Context; using PARR.DAL.Contracts; 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; -using System.Text.RegularExpressions; - namespace PARR.DAL.DomainServices.Implementations { @@ -15,75 +15,54 @@ namespace PARR.DAL.DomainServices.Implementations private readonly ILogger logger; private readonly IJobService jobService; private readonly IUnitService unitService; + private readonly IUnitInUnitService unitInUnitService; + private readonly IUnitInValueService unitInValueService; public UnitFilterService( ILogger logger, IJobService jobService, - IUnitService unitService - ) + IUnitService unitService, + IUnitInUnitService unitInUnitService, + IUnitInValueService unitInValueService) { this.logger = logger; this.jobService = jobService; this.unitService = unitService; + this.unitInUnitService = unitInUnitService; + this.unitInValueService = unitInValueService; } - public async Task?> GetUnitsIdByJobFilterAsync(Guid jobId, int? takeCount = null) { var job = await jobService - .Get().AsNoTracking() - .Include(t => t.UnitFilters) - .ThenInclude(t => t.FieldFilters) - .Include(t => t.UnitFilters) - .ThenInclude(t => t.RelationshipFilters) - .Include(t => t.Group) - .ThenInclude(t => t.GroupType) - .FirstOrDefaultAsync(t => t.Id == jobId); + .Get().AsNoTracking() + .Include(j => j.UnitFilters).ThenInclude(uf => uf.FieldFilters) + .Include(j => j.UnitFilters).ThenInclude(uf => uf.RelationshipFilters) + .Include(j => j.Group).ThenInclude(g => g.GroupType) + .FirstOrDefaultAsync(j => j.Id == jobId); - if (job == null) - return null; - - return await GetUnitsIdByJobFilterAsync(job, takeCount); + return job == null ? null : await GetUnitsIdByJobFilterAsync(job, takeCount); } - public async Task?> GetUnitsIdByJobFilterAsync(Job job, int? takeCount = null) { - #region проверка инклудов + #region Проверка обязательных зависимостей if (job.Group == null) - { - logger.LogWarning("JobId={JobId} не содержит Group - пропускаем фильтрацию", job.Id); - throw new ArgumentNullException(nameof(Group)); - } - + throw new ArgumentNullException(nameof(job.Group), $"Job {job.Id} не содержит Group"); if (job.Group.GroupType == null) - { - logger.LogWarning("JobId={JobId} не содержит GroupType - пропускаем фильтрацию", job.Id); - throw new ArgumentNullException(nameof(JobGroupType)); - } - + throw new ArgumentNullException(nameof(job.Group.GroupType), $"Job {job.Id} не содержит GroupType"); if (job.UnitFilters == null || !job.UnitFilters.Any()) - { - logger.LogWarning("JobId={JobId} не содержит UnitFilters - пропускаем фильтрацию", job.Id); - throw new ArgumentNullException(nameof(JobUnitFilter)); - } + throw new ArgumentNullException(nameof(job.UnitFilters), $"Job {job.Id} не содержит UnitFilters"); #endregion var maxCount = takeCount ?? int.MaxValue; - var collectedIds = new HashSet(); // ← гарантирует уникальность + var collectedIds = new HashSet(); var filterNumber = 0; foreach (var filter in job.UnitFilters) { filterNumber++; - - // Прекращаем, если набрали достаточно - if (collectedIds.Count >= maxCount) - { - logger.LogDebug("Достигнут лимит takeCount={TakeCount} после {FilterCount} фильтров", maxCount, filterNumber - 1); - break; - } - + if (collectedIds.Count >= maxCount) break; var remaining = maxCount - collectedIds.Count; if (remaining <= 0) break; @@ -91,183 +70,34 @@ namespace PARR.DAL.DomainServices.Implementations { logger.LogDebug("Применяем фильтр #{Index} (Id={FilterId})", filterNumber, filter.Id); - // Стартуем с базового условия — имя Unit'а var query = unitService.Get().AsNoTracking() - .Where(unit => EF.Functions.ILike(unit.Name, filter.UnitFilter)); + .Where(unit => EF.Functions.Like(unit.Name, filter.UnitFilter)); logger.LogDebug("Базовый фильтр по Name: {NameFilter}", filter.UnitFilter); - // 1. Фильтры по полям (UnitValues) foreach (var fieldFilter in filter.FieldFilters) { - var fieldId = fieldFilter.FieldId; - var valueMask = fieldFilter.ValueMask; - - logger.LogDebug("Фильтр по полю: FieldId={FieldId}, ValueMask={ValueMask}", fieldId, valueMask); - - query = query.Where(unit => - unit.UnitValues.Any(value => - value.FieldId == fieldId - && value.Value != null - && value.Value.Value != null - && EF.Functions.ILike(value.Value.Value, valueMask))); + query = ApplyFieldFilter(query, fieldFilter); } - // 2. Фильтры по связям (родителям и детям) foreach (var relFilter in filter.RelationshipFilters) { - var fieldId = relFilter.FieldId; - var valueMask = relFilter.ValueMask; - - logger.LogDebug("Фильтр по связи: IsParent={IsParent}, IsInverse={IsInverse}, IsFullMatch={IsFullMatch}, FieldId={FieldId}, ValueMask={ValueMask}", - relFilter.IsParent, relFilter.IsInverse, relFilter.IsFullMatch, relFilter.FieldId, relFilter.ValueMask); - - if (relFilter.IsParent) - { - if (relFilter.IsInverse) - { - if (relFilter.IsFullMatch) - { - // Все родители НЕ должны иметь такое значение - query = query.Where(unit => - !unit.ParentUnits.Any() || // если связей нет — подходит - unit.ParentUnits.All(parentLink => - !parentLink.ParentUnit!.UnitValues.Any(parentValue => - parentValue.FieldId == fieldId - && parentValue.Value != null - && parentValue.Value.Value != null - && EF.Functions.ILike(parentValue.Value.Value, valueMask)))); - } - else - { - // Хотя бы один родитель НЕ должен иметь такое значение - query = query.Where(unit => - !unit.ParentUnits.Any() || - unit.ParentUnits.Any(parentLink => - !parentLink.ParentUnit!.UnitValues.Any(parentValue => - parentValue.FieldId == fieldId - && parentValue.Value != null - && parentValue.Value.Value != null - && EF.Functions.ILike(parentValue.Value.Value, valueMask)))); - } - } - else - { - // Прямой фильтр: связанные Unit'ы ДОЛЖНЫ иметь значение - if (relFilter.IsFullMatch) - { - // Все родители должны иметь такое значение - query = query.Where(unit => - !unit.ParentUnits.Any() || - unit.ParentUnits.All(parentLink => - parentLink.ParentUnit!.UnitValues.Any(parentValue => - parentValue.FieldId == fieldId - && parentValue.Value != null - && parentValue.Value.Value != null - && EF.Functions.ILike(parentValue.Value.Value, valueMask)))); - } - else - { - // Хотя бы один родитель должен иметь такое значение - query = query.Where(unit => - unit.ParentUnits.Any(parentLink => - parentLink.ParentUnit!.UnitValues.Any(parentValue => - parentValue.FieldId == fieldId - && parentValue.Value != null - && parentValue.Value.Value != null - && EF.Functions.ILike(parentValue.Value.Value, valueMask)))); - } - } - } - else - { - // relFilter.IsParent == false → работаем с детьми (ChildUnits) - if (relFilter.IsInverse) - { - if (relFilter.IsFullMatch) - { - query = query.Where(unit => - !unit.ChildUnits.Any() || - unit.ChildUnits.All(childLink => - !childLink.ChildUnit!.UnitValues.Any(childValue => - childValue.FieldId == fieldId - && childValue.Value != null - && childValue.Value.Value != null - && EF.Functions.ILike(childValue.Value.Value, valueMask)))); - } - else - { - query = query.Where(unit => - !unit.ChildUnits.Any() || - unit.ChildUnits.Any(childLink => - !childLink.ChildUnit!.UnitValues.Any(childValue => - childValue.FieldId == fieldId - && childValue.Value != null - && childValue.Value.Value != null - && EF.Functions.ILike(childValue.Value.Value, valueMask)))); - } - } - else - { - if (relFilter.IsFullMatch) - { - query = query.Where(unit => - !unit.ChildUnits.Any() || - unit.ChildUnits.All(childLink => - childLink.ChildUnit!.UnitValues.Any(childValue => - childValue.FieldId == fieldId - && childValue.Value != null - && childValue.Value.Value != null - && EF.Functions.ILike(childValue.Value.Value, valueMask)))); - } - else - { - query = query.Where(unit => - unit.ChildUnits.Any(childLink => - childLink.ChildUnit!.UnitValues.Any(childValue => - childValue.FieldId == fieldId - && childValue.Value != null - && childValue.Value.Value != null - && EF.Functions.ILike(childValue.Value.Value, valueMask)))); - } - } - } + query = ApplyRelationshipFilterToQuery(query, relFilter); } - // 3. Ограничение по количеству связей (только для umbrella-групп) if (job.Group.GroupType.Code == JobGroupTypesEnum.Umbrella) { - int min = job.MinValueRelationships.GetValueOrDefault(0); - int max = job.MaxValueRelationships.GetValueOrDefault(int.MaxValue); - - logger.LogDebug("Фильтр по количеству связей: Min={Min}, Max={Max}, IsParent={IsParent}", min, max, job.IsParentRelationships); - - if (job.IsParentRelationships == true) - { - query = query.Where(unit => - unit.ParentUnits.Count >= min && unit.ParentUnits.Count <= max); - } - else - { - query = query.Where(unit => - unit.ChildUnits.Count >= min && unit.ChildUnits.Count <= max); - } + query = ApplyRelationshipCountFilter(query, job); } - // Выполняем — только Id, с учётом оставшегося лимита var newIds = await query - .Select(unit => unit.Id) + .Select(u => u.Id) .Take(remaining) .ToListAsync(); - logger.LogDebug("Фильтр #{Index}: найдено {Count} Unit'ов", filterNumber, newIds.Count); - collectedIds.UnionWith(newIds); - - logger.LogDebug( - "Фильтр #{Index} (Id={FilterId}): найдено {Count} Unit'ов. Всего: {Total}", - filterNumber, filter.Id, newIds.Count, collectedIds.Count); - + logger.LogDebug("Фильтр #{Index}: найдено {Count} Unit'ов. Всего: {Total}", + filterNumber, newIds.Count, collectedIds.Count); } catch (Exception ex) { @@ -282,5 +112,265 @@ namespace PARR.DAL.DomainServices.Implementations return result; } + + public async Task> GetRelatedUnitNamesAsync(Guid jobId, Guid unitId) + { + logger.LogDebug("Начало GetRelatedUnitNamesAsync. JobId: {JobId}, UnitId: {UnitId}", jobId, unitId); + + var job = await jobService + .Get().AsNoTracking() + .Include(j => j.UnitFilters).ThenInclude(uf => uf.RelationshipFilters) + .FirstOrDefaultAsync(j => j.Id == jobId); + + if (job == null) + { + logger.LogWarning("Job с Id {JobId} не найден.", jobId); + throw new ArgumentException($"Job {jobId} не найден.", nameof(jobId)); + } + + logger.LogDebug("Найден Job: {JobName}. Количество UnitFilters: {FilterCount}", job.Name, job.UnitFilters.Count()); + + var result = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var filter in job.UnitFilters) + { + if (!filter.RelationshipFilters.Any()) + { + logger.LogDebug("UnitFilter.Id {FilterId} не содержит RelationshipFilters. Пропускаем.", filter.Id); + continue; + } + + logger.LogDebug("Обработка UnitFilter.Id {FilterId}. Количество RelationshipFilters: {RelFilterCount}", filter.Id, filter.RelationshipFilters.Count()); + + foreach (var rf in filter.RelationshipFilters) + { + logger.LogDebug("Обработка RelationshipFilter (UnitFilterId={UnitFilterId}): IsParent={IsParent}, FieldId={FieldId}, ValueMask={ValueMask}", + rf.UnitFilterId, rf.IsParent, rf.FieldId, rf.ValueMask); + + var valueMask = rf.ValueMask?.Trim(); + if (string.IsNullOrEmpty(valueMask)) + { + logger.LogDebug("ValueMask пуст. Пропускаем фильтр (UnitFilterId={UnitFilterId}).", rf.UnitFilterId); + continue; + } + + // --- НОВАЯ ЛОГИКА: Используем новые сервисы --- + + List relevantLinks; + if (rf.IsParent) + { + // u - родитель, его ребёнок - unitId + relevantLinks = await unitInUnitService.GetByChildIdAsync(unitId); + } + else + { + // u - ребёнок, его родитель - unitId + relevantLinks = await unitInUnitService.GetByParentIdAsync(unitId); + } + + logger.LogDebug("Найдено {Count} связей через UnitInUnitService.", relevantLinks.Count); + + // Получаем Id юнитов, которые участвуют в связях (дети или родители) + var unitIdsToCheck = rf.IsParent + ? relevantLinks.Select(l => l.ParentUnitId).ToList() + : relevantLinks.Select(l => l.ChildUnitId).ToList(); + + if (!unitIdsToCheck.Any()) + { + logger.LogDebug("Нет юнитов для проверки значений."); + continue; + } + + // Получаем значения полей для этих юнитов + var unitValues = await unitInValueService.GetByUnitIdsAsync(unitIdsToCheck); + + logger.LogDebug("Получено {Count} значений полей через UnitInValueService.", unitValues.Count); + + // Фильтруем юниты, у которых есть нужное значение + var matchingUnitIds = unitValues + .Where(uv => uv.FieldId == rf.FieldId + && uv.Value != null + && uv.Value.Value != null + && uv.Value.Value.ToString()!.Contains(valueMask, StringComparison.OrdinalIgnoreCase)) + .Select(uv => uv.UnitId) + .Distinct() + .ToList(); + + logger.LogDebug("Найдено {Count} юнитов с подходящим значением поля.", matchingUnitIds.Count); + + // Получаем имена этих юнитов через IUnitService + if (matchingUnitIds.Any()) + { + var matchingUnitNames = await unitService.Get().AsNoTracking() + .Where(u => matchingUnitIds.Contains(u.Id)) + .Select(u => u.Name) + .ToListAsync(); + + logger.LogDebug("Найдены имена: [{Names}]", string.Join(", ", matchingUnitNames)); + result.UnionWith(matchingUnitNames); + } + } + } + + logger.LogDebug("Итоговый результат: [{Result}]", string.Join(", ", result)); + + return result.ToList(); + } + + #region Вспомогательные методы фильтрации + + private IQueryable ApplyFieldFilter(IQueryable query, JobFieldFilter fieldFilter) + { + var fieldId = fieldFilter.FieldId; + var valueMask = fieldFilter.ValueMask?.Trim(); + + if (string.IsNullOrEmpty(valueMask)) + return query; + + logger.LogDebug("Фильтр по полю: FieldId={FieldId}, ValueMask={ValueMask}", fieldId, valueMask); + + return query.Where(unit => + unit.UnitValues.Any(v => + v.FieldId == fieldId && + v.Value != null && + v.Value.Value != null && + EF.Functions.Like(v.Value.Value, valueMask))); + } + + private IQueryable ApplyRelationshipFilterToQuery(IQueryable query, JobRelationshipFilter relFilter) + { + var fieldId = relFilter.FieldId; + var valueMask = relFilter.ValueMask ?? ""; + + if (string.IsNullOrWhiteSpace(valueMask)) + return query; + + logger.LogDebug("Фильтр по связи: IsParent={IsParent}, IsInverse={IsInverse}, IsFullMatch={IsFullMatch}, FieldId={FieldId}, ValueMask={ValueMask}", + relFilter.IsParent, relFilter.IsInverse, relFilter.IsFullMatch, fieldId, valueMask); + + if (relFilter.IsParent) + { + if (relFilter.IsInverse) + { + if (relFilter.IsFullMatch) + { + return query.Where(unit => + !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 + { + if (relFilter.IsFullMatch) + { + return query.Where(unit => + !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(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) + { + 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() || + 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 + { + if (relFilter.IsFullMatch) + { + 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 ApplyRelationshipCountFilter(IQueryable query, Job job) + { + int min = job.MinValueRelationships.GetValueOrDefault(0); + int max = job.MaxValueRelationships.GetValueOrDefault(int.MaxValue); + + logger.LogDebug("Фильтр по количеству связей: Min={Min}, Max={Max}, IsParent={IsParent}", min, max, job.IsParentRelationships); + + if (job.IsParentRelationships == true) + { + return query.Where(unit => unit.ParentUnits.Count >= min && unit.ParentUnits.Count <= max); + } + else + { + return query.Where(unit => unit.ChildUnits.Count >= min && unit.ChildUnits.Count <= max); + } + } + + #endregion } -} +} \ No newline at end of file diff --git a/PARR.DAL/DomainServices/Interfaces/IUnitFilterService.cs b/PARR.DAL/DomainServices/Interfaces/IUnitFilterService.cs index 0735763b..21ed7eaf 100644 --- a/PARR.DAL/DomainServices/Interfaces/IUnitFilterService.cs +++ b/PARR.DAL/DomainServices/Interfaces/IUnitFilterService.cs @@ -5,18 +5,35 @@ namespace PARR.DAL.DomainServices.Interfaces public interface IUnitFilterService { /// - /// Получить все Unit.Id соответствующие настройкам фильтрации Job + /// Получает список ID Unit'ов, соответствующих фильтрам Job. /// - /// Id работы - /// - Task?> GetUnitsIdByJobFilterAsync(Guid jobId, int? takeCount = null); - + /// ID Job + /// Максимальное количество Unit'ов (опционально) + /// Список ID Unit'ов или null, если Job не найден + Task?> GetUnitsIdByJobFilterAsync( + Guid jobId, + int? takeCount = null); /// - /// Получить все Unit.Id соответствующие настройкам фильтрации Job + /// Получает список ID Unit'ов, соответствующих фильтрам Job. /// - /// Job обязательно должен содержать UnitFilter, Group и GroupType - /// - Task?> GetUnitsIdByJobFilterAsync(Job job, int? takeCount = null); + /// Job с предзагруженными UnitFilters, Group, GroupType + /// Максимальное количество Unit'ов (опционально) + /// Список ID Unit'ов + Task?> GetUnitsIdByJobFilterAsync( + Models.Job.Job job, + int? takeCount = null); + + /// + /// Получает имена связанных Unit'ов для заданного Unit, отфильтрованных по RelationshipFilters Job. + /// Используется для шорткода %СВЯЗИ%. + /// + /// ID Job + /// ID Unit, для которого ищутся связи + /// Токен отмены (опционально) + /// Список имён связанных Unit'ов (уникальные, без дублей) + Task> GetRelatedUnitNamesAsync( + Guid jobId, + Guid unitId); } } diff --git a/PARR.DAL/PARR.DAL.csproj b/PARR.DAL/PARR.DAL.csproj index b9a6e5a1..0bdd442b 100644 --- a/PARR.DAL/PARR.DAL.csproj +++ b/PARR.DAL/PARR.DAL.csproj @@ -24,11 +24,4 @@ - - - - <_Parameter1>PARR.DAL.Tests - - - diff --git a/PARR.DAL/ParrDalInstaller.cs b/PARR.DAL/ParrDalInstaller.cs index 9772179a..b64795f5 100644 --- a/PARR.DAL/ParrDalInstaller.cs +++ b/PARR.DAL/ParrDalInstaller.cs @@ -98,6 +98,8 @@ namespace PARR.DAL services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); #endregion diff --git a/PARR.DAL/Properties/AssemblyInfo.cs b/PARR.DAL/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..32a4a814 --- /dev/null +++ b/PARR.DAL/Properties/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("PARR.DAL.Tests")] +[assembly: InternalsVisibleTo("PARR.DAL.IntegrationTests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file diff --git a/PARR.DAL/Services/Implementations/Unit/UnitInUnitService.cs b/PARR.DAL/Services/Implementations/Unit/UnitInUnitService.cs new file mode 100644 index 00000000..5cffcc65 --- /dev/null +++ b/PARR.DAL/Services/Implementations/Unit/UnitInUnitService.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PARR.DAL.Context; +using PARR.DAL.Models.Unit; +using PARR.DAL.Services.Abstracts; +using PARR.DAL.Services.Interfaces.Unit; + +namespace PARR.DAL.Services.Implementations.Unit +{ + internal class UnitInUnitService : IUnitInUnitService + { + private readonly DataContext dataContext; + private readonly ILogger logger; + + public UnitInUnitService( + DataContext dataContext, + ILogger logger + ) + { + this.dataContext = dataContext; + this.logger = logger; + } + + + + public Task> GetByParentIdAsync(Guid parentId) + { + return dataContext.UnitInUnits + .Where(u => u.ParentUnitId == parentId) + .ToListAsync(); + } + + public Task> GetByChildIdAsync(Guid childId) + { + return dataContext.UnitInUnits + .Where(u => u.ChildUnitId == childId) + .ToListAsync(); + } + } +} diff --git a/PARR.DAL/Services/Implementations/Unit/UnitInValueService.cs b/PARR.DAL/Services/Implementations/Unit/UnitInValueService.cs new file mode 100644 index 00000000..6af29be5 --- /dev/null +++ b/PARR.DAL/Services/Implementations/Unit/UnitInValueService.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using PARR.DAL.Context; +using PARR.DAL.Models.Unit; +using PARR.DAL.Services.Interfaces.Unit; + +namespace PARR.DAL.Services.Implementations.Unit +{ + internal class UnitInValueService : IUnitInValueService + { + private readonly ILogger logger; + private readonly DataContext dataContext; + + public UnitInValueService( + ILogger logger, + DataContext dataContext + ) + { + this.logger = logger; + this.dataContext = dataContext; + } + + public async Task> GetByUnitIdAsync(Guid unitId) + { + return await dataContext.UnitInValues + .Where(uv => uv.UnitId == unitId) + .ToListAsync(); + } + + public async Task> GetByUnitIdsAsync(IEnumerable unitIds) + { + return await dataContext.UnitInValues + .Where(uv => unitIds.Contains(uv.UnitId)) + .ToListAsync(); + } + } +} diff --git a/PARR.DAL/Services/Interfaces/Unit/IUnitInUnitService.cs b/PARR.DAL/Services/Interfaces/Unit/IUnitInUnitService.cs new file mode 100644 index 00000000..ef2d98cc --- /dev/null +++ b/PARR.DAL/Services/Interfaces/Unit/IUnitInUnitService.cs @@ -0,0 +1,11 @@ +using PARR.DAL.Models.Unit; +using PARR.DAL.Services.Interfaces.Base; + +namespace PARR.DAL.Services.Interfaces.Unit +{ + public interface IUnitInUnitService + { + Task> GetByParentIdAsync(Guid parentId); + Task> GetByChildIdAsync(Guid childId); + } +} diff --git a/PARR.DAL/Services/Interfaces/Unit/IUnitInValueService.cs b/PARR.DAL/Services/Interfaces/Unit/IUnitInValueService.cs new file mode 100644 index 00000000..3e8ac488 --- /dev/null +++ b/PARR.DAL/Services/Interfaces/Unit/IUnitInValueService.cs @@ -0,0 +1,15 @@ +using PARR.DAL.Models.Unit; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PARR.DAL.Services.Interfaces.Unit +{ + public interface IUnitInValueService + { + Task> GetByUnitIdsAsync(IEnumerable unitIds); + Task> GetByUnitIdAsync(Guid unitId); + } +}