using AutoMapper; using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using PARR.API.Contracts.V1; using PARR.API.Contracts.V1.Requests; using PARR.API.Contracts.V1.Requests.Queries; using PARR.API.Contracts.V1.Responses; using PARR.API.Contracts.V1.Responses.Base; using PARR.API.Controllers.V1.Base; using PARR.API.Extensions; using PARR.API.Services.Interfaces; using PARR.API.Settings; using PARR.Core.Common.Helpers; using PARR.Core.Common.Interfaces.RabbitServices; using PARR.Core.Extensions; using PARR.Core.Repositories.Interfaces; using PARR.Core.Repositories.Interfaces.JobGroupRepositories; using PARR.Core.Repositories.Interfaces.JobRepositories; using PARR.Core.Repositories.Interfaces.Unit; using PARR.Core.Services.MatchingStatusService; using PARR.Core.Services.UnitFilterService; using PARR.Domain.Common.Pagination; using PARR.Domain.Common.Rabbit.Messages; using PARR.Domain.Common.Roles; using PARR.Domain.Entities.Base.History; using PARR.Domain.Entities.JobEntities; using PARR.Domain.Enums; namespace PARR.API.Controllers.V1 { /// /// Управление работами /// [Authorize(Roles = ParrRoles.Administrator.Role)] public class JobController : BaseApiController { private readonly ILogger _logger; private readonly IMapper _mapper; private readonly IUriService _uriService; private readonly IJobRepository _jobRepository; private readonly ITemplateRepository _templateRepository; private readonly IRabbitService _mqService; private readonly MqSettings _mqSettings; private readonly IClientService _clientService; private readonly IMatchingStatusService _matchingStatusService; public JobController( ILogger logger, IMapper mapper, IUriService uriService, IJobRepository jobRepository, ITemplateRepository templateRepository, IJobGroupRepository jobGroupRepository, IUnitFilterService unitFilterRepository, IUnitRepository unitRepository, IRabbitService mqService, MqSettings mqSettings, IClientService clientService, IMatchingStatusService matchingStatusService ) { _logger = logger; _mapper = mapper; _uriService = uriService; _jobRepository = jobRepository; _templateRepository = templateRepository; _mqService = mqService; _mqSettings = mqSettings; _clientService = clientService; _matchingStatusService = matchingStatusService; } /// /// Получить список заданий на выполнение работ(Job) постранично /// /// [HttpGet(ApiRoutes.Job.GetAll)] public async Task GetAll([FromQuery] PaginationQuery paginationQuery, [FromQuery] JobQuery filter) { var paginationFilter = _mapper.Map(paginationQuery); IQueryable query = _jobRepository.Get() .Include(t => t.AutoControl); query = query.OrderBy(t => t.Name); if (!string.IsNullOrEmpty(filter.Name)) { //query = query.Where(t => t.Name.ToLower().Contains(filter.Name.ToLower())); query = query.Where(t => EF.Functions.Like(t.Name.ToLower(), SqlHelpers.RegexToLike(filter.Name))); } if (filter.GroupId.HasValue) query = query.Where(t => t.GroupId == filter.GroupId.Value); if (filter.TnkId.HasValue) query = query.Where(t => t.TnkId == filter.TnkId.Value); if (filter.IsFull) { query = query .Include(t => t.Tnk) .Include(t => t.Group).ThenInclude(t => t.GroupType) .Include(t => t.Group).ThenInclude(t => t.GroupingUnitField) .Include(t => t.Group).ThenInclude(t => t.AutoControl); query = query .Include(t => t.UnitFilters) .ThenInclude(t => t.FieldFilters) .ThenInclude(t => t.UnitField) .Include(t => t.UnitFilters) .ThenInclude(t => t.RelationshipFilters) .ThenInclude(t => t.UnitField); } var jobs = await _jobRepository.GetPage(query, paginationFilter).ToListAsync(); if (!jobs.Any()) return NoContent(); var response = _mapper.Map>(jobs);//TODO Migration to job foreach (var jobResponse in response) { //jobResponse.TemplatesCount = await templateService.Get().CountAsync(t => t.JobId == jobResponse.Id); jobResponse.TemplatesCount = await GetCountTemplatesAsync(jobResponse.Id); if (filter.IsFull) { jobResponse.MatchingStatus = await GetMatchingStatusAsync(jobResponse.Id); } } var paginationResponse = new PagedResponse(response, true).GetPaginatedProps(paginationFilter, query); return Ok(paginationResponse); } /// /// Получить задание на выполнение работ по id /// /// /// [HttpGet(ApiRoutes.Job.Get)] public async Task GetById([FromRoute] Guid id) { var job = await _jobRepository.Get() .Include(t => t.Tnk) .Include(t => t.Group).ThenInclude(t => t.GroupType) .Include(t => t.Group).ThenInclude(t => t.AutoControl) .Include(t => t.Group).ThenInclude(t => t.GroupingUnitField) .Include(t => t.UnitFilters) .ThenInclude(t => t.FieldFilters) .ThenInclude(t => t.UnitField) .Include(t => t.UnitFilters) .ThenInclude(t => t.RelationshipFilters) .ThenInclude(t => t.UnitField) .Include(t => t.AutoControl) .FirstOrDefaultAsync(t => t.Id == id); if (job == null) return NotFound(); var response = _mapper.Map(job); response.TemplatesCount = await GetCountTemplatesAsync(id); //await templateService.Get().CountAsync(t => t.JobId == id); response.MatchingStatus = await GetMatchingStatusAsync(id); //var statistics = await GetStatisticsAsync(response.Id); //BindStatistics(response, statistics); return Ok(new Response(response, true)); } /// /// Создать задание на выполнение работ (Job) /// /// /// [HttpPost(ApiRoutes.Job.Create)] public async Task Create([FromBody] JobRequest request) { #region Проверка существования работы с такими же настройками связей параметрами if (request.Relationships != null) { //todo: не сильно правильный запрос, в нем проверяем полное совпадение, но не проверяем пересечения var isExistTheSameLinks = await _jobRepository.Get() //.Include(t => t.Group).ThenInclude(t => t.GroupType) .AsNoTracking() .CountAsync(t => //t.Group!.GroupType!.Code == JobGroupTypesEnum.Umbrella //(t.Group!.GroupType!.Code == JobGroupTypesEnum.Umbrella || t.Group!.GroupType!.Code == JobGroupTypesEnum.Group) && t.Group!.GroupType!.IsRelationshipsAllowed && t.GroupId == request.GroupId && (t.MinValueRelationships == request.Relationships.MinValueRelationships || t.MaxValueRelationships == request.Relationships.MaxValueRelationships) ); if (isExistTheSameLinks > 0) return BadRequest(new Response(false, new List { new ErrorModel { FieldName = nameof(request.Name), Message = $"Работа с указанным диапазоном связей пересекается с уже имеющейся в базе данных({request.Relationships.MinValueRelationships}-{request.Relationships.MaxValueRelationships})" } })); } #endregion var job = _mapper.Map(request); if (request.AutoControl != null) { // разрешен автоконтроль или нет, проверил в валидаторе job.AutoControl = new JobAutoControl { JobId = job.Id, IsEnable = request.AutoControl.IsEnable, InitUsedScheduleState = request.AutoControl.InitUsedScheduleState, InitUsedTemplateState = request.AutoControl.InitUsedTemplateState }; } if (!await _jobRepository.CreateAsync(job) || !await _jobRepository.CommitAsync()) return BadRequest(new Response(false, new List { new ErrorModel { Message = "Ошибка при созании задания на выполнение работ" } })); _logger.LogInformation($"Пользователь {User.Identity?.Name} добавил задание на выполнение работ: {job.Id}, {job.Name}, {job.WorkName}"); var createdJob = await _jobRepository.Get() .Include(t => t.Tnk) .Include(t => t.Group).ThenInclude(t => t.GroupType) .Include(t => t.Group).ThenInclude(t => t.AutoControl) .Include(t => t.Group).ThenInclude(t => t.GroupingUnitField) .Include(t => t.UnitFilters) .ThenInclude(t => t.FieldFilters) .ThenInclude(t => t.UnitField) .Include(t => t.UnitFilters) .ThenInclude(t => t.RelationshipFilters) .Include(t => t.AutoControl) .FirstOrDefaultAsync(t => t.Id == job.Id); var locationUri = _uriService.GetUri(ApiRoutes.Job.Get, ApiRoutes.Job.getParam, createdJob!.Id); var response = _mapper.Map(createdJob); // так как мы только что создали Job, то у него нет шаблонов, смело ставим = 0 (ускоряем запрос) response.TemplatesCount = 0; response.MatchingStatus = await GetMatchingStatusAsync(response.Id); return Created(locationUri, new Response(response, true)); } /// /// Обновить задание на выполнение работ (Job) /// /// /// /// [HttpPut(ApiRoutes.Job.Update)] public async Task Update([FromRoute] Guid id, [FromBody] JobRequest request) { var orig = await _jobRepository.Get() .Include(t => t.Tnk) .Include(t => t.Group).ThenInclude(t => t.GroupType) .Include(t => t.Group).ThenInclude(t => t.GroupingUnitField) .Include(t => t.UnitFilters) .ThenInclude(t => t.FieldFilters) .Include(t => t.UnitFilters) .ThenInclude(t => t.RelationshipFilters) .Include(t => t.AutoControl) .AsSingleQuery() .FirstOrDefaultAsync(t => t.Id == id); if (orig == null) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при изменении задания на выполнение работ. Не найдено задание с Id: {id}" } })); // если изменили маску шаблона, ниже отправим в очередь, на изменение связанных имен шаблонов var isChangedTemplateNameMask = orig.TemplateNameMask != request.TemplateNameMask.Trim(); //TODO: ВОТ ЭТО ВООБЩЕ МЫ БУДЕМ ПРОВЕРЯТЬ, АААА???? - Проверка существования работы с такими же параметрами #region обновление полей задания на работу orig.Name = request.Name.Trim(); orig.WorkName = request.WorkName.Trim(); orig.MinValueRelationships = request.Relationships?.MinValueRelationships; orig.MaxValueRelationships = request.Relationships?.MaxValueRelationships; orig.IsParentRelationships = request.Relationships?.IsParentRelationships; orig.TemplateNameMask = request.TemplateNameMask.Trim(); orig.WorkGroupMask = request.WorkGroupMask.Trim(); orig.TnkId = request.TnkId; orig.GroupId = request.GroupId; orig.ResponseAreaMask = request.ResponseAreaMask.Trim(); #region Настройки автоконтроля // валидатор проверяет корректность if (request.AutoControl != null) { // Создаем новую запись или обновляем существующую if (orig.AutoControl != null) { orig.AutoControl.IsEnable = request.AutoControl.IsEnable; orig.AutoControl.InitUsedScheduleState = request.AutoControl.InitUsedScheduleState; orig.AutoControl.InitUsedTemplateState = request.AutoControl.InitUsedTemplateState; } else { orig.AutoControl = new JobAutoControl { JobId = id, InitUsedScheduleState = request.AutoControl.InitUsedScheduleState, InitUsedTemplateState = request.AutoControl.InitUsedTemplateState, IsEnable = request.AutoControl.IsEnable }; } } else { // Удаляем настройки, скорей всего автоконтролем управляет группа работ orig.AutoControl = null; } #endregion orig.DateModified = DateTimeOffset.UtcNow; #endregion var job = _mapper.Map(request); job.Id = id;//На всякий. Пусть будет для чистоты UpdateUnitFilters(orig, job);//Обновление вложенных дочерних элементов-фильтров if (!await _jobRepository.CommitAsync()) return BadRequest(new Response(false, new List { new ErrorModel { Message = "Ошибка при изменении задания на выполнение работ." } })); // если изменили маску, отправим задание на переименование связанных шаблонов if (isChangedTemplateNameMask) { var mqResult = await SendRequestToUpdateTemplates(orig); //todo: если ошибка. пользователя не предупреждаем... возможно ему это и не нужно знать...ну не переименуются шаблоны, может они переименуются позже... } _logger.LogInformation($"Пользователь {User.Identity?.Name} обновил задание на выполнение работ: {orig.Id}," + $" {orig.Name}, {orig.WorkName}, {orig.MinValueRelationships}, {orig.MaxValueRelationships}," + $" {orig.TemplateNameMask}, {orig.TnkId}, {nameof(orig.GroupId)}"); var updatedJob = await _jobRepository.Get() .Include(t => t.Tnk) .Include(t => t.Group).ThenInclude(t => t.GroupType) .Include(t => t.Group).ThenInclude(t => t.AutoControl) .Include(t => t.Group).ThenInclude(t => t.GroupingUnitField) .Include(t => t.UnitFilters) .ThenInclude(t => t.FieldFilters) .ThenInclude(t => t.UnitField) .Include(t => t.UnitFilters) .ThenInclude(t => t.RelationshipFilters) .Include(t => t.AutoControl) .FirstAsync(t => t.Id == orig.Id); var response = _mapper.Map(updatedJob); response.TemplatesCount = await GetCountTemplatesAsync(id); //await templateService.Get().CountAsync(t => t.JobId == id); response.MatchingStatus = await GetMatchingStatusAsync(id); //не используется //var statistics = await GetStatisticsAsync(response.Id); //BindStatistics(response, statistics); return Ok(new Response(response, true)); } private void UpdateUnitFilters(Job orig, Job mappedRequest) { orig.UnitFilters.Clear(); foreach (var mappedUnitFilter in mappedRequest.UnitFilters) { var newUnitFilter = new JobUnitFilter { UnitFilter = mappedUnitFilter.UnitFilter, DateCreated = DateTimeOffset.UtcNow, JobId = orig.Id }; foreach (var newRequestFieldFilter in mappedUnitFilter.FieldFilters) { var newFieldFilter = CreateFieldFilter(newUnitFilter.Id, newRequestFieldFilter.FieldId, newRequestFieldFilter.ValueMask, newRequestFieldFilter.IsInverse); newUnitFilter.FieldFilters.Add(newFieldFilter); } foreach (var newRequestRelationshipFilter in mappedUnitFilter.RelationshipFilters) { var newRelationshipFilter = CreateRelationshipFilter(newRequestRelationshipFilter.FieldId, newRequestRelationshipFilter.IsParent, newRequestRelationshipFilter.IsFullMatch, newRequestRelationshipFilter.IsInverse, newRequestRelationshipFilter.ValueMask); newUnitFilter.RelationshipFilters.Add(newRelationshipFilter); } orig.UnitFilters.Add(newUnitFilter); } #region old /* Писал полноценный апдейт, но Миша сказал что нахрен это - просто все удаляем, а потом создаём заново... Ох уж этот Миша... //Сразу удаляем UnitFilter которых нет var toDelete = orig.UnitFilters.Where(t => !mappedRequest.UnitFilters.Any(e => e.Id == t.Id)); foreach (var item in toDelete) orig.UnitFilters.Remove(item); foreach (var mappedUnitFilter in mappedRequest.UnitFilters) { var origUnitFilter = orig.UnitFilters.FirstOrDefault(t => t.Id == mappedUnitFilter.Id); if (origUnitFilter == null) { var newUnitFilter = new JobUnitFilter { UnitFilter = mappedUnitFilter.UnitFilter, DateCreated = DateTimeOffset.UtcNow, JobId = orig.Id }; foreach (var newRequestFieldFilter in mappedUnitFilter.FieldFilters) { var newFieldFilter = CreateFieldFilter(newUnitFilter.Id, newRequestFieldFilter.FieldId, newRequestFieldFilter.ValueMask); newUnitFilter.FieldFilters.Add(newFieldFilter); } foreach (var newRequestRelationshipFilter in mappedUnitFilter.RelationshipFilters) { var newRelationshipFilter = CreateRelationshipFilter(newRequestRelationshipFilter.FieldId, newRequestRelationshipFilter.IsParent, newRequestRelationshipFilter.IsFullMatch, newRequestRelationshipFilter.IsInverse, newRequestRelationshipFilter.ValueMask); newUnitFilter.RelationshipFilters.Add(newRelationshipFilter); } orig.UnitFilters.Add(newUnitFilter); } else { #region Непосредственно UnitFilter if (origUnitFilter.UnitFilter != mappedUnitFilter.UnitFilter) { origUnitFilter.UnitFilter = mappedUnitFilter.UnitFilter; origUnitFilter.DateModified = DateTimeOffset.UtcNow; } #endregion #region FieldFilters //Сразу удаляем неактуальные var fieldFiltersToDelete = origUnitFilter.FieldFilters.Where(t => !mappedUnitFilter.FieldFilters.Any(m => m.FieldId == t.FieldId)); foreach (var item in fieldFiltersToDelete) origUnitFilter.FieldFilters.Remove(item); //Перебираем FieldFilter foreach (var mappedFieldFilter in mappedUnitFilter.FieldFilters) { var origFieldFilter = origUnitFilter.FieldFilters.FirstOrDefault(t => t.FieldId == mappedFieldFilter.FieldId); if (origFieldFilter == null)//Нет в БД? не проблема - создадим { var newFieldFilter = CreateFieldFilter(origUnitFilter.Id, mappedFieldFilter.FieldId, mappedFieldFilter.ValueMask); origUnitFilter.FieldFilters.Add(newFieldFilter); } else { if (origFieldFilter.ValueMask != mappedFieldFilter.ValueMask) { origFieldFilter.ValueMask = mappedFieldFilter.ValueMask; origFieldFilter.DateModified = DateTimeOffset.UtcNow; } } } #endregion #region RelationshipFilters //Сразу удаляем неактуальные var relationshipFiltersToDelete = origUnitFilter.RelationshipFilters.Where(t => !mappedUnitFilter.RelationshipFilters.Any(m => m.FieldId == t.FieldId)); foreach (var item in relationshipFiltersToDelete) origUnitFilter.RelationshipFilters.Remove(item); //Перебираем RelationshipFilters foreach (var mappedRelationshipFilter in mappedUnitFilter.RelationshipFilters) { var origRelationshipFilter = origUnitFilter.RelationshipFilters.FirstOrDefault(t => t.FieldId == mappedRelationshipFilter.FieldId); if (origRelationshipFilter == null)//Нет в БД? не проблема - создадим { var newRelationshipFilter = CreateRelationshipFilter(mappedRelationshipFilter.FieldId, mappedRelationshipFilter.IsParent, mappedRelationshipFilter.IsFullMatch, mappedRelationshipFilter.IsInverse, mappedRelationshipFilter.ValueMask); origUnitFilter.RelationshipFilters.Add(newRelationshipFilter); } else { if (origRelationshipFilter.IsParent != mappedRelationshipFilter.IsParent) origRelationshipFilter.IsParent = mappedRelationshipFilter.IsParent; if (origRelationshipFilter.ValueMask != mappedRelationshipFilter.ValueMask) origRelationshipFilter.ValueMask = mappedRelationshipFilter.ValueMask; if (origRelationshipFilter.IsFullMatch != mappedRelationshipFilter.IsFullMatch) origRelationshipFilter.IsFullMatch = mappedRelationshipFilter.IsFullMatch; if (origRelationshipFilter.IsInverse != mappedRelationshipFilter.IsInverse) origRelationshipFilter.IsInverse = mappedRelationshipFilter.IsInverse; } } #endregion } }*/ #endregion } private static JobRelationshipFilter CreateRelationshipFilter(Guid fieldId, bool isParent, bool isFullMatch, bool isInverse, string valueMask) { return new JobRelationshipFilter { FieldId = fieldId, IsParent = isParent, IsFullMatch = isFullMatch, IsInverse = isInverse, ValueMask = valueMask }; } private static JobFieldFilter CreateFieldFilter(Guid unitFilterId, Guid fieldId, string valueMask, bool isInverse) { return new JobFieldFilter { UnitFilterId = unitFilterId, DateCreated = DateTimeOffset.UtcNow, FieldId = fieldId, ValueMask = valueMask, IsInverse = isInverse }; } /// /// Удалить задание на выполнение работ (только если нет связанных шаблонов) /// /// /// [HttpDelete(ApiRoutes.Job.Delete)] public async Task Delete([FromRoute] Guid id) { var job = await _jobRepository.Get().Include(t => t.Tnk) .FirstOrDefaultAsync(t => t.Id == id); if (job == null) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении задания на выполнение работ. Не найдено задание на выполнение работ Id: {id}" } })); var templateCount = await _templateRepository.Get().CountAsync(t => t.JobId == id); if (templateCount > 0) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении задания на выполнение работ. С данным заданием связаны шаблоны: {templateCount} шт." } })); if (!_jobRepository.Delete(job) || !await _jobRepository.CommitAsync()) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении задания на выполнение работ" } })); _logger.LogInformation($"Пользователь {User.Identity?.Name} удалил задание на выполнение работ: {job.Id},{job.Name}," + $" {job.WorkName}, {job.MinValueRelationships}, {job.MaxValueRelationships}," + $" {job.TemplateNameMask}, {job.TnkId}, {job.GroupId}"); return NoContent(); } /// /// Загрузка статистики /// /// /// private async Task GetStatisticsAsync(Guid jobId) { var statResult = await _jobRepository.Get() .Include(t => t.Templates) .ThenInclude(t => t.RobotConfigurations) .Where(x => x.Id == jobId) .Select(t => new { TemplateActivated = t.Templates.Count(x => x.IsActiveTemplate), TemplateSynchronized = t.Templates.Count(x => x.RobotConfigurations.Any(c => c.TaskStatusCode == (int)TaskStatusEnum.Ok && c.RobotCode == (int)RobotsEnum.TemplateOrder)), TemplateErrors = t.Templates.Count(x => x.RobotConfigurations.Any(c => c.RobotStatusCode == (int)RobotStatusEnum.Error && c.RobotCode == (int)RobotsEnum.TemplateOrder)), ScheduleActivated = t.Templates.Count(x => x.IsActiveSchedule), ScheduleSynchronized = t.Templates.Count(x => x.RobotConfigurations.Any(c => c.TaskStatusCode == (int)TaskStatusEnum.Ok && c.RobotCode == (int)RobotsEnum.ScheduleOrder)), ScheduleErrors = t.Templates.Count(x => x.RobotConfigurations.Any(c => c.RobotStatusCode == (int)RobotStatusEnum.Error && c.RobotCode == (int)RobotsEnum.ScheduleOrder)) }).FirstOrDefaultAsync(); return new JobStatModel { ScheduleStatistics = new ScheduleStats { Activated = statResult?.ScheduleActivated ?? 0, Errors = statResult?.ScheduleErrors ?? 0, Synchronized = statResult?.ScheduleSynchronized ?? 0 }, TemplateStatistics = new TemplateStats { Activated = statResult?.TemplateActivated ?? 0, Errors = statResult?.TemplateErrors ?? 0, Synchronized = statResult?.TemplateSynchronized ?? 0 } }; } private void BindStatistics(JobResponse job, JobStatModel statistics) { //job.TemplateStatistics = new TemplateStats { Activated = statistics.TemplateStatistics.Activated, Errors = statistics.TemplateStatistics.Errors, Synchronized = statistics.TemplateStatistics.Synchronized }; //job.ScheduleStatistics = new ScheduleStats { Activated = statistics.ScheduleStatistics.Activated, Errors = statistics.ScheduleStatistics.Errors, Synchronized = statistics.ScheduleStatistics.Synchronized }; } private async Task SendRequestToUpdateTemplates(Job job) { if (job.Group?.GroupType == null) throw new ArgumentException("Не хватает include для job.Group.GroupType", nameof(job.Group.GroupType)); Guid Id = default; SyncTaskEntityTypeEnum EntityType = default; // Смотрим кто управляет автоконтролем, и какой объект можно синхронизировать if (job.Group.GroupType.IsJobGroupAutoControl) { // Управляет JobGroup Id = job.GroupId; EntityType = SyncTaskEntityTypeEnum.JobGroup; } else { // Управляет Job Id = job.Id; EntityType = SyncTaskEntityTypeEnum.Job; } var request = new TemplateMatcherMq { Id = Id, EntityType = EntityType, Action = TemplateMatcherActionEnum.Update, Initiator = new HistoryInitiator { InitiatorIp = _clientService.GetClientIp()?.ToString(), InitiatorParrComponentId = ParrComponentsEnum.Api, InitiatorComment = $"В GUI изменено имя шаблона, при сохранении Job отправлен запрос на обновление связанных шаблонов" } }; var result = await _mqService.SendAsync(_mqSettings.TemplatesMatcher, new List { request }); _logger.LogDebug("Получен код отпрвки: {IsSuccess}", result.IsSuccess); if (!result.IsSuccess) { _logger.LogError($"Ошибка при отправке запроса в очередь на обновление связанных шаблонов, после обновления маски шаблона. {request.ToJson()}"); return false; } _logger.LogInformation($"После изменения маски шаблона в jobId: {job.Id}, отправлен запрос в очередь на переименование связанных шаблонов: {request.ToJson()}"); return true; } /// /// Получить кол-во шаблонов в Job /// /// /// private async Task GetCountTemplatesAsync(Guid jobId) { // Получаем только шаблоны в статусе used return await _templateRepository.Get().CountAsync(t => t.JobId == jobId && t.StatusTypeId == TemplateStatusTypeEnum.Used); } /// /// Получить статус matching`a /// /// /// private async Task GetMatchingStatusAsync(Guid jobId) { var statusMatching = await _matchingStatusService.GetStatusAsync(jobId, SyncTaskEntityTypeEnum.Job); return _mapper.Map(statusMatching); } } public class JobStatModel { public required TemplateStats TemplateStatistics { get; set; } public required ScheduleStats ScheduleStatistics { get; set; } } }