diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md new file mode 100644 index 00000000..0136e78a --- /dev/null +++ b/.gitlab/issue_templates/Bug.md @@ -0,0 +1,21 @@ +### 📋 Описание бага +Кратко и понятно опишите, что идет не так. + +### 🔄 Шаги для воспроизведения +1. Перейти на страницу... +2. Нажать кнопку... +3. Ввести в поле поиска... + +### ❌ Фактический результат +Что произошло на самом деле? (Например: Приложение завершилось с ошибкой 500). + +### ✅ Ожидаемый результат +Что должно было произойти? (Например: Появился список найденных товаров). + +### 🖥️ Окружение +* **ОС:** Windows 11 / macOS Sequoia +* **Браузер / Версия:** Chrome 124 / iOS App v2.1 +* **Стенд:** Staging / Production + +### 📎 Вложения +Скриншоты, гифки или логи, которые помогают понять проблему. diff --git a/PARR.API/Controllers/V1/SyncTaskController.cs b/PARR.API/Controllers/V1/SyncTaskController.cs index 2ff7528a..4b7a13b0 100644 --- a/PARR.API/Controllers/V1/SyncTaskController.cs +++ b/PARR.API/Controllers/V1/SyncTaskController.cs @@ -48,9 +48,6 @@ namespace PARR.API.Controllers.V1 [HttpPost(ApiRoutes.SyncTask.MatchTemplates)] public async Task MatchTemplatesForJob([FromBody] MatchTemplatesRequest request) { - //todo: Валидатор! Валидатор то забыли!!! - - // проверяем, если уже идет синхронизация по этому объекту, то ахтунг, ошибка var matchingStatus = await matchingStatusService.GetStatusAsync(request.ObjectId, request.EntityType); if (matchingStatus.IsMatchingObject) diff --git a/PARR.API/MappingProfiles/RequestToDomainProfile.cs b/PARR.API/MappingProfiles/RequestToDomainProfile.cs index 4264fc5c..ba67bb7e 100644 --- a/PARR.API/MappingProfiles/RequestToDomainProfile.cs +++ b/PARR.API/MappingProfiles/RequestToDomainProfile.cs @@ -21,6 +21,7 @@ namespace PARR.API.MappingProfiles .ForMember(d => d.MaxValueRelationships, o => o.MapFrom(s => s.Relationships != null ? s.Relationships.MaxValueRelationships : (int?)null)) .ForMember(d => d.MinValueRelationships, o => o.MapFrom(s => s.Relationships != null ? s.Relationships.MinValueRelationships : (int?)null)) .ForMember(d => d.DateCreated, o => o.MapFrom(s => DateTimeOffset.UtcNow)) + .ForMember(d => d.AutoControl, o => o.Ignore()) .AfterMap((s, d) => { if (d.UnitFilters != null) diff --git a/PARR.API/Validators/MatchTemplatesRequestValidator.cs b/PARR.API/Validators/MatchTemplatesRequestValidator.cs new file mode 100644 index 00000000..959a17f8 --- /dev/null +++ b/PARR.API/Validators/MatchTemplatesRequestValidator.cs @@ -0,0 +1,50 @@ +using FluentValidation; +using Microsoft.EntityFrameworkCore; +using PARR.API.Contracts.V1.Requests; +using PARR.Core.Repositories.Interfaces; +using PARR.Core.Repositories.Interfaces.Job; +using PARR.Domain.Enums; + +namespace PARR.API.Validators +{ + public class MatchTemplatesRequestValidator : AbstractValidator + { + public MatchTemplatesRequestValidator( + IJobGroupRepository jobGroupRepository, + IJobRepository jobRepository, + ITemplateRepository templateRepository + ) + { + RuleFor(t => t.ObjectId) + .NotNull() + .NotEmpty() + .WithMessage("Идентификатор объекта не может быть пустым."); + + RuleFor(t => t) + .MustAsync(async (request, cancellation) => + { + return request.EntityType switch + { + SyncTaskEntityTypeEnum.JobGroup => + await jobGroupRepository.Get().AsNoTracking().AnyAsync(t => t.Id == request.ObjectId, cancellation), + + SyncTaskEntityTypeEnum.Job => + await jobRepository.Get().AsNoTracking().AnyAsync(t => t.Id == request.ObjectId && t.Group!.GroupType!.IsJobGroupAutoControl == false, cancellation), + + SyncTaskEntityTypeEnum.Template => + false, // TODO: Синхронизировать шаблоны нельзя + + _ => false + }; + }) + .WithMessage(request => request.EntityType switch + { + SyncTaskEntityTypeEnum.Template => "Синхронизация шаблонов пока не поддерживается.", + SyncTaskEntityTypeEnum.Job => "Объект не найден или данные работы нельзя отправить на синхронизацию.", + _ => "Не найден объект с переданным ИД." + }); + + + } + } +} diff --git a/PARR.Core/Common/Interfaces/RabbitServices/IRabbitService.cs b/PARR.Core/Common/Interfaces/RabbitServices/IRabbitService.cs index 05ef85d1..954ca3cf 100644 --- a/PARR.Core/Common/Interfaces/RabbitServices/IRabbitService.cs +++ b/PARR.Core/Common/Interfaces/RabbitServices/IRabbitService.cs @@ -24,6 +24,6 @@ namespace PARR.Core.Common.Interfaces.RabbitServices /// /// /// - Task SendAsync(IMqSettings mqSettings, List msgObjectList); + Task SendAsync(IMqSettings mqSettings, IEnumerable msgObjectList); } } diff --git a/PARR.Infrastructure/Rabbit/RabbitService.cs b/PARR.Infrastructure/Rabbit/RabbitService.cs index 7c3ed030..21c2e969 100644 --- a/PARR.Infrastructure/Rabbit/RabbitService.cs +++ b/PARR.Infrastructure/Rabbit/RabbitService.cs @@ -115,7 +115,7 @@ namespace PARR.Infrastructure.Rabbit } } - public async Task SendAsync(IMqSettings mqSettings, List msgObjectList) + public async Task SendAsync(IMqSettings mqSettings, IEnumerable msgObjectList) { var msgStringList = msgObjectList.Select(t => JsonSerializer.Serialize(t, jsonOptions)).ToArray(); diff --git a/PARR.JobAutoControl/JobAutoControlManager.cs b/PARR.JobAutoControl/JobAutoControlManager.cs index 2325337d..ac0c60e4 100644 --- a/PARR.JobAutoControl/JobAutoControlManager.cs +++ b/PARR.JobAutoControl/JobAutoControlManager.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using PARR.Core.Common.Interfaces; using PARR.Core.Common.Interfaces.RabbitServices; using PARR.Core.Repositories.Interfaces.Job; +using PARR.Core.Repositories.Interfaces.JobGroupRepositories; using PARR.Domain.Common.Rabbit.Messages; using PARR.Domain.Entities.Base.History; using PARR.Domain.Enums; @@ -13,11 +14,11 @@ namespace PARR.JobAutoControl { internal class JobAutoControlManager : IJobAutoControlManager { - private readonly WorkerSettings workerSettings; - private readonly ILogger logger; - private readonly IIntervalService intervalService; - private readonly IServiceProvider serviceProvider; - private readonly MqSettings mqSettings; + private readonly WorkerSettings _workerSettings; + private readonly ILogger _logger; + private readonly IIntervalService _intervalService; + private readonly IServiceProvider _serviceProvider; + private readonly MqSettings _mqSettings; public JobAutoControlManager( WorkerSettings workerSettings, @@ -27,68 +28,87 @@ namespace PARR.JobAutoControl MqSettings mqSettings ) { - this.workerSettings = workerSettings; - this.logger = logger; - this.intervalService = intervalService; - this.serviceProvider = serviceProvider; - this.mqSettings = mqSettings; + _workerSettings = workerSettings; + _logger = logger; + _intervalService = intervalService; + _serviceProvider = serviceProvider; + _mqSettings = mqSettings; } public async Task StartAsync() { - logger.LogInformation("Запуск сервиса управления авто-контролем РР (JobAutoControl)"); + _logger.LogInformation("Запуск сервиса управления авто-контролем РР (JobAutoControl)"); - await intervalService.IntervalInitAsync(async () => + await _intervalService.IntervalInitAsync(async () => { - await using (var scope = serviceProvider.CreateAsyncScope()) - //using (var scope = serviceProvider.CreateScope()) + await using (var scope = _serviceProvider.CreateAsyncScope()) { - var jobAutoControlService = scope.ServiceProvider.GetService(); - var mqService = scope.ServiceProvider.GetService(); + var jobAutoControlRepository = scope.ServiceProvider.GetRequiredService(); + var jobGroupAutoControlRepository = scope.ServiceProvider.GetRequiredService(); + var mqService = scope.ServiceProvider.GetRequiredService(); - if (jobAutoControlService == null || mqService == null) - throw new Exception($"Не смог получить серивс {nameof(IJobAutoControlRepository)} или {nameof(IRabbitService)}"); - - await HandlerAsync(jobAutoControlService, mqService); + await HandlerAsync(jobAutoControlRepository, jobGroupAutoControlRepository, mqService); } - }, workerSettings.RepeatEvery); + }, _workerSettings.RepeatEvery); } - private async Task HandlerAsync(IJobAutoControlRepository jobAutoControlService, IRabbitService mqService) + private async Task HandlerAsync(IJobAutoControlRepository jobAutoControlRepository, IJobGroupAutoControlRepository jobGroupAutoControlRepository, IRabbitService mqService) { - var jobsWithAutoControl = await jobAutoControlService.Get().Where(t => t.IsEnable).ToListAsync(); + // Список JobGroup с включенным автоконтролем + var jobGroupsIds = await jobGroupAutoControlRepository.Get() + .AsNoTracking() + .Where(t => t.IsEnable) + .Select(t => t.JobGroupId) + .ToListAsync(); - logger.LogInformation($"Работ с включенным авто-контролем: {jobsWithAutoControl.Count} шт."); + // Список Job с включенным автоконтролем, но у которых в JobGroupType.IsJobGroupAutocOntrol==false + var jobIds = await jobAutoControlRepository.Get() + .AsNoTracking() + .Where(t => t.IsEnable && t.Job!.Group!.GroupType!.IsJobGroupAutoControl == false) + .Select(t => t.JobId) + .ToListAsync(); - if (jobsWithAutoControl.Count == 0) + _logger.LogInformation("Найдено объектов с включенным автоконтролем, групп: {JobGroupCount} шт., работ: {JobCount} шт.", jobGroupsIds.Count, jobIds.Count); + + if (jobGroupsIds.Count == 0 && jobIds.Count == 0) return; - // формируем сообщения - var msgList = jobsWithAutoControl.Select(t => new TemplateMatcherMq + var msgList = new List(jobGroupsIds.Count + jobIds.Count); + + var initiator = new HistoryInitiator + { + InitiatorComment = $"Инициатор авто-контроль, периодичность: {_workerSettings.RepeatEvery}", + InitiatorIp = null, + InitiatorParrComponentId = ParrComponentsEnum.JobAutoControl + }; + + // группы + msgList.AddRange(jobGroupsIds.Select(id => new TemplateMatcherMq + { + Action = TemplateMatcherActionEnum.Sync, + EntityType = SyncTaskEntityTypeEnum.JobGroup, + Id = id, + Initiator = initiator + })); + + // работы + msgList.AddRange(jobIds.Select(id => new TemplateMatcherMq { Action = TemplateMatcherActionEnum.Sync, EntityType = SyncTaskEntityTypeEnum.Job, - Id = t.JobId, - Initiator = new HistoryInitiator - { - InitiatorComment = $"Инициатор авто-контроль, периодичность: {workerSettings.RepeatEvery}", - InitiatorIp = null, - InitiatorParrComponentId = ParrComponentsEnum.JobAutoControl - } - }).ToList(); - - //var msgStrList = msgList.Select(t => JsonSerializer.Serialize(t)); + Id = id, + Initiator = initiator + })); // отправляем задания в очередь template matcher`a - //var sendResult = await mqService.SendAsync(mqSettings.TemplateMatcher, msgStrList.ToArray()); - var sendResult = await mqService.SendAsync(mqSettings.TemplateMatcher, msgList.ToList()); + var sendResult = await mqService.SendAsync(_mqSettings.TemplateMatcher, msgList); if (!sendResult.IsSuccess) - logger.LogError($"Ошибка при отправке сообщений ({msgList.Count()} шт.) в очередь."); + _logger.LogError("Ошибка при отправке сообщений ({Count} шт.) в очередь.", msgList.Count); else - logger.LogInformation($"Выполнена отправка сообщений в очередь, {msgList.Count()} шт."); + _logger.LogInformation("Выполнена отправка сообщений в очередь, {Count} шт.", msgList.Count); } }