feat(api, nextRun): при изменении расписания в jobGroup, отправляется запрос на обновление nextRun в связанных шаблонах.

This commit is contained in:
Mikhail Trubnikov
2026-02-27 16:37:52 +10:00
parent 10bf68709b
commit a4f8e9eccc
9 changed files with 162 additions and 32 deletions

View File

@@ -407,6 +407,13 @@
public const string getParam = "{id}";
}
public static class JobGroupUpdateNextRun
{
public const string UpdateNextRun = Base + "/job-groups/" + getParam + "/update-next-run";
public const string getParam = "{jobGroupId}";
}
public static class JobGroupType
{
public const string GetAll = Base + "/job-group-types/";

View File

@@ -11,14 +11,17 @@ 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.Helpers;
using PARR.BLL.Services.Interfaces;
using PARR.Common.Domain;
using PARR.Constants;
using PARR.DAL.Contracts;
using PARR.DAL.DomainModels;
using PARR.DAL.DomainServices.Interfaces;
using PARR.DAL.Models;
using PARR.DAL.Models.Job;
using PARR.DAL.NextRunServices;
using PARR.DAL.Services.Interfaces;
using PARR.DAL.Services.Interfaces.Job;
using PARR.DAL.Services.Interfaces.Schedule;
@@ -41,6 +44,8 @@ namespace PARR.API.Controllers.V1
private readonly IJobGroupTypeService jobGroupTypeService;
private readonly IMatchingStatusService matchingStatusService;
private readonly IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService;
private readonly IMqService mqService;
private readonly MqSettings mqSettings;
public JobGroupController(
ILogger<JobController> logger,
@@ -52,7 +57,9 @@ namespace PARR.API.Controllers.V1
IValidator<JobGroupRequest> validator,
IJobGroupTypeService jobGroupTypeService,
IMatchingStatusService matchingStatusService,
IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService
IScheduleResponseAreaTimeOffsetService scheduleResponseAreaTimeOffsetService,
IMqService mqService,
MqSettings mqSettings
)
{
this.logger = logger;
@@ -65,6 +72,8 @@ namespace PARR.API.Controllers.V1
this.jobGroupTypeService = jobGroupTypeService;
this.matchingStatusService = matchingStatusService;
this.scheduleResponseAreaTimeOffsetService = scheduleResponseAreaTimeOffsetService;
this.mqService = mqService;
this.mqSettings = mqSettings;
}
@@ -248,7 +257,7 @@ namespace PARR.API.Controllers.V1
if (orig == null)
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при изменении группы заданий на выполнение работ. Не найдена группа с Id: {id}" } }));
// //Расписание было изменено, ниже добавим задание в очередь на обновление расписаний у связанных шаблонов
//Расписание было изменено, ниже добавим задание в очередь на обновление расписаний у связанных шаблонов
var isScheduleChanged = IsScheduleChanged(orig, request);
orig.GroupName = request.Name.Trim();
@@ -324,26 +333,25 @@ namespace PARR.API.Controllers.V1
$" {orig.Solution}, {orig.TemplateDuration}, {orig.ReferenceDate}, {orig.IsAutoDistributionEnabled}" +
$", {orig.IsAgent}, {orig.AgentName}, {orig.AgentTimeOutSec}, {orig.AgentScript}");
//TODO: Восстановить после перехода на JobGroup
//if (isScheduleChanged)
//{
// //расписание было обновлено, отправим задание в очередь на перерасчет NextRun
// var requestToMq = new TemplateDistributorMq
// {
// ApplicationInWorkId = id
// };
// var msg = JsonSerializer.Serialize(requestToMq);
#region расписание было изменено, отправим задание на перерасчет nextRun
if (isScheduleChanged)
{
// расписание было изменено, отправим задание на перерасчет nextRun
var requestToMq = new NextRunUpdateMq
{
JobGroupId = id,
Initiator = new HistoryInitiator { InitiatorComment = "Изменилось расписание группы работ в ГУИ, отправлен запрос на перерасчет nextRun", InitiatorParrComponentId = ParrComponentsEnum.Api }
};
var sendResult = await mqService.SendAsync(mqSettings.NextRun, new List<object> { requestToMq });
// logger.LogDebug($"Расписание в РР applicationInWorkId: {id} было изменено. Отправляем задание в очередь на перерасчет NextRun");
if (sendResult.IsSuccess)
logger.LogInformation("Задание на перерасчет NextRun успешно отправлено в очередь MQ {queueName}", mqSettings.NextRun.QueueName);
else
logger.LogError("Ошибка при отправке задания на перерасчет NextRun в очередь MQ {queueName}", mqSettings.NextRun.QueueName);
}
#endregion
// 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 updatedJobGroup = await groupService.Get()
.Include(t => t.Jobs)
@@ -415,8 +423,30 @@ namespace PARR.API.Controllers.V1
if (request.Schedule.Count() != orig.EsppSchValues.Count())
isScheduleChanged = true;
//if (request.IsAutoDistributionEnabled != orig.IsAutoDistributionEnabled)
// isScheduleChanged = true;
if (request.IsAutoDistributionEnabled != orig.IsAutoDistributionEnabled)
isScheduleChanged = true;
if (request.DistributionConfig?.DistributionPeriodId != orig.DistributionConfig?.DistributionPeriodId)
isScheduleChanged = true;
if (request.DistributionConfig?.IsExcludeWeekends != orig.DistributionConfig?.IsExcludeWeekends)
isScheduleChanged = true;
if (request.DistributionConfig?.IsGroupingByWorkGroup != orig.DistributionConfig?.IsGroupingByWorkGroup)
isScheduleChanged = true;
if (request.ScheduleExcludeTypeId != orig.ScheduleExcludeTypeId)
isScheduleChanged = true;
if (request.ScheduleExcludeTypeCalendarId != orig.ScheduleExcludeTypeCalendarId)
isScheduleChanged = true;
if (request.IsWorkGroupTimezone != orig.IsResponseAreaTimezone)
isScheduleChanged = true;
if (request.UserTimeZoneOffsetMinutes != orig.UserTimeZoneOffsetMinutes)
isScheduleChanged = true;
request.Schedule.ForEach(requestSchedule =>
{

View File

@@ -0,0 +1,70 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PARR.API.Contracts.V1;
using PARR.API.Contracts.V1.Responses.Base;
using PARR.API.Controllers.V1.Base;
using PARR.API.Settings;
using PARR.BLL.Domain.Mq;
using PARR.BLL.Services.Interfaces;
using PARR.Common.Domain;
using PARR.Constants;
using PARR.DAL.Services.Interfaces.Job;
using Serilog.Core;
namespace PARR.API.Controllers.V1
{
/// <summary>
/// Пересчитать все nextRun для JobGroup
/// </summary>
[Authorize(Roles = ParrRoles.Administrator.Role)]
public class JobGroupUpdateNextRunController : BaseApiController
{
private readonly IMqService mqService;
private readonly MqSettings mqSettings;
private readonly IJobGroupService jobGroupService;
public JobGroupUpdateNextRunController(
IMqService mqService,
MqSettings mqSettings,
IJobGroupService jobGroupService
)
{
this.mqService = mqService;
this.mqSettings = mqSettings;
this.jobGroupService = jobGroupService;
}
/// <summary>
/// Обновить nextRun для всех шаблонах в JobGroup
/// </summary>
/// <returns></returns>
[HttpPost(ApiRoutes.JobGroupUpdateNextRun.UpdateNextRun)]
public async Task<IActionResult> UpdateNextRun([FromRoute] Guid jobGroupId)
{
var jobGroup = await jobGroupService.Get().AnyAsync(t => t.Id == jobGroupId);
if (!jobGroup)
return BadRequest(new Response<string?>(null, false, new List<ErrorModel> { new ErrorModel { Message = "Не найдена группа работ с ИД" } }));
var requestToMq = new NextRunUpdateMq
{
JobGroupId = jobGroupId,
Initiator = new HistoryInitiator
{
InitiatorComment = "Вручную отправлен запрос на обновление nextRun для группы работ",
InitiatorParrComponentId = ParrComponentsEnum.Api
}
};
var sendResult = await mqService.SendAsync(mqSettings.NextRun, new List<object> { requestToMq });
if (sendResult.IsSuccess)
return Created("", new Response<string?>(null, true, new List<ErrorModel>(), "Отправлен запрос на расчет nextRun для группы работ."));
else
return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка при отправке данных." } }));
}
}
}

View File

@@ -10,6 +10,7 @@ namespace PARR.API.Settings
public MqStatistics Statistics { get; set; } = new MqStatistics();
public MqTemplatesMatcher TemplatesMatcher { get; set; } = new MqTemplatesMatcher();
public MqTemplatesUpdater TemplatesUpdater { get; set; } = new MqTemplatesUpdater();
public MqNextRun NextRun { get; set; } = new MqNextRun();
}
public class MqGenerateTemplates : IMqSettings
@@ -70,4 +71,13 @@ namespace PARR.API.Settings
public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
}
public class MqNextRun : IMqSettings
{
public string HostName { get; set; } = string.Empty;
public string QueueName { get; set; } = string.Empty;
public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public ushort? PrefetchCount { get; set; } = 0;
}
}

View File

@@ -45,6 +45,9 @@
"TemplatesUpdater": {
"HostName": "10.99.253.216"
},
"NextRun": {
"HostName": "10.99.253.216"
},
"Statistics": {
"StatMqAuth": {
"HostName": "10.99.253.216"

View File

@@ -60,6 +60,12 @@
"User": "template_updater_writer",
"Password": "sjdhgfkJHGIUFDi14asd^12"
},
"NextRun": {
"HostName": "parr-rabbitmq",
"QueueName": "parr-next-run-updater",
"User": "next_run_updater_writer",
"Password": "KJHGfdsjfhg$976412!kay"
},
"Statistics": {
"StatMqAuth": {
"HostName": "parr-rabbitmq",

View File

@@ -1,4 +1,6 @@
namespace PARR.BLL.Domain.Mq
using PARR.Common.Domain;
namespace PARR.BLL.Domain.Mq
{
/// <summary>
/// Запрос на обновление всех nextRun для JobGroup
@@ -6,5 +8,7 @@
public class NextRunUpdateMq
{
public Guid JobGroupId { get; set; }
public required HistoryInitiator Initiator { get; set; }
}
}

View File

@@ -71,7 +71,7 @@ namespace PARR.NextRun
return;
}
await UpdateNextRunAsync(query.JobGroupId);
await UpdateNextRunAsync(query);
}
/// <summary>
@@ -95,7 +95,7 @@ namespace PARR.NextRun
/// </summary>
/// <param name="jobGroupId"></param>
/// <returns></returns>
private async Task UpdateNextRunAsync(Guid jobGroupId)
private async Task UpdateNextRunAsync(NextRunUpdateMq queryMq)
{
using var scope = serviceProvider.CreateScope();
@@ -105,11 +105,11 @@ namespace PARR.NextRun
// Берем шаблоны только в статусе Used
var templates = await templateService.Get()
.Where(t =>
t.Job!.GroupId == jobGroupId
t.Job!.GroupId == queryMq.JobGroupId
&& t.StatusTypeId == TemplateStatusTypeEnum.Used
).ToListAsync();
logger.LogInformation("Найдено шаблонов {count} шт. в статусе Used в группе работ {jobGroupId}", templates.Count, jobGroupId);
logger.LogInformation("Найдено шаблонов {count} шт. в статусе Used в группе работ {jobGroupId}", templates.Count, queryMq.JobGroupId);
if (!templates.Any())
return;
@@ -145,18 +145,18 @@ namespace PARR.NextRun
if (updatedTemplates > 0)
{
if (await templateService.CommitAsync(new HistoryInitiator { InitiatorComment = "Запрос из очереди на перерасчет всех nextRun для jobGroupId", InitiatorParrComponentId = ParrComponentsEnum.NextRun }))
if (await templateService.CommitAsync(queryMq.Initiator))
{
logger.LogInformation("Успешно обновлены nextRun у {count} шаблонов, группа работ: {jobGroupId}", updatedTemplates, jobGroupId);
logger.LogInformation("Успешно обновлены nextRun у {count} шаблонов, группа работ: {jobGroupId}", updatedTemplates, queryMq.JobGroupId);
}
else
{
logger.LogError("При сохранении nextRun для шаблонов {count} шт, произошла ошибка при сохранении в БД. JobGroupId: {jobGroupId}", updatedTemplates, jobGroupId);
logger.LogError("При сохранении nextRun для шаблонов {count} шт, произошла ошибка при сохранении в БД. JobGroupId: {jobGroupId}", updatedTemplates, queryMq.JobGroupId);
}
}
else
{
logger.LogInformation("Для группы работ {jobGroupId}, все nextRun актуальны. Нечего обновлять.", jobGroupId);
logger.LogInformation("Для группы работ {jobGroupId}, все nextRun актуальны. Нечего обновлять.", queryMq.JobGroupId);
}
}
}

View File

@@ -16,7 +16,7 @@ namespace PARR.NextRunWorker
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
logger.LogInformation("Запуск {workerName}", nameof(RabbitWorker));
await nextRunRabbitService.StopAsync();
await nextRunRabbitService.StartAsync();
}
public override Task StopAsync(CancellationToken cancellationToken)