2025-07-01 17:20:48 +10:00
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 ;
2025-09-09 17:19:41 +10:00
using PARR.DAL.Contracts ;
2025-07-01 17:20:48 +10:00
using PARR.DAL.DomainModels ;
2026-01-16 11:04:26 +10:00
using PARR.DAL.DomainServices.Interfaces ;
2025-09-04 14:51:51 +10:00
using PARR.DAL.Models ;
2025-07-01 17:20:48 +10:00
using PARR.DAL.Models.Job ;
2025-09-09 17:19:41 +10:00
using PARR.DAL.Services.Interfaces ;
2025-07-01 17:20:48 +10:00
using PARR.DAL.Services.Interfaces.Job ;
namespace PARR.API.Controllers.V1
{
/// <summary>
/// Управление группами работам
/// </summary>
[Authorize(Roles = ParrRoles.Administrator.Role)]
public class JobGroupController : BaseApiController
{
private readonly ILogger < JobController > logger ;
private readonly IMapper mapper ;
private readonly IUriService uriService ;
private readonly IJobGroupService groupService ;
2025-09-09 10:44:33 +10:00
private readonly IJobService jobService ;
2025-09-09 17:19:41 +10:00
private readonly IEsppSchTypeConfigService esppConfigService ;
2025-07-01 17:20:48 +10:00
private readonly IValidator < JobGroupRequest > validator ;
2025-12-19 10:48:41 +10:00
private readonly IJobGroupTypeService jobGroupTypeService ;
2025-09-09 17:19:41 +10:00
private readonly SettingsFromDb settingsFromDb ;
2026-01-16 11:04:26 +10:00
private readonly IMatchingStatusService matchingStatusService ;
2025-07-01 17:20:48 +10:00
public JobGroupController (
ILogger < JobController > logger ,
IMapper mapper ,
IUriService uriService ,
IJobGroupService groupService ,
2025-09-09 10:44:33 +10:00
IJobService jobService ,
2025-09-09 17:19:41 +10:00
IEsppSchTypeConfigService esppConfigService ,
IValidator < JobGroupRequest > validator ,
2025-12-19 10:48:41 +10:00
IJobGroupTypeService jobGroupTypeService ,
2026-01-16 11:04:26 +10:00
SettingsFromDb settingsFromDb ,
IMatchingStatusService matchingStatusService
2025-07-01 17:20:48 +10:00
)
{
this . logger = logger ;
this . mapper = mapper ;
this . uriService = uriService ;
this . groupService = groupService ;
2025-09-09 10:44:33 +10:00
this . jobService = jobService ;
2025-09-09 17:19:41 +10:00
this . esppConfigService = esppConfigService ;
2025-07-01 17:20:48 +10:00
this . validator = validator ;
2025-12-19 10:48:41 +10:00
this . jobGroupTypeService = jobGroupTypeService ;
2025-09-09 17:19:41 +10:00
this . settingsFromDb = settingsFromDb ;
2026-01-16 11:04:26 +10:00
this . matchingStatusService = matchingStatusService ;
2025-07-01 17:20:48 +10:00
}
/// <summary>
/// Получить список групп заданий на выполнение работ постранично
/// </summary>
/// <returns></returns>
[HttpGet(ApiRoutes.JobGroup.GetAll)]
public async Task < IActionResult > GetAll ( [ FromQuery ] PaginationQuery paginationQuery , [ FromQuery ] JobGroupQuery filter )
{
var paginationFilter = mapper . Map < PaginationFilter > ( paginationQuery ) ;
2025-12-19 10:48:41 +10:00
IQueryable < JobGroup > query = groupService . Get ( )
. Include ( t = > t . GroupType )
2025-12-26 11:21:45 +10:00
. Include ( t = > t . GroupingUnitField )
. Include ( t = > t . ScheduleExcludeType )
. Include ( t = > t . ScheduleExcludeTypeCalendar ) ;
2025-07-01 17:20:48 +10:00
query = query . OrderBy ( t = > t . GroupName ) ;
if ( ! string . IsNullOrEmpty ( filter . Name ) )
query = query . Where ( t = > t . GroupName . ToLower ( ) . Contains ( filter . Name . ToLower ( ) ) ) ;
2025-09-09 17:19:41 +10:00
if ( filter . IsFull )
2026-01-22 14:05:48 +10:00
query = query
. Include ( t = > t . Jobs ) . ThenInclude ( t = > t . Tnk )
. Include ( t = > t . DistributionConfig ) . ThenInclude ( t = > t . DistributionPeriod ) ;
2025-07-01 17:20:48 +10:00
2025-09-04 14:51:51 +10:00
var jobGroups = await groupService . GetPage ( query , paginationFilter ) . ToListAsync ( ) ;
2025-07-01 17:20:48 +10:00
2025-09-04 14:51:51 +10:00
if ( ! jobGroups . Any ( ) )
2025-07-01 17:20:48 +10:00
return NoContent ( ) ;
2025-09-04 16:03:30 +10:00
var response = mapper . Map < List < JobGroupResponse > > ( jobGroups ) ;
2025-07-01 17:20:48 +10:00
2025-09-09 17:19:41 +10:00
if ( filter . IsFull )
foreach ( var jobGroupResponse in response )
await AppendMissingDataAsync ( jobGroupResponse ) ;
2025-09-04 16:03:30 +10:00
var paginationResponse = new PagedResponse < JobGroupResponse > ( response , true ) . GetPaginatedProps ( paginationFilter , query ) ;
2025-07-01 17:20:48 +10:00
return Ok ( paginationResponse ) ;
}
/// <summary>
/// Получить группу заданий на выполнение работ по id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet(ApiRoutes.JobGroup.Get)]
public async Task < IActionResult > GetById ( [ FromRoute ] Guid id )
{
var jobGroup = await groupService . Get ( )
. Include ( t = > t . Jobs ) . ThenInclude ( t = > t . Tnk )
2025-11-17 12:06:33 +10:00
. Include ( t = > t . GroupType )
2025-12-19 10:48:41 +10:00
. Include ( t = > t . GroupingUnitField )
2025-12-26 11:21:45 +10:00
. Include ( t = > t . ScheduleExcludeType )
. Include ( t = > t . ScheduleExcludeTypeCalendar )
2026-01-22 14:05:48 +10:00
. Include ( t = > t . DistributionConfig ) . ThenInclude ( t = > t . DistributionPeriod )
2025-07-01 17:20:48 +10:00
. FirstOrDefaultAsync ( t = > t . Id = = id ) ;
if ( jobGroup = = null )
return NotFound ( ) ;
var response = mapper . Map < JobGroupResponse > ( jobGroup ) ;
2025-09-11 16:03:51 +10:00
await AppendMissingDataAsync ( response ) ;
2025-07-01 17:20:48 +10:00
return Ok ( new Response < JobGroupResponse > ( response , true ) ) ;
}
/// <summary>
/// Создать группу заданий на выполнение работ (JobGroup)
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost(ApiRoutes.JobGroup.Create)]
public async Task < IActionResult > Create ( [ FromBody ] JobGroupRequest request )
{
var resultValidate = await validator . ValidateAsync ( request ) ;
if ( ! resultValidate . IsValid )
return BadRequest ( new Response ( resultValidate . Errors ) ) ;
var jobGroup = new JobGroup
{
Id = Guid . NewGuid ( ) ,
GroupName = request . Name . Trim ( ) ,
2025-11-17 12:06:33 +10:00
//IsUmbrella = request.IsUmbrella,
GroupTypeId = request . GroupTypeId ,
2025-12-19 10:48:41 +10:00
GroupingUnitFieldId = await GetGroupingUnitFieldIdAsync ( request ) ,
2025-07-01 17:20:48 +10:00
ShortDescription = request . ShortDescription . Trim ( ) ,
FullDescription = request . FullDescription . Trim ( ) ,
Solution = request . Solution . Trim ( ) ,
TemplateDuration = request . TemplateDuration . Trim ( ) ,
ReferenceDate = request . ReferenceDate ,
2025-12-26 11:21:45 +10:00
ScheduleExcludeTypeId = request . ScheduleExcludeTypeId ,
ScheduleExcludeTypeCalendarId = request . ScheduleExcludeTypeCalendarId
2025-09-05 10:33:52 +10:00
//IsAutoDistributionEnabled = request.IsAutoDistributionEnabled,
//IsAgent = request.IsAgent,
//AgentName = request.AgentName,
//AgentTimeOutSec = request.AgentTimeOutSec,
//AgentScript = request.AgentScript
2025-07-01 17:20:48 +10:00
} ;
2025-09-04 14:51:51 +10:00
//Добавляем настройки планировщика
request . Schedule . ForEach ( item = >
{
jobGroup . EsppSchValues . Add ( new EsppSchValue
{
JobGroupId = jobGroup . Id ,
TypeConfigId = item . TypeConfigId ,
TypeValueId = item . TypeValueId
} ) ;
} ) ;
2025-07-01 17:20:48 +10:00
if ( ! await groupService . CreateAsync ( jobGroup ) | | ! await groupService . CommitAsync ( ) )
return BadRequest ( new Response ( false , new List < ErrorModel > { new ErrorModel { Message = "Ошибка при создании группы заданий на выполнение работ" } } ) ) ;
logger . LogInformation ( $"Пользователь {User.Identity?.Name} добавил группу заданий на выполнение работ: {jobGroup.Id}, {jobGroup.GroupName}, {jobGroup.ShortDescription}" ) ;
2025-09-04 14:51:51 +10:00
var createdJobGroup = await groupService . Get ( ) . Include ( t = > t . Jobs ) . ThenInclude ( t = > t . Tnk )
2025-11-17 12:06:33 +10:00
. Include ( t = > t . GroupType )
2025-12-19 10:48:41 +10:00
. Include ( t = > t . GroupingUnitField )
2025-12-26 11:21:45 +10:00
. Include ( t = > t . ScheduleExcludeType )
. Include ( t = > t . ScheduleExcludeTypeCalendar )
2026-01-22 14:05:48 +10:00
. Include ( t = > t . DistributionConfig ) . ThenInclude ( t = > t . DistributionPeriod )
2025-07-01 17:20:48 +10:00
. FirstAsync ( t = > t . Id = = jobGroup . Id ) ;
var locationUri = uriService . GetUri ( ApiRoutes . JobGroup . Get , ApiRoutes . JobGroup . getParam , createdJobGroup . Id ) ;
var response = mapper . Map < JobGroupResponse > ( createdJobGroup ) ;
2025-12-26 13:50:30 +10:00
await AppendMissingDataAsync ( response ) ;
2025-07-01 17:20:48 +10:00
return Created ( locationUri , new Response < JobGroupResponse > ( response , true ) ) ;
}
/// <summary>
/// Обновить группу заданий на выполнение работ (JobGroup)
/// </summary>
/// <param name="id"></param>
/// <param name="request"></param>
/// <returns></returns>
[HttpPut(ApiRoutes.JobGroup.Update)]
public async Task < IActionResult > Update ( [ FromRoute ] Guid id , [ FromBody ] JobGroupRequest request )
{
var resultValidate = await validator . ValidateAsync ( request ) ;
if ( ! resultValidate . IsValid )
return BadRequest ( new Response ( resultValidate . Errors ) ) ;
var orig = await groupService . Get ( )
2025-09-04 14:51:51 +10:00
. Include ( t = > t . Jobs )
2025-07-01 17:20:48 +10:00
. ThenInclude ( t = > t . Tnk )
2025-09-04 14:51:51 +10:00
. Include ( t = > t . EsppSchValues )
2025-07-01 17:20:48 +10:00
. FirstOrDefaultAsync ( t = > t . Id = = id ) ;
if ( orig = = null )
return BadRequest ( new Response ( false , new List < ErrorModel > { new ErrorModel { Message = $"Ошибка при изменении группы заданий на выполнение работ. Н е найдена группа с Id: {id}" } } ) ) ;
2025-09-04 14:51:51 +10:00
// //Расписание было изменено, ниже добавим задание в очередь на обновление расписаний у связанных шаблонов
var isScheduleChanged = IsScheduleChanged ( orig , request ) ;
2025-07-01 17:20:48 +10:00
orig . GroupName = request . Name . Trim ( ) ;
2025-11-17 12:06:33 +10:00
//orig.IsUmbrella = request.IsUmbrella;
orig . GroupTypeId = request . GroupTypeId ;
2025-12-19 10:48:41 +10:00
orig . GroupingUnitFieldId = await GetGroupingUnitFieldIdAsync ( request ) ;
2025-07-01 17:20:48 +10:00
orig . ShortDescription = request . ShortDescription . Trim ( ) ;
orig . FullDescription = request . FullDescription . Trim ( ) ;
orig . Solution = request . Solution . Trim ( ) ;
orig . TemplateDuration = request . TemplateDuration . Trim ( ) ;
orig . ReferenceDate = request . ReferenceDate ;
2025-12-26 11:21:45 +10:00
orig . ScheduleExcludeTypeId = request . ScheduleExcludeTypeId ;
orig . ScheduleExcludeTypeCalendarId = request . ScheduleExcludeTypeCalendarId ;
2025-09-05 10:33:52 +10:00
//orig.IsAutoDistributionEnabled = request.IsAutoDistributionEnabled;
//orig.IsAgent = request.IsAgent;
//orig.AgentName = request.AgentName;
//orig.AgentTimeOutSec = request.AgentTimeOutSec;
//orig.AgentScript = request.AgentScript;
2025-09-04 14:51:51 +10:00
orig . DateModified = DateTimeOffset . UtcNow ;
//обновляем планировщик
orig . EsppSchValues . Clear ( ) ;
request . Schedule . ForEach ( item = >
{
orig . EsppSchValues . Add ( new EsppSchValue
{
JobGroupId = orig . Id ,
TypeConfigId = item . TypeConfigId ,
TypeValueId = item . TypeValueId
} ) ;
} ) ;
2025-07-01 17:20:48 +10:00
if ( ! await groupService . CommitAsync ( ) )
return BadRequest ( new Response ( false , new List < ErrorModel > { new ErrorModel { Message = "Ошибка при изменении группы заданий на выполнение работ." } } ) ) ;
logger . LogInformation ( $"Пользователь {User.Identity?.Name} обновил группу заданий на выполнение работ: {orig.Id}," +
2025-11-25 12:26:24 +10:00
$" {orig.GroupName}, {orig.ShortDescription}, {orig.FullDescription}," +
2025-07-01 17:20:48 +10:00
$" {orig.Solution}, {orig.TemplateDuration}, {orig.ReferenceDate}, {orig.IsAutoDistributionEnabled}" +
$", {orig.IsAgent}, {orig.AgentName}, {orig.AgentTimeOutSec}, {orig.AgentScript}" ) ;
2025-09-04 14:51:51 +10:00
//TODO: Восстановить после перехода на JobGroup
//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}");
//}
2025-07-01 17:20:48 +10:00
var updatedJobGroup = await groupService . Get ( )
. Include ( t = > t . Jobs )
. ThenInclude ( t = > t . Tnk )
2025-11-17 12:06:33 +10:00
. Include ( t = > t . GroupType )
2025-12-19 10:48:41 +10:00
. Include ( t = > t . GroupingUnitField )
2025-12-26 11:21:45 +10:00
. Include ( t = > t . ScheduleExcludeType )
. Include ( t = > t . ScheduleExcludeTypeCalendar )
2026-01-22 14:05:48 +10:00
. Include ( t = > t . DistributionConfig ) . ThenInclude ( t = > t . DistributionPeriod )
2025-07-01 17:20:48 +10:00
. FirstAsync ( t = > t . Id = = orig . Id ) ;
2025-09-04 14:51:51 +10:00
var response = mapper . Map < JobGroupResponse > ( updatedJobGroup ) ;
2025-12-26 13:50:30 +10:00
await AppendMissingDataAsync ( response ) ;
2025-07-01 17:20:48 +10:00
2025-09-04 14:51:51 +10:00
return Ok ( new Response < JobGroupResponse > ( response , true ) ) ;
2025-07-01 17:20:48 +10:00
}
/// <summary>
2025-09-09 17:19:41 +10:00
/// Удалить группу заданий на выполнение работ (только если нет связанных заданий)
2025-07-01 17:20:48 +10:00
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
2025-09-09 10:44:33 +10:00
[HttpDelete(ApiRoutes.JobGroup.Delete)]
public async Task < IActionResult > Delete ( [ FromRoute ] Guid id )
{
var jobGroup = await groupService . Get ( )
. FirstOrDefaultAsync ( t = > t . Id = = id ) ;
if ( jobGroup = = null )
return BadRequest ( new Response ( false , new List < ErrorModel > { new ErrorModel {
Message = $"Ошибка при удалении группы заданий на выполнение работ. Н е найдена группа заданий на выполнение работ Id: {id}"
} } ) ) ;
var jobCount = await jobService . Get ( ) . CountAsync ( t = > t . GroupId = = id ) ;
if ( jobCount > 0 )
return BadRequest ( new Response ( false , new List < ErrorModel > { new ErrorModel {
Message = $"Ошибка при удалении группы заданий на выполнение работ. С данным группой связаны задания: {jobCount} шт."
} } ) ) ;
if ( ! groupService . Delete ( jobGroup ) | | ! await groupService . CommitAsync ( ) )
return BadRequest ( new Response ( false , new List < ErrorModel > { new ErrorModel {
Message = $"Ошибка при удалении группы заданий на выполнение работ"
} } ) ) ;
logger . LogInformation ( $"Пользователь {User.Identity?.Name} удалил группу заданий на выполнение работ: {jobGroup.Id},{jobGroup.GroupName}," +
2025-11-25 12:26:24 +10:00
$" {jobGroup.ShortDescription}, {jobGroup.FullDescription}," +
$" {jobGroup.Solution}, {jobGroup.TemplateDuration}, {jobGroup.ReferenceDate}," +
$" {jobGroup.IsAutoDistributionEnabled}, {jobGroup.IsAgent}, {jobGroup.AgentName}," +
$" {jobGroup.AgentTimeOutSec}, {jobGroup.AgentScript}" ) ;
2025-09-09 10:44:33 +10:00
return NoContent ( ) ;
}
2025-09-04 14:51:51 +10:00
/// <summary>
/// Проверка, были ли изменения в расписании
/// </summary>
/// <param name="orig"></param>
/// <param name="request"></param>
/// <returns></returns>
private bool IsScheduleChanged ( JobGroup orig , JobGroupRequest request )
2025-07-01 17:20:48 +10:00
{
2025-09-04 14:51:51 +10:00
var isScheduleChanged = false ;
2025-07-01 17:20:48 +10:00
2025-09-04 14:51:51 +10:00
if ( orig . ReferenceDate ! = request . ReferenceDate )
isScheduleChanged = true ;
if ( request . Schedule . Count ( ) ! = orig . EsppSchValues . Count ( ) )
isScheduleChanged = true ;
2025-09-05 10:33:52 +10:00
//if (request.IsAutoDistributionEnabled != orig.IsAutoDistributionEnabled)
// isScheduleChanged = true;
2025-09-04 14:51:51 +10:00
request . Schedule . ForEach ( requestSchedule = >
{
var schExist = orig . EsppSchValues . FirstOrDefault ( t = > t . JobGroupId = = orig . Id
& & t . TypeValueId = = requestSchedule . TypeValueId
& & t . TypeConfigId = = requestSchedule . TypeConfigId ) ;
if ( schExist = = null )
isScheduleChanged = true ;
} ) ;
return isScheduleChanged ;
2025-07-01 17:20:48 +10:00
}
2025-09-09 17:19:41 +10:00
2025-12-26 13:50:30 +10:00
/// <summary>
2026-01-16 11:04:26 +10:00
/// Заполнить обязательные поля
2025-12-26 13:50:30 +10:00
/// </summary>
/// <param name="jobGroupResponse"></param>
/// <returns></returns>
2025-09-09 17:19:41 +10:00
private async Task AppendMissingDataAsync ( JobGroupResponse jobGroupResponse )
{
var schedule = await esppConfigService . GetEsppScheduleDtoAsync ( jobGroupResponse . Id ) ;
if ( schedule = = null )
{
logger . LogError ( $"Н е смог замапить расписание, так как оно null. JobGroupId: {jobGroupResponse.Id}" ) ;
return ;
}
var scheduleResponse = new JobGroupScheduleResponse
{
Timezone = settingsFromDb . ScheduleTimezone ,
TypeSchedule = mapper . Map < EsppScheduleTypeScheduleResponse > ( schedule . TypeSchedule ) ,
Values = mapper . Map < List < EsppScheduleValResponse > > ( schedule . Values ) . OrderBy ( t = > t . Order ) . ToList ( )
} ;
jobGroupResponse . Schedule = scheduleResponse ;
jobGroupResponse . JobsCount = await jobService . Get ( ) . CountAsync ( t = > t . GroupId = = jobGroupResponse . Id ) ;
2026-01-16 11:04:26 +10:00
jobGroupResponse . MatchingStatus = await GetMatchingStatusAsync ( jobGroupResponse . Id ) ;
2025-12-19 10:48:41 +10:00
}
/// <summary>
/// Смотрим на тип Группы, и возвращаем GroupingUnitFieldId или обнуляем е г о
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private async Task < Guid ? > GetGroupingUnitFieldIdAsync ( JobGroupRequest request )
{
// Если request.GroupingUnitFieldId == null, то ничего проверять не будем
if ( request . GroupingUnitFieldId = = null )
return request . GroupingUnitFieldId ;
2025-09-09 17:19:41 +10:00
2025-12-19 10:48:41 +10:00
var groupingType = await jobGroupTypeService . Get ( ) . FirstAsync ( t = > t . Code = = JobGroupTypesEnum . Group ) ;
if ( request . GroupTypeId = = groupingType . Id )
{
// Это сгруппированный тип, все ок
return request . GroupingUnitFieldId ;
}
else
{
// Это не сгруппированный тип, обнуляем GroupingUnitFieldId
logger . LogWarning ( $"При сохраненни JobGroup, был передан {nameof(request.GroupingUnitFieldId)}: {request.GroupingUnitFieldId}, но при этом тип группы был не ГРУППА, а {request.GroupTypeId}. Обнулил request.GroupingUnitFieldId" ) ;
return null ;
}
2025-09-09 17:19:41 +10:00
}
2026-01-16 11:04:26 +10:00
/// <summary>
/// Получить статус matching`a
/// </summary>
/// <param name="jobGroupId"></param>
/// <returns></returns>
private async Task < MatchingStatusResponse ? > GetMatchingStatusAsync ( Guid jobGroupId )
{
var statusMatching = await matchingStatusService . GetStatusAsync ( jobGroupId , SyncTaskEntityTypeEnum . JobGroup ) ;
return mapper . Map < MatchingStatusResponse > ( statusMatching ) ;
}
2025-07-01 17:20:48 +10:00
}
}