feat(dal): В TemplateService.ReserveUnusedTemplateAsync больше не передаем JobId при захвате неиспользуемого шаблона.

This commit is contained in:
Mikhail Kuznetsov
2026-03-27 15:02:36 +10:00
parent 3800cac97a
commit 01cb312ea5
6 changed files with 78 additions and 73 deletions

View File

@@ -109,64 +109,61 @@ namespace PARR.DAL.Services.Implementations
} }
public async Task<Guid?> ReserveUnusedTemplateAsync(Guid newUnitId, Guid newJobId, HistoryInitiator initiator) public async Task<Guid?> ReserveUnusedTemplateAsync(Guid newUnitId, HistoryInitiator initiator)
{ {
logger.LogDebug("Резервирую неиспользуемый шаблон с проверкой конфигураций роботов для UnitId: {UnitId}, JobId: {JobId}", newUnitId, newJobId); logger.LogDebug("Резервирую неиспользуемый шаблон с проверкой конфигураций роботов для UnitId: {UnitId}", newUnitId);
var sql = @" var sql = @"
UPDATE ""Templates"" UPDATE ""Templates""
SET ""StatusTypeId"" = @NewStatus, SET ""StatusTypeId"" = @NewStatus,
""DateModified"" = @DateModified, ""DateModified"" = @DateModified,
""UnitId"" = @NewUnitId, ""InitiatorIp"" = @InitiatorIp,
""JobId"" = @NewJobId, ""InitiatorParrComponentId"" = @InitiatorComponent,
""InitiatorIp"" = @InitiatorIp, ""InitiatorComment"" = @InitiatorComment
""InitiatorParrComponentId"" = @InitiatorComponent, WHERE ""Id"" = (
""InitiatorComment"" = @InitiatorComment SELECT t.""Id""
WHERE ""Id"" = ( FROM ""Templates"" t
SELECT t.""Id"" WHERE t.""StatusTypeId"" = @OldStatus
FROM ""Templates"" t AND t.""UnitId"" != @NewUnitId
WHERE t.""StatusTypeId"" = @OldStatus AND EXISTS (
AND t.""UnitId"" != @NewUnitId SELECT 1
AND EXISTS ( FROM ""RobotConfigurations"" rc
SELECT 1 WHERE rc.""TemplateId"" = t.""Id""
FROM ""RobotConfigurations"" rc AND rc.""RobotCode"" = @RobotCode1
WHERE rc.""TemplateId"" = t.""Id"" AND rc.""TaskStatusCode"" = @TaskStatus
AND rc.""RobotCode"" = @RobotCode1 AND rc.""RobotStatusCode"" = @RobotStatus
AND rc.""TaskStatusCode"" = @TaskStatus )
AND rc.""RobotStatusCode"" = @RobotStatus AND EXISTS (
) SELECT 1
AND EXISTS ( FROM ""RobotConfigurations"" rc
SELECT 1 WHERE rc.""TemplateId"" = t.""Id""
FROM ""RobotConfigurations"" rc AND rc.""RobotCode"" = @RobotCode2
WHERE rc.""TemplateId"" = t.""Id"" AND rc.""TaskStatusCode"" = @TaskStatus
AND rc.""RobotCode"" = @RobotCode2 AND rc.""RobotStatusCode"" = @RobotStatus
AND rc.""TaskStatusCode"" = @TaskStatus )
AND rc.""RobotStatusCode"" = @RobotStatus ORDER BY t.""DateCreated"" ASC
) LIMIT 1
ORDER BY t.""DateCreated"" ASC )
LIMIT 1 RETURNING ""Id"";";
)
RETURNING ""Id"";";
var parameters = new[] var parameters = new[]
{ {
new NpgsqlParameter("@NewStatus", (int)TemplateStatusTypeEnum.Updating), new NpgsqlParameter("@NewStatus", (int)TemplateStatusTypeEnum.Updating),
new NpgsqlParameter("@DateModified", DateTimeOffset.UtcNow), new NpgsqlParameter("@DateModified", DateTimeOffset.UtcNow),
new NpgsqlParameter("@NewUnitId", newUnitId), new NpgsqlParameter("@InitiatorIp", initiator.InitiatorIp ?? (object)DBNull.Value),
new NpgsqlParameter("@NewJobId", newJobId), new NpgsqlParameter("@InitiatorComponent",
new NpgsqlParameter("@InitiatorIp", initiator.InitiatorIp ?? (object)DBNull.Value), initiator.InitiatorParrComponentId.HasValue
new NpgsqlParameter("@InitiatorComponent", ? (object)(int)initiator.InitiatorParrComponentId.Value
initiator.InitiatorParrComponentId.HasValue : DBNull.Value),
? (object)(int)initiator.InitiatorParrComponentId.Value new NpgsqlParameter("@InitiatorComment", initiator.InitiatorComment ?? (object)DBNull.Value),
: DBNull.Value), new NpgsqlParameter("@OldStatus", (int)TemplateStatusTypeEnum.Unused),
new NpgsqlParameter("@InitiatorComment", initiator.InitiatorComment ?? (object)DBNull.Value), new NpgsqlParameter("@NewUnitId", newUnitId),
new NpgsqlParameter("@OldStatus", (int)TemplateStatusTypeEnum.Unused), // Параметры для проверки конфигураций роботов
// Параметры для проверки конфигураций роботов new NpgsqlParameter("@RobotCode1", (int)RobotsEnum.TemplateOrder),
new NpgsqlParameter("@RobotCode1", (int)RobotsEnum.TemplateOrder), new NpgsqlParameter("@RobotCode2", (int)RobotsEnum.ScheduleOrder),
new NpgsqlParameter("@RobotCode2", (int)RobotsEnum.ScheduleOrder), new NpgsqlParameter("@TaskStatus", (int)TaskStatusEnum.Ok),
new NpgsqlParameter("@TaskStatus", (int)TaskStatusEnum.Ok), new NpgsqlParameter("@RobotStatus", (int)RobotStatusEnum.Complete)
new NpgsqlParameter("@RobotStatus", (int)RobotStatusEnum.Complete) };
};
try try
{ {
@@ -178,19 +175,19 @@ namespace PARR.DAL.Services.Implementations
if (reservedTemplateId != default(Guid)) if (reservedTemplateId != default(Guid))
{ {
logger.LogInformation("Успешно зарезервирован шаблон с ID: {TemplateId} для UnitId: {UnitId}, JobId: {JobId}", logger.LogInformation("Успешно зарезервирован шаблон с ID: {TemplateId} для UnitId: {UnitId}",
reservedTemplateId, newUnitId, newJobId); reservedTemplateId, newUnitId);
} }
else else
{ {
logger.LogDebug("Не удалось зарезервировать шаблон для UnitId: {UnitId}, JobId: {JobId} (не найдено подходящих конфигураций роботов)", newUnitId, newJobId); logger.LogDebug("Не удалось зарезервировать шаблон для UnitId: {UnitId} (не найдено подходящих конфигураций роботов)", newUnitId);
} }
return reservedTemplateId; return reservedTemplateId;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogError(ex, "Ошибка при резервировании шаблона для UnitId: {UnitId}, JobId: {JobId}", newUnitId, newJobId); logger.LogError(ex, "Ошибка при резервировании шаблона для UnitId: {UnitId}", newUnitId);
throw; throw;
} }
} }

View File

@@ -8,18 +8,18 @@ namespace PARR.DAL.Services.Interfaces
{ {
Task<Template?> GetTemplateByNameAsync(string name); Task<Template?> GetTemplateByNameAsync(string name);
/// <summary> /// <summary>
/// Резервирует неиспользуемый шаблон для указанного ЭК и работы. /// Резервирует неиспользуемый шаблон: устанавливает статус <see cref="TemplateStatusTypeEnum.Updating"/>
/// Для корректного расчета NextRun назначает захваченному шаблону ЭК и работу /// и фиксирует данные инициатора.
/// </summary> /// </summary>
/// <param name="newUnitId">Id ЭК.</param> /// <param name="newUnitId">Идентификатор подразделения (используется для фильтрации и логирования).</param>
/// <param name="newJobId">Id работы.</param>
/// <param name="initiator">Данные инициатора операции.</param> /// <param name="initiator">Данные инициатора операции.</param>
/// <returns> /// <returns>
/// Id зарезервированного шаблона или <see langword="null"/>, /// Идентификатор зарезервированного шаблона или <see langword="null"/>,
/// если подходящий шаблон не найден. /// если подходящий шаблон не найден.
/// </returns> /// </returns>
Task<Guid?> ReserveUnusedTemplateAsync(Guid newUnitId, Guid newJobId, HistoryInitiator initiator); Task<Guid?> ReserveUnusedTemplateAsync(Guid newUnitId, HistoryInitiator initiator);
IQueryable<Template> GetWithIncludes(); IQueryable<Template> GetWithIncludes();
} }

View File

@@ -441,11 +441,6 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName); var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName);
//var existingTemplateByName = await templateService.Get().AsNoTracking()
// .FirstOrDefaultAsync(t => EF.Functions.ILike(t.Name, expectedName));
//if (existingTemplateByName != null)
// logger.LogInformation("Переиспользован шаблон");
var updateRequest = new TemplateUpdaterMq var updateRequest = new TemplateUpdaterMq
{ {
TemplateId = reusableTemplate.Id, TemplateId = reusableTemplate.Id,

View File

@@ -31,7 +31,7 @@ internal class TemplateReuser : ITemplateReuser
try try
{ {
// Атомарно резервируем один шаблон через DAL // Атомарно резервируем один шаблон через DAL
var templateId = await templateService.ReserveUnusedTemplateAsync(unitId, jobId, initiator); var templateId = await templateService.ReserveUnusedTemplateAsync(unitId, initiator);
if (templateId == null) if (templateId == null)
{ {

View File

@@ -1,6 +1,8 @@
using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.BLL.Domain.Mq; using PARR.BLL.Domain.Mq;
using PARR.BLL.Services.Interfaces; using PARR.BLL.Services.Interfaces;
using PARR.DAL.Services.Interfaces;
using PARR.TemplateMatcher.Services.Interfaces; using PARR.TemplateMatcher.Services.Interfaces;
using PARR.TemplateMatcher.Settings; using PARR.TemplateMatcher.Settings;
@@ -11,15 +13,19 @@ internal class TemplateUpdaterMqSender : ITemplateUpdaterMqSender
private readonly ILogger<TemplateUpdaterMqSender> logger; private readonly ILogger<TemplateUpdaterMqSender> logger;
private readonly IMqService mqService; private readonly IMqService mqService;
private readonly MqSettings mqSettings; private readonly MqSettings mqSettings;
private readonly ITemplateService templateService;
public TemplateUpdaterMqSender( public TemplateUpdaterMqSender(
ILogger<TemplateUpdaterMqSender> logger, ILogger<TemplateUpdaterMqSender> logger,
IMqService mqService, IMqService mqService,
MqSettings mqSettings) MqSettings mqSettings,
ITemplateService templateService
)
{ {
this.logger = logger; this.logger = logger;
this.mqService = mqService; this.mqService = mqService;
this.mqSettings = mqSettings; this.mqSettings = mqSettings;
this.templateService = templateService;
} }
public async Task SendTemplateUpdateMessageAsync(TemplateUpdaterMq updateRequest) public async Task SendTemplateUpdateMessageAsync(TemplateUpdaterMq updateRequest)
@@ -27,8 +33,15 @@ internal class TemplateUpdaterMqSender : ITemplateUpdaterMqSender
logger.LogDebug("Отправка сообщения в очередь '{Queue}' для шаблона {TemplateId}", logger.LogDebug("Отправка сообщения в очередь '{Queue}' для шаблона {TemplateId}",
mqSettings.TemplateUpdater.QueueName, updateRequest.TemplateId); mqSettings.TemplateUpdater.QueueName, updateRequest.TemplateId);
//var msg = JsonSerializer.Serialize(updateRequest);
//var result = await mqService.SendAsync(mqSettings.TemplateUpdater, new[] { msg }); //var existingTemplateByName = await templateService.Get().AsNoTracking()
// .FirstOrDefaultAsync(t => EF.Functions.ILike(t.Name, updateRequest.Name) && updateRequest.TemplateId != t.Id);
//if (existingTemplateByName != null)
//{
// logger.LogError("Шаблон с именем {TemplateName} уже существует в базе данных. Текущий Id:{TemplateId}", existingTemplateByName.Name, existingTemplateByName.Id);
// return;
//}
var result = await mqService.SendAsync(mqSettings.TemplateUpdater, new List<object> { updateRequest }); var result = await mqService.SendAsync(mqSettings.TemplateUpdater, new List<object> { updateRequest });
if (result.IsSuccess) if (result.IsSuccess)

View File

@@ -48,7 +48,7 @@ namespace PARR.TemplateUpdater
private async Task UpdateTemplateAsync(string msg) private async Task UpdateTemplateAsync(string msg)
{ {
logger.LogInformation($"Получили запрос: {msg}"); logger.LogInformation("Получили запрос: {msg}", msg);
var query = transformService.GetModelFromJson<TemplateUpdaterMq>(msg); var query = transformService.GetModelFromJson<TemplateUpdaterMq>(msg);
if (query == null) if (query == null)