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.BLL.Domain.Mq; using PARR.BLL.Services.Interfaces; using PARR.Constants; using PARR.DAL.Contracts; using PARR.DAL.DomainModels; using PARR.DAL.Models; using PARR.DAL.Services.Interfaces; using System.Text.Json; using static PARR.API.Contracts.V1.ApiRoutes; namespace PARR.API.Controllers.V1 { [Authorize(Roles = ParrRoles.Administrator.Role)] public class ApplicationInWorkController : BaseApiController { private readonly ILogger logger; private readonly IMapper mapper; private readonly IApplicationsInWorkService applicationsInWorkService; private readonly IValidator validator; private readonly IUriService uriService; private readonly ITemplateService templateService; private readonly IMqService mqService; private readonly MqSettings mqSettings; public ApplicationInWorkController( ILogger logger, IMapper mapper, IApplicationsInWorkService applicationsInWorkService, IValidator validator, IUriService uriService, ITemplateService templateService, IMqService mqService, MqSettings mqSettings ) { this.logger = logger; this.mapper = mapper; this.applicationsInWorkService = applicationsInWorkService; this.validator = validator; this.uriService = uriService; this.templateService = templateService; this.mqService = mqService; this.mqSettings = mqSettings; } /// /// Получить список заданий на выполнение работ(ApplicationInWork) постранично /// /// //[HttpGet(ApiRoutes.Job.GetAll)] //public async Task GetAll([FromQuery] PaginationQuery paginationQuery, [FromQuery] ApplicationInWorkQuery filter) //{ // var paginationFilter = mapper.Map(paginationQuery); // IQueryable query = applicationsInWorkService.Get().Include(t => t.Application).ThenInclude(t => t!.ApplicationType); // if (filter.IsLight != true) // { // query = query // .Include(t => t.Work) // //.Include(t => t.Templates) - большой запрос, делаем его отдельно // .Include(t => t.WorkGroups).ThenInclude(t => t.WorkGroup) // .Include(t => t.JobAutoControl); // } // query = query.OrderBy(t => t.ShortDescription).ThenBy(t => t.Application!.Name); // if (!string.IsNullOrEmpty(filter.ShortDescription)) // query = query.Where(t => t.ShortDescription.ToLower().Contains(filter.ShortDescription.ToLower())); // if (filter.WorkId.HasValue) // query = query.Where(t => t.WorkId == filter.WorkId.Value); // var appInWorks = await applicationsInWorkService.GetPage(query, paginationFilter).ToListAsync(); // if (!appInWorks.Any()) // return NoContent(); // var response = mapper.Map>(appInWorks);//TODO Migration to job // if (filter.IsLight != true) // { // //Если запрос не легкий, загружаем кол-во шаблонов отдельно, это значительно ускоряет запрос // foreach (var item in response) // { // item.TemplatesCount = await templateService.Get().CountAsync(t => t.JobId == item.Id); // var statistics = await GetStatisticsAsync(item.Id); // BindStatistics(item, statistics); // } // } // 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 applicationInWork = await applicationsInWorkService.Get() // .Include(t => t.Application).ThenInclude(t => t!.ApplicationType) // .Include(t => t.Work) // //.Include(t => t.Templates) // .Include(t => t.WorkGroups).ThenInclude(t => t.WorkGroup) // .Include(t => t.JobAutoControl) // .FirstOrDefaultAsync(t => t.Id == id); // if (applicationInWork == null) // return NotFound(); // var response = mapper.Map(applicationInWork); // response.TemplatesCount = await templateService.Get().CountAsync(t => t.ApplicationInWorkId == id); // var statistics = await GetStatisticsAsync(response.Id); // BindStatistics(response, statistics); // return Ok(new Response(response, true)); //} /// /// Создать задание на выполнение работ (ApplicationInWork) /// /// /// //[HttpPost(ApiRoutes.Job.Create)] //public async Task Create([FromBody] ApplicationInWorkRequest request) //{ // var resultValidate = await validator.ValidateAsync(request); // if (!resultValidate.IsValid) // return BadRequest(new Response(resultValidate.Errors)); // //уникальная запись по полям ApplicationId, WorkId // var existSameAiW = await applicationsInWorkService.GetAsync(request.ApplicationId, request.WorkId); // if (existSameAiW != null) // return BadRequest(new Response(false, new List { new ErrorModel { Message = "Задание на выполнение работ с такими ПО и работой уже существует" } })); // var applicationInWork = new ApplicationsInWork // { // Id = Guid.NewGuid(), // WorkId = request.WorkId, // ApplicationId = request.ApplicationId, // TemplateDuration = request.TemplateDuration, // ShortDescription = request.ShortDescription.Trim(), // FullDescription = request.FullDescription.Trim(), // Solution = request.Solution.Trim(), // //NextRun = request.NextRun, // IsAutoDistributionEnabled = request.IsAutoDistributionEnabled, // ReferenceDate = request.ReferenceDate, // IsAgent = request.IsAgent, // AgentName = request.AgentName?.Trim(), // AgentTimeOutSec = request.AgentTimeOutSec, // AgentScript = request.AgentScript?.Trim() // }; // //Добавляем настройки планировщика // request.Schedule.ForEach(item => // { // applicationInWork.EsppSchValues.Add(new EsppSchValue // { // ApplicationsInWorkId = applicationInWork.Id, // TypeConfigId = item.TypeConfigId, // TypeValueId = item.TypeValueId // }); // }); // //Добавляем рабочие группы // request.WorkGroups.ForEach(workGroupId => // { // applicationInWork.WorkGroups.Add(new AppInWorkInWorkGroup // { // ApplicationsInWorkId = applicationInWork.Id, // WorkGroupId = workGroupId // }); // }); // if (!await applicationsInWorkService.CreateAsync(applicationInWork) || !await applicationsInWorkService.CommitAsync()) // return BadRequest(new Response(false, new List { new ErrorModel { Message = "Ошибка при созании задания на выполнение работ" } })); // logger.LogInformation($"Пользователь {User.Identity?.Name} добавил задание на выполнение работ: {applicationInWork.Id}, {applicationInWork.ShortDescription}, {applicationInWork.Solution}"); // var createdApplicationInWork = await applicationsInWorkService.Get() // .Include(t => t.Application).ThenInclude(t => t!.ApplicationType) // .Include(t => t.Work) // //.Include(t => t.Templates) // .Include(t => t.WorkGroups).ThenInclude(t => t.WorkGroup) // .Include(t => t.JobAutoControl) // .FirstAsync(t => t.Id == applicationInWork.Id); // var locationUri = uriService.GetUri(ApiRoutes.Job.Get, ApiRoutes.Job.getParam, createdApplicationInWork.Id); // var response = mapper.Map(createdApplicationInWork); // // так как мы только что создали AppInW, то у него нет шаблонов, смело ставим = 0 (ускоряем запрос) // response.TemplatesCount = 0; // return Created(locationUri, new Response(response, true)); //} /// /// Обновить задание на выполнение работ (ApplicationInWork) /// /// /// /// //[HttpPut(ApiRoutes.Job.Update)] //public async Task Update([FromRoute] Guid id, [FromBody] ApplicationInWorkRequest request) //{ // var resultValidate = await validator.ValidateAsync(request); // if (!resultValidate.IsValid) // return BadRequest(new Response(resultValidate.Errors)); // //уникальная запись по полям ApplicationId, WorkId у которой id!=[FromRoute]id // var existSameAiW = await applicationsInWorkService.Get() // .FirstOrDefaultAsync(t => t.Id != id && t.ApplicationId == request.ApplicationId && t.WorkId == request.WorkId); // if (existSameAiW != null) // return BadRequest(new Response(false, new List { new ErrorModel { Message = "Задание на выполнение работ с такими ПО и работой уже существует" } })); // var orig = await applicationsInWorkService.Get() // .Include(t => t.WorkGroups) // .Include(t => t.EsppSchValues) // .FirstOrDefaultAsync(t => t.Id == id); // if (orig == null) // return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при изменении задания на выполнение работ. Не найдено задание с Id: {id}" } })); // //Расписание было изменено, ниже добавим задание в очередь на обновление расписаний у связанных шаблонов // var isScheduleChanged = IsScheduleChanged(orig, request); // orig.WorkId = request.WorkId; // orig.ApplicationId = request.ApplicationId; // orig.TemplateDuration = request.TemplateDuration; // orig.ShortDescription = request.ShortDescription.Trim(); // orig.FullDescription = request.FullDescription.Trim(); // orig.Solution = request.Solution.Trim(); // //orig.NextRun = request.NextRun; // orig.ReferenceDate = request.ReferenceDate; // orig.IsAutoDistributionEnabled = request.IsAutoDistributionEnabled; // orig.IsAgent = request.IsAgent; // orig.AgentName = request.AgentName?.Trim(); // orig.AgentTimeOutSec = request.AgentTimeOutSec; // orig.AgentScript = request.AgentScript?.Trim(); // orig.DateModified = DateTimeOffset.UtcNow; // //обновляем планировщик // orig.EsppSchValues.Clear(); // request.Schedule.ForEach(item => // { // orig.EsppSchValues.Add(new EsppSchValue // { // ApplicationsInWorkId = orig.Id, // TypeConfigId = item.TypeConfigId, // TypeValueId = item.TypeValueId // }); // }); // //Обновляем рабочие группы // orig.WorkGroups.Clear(); // request.WorkGroups.ForEach(workGroupId => // { // orig.WorkGroups.Add(new AppInWorkInWorkGroup // { // ApplicationsInWorkId = orig.Id, // WorkGroupId = workGroupId // }); // }); // if (!await applicationsInWorkService.CommitAsync()) // return BadRequest(new Response(false, new List { new ErrorModel { Message = "Ошибка при изменении задания на выполнение работ." } })); // logger.LogInformation($"Пользователь {User.Identity?.Name} обновил задание на выполнение работ: {orig.Id}," + // $" {orig.WorkId}, {orig.ApplicationId}, {orig.TemplateDuration}, {orig.ShortDescription}," + // $" {orig.FullDescription}, {orig.Solution}, {nameof(orig.IsAutoDistributionEnabled)}: {orig.IsAutoDistributionEnabled}, {orig.IsAgent}, {orig.AgentName}, {orig.AgentTimeOutSec}, {orig.AgentScript}"); // if (isScheduleChanged) // { // //расписание было обновлено, отправим задание в очередь на перерасчет NextRun // var requestToMq = new TemplateDistributorMq // { // ApplicationInWorkId = id // }; // var msg = JsonSerializer.Serialize(requestToMq); // logger.LogDebug($"Расписание в РР applicationInWorkId: {id} было изменено. Отправляем задание в очередь на перерасчет NextRun"); // var sendResult = mqService.Send(mqSettings.TemplateDistributor, new[] { msg }); // if (sendResult.IsSuccess) // logger.LogInformation($"Задание на перерасчет NextRun успешно отправлено в очередь MQ {mqSettings.TemplateDistributor.QueueName}"); // else // logger.LogError($"Ошибка при отправке задания на перерасчет NextRun в очередь MQ {mqSettings.TemplateDistributor.QueueName}"); // } // var updatedApplicationInWork = await applicationsInWorkService.Get() // .Include(t => t.Application).ThenInclude(t => t!.ApplicationType) // .Include(t => t.Work) // //.Include(t => t.Templates) // .Include(t => t.WorkGroups).ThenInclude(t => t.WorkGroup) // .Include(t => t.JobAutoControl) // .FirstAsync(t => t.Id == orig.Id); // var response = mapper.Map(updatedApplicationInWork); // response.TemplatesCount = await templateService.Get().CountAsync(t => t.ApplicationInWorkId == id); // var statistics = await GetStatisticsAsync(response.Id); // BindStatistics(response, statistics); // return Ok(new Response(response, true)); //} /// /// Удалить задание на выполнение работ (только если нет связанных шаблонов) /// /// /// //[HttpDelete(ApiRoutes.Job.Delete)] //public async Task Delete([FromRoute] Guid id) //{ // var applicationsInWork = await applicationsInWorkService.Get() // //.Include(t => t.Templates) // .FirstOrDefaultAsync(t => t.Id == id); // if (applicationsInWork == null) // return BadRequest(new Response(false, new List { new ErrorModel { // Message = $"Ошибка при удалении задания на выполнение работ. Не найдено задание на выполнение работ Id: {id}" // } })); // var templateCount = await templateService.Get().CountAsync(t => t.ApplicationInWorkId == id); // //if (applicationsInWork.Templates.Any()) // if (templateCount > 0) // return BadRequest(new Response(false, new List { new ErrorModel { // Message = $"Ошибка при удалении задания на выполнение работ. С данным заданием связаны шаблоны: {templateCount} шт." // } })); // if (!applicationsInWorkService.Delete(applicationsInWork) || !await applicationsInWorkService.CommitAsync()) // return BadRequest(new Response(false, new List { new ErrorModel { // Message = $"Ошибка при удалении задания на выполнение работ" // } })); // logger.LogInformation($"Пользователь {User.Identity?.Name} удалил задание на выполнение работ: {applicationsInWork.Id},{applicationsInWork.WorkId}," + // $" {applicationsInWork.ApplicationId}, {applicationsInWork.TemplateDuration}, {applicationsInWork.ShortDescription}," + // $" {applicationsInWork.FullDescription}, {applicationsInWork.Solution}, {nameof(applicationsInWork.IsAutoDistributionEnabled)}: {applicationsInWork.IsAutoDistributionEnabled}, {applicationsInWork.IsAgent}," + // $" {applicationsInWork.AgentName}, {applicationsInWork.AgentTimeOutSec}, {applicationsInWork.AgentScript}"); // return NoContent(); //} /// /// Проверка, были ли изменения в расписании /// /// /// /// //private bool IsScheduleChanged(ApplicationsInWork orig, ApplicationInWorkRequest request) //{ // var isScheduleChanged = false; // if (orig.ReferenceDate != request.ReferenceDate) // isScheduleChanged = true; // if (request.Schedule.Count() != orig.EsppSchValues.Count()) // isScheduleChanged = true; // if (request.IsAutoDistributionEnabled != orig.IsAutoDistributionEnabled) // isScheduleChanged = true; // request.Schedule.ForEach(requestSchedule => // { // var schExist = orig.EsppSchValues.FirstOrDefault(t => t.ApplicationsInWorkId == orig.Id // && t.TypeValueId == requestSchedule.TypeValueId // && t.TypeConfigId == requestSchedule.TypeConfigId); // if (schExist == null) // isScheduleChanged = true; // }); // return isScheduleChanged; //} /// /// Загрузка статистики /// /// /// //private async Task GetStatisticsAsync(Guid applicationInWorkId) //{ // var statResult = await applicationsInWorkService.Get() // .Include(t => t.Templates).ThenInclude(t => t.RobotConfigurations) // .Where(x => x.Id == applicationInWorkId) // .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 AppInWorkStatModel // { // 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(ApplicationInWorkResponse applicationsInWork, AppInWorkStatModel statistics) { applicationsInWork.TemplateStatistics = new TemplateStats { Activated = statistics.TemplateStatistics.Activated, Errors = statistics.TemplateStatistics.Errors, Synchronized = statistics.TemplateStatistics.Synchronized }; applicationsInWork.ScheduleStatistics = new ScheduleStats { Activated = statistics.ScheduleStatistics.Activated, Errors = statistics.ScheduleStatistics.Errors, Synchronized = statistics.ScheduleStatistics.Synchronized }; } } public class AppInWorkStatModel { public TemplateStats TemplateStatistics { get; set; } public ScheduleStats ScheduleStatistics { get; set; } } }