feat(templateMatcher): Убрана избыточная проверка связанных юнитов, теперь это выполняется в UnitFilter. Добавлена группировка по рабочей группе или ответственному за ЭК

This commit is contained in:
Mikhail Kuznetsov
2026-03-26 18:08:13 +10:00
parent 4864b9b343
commit 73d7f4b200
7 changed files with 368 additions and 429 deletions

View File

@@ -738,7 +738,8 @@ namespace PARR.DAL.DomainServices.Shortcodes
return NoContent;
}
var cacheKey = $"gr_shcd_{jobGroupId:N}_{unitId:N}_{ComputeHash(fullShortcode)}";
//var cacheKey = $"gr_shcd_{jobGroupId:N}_{unitId:N}_{ComputeHash(fullShortcode)}";
var cacheKey = cacheService.GetKey(new[] { "grouped", "shortcode", $"{jobGroupId:N}", $"{unitId:N}" }, new[] { fullShortcode });
var cachedData = await cacheService.GetCachedDataAsync<CachedGroupedShortCode>(cacheKey).ConfigureAwait(false);
if (cachedData != null)
@@ -872,12 +873,5 @@ namespace PARR.DAL.DomainServices.Shortcodes
}
return result.ToString();
}
private static string ComputeHash(string input)
{
using var sha256 = System.Security.Cryptography.SHA256.Create();
var hashedBytes = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input));
return Convert.ToBase64String(hashedBytes).Replace('+', '-').Replace('/', '_').Substring(0, 16);
}
}
}

View File

@@ -566,6 +566,7 @@ internal class UnitFilterService : IUnitFilterService
return result;
}
/// <summary>
/// Формируем итоговый результат
/// </summary>
@@ -781,6 +782,7 @@ internal class UnitFilterService : IUnitFilterService
return result.ToList();
}
#region вспомогательные методы
/// <summary>
/// Применяет фильтры к связям (родителям или детям)
@@ -959,7 +961,6 @@ internal class UnitFilterService : IUnitFilterService
}
private async Task<List<Guid>> GetUnitIdsFromCacheOrDbAsync(JobUnitFilter filter, CancellationToken cancellationToken = default)
{
// var cacheKey = cacheService.GetKey(new[] { "uf_ids", filter.UnitFilter }, isUseHash: true);
@@ -991,7 +992,6 @@ internal class UnitFilterService : IUnitFilterService
}
/// <summary>
/// Обрабатывает маску LIKE для корректной работы с SQL
/// </summary>

View File

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

View File

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