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.Constants; using PARR.DAL.Contracts; using PARR.DAL.DomainModels; using PARR.DAL.Models.Job; using PARR.DAL.Services.Interfaces; using PARR.DAL.Services.Interfaces.Job; 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 IJobService jobService; private readonly ITemplateService templateService; private readonly IValidator validator; public JobController( ILogger logger, IMapper mapper, IUriService uriService, IJobService jobService, ITemplateService templateService, IValidator validator ) { this.logger = logger; this.mapper = mapper; this.uriService = uriService; this.jobService = jobService; this.templateService = templateService; this.validator = validator; } /// /// Получить список заданий на выполнение работ(Job) постранично /// /// [HttpGet(ApiRoutes.Job.GetAll)] public async Task GetAll([FromQuery] PaginationQuery paginationQuery, [FromQuery] JobQuery filter) { var paginationFilter = mapper.Map(paginationQuery); IQueryable query = jobService.Get(); query = query.OrderBy(t => t.Name); if (!string.IsNullOrEmpty(filter.Name)) query = query.Where(t => t.Name.ToLower().Contains(filter.Name.ToLower())); if (filter.GroupId.HasValue) query = query.Where(t => t.GroupId == filter.GroupId.Value); if (filter.IsFull) { query = query .Include(t => t.Tnk) .Include(t => t.Group); query = query .Include(t => t.UnitFilters) .ThenInclude(t => t.FieldFilters) .ThenInclude(t => t.Field) .Include(t => t.UnitFilters) .ThenInclude(t => t.RelationshipFilters) .ThenInclude(t => t.UnitField); } var jobs = await jobService.GetPage(query, paginationFilter).ToListAsync(); if (!jobs.Any()) return NoContent(); var response = mapper.Map>(jobs);//TODO Migration to job 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 jobService.Get() .Include(t => t.Tnk) .Include(t => t.Group) .Include(t => t.UnitFilters) .ThenInclude(t => t.FieldFilters) .ThenInclude(t => t.Field) .Include(t => t.UnitFilters) .ThenInclude(t => t.RelationshipFilters) .FirstOrDefaultAsync(t => t.Id == id); if (job == null) return NotFound(); var response = mapper.Map(job); response.TemplatesCount = await templateService.Get().CountAsync(t => t.JobId == 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) { var resultValidate = await validator.ValidateAsync(request); if (!resultValidate.IsValid) return BadRequest(new Response(resultValidate.Errors)); var job = new Job { Id = Guid.NewGuid(), Name = request.Name.Trim(), WorkName = request.WorkName.Trim(), MinValueRelationships = request.MinValueRelationships, MaxValueRelationships = request.MaxValueRelationships, isParentRelationships = request.isParentRelationships, TemplateNameMask = request.TemplateNameMask.Trim(), WorkGroupMask = request.WorkGroupMask.Trim(), TnkId = request.TnkId, GroupId = request.GroupId }; if (!await jobService.CreateAsync(job) || !await jobService.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 jobService.Get().Include(t => t.Tnk) .FirstAsync(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; return Created(locationUri, new Response(response, true)); } /// /// Обновить задание на выполнение работ (Job) /// /// /// /// [HttpPut(ApiRoutes.Job.Update)] public async Task Update([FromRoute] Guid id, [FromBody] JobRequest request) { var resultValidate = await validator.ValidateAsync(request); if (!resultValidate.IsValid) return BadRequest(new Response(resultValidate.Errors)); var orig = await jobService.Get().Include(t => t.Tnk) .FirstOrDefaultAsync(t => t.Id == id); if (orig == null) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при изменении задания на выполнение работ. Не найдено задание с Id: {id}" } })); orig.Name = request.Name.Trim(); orig.WorkName = request.WorkName.Trim(); orig.MinValueRelationships = request.MinValueRelationships; orig.MaxValueRelationships = request.MaxValueRelationships; orig.isParentRelationships = request.isParentRelationships; orig.TemplateNameMask = request.TemplateNameMask.Trim(); orig.WorkGroupMask = request.WorkGroupMask.Trim(); orig.TnkId = request.TnkId; orig.GroupId = request.GroupId; if (!await jobService.CommitAsync()) return BadRequest(new Response(false, new List { new ErrorModel { Message = "Ошибка при изменении задания на выполнение работ." } })); logger.LogInformation($"Пользователь {User.Identity?.Name} обновил задание на выполнение работ: {orig.Id}," + $" {orig.Name}, {orig.WorkName}, {orig.MinValueRelationships}, {orig.MaxValueRelationships}," + $" {orig.TemplateNameMask}, {orig.TnkId}, {nameof(orig.GroupId)}"); var updatedApplicationInWork = await jobService.Get().Include(t => t.Tnk) .FirstAsync(t => t.Id == orig.Id); var response = mapper.Map(updatedApplicationInWork); response.TemplatesCount = await templateService.Get().CountAsync(t => t.JobId == 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 job = await jobService.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 templateService.Get().CountAsync(t => t.JobId == id); if (templateCount > 0) return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении задания на выполнение работ. С данным заданием связаны шаблоны: {templateCount} шт." } })); if (!jobService.Delete(job) || !await jobService.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 jobService.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 }; } } public class JobStatModel { public required TemplateStats TemplateStatistics { get; set; } public required ScheduleStats ScheduleStatistics { get; set; } } }