Files
parr_api/PARR.API/Controllers/V1/TemplateController.cs

357 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AutoMapper;
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.BLL.Helpers;
using PARR.Common.Domain;
using PARR.Constants;
using PARR.DAL.Contracts;
using PARR.DAL.DomainModels;
using PARR.DAL.DomainServices.Shortcodes;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
using PARR.DAL.Services.Interfaces.Schedule;
namespace PARR.API.Controllers.V1
{
/// <summary>
/// Шаблоны
/// </summary>
[Authorize(Roles = ParrRoles.Administrator.Role)]
public class TemplateController : BaseApiController
{
private readonly IMapper mapper;
private readonly ITemplateService templateService;
private readonly IRobotConfigurationService robotConfigurationService;
private readonly IClientService clientService;
private readonly ILogger<TemplateController> logger;
private readonly IShortcodesService shortcodesService;
private readonly IOrderService orderService;
private readonly SettingsFromDb settingsFromDb;
private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService;
public TemplateController(
IMapper mapper,
ITemplateService templateService,
IRobotConfigurationService robotConfigurationService,
IClientService clientService,
ILogger<TemplateController> logger,
IShortcodesService shortcodesService,
IOrderService orderService,
SettingsFromDb settingsFromDb,
IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService
)
{
this.mapper = mapper;
this.templateService = templateService;
this.robotConfigurationService = robotConfigurationService;
this.clientService = clientService;
this.logger = logger;
this.shortcodesService = shortcodesService;
this.orderService = orderService;
this.settingsFromDb = settingsFromDb;
this.scheduleResponseAreaTimeOffsetService = scheduleResponseAreaTimeOffsetService;
}
/// <summary>
/// Получить список всех шаблонов постранично
/// </summary>
/// <param name="paginationQuery"></param>
/// <returns></returns>
[HttpGet(ApiRoutes.Template.GetAll)]
public async Task<IActionResult> GetAll([FromQuery] PaginationQuery paginationQuery, [FromQuery] TemplateQuery filter)
{
var paginationFilter = mapper.Map<PaginationFilter>(paginationQuery);
IQueryable<Template> query = templateService.Get()
.Include(t => t.Unit).ThenInclude(t => t!.UnitValues).ThenInclude(t => t.Field)
.Include(t => t.Unit).ThenInclude(t => t!.UnitValues).ThenInclude(t => t.Value)
.Include(t => t.StatusType)
.Include(t => t.Job).ThenInclude(t => t.Group)
.Include(t => t.RobotConfigurations).ThenInclude(t => t.Robot)
.Include(t => t.RobotConfigurations).ThenInclude(t => t.TaskStatus)
.Include(t => t.RobotConfigurations).ThenInclude(t => t.RobotStatus)
.OrderBy(t => t.Name)
.AsNoTracking();
if (!string.IsNullOrEmpty(filter.Mask))
query = query.Where(t => EF.Functions.Like(t.Name.ToLower(), SqlHelpers.RegexToLike(filter.Mask)));
if (filter.JobId.HasValue)
query = query.Where(t => t.JobId == filter.JobId);
if (filter.StatusTypeId.HasValue)
query = query.Where(t => t.StatusTypeId == filter.StatusTypeId);
#region Статус синхронизации
switch (filter.SyncStatus)
{
case (TemplateQuerySyncStatusEnum.Waiting):
query = query.Where(t => t.RobotConfigurations.Any(x => x.TaskStatusCode != (int)TaskStatusEnum.Ok));
break;
case (TemplateQuerySyncStatusEnum.Complete):
query = query.Where(t => t.RobotConfigurations.All(x => x.TaskStatusCode == (int)TaskStatusEnum.Ok));
break;
case (TemplateQuerySyncStatusEnum.Error):
query = query.Where(t => t.RobotConfigurations.Any(x => x.RobotStatusCode == (int)RobotStatusEnum.Error));
break;
default:
break;
}
#endregion
var templates = await templateService.GetPage(query, paginationFilter).ToListAsync();
//logger.LogDebug("Загрузка шаблонов из БД: {ElapsedMs} мс", sw.ElapsedMilliseconds);
if (!templates.Any())
return NoContent();
var templateResponse = mapper.Map<List<TemplateListResponse>>(templates);
// словари для быстрого поиска
var templatesDict = templates.ToDictionary(t => t.Id);
var templateResponseDict = templateResponse.ToDictionary(t => t.Id);
#region Заполняем шорткоды и NextRunWithResponseAreaOffset
if (filter.ApplyShortcode == true)
{
//sw.Restart();
foreach (var responseItem in templateResponse)
{
//await ApplyTemplateShortcodesAsync(responseItem, templates.First(t => t.Id == responseItem.Id));
await ApplyTemplateShortcodesAsync(responseItem, templatesDict[responseItem.Id]);
//FillResponseAreaOffset(responseItem);
}
//logger.LogDebug("Получение шорткодов: {ElapsedMs} мс", sw.ElapsedMilliseconds);
//sw.Stop();
}
#endregion
#region заполнение OrdersCount
var templatesWithOrdersCount = await GetOrdersCountAsync(templates.Select(t => t.Id).ToList());
foreach (var (templateId, orderCount) in templatesWithOrdersCount)
{
if (templateResponseDict.TryGetValue(templateId, out var response))
response.OrderCount = orderCount;
}
#endregion
var paginationResponse = new PagedResponse<TemplateListResponse>(templateResponse, true).GetPaginatedProps(paginationFilter, query);
return Ok(paginationResponse);
}
/// <summary>
/// Получить шаблон по id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet(ApiRoutes.Template.Get)]
public async Task<IActionResult> GetById([FromRoute] Guid id)
{
var template = await templateService.GetWithIncludes().AsNoTracking()
.Include(t => t.RobotConfigurations).ThenInclude(t => t.Robot)
.Include(t => t.RobotConfigurations).ThenInclude(t => t.TaskStatus)
.Include(t => t.RobotConfigurations).ThenInclude(t => t.RobotStatus)
//.Include(t => t.Orders)
.Include(t => t.StatusType)
.Include(t => t.Job).ThenInclude(t => t.Group).ThenInclude(t => t.ScheduleExcludeType)
.Include(t => t.Job).ThenInclude(t => t.Group).ThenInclude(t => t.ScheduleExcludeTypeCalendar)
.AsSplitQuery()
.FirstOrDefaultAsync(t => t.Id == id);
if (template == null)
return NotFound();
var response = mapper.Map<TemplateResponse>(template);
await ApplyTemplateShortcodesAsync(response, template);
//FillResponseAreaOffset(response);
var ordersCountResult = await GetOrdersCountAsync(new List<Guid> { response.Id });
response.OrderCount = ordersCountResult.Count > 0 ? ordersCountResult.First().Value : 0;
return Ok(new Response<TemplateResponse>(response, true));
}
/// <summary>
/// Изменить статус у шаблона по ИД (активировать/деактивировать)
/// </summary>
/// <param name="id">ИД шаблона</param>
/// <returns></returns>
[HttpPut(ApiRoutes.Template.ChangeState)]
public async Task<IActionResult> ChangeState([FromRoute] Guid id, [FromBody] TemplateChangeStateRequest request)
{
//TODO:!!! Возможно тут не надо сразу менять, а отправить запрос в TemplateActivator и пусть он сам занимается своей работой!!!
#region old
//var template = await templateService.GetWithIncludes()
// //.Include(t => t.Unit).ThenInclude(t => t!.UnitValues).ThenInclude(t => t.Field)
// //.Include(t => t.Unit).ThenInclude(t => t!.UnitValues).ThenInclude(t => t.Value)
// .Include(t => t.RobotConfigurations).ThenInclude(t => t.Robot)
// .Include(t => t.RobotConfigurations).ThenInclude(t => t.TaskStatus)
// .Include(t => t.RobotConfigurations).ThenInclude(t => t.RobotStatus)
// .Include(t => t.Orders)
// .Include(t => t.StatusType)
// .Include(t => t.Job).ThenInclude(t => t.Group).ThenInclude(t => t.ScheduleExcludeType)
// .Include(t => t.Job).ThenInclude(t => t.Group).ThenInclude(t => t.ScheduleExcludeTypeCalendar)
// .AsSplitQuery()
// .FirstOrDefaultAsync(t => t.Id == id);
#endregion
var template = await templateService.Get()
.Include(t => t.RobotConfigurations)
.FirstOrDefaultAsync(t => t.Id == id);
if (template == null)
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найден шаблона с id: {id}" } }));
if (template.IsActiveTemplate != request.IsActiveTemplate)
{
template.IsActiveTemplate = request.IsActiveTemplate;
//необходимо обновить шаблон
var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.TemplateOrder, template);
//robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config);
robotConfigurationService.SetUpdateTaskStatusIfAllow(config);
}
if (template.IsActiveSchedule != request.IsActiveSchedule)
{
template.IsActiveSchedule = request.IsActiveSchedule;
//необходимо обновить расписание
var config = robotConfigurationService.GetFromTemplateByRobotCode(RobotsEnum.ScheduleOrder, template);
//robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, config);
robotConfigurationService.SetUpdateTaskStatusIfAllow(config);
}
if (!await templateService.CommitAsync(new HistoryInitiator { InitiatorComment = "Изменён статус шаблона/расписания", InitiatorIp = clientService.GetClientIp()?.ToString(), InitiatorParrComponentId = ParrComponentsEnum.Api }))
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка при изменении шаблона." } }));
#region old
//var templateToResponse = await templateService.GetWithIncludes().AsNoTracking()
// //.Include(t => t.Unit).ThenInclude(t => t.UnitValues).ThenInclude(t => t.Field)
// //.Include(t => t.Unit).ThenInclude(t => t.UnitValues).ThenInclude(t => t.Value)
// .Include(t => t.RobotConfigurations).ThenInclude(t => t.Robot)
// .Include(t => t.RobotConfigurations).ThenInclude(t => t.TaskStatus)
// .Include(t => t.RobotConfigurations).ThenInclude(t => t.RobotStatus)
// .Include(t => t.Orders)
// .Include(t => t.StatusType)
// .Include(t => t.Job).ThenInclude(t => t.Group).ThenInclude(t => t.ScheduleExcludeType)
// .Include(t => t.Job).ThenInclude(t => t.Group).ThenInclude(t => t.ScheduleExcludeTypeCalendar)
// .AsSplitQuery()
// .FirstAsync(t => t.Id == id);
//var response = mapper.Map<TemplateResponse>(templateToResponse);
//await ApplyTemplateShortcodesAsync(response, templateToResponse);
//return Ok(new Response<TemplateResponse>(response, true));
#endregion
var templateToResponse = await templateService.Get()
.AsNoTracking()
.Include(t => t.Unit).ThenInclude(t => t!.UnitValues).ThenInclude(t => t.Field)
.Include(t => t.Unit).ThenInclude(t => t!.UnitValues).ThenInclude(t => t.Value)
.Include(t => t.StatusType)
.Include(t => t.Job).ThenInclude(t => t.Group)
.Include(t => t.RobotConfigurations).ThenInclude(t => t.Robot)
.Include(t => t.RobotConfigurations).ThenInclude(t => t.TaskStatus)
.Include(t => t.RobotConfigurations).ThenInclude(t => t.RobotStatus)
.FirstAsync(t => t.Id == id);
var response = mapper.Map<TemplateListResponse>(templateToResponse);
await ApplyTemplateShortcodesAsync(response, templateToResponse);
//FillResponseAreaOffset(response);
var ordersCountResult = await GetOrdersCountAsync(new List<Guid> { response.Id });
response.OrderCount = ordersCountResult.Count > 0 ? ordersCountResult.First().Value : 0;
return Ok(new Response<TemplateListResponse>(response, true));
}
/// <summary>
/// Применить шорткоды
/// </summary>
/// <param name="response"></param>
/// <param name="template"></param>
/// <returns></returns>
private async Task ApplyTemplateShortcodesAsync(TemplateBaseResponse response, Template template)
{
response.WorkGroup = await shortcodesService.ApplyShortcodesAsync(template.Job.WorkGroupMask, template);
response.ResponseArea = await shortcodesService.ApplyShortcodesAsync(template.Job.ResponseAreaMask, template);
}
/// <summary>
/// Получить кол-во нарядов для шаблонов
/// </summary>
/// <param name="templateIdList"></param>
/// <returns></returns>
private async Task<Dictionary<Guid, int>> GetOrdersCountAsync(List<Guid> templateIdList)
{
if (!templateIdList.Any())
return new Dictionary<Guid, int>();
var templateWithOrders = await orderService.Get()
.Where(t => t.TemplateId.HasValue && templateIdList.Contains(t.TemplateId.Value))
.GroupBy(t => t.TemplateId)
.Select(t => new { TemplateId = t.Key, OrderCount = t.Count() })
.AsNoTracking()
.ToListAsync();
if (templateWithOrders == null)
return new Dictionary<Guid, int>();
return templateWithOrders.ToDictionary(t => t.TemplateId!.Value, t => t.OrderCount);
}
///// <summary>
///// Заполнить ResponseAreaOffset
///// </summary>
///// <param name="response"></param>
//private void FillResponseAreaOffset(TemplateBaseResponse response)
//{
// // если стоит галка IsResponseAreaTimezone и есть ЗО, то возвращаем оффсет
// if (response.IsResponseAreaTimezone && !string.IsNullOrEmpty(response.ResponseArea))
// {
// var responseArea = response.ResponseArea;
// response.ResponseAreaOffset = mapper.Map<ScheduleResponseAreaTimeOffsetResponse>(scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea));
// }
// else
// {
// response.ResponseAreaOffset = null;
// }
// //var responseArea = response.IsResponseAreaTimezone && !string.IsNullOrEmpty(response.ResponseArea)
// // ? response.ResponseArea
// // : settingsFromDb.DefaultResponseAreaToTimeOffset;
// //response.NextRunResponseAreaInLocal = response.IsResponseAreaTimezone && !string.IsNullOrEmpty(response.ResponseArea)
// // ? nextRunService.GetNextRunWithResponseAreaOffset(response.NextRun, response.ResponseArea)
// // // возвращаем в дефолтной зоне
// // : nextRunService.GetNextRunWithResponseAreaOffset(response.NextRun, settingsFromDb.DefaultResponseAreaToTimeOffset);
// //response.NextRunResponseAreaInLocal = nextRunService.GetNextRunWithResponseAreaOffset(response.NextRun, responseArea);
// //response.ResponseAreaOffset = mapper.Map<ScheduleResponseAreaTimeOffsetResponse>(scheduleResponseAreaTimeOffsetService.GetByResponseAreaOrDefault(responseArea));
//}
}
}