diff --git a/PARR.API/Controllers/V1/RobotTaskController.cs b/PARR.API/Controllers/V1/RobotTaskController.cs index 9f3f1d3c..e6d587e8 100644 --- a/PARR.API/Controllers/V1/RobotTaskController.cs +++ b/PARR.API/Controllers/V1/RobotTaskController.cs @@ -67,6 +67,7 @@ namespace PARR.API.Controllers.V1 var query = robotConfigurationService.Get() + .AsSplitQuery() .Where(t => t.RobotCode == (int)robotCode && t.TaskStatusCode == (int)taskStatusCode); switch (robotCode) @@ -85,6 +86,7 @@ namespace PARR.API.Controllers.V1 .Include(t => t.Template) .ThenInclude(a => a!.Job) .ThenInclude(t => t!.Group) + .ThenInclude(g => g.GroupType) .Include(t => t.Template) .ThenInclude(w => w!.Job) .ThenInclude(t => t!.Tnk) @@ -93,7 +95,7 @@ namespace PARR.API.Controllers.V1 query = query .Include(t => t.Template) - .ThenInclude(t => t.UnitsInTemplate); + .ThenInclude(t => t!.UnitsInTemplate); break; @@ -108,6 +110,10 @@ namespace PARR.API.Controllers.V1 .ThenInclude(t => t!.Unit) .ThenInclude(t => t!.UnitValues) .ThenInclude(t => t.Value) + .Include(t => t.Template) + .ThenInclude(a => a!.Job) + .ThenInclude(t => t!.Group) + .ThenInclude(g => g.GroupType) .Include(t => t.Template) .ThenInclude(a => a!.Job) .ThenInclude(t => t!.Group) @@ -129,6 +135,7 @@ namespace PARR.API.Controllers.V1 } // сортируем по NextRun, чтобы те у которых дата след срабатывания ближе к текущей, выполнились скорее + //query = query.Where(t => t.Template.Job.GroupId == Guid.Parse("8a75030e-d2dc-4006-a773-002db36ebe27")); query = query.OrderBy(t => t.Template!.NextRun); RobotConfiguration? task = null; @@ -203,7 +210,7 @@ namespace PARR.API.Controllers.V1 }; if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.FullDescription)) - robotTaskTemplateResponse.FullDescription = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.FullDescription , templateForShortcodes); + robotTaskTemplateResponse.FullDescription = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.FullDescription, templateForShortcodes); if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.ShortDescription)) robotTaskTemplateResponse.ShortDescription = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.ShortDescription, templateForShortcodes); if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.Solution)) diff --git a/PARR.DAL/DomainServices/Implementations/GroupedShortcodesCacheService.cs b/PARR.DAL/DomainServices/Implementations/GroupedShortcodesCacheService.cs index 099f2067..229f37bc 100644 --- a/PARR.DAL/DomainServices/Implementations/GroupedShortcodesCacheService.cs +++ b/PARR.DAL/DomainServices/Implementations/GroupedShortcodesCacheService.cs @@ -2,7 +2,8 @@ using PARR.DAL.CacheServices; using PARR.DAL.DomainServices.Interfaces; using PARR.DAL.Settings; -using System.Text.RegularExpressions; +using System.Security.Cryptography; +using System.Text; namespace PARR.DAL.DomainServices.Implementations { @@ -23,6 +24,7 @@ namespace PARR.DAL.DomainServices.Implementations } public async Task GetAggregatedValueAsync( + Guid jobGroupId, Guid unitId, string shortcode, Func> computeIfMissing) @@ -30,7 +32,7 @@ namespace PARR.DAL.DomainServices.Implementations if (string.IsNullOrEmpty(shortcode)) throw new ArgumentException("Ключ шорткода должен быть указан.", nameof(shortcode)); - var cacheKey = GetCacheKey(unitId, shortcode); + var cacheKey = GetCacheKey(jobGroupId, unitId, shortcode); try { @@ -68,7 +70,7 @@ namespace PARR.DAL.DomainServices.Implementations } } - private static string GetCacheKey(Guid unitId, string shortcodeKey) + private static string GetCacheKey(Guid jobGroupId, Guid unitId, string shortcodeKey) { var safeKey = shortcodeKey .Trim() @@ -81,9 +83,13 @@ namespace PARR.DAL.DomainServices.Implementations .Replace("/", "_") .Replace("\\", "_"); - safeKey = Regex.Replace(safeKey, @"[^a-zA-Z0-9_-]", "_"); + // ✅ SHA256 от safeKey + using var sha256 = SHA256.Create(); + var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(safeKey)); + var hashHex = BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); - return $"gr_shcd_{unitId:N}_{safeKey}"; // :N — без дефисов в Guid + // ✅ Новый формат ключа + return $"gr_shcd_{jobGroupId:N}{unitId:N}_{hashHex}"; } } } \ No newline at end of file diff --git a/PARR.DAL/DomainServices/Interfaces/IGroupedShortcodesCacheService.cs b/PARR.DAL/DomainServices/Interfaces/IGroupedShortcodesCacheService.cs index 210917ce..9e2a442d 100644 --- a/PARR.DAL/DomainServices/Interfaces/IGroupedShortcodesCacheService.cs +++ b/PARR.DAL/DomainServices/Interfaces/IGroupedShortcodesCacheService.cs @@ -3,6 +3,7 @@ public interface IGroupedShortcodesCacheService { Task GetAggregatedValueAsync( + Guid jobGroupId, Guid unitId, string shortcodeKey, Func> computeIfMissing); diff --git a/PARR.DAL/DomainServices/Shortcodes/ShortcodesService.cs b/PARR.DAL/DomainServices/Shortcodes/ShortcodesService.cs index 02ee6121..bfce6b8e 100644 --- a/PARR.DAL/DomainServices/Shortcodes/ShortcodesService.cs +++ b/PARR.DAL/DomainServices/Shortcodes/ShortcodesService.cs @@ -124,7 +124,8 @@ namespace PARR.DAL.DomainServices.Shortcodes var maxShortcodes = Regex.Matches(resultName, maxShortcodePattern); if (maxShortcodes.Count > 0) { - resultName = await ReplaceMaxShortcodesAsync(job.Group.Id, template.UnitId, resultName, maxShortcodes); // ✅ Исправлено: job.GroupId + var unitIds = template.UnitsInTemplate.Select(uit => uit.UnitId).ToList(); + resultName = await ReplaceMaxShortcodesAsync(job.Group.Id, template.UnitId, unitIds, resultName, maxShortcodes); } } @@ -160,13 +161,14 @@ namespace PARR.DAL.DomainServices.Shortcodes { var fieldValues = await unitInValueService.Get() .AsNoTracking() + //.AsSplitQuery() .Include(uv => uv.Value) .Where(uv => uv.FieldId == groupingFieldId.Value && unitIds.Contains(uv.UnitId) && uv.Value != null && !string.IsNullOrWhiteSpace(uv.Value.Value)) - .Select(uv => new { uv.UnitId, Value = uv.Value.Value }) + .Select(uv => new { uv.UnitId, Value = uv.Value!.Value }) .ToListAsync(); valuesByUnit = fieldValues @@ -221,7 +223,9 @@ namespace PARR.DAL.DomainServices.Shortcodes public bool IsAnyShortcodes(string str) { - return Regex.IsMatch(str, shortcodePattern); + return Regex.IsMatch(str, shortcodePattern) || + Regex.IsMatch(str, maxShortcodePattern) || + Regex.IsMatch(str, lettersShortcodePattern); } private static List GetShortCodes(string resultName) @@ -359,7 +363,7 @@ namespace PARR.DAL.DomainServices.Shortcodes return resultName; } - private async Task ReplaceMaxShortcodesAsync(Guid jobGroupId, Guid unitId, string input, MatchCollection maxShortcodes) + private async Task ReplaceMaxShortcodesAsync(Guid jobGroupId, Guid unitId, List unitIds, string input, MatchCollection maxShortcodes) { var shortcodeToMatches = maxShortcodes .Cast() @@ -370,18 +374,21 @@ namespace PARR.DAL.DomainServices.Shortcodes { var fullShortcode = kvp.Key; var matches = kvp.Value; - var fieldName = fullShortcode.Trim('%').Split(':', 2)[1].Trim(); // "РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК" + var fieldName = fullShortcode.Trim('%').Split(':', 2)[1].Trim(); - logger.LogDebug("Обработка {Shortcode} для JobGroup {JobGroupId}, Template.UnitId {UnitId}", - fullShortcode, jobGroupId, unitId); + logger.LogDebug("Обработка {Shortcode} для JobGroup {JobGroupId}, Template.UnitId {UnitId}, fieldName {FieldName}", + fullShortcode, jobGroupId, unitId, fieldName); var mostFrequentValue = await groupedShortcodesCacheService.GetAggregatedValueAsync( + jobGroupId, unitId, fullShortcode, async () => { - // Логика вычисления, если кэш пуст - var unitIds = await jobService.Get() + logger.LogDebug("Кэш промахнут для {Shortcode}, начинаем вычисление.", fullShortcode); + + // ✅ Пытаемся загрузить UnitsInTemplate из БД + var dbUnitIds = await jobService.Get() .Where(j => j.GroupId == jobGroupId) .Join( templateService.Get() @@ -396,11 +403,23 @@ namespace PARR.DAL.DomainServices.Shortcodes .Distinct() .ToListAsync(); + logger.LogDebug("Загружено {Count} unitIds из БД для JobGroup {JobGroupId}", dbUnitIds.Count, jobGroupId); + + // ✅ Если в БД нет UnitsInTemplate — используем переданные unitIds + var effectiveUnitIds = dbUnitIds.Any() ? dbUnitIds : unitIds; + + logger.LogDebug("EffectiveUnitIds: [{Ids}], Count: {Count}", string.Join(", ", effectiveUnitIds), effectiveUnitIds.Count); + // Вызываем метод из сервиса - var result = await unitInValueService.GetMostFrequentValueForFieldAsync(unitIds, fieldName); + var result = await unitInValueService.GetMostFrequentValueForFieldAsync(effectiveUnitIds, fieldName); + + logger.LogDebug("Результат GetMostFrequentValueForFieldAsync: {Result}, для поля {FieldName}, unitIds: [{Ids}]", result, fieldName, string.Join(", ", effectiveUnitIds)); + return result ?? string.Empty; }); + logger.LogDebug("Итоговое значение для {Shortcode}: {Value}", fullShortcode, mostFrequentValue); + foreach (var match in matches) { input = input.Replace(match.Value, mostFrequentValue); diff --git a/PARR.TemplateMatcher/Services/Implemetaions/GroupedTemplateSynchronizer.cs b/PARR.TemplateMatcher/Services/Implemetaions/GroupedTemplateSynchronizer.cs index 17125f9d..315ef4b0 100644 --- a/PARR.TemplateMatcher/Services/Implemetaions/GroupedTemplateSynchronizer.cs +++ b/PARR.TemplateMatcher/Services/Implemetaions/GroupedTemplateSynchronizer.cs @@ -87,6 +87,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer // 1. Получить JobGroup и связанные Job'ы var jobGroup = await jobGroupService.Get() .AsNoTracking() + .Include(jg => jg.GroupType) .Include(jg => jg.Jobs) .ThenInclude(j => j.AutoControl) .Include(jg => jg.Jobs) diff --git a/PARR.TemplateMatcherWorker/appsettings.Development.json b/PARR.TemplateMatcherWorker/appsettings.Development.json index b8244af8..c0dd04f9 100644 --- a/PARR.TemplateMatcherWorker/appsettings.Development.json +++ b/PARR.TemplateMatcherWorker/appsettings.Development.json @@ -1,22 +1,25 @@ { - "Serilog": { - "MinimumLevel": { - "Default": "Debug", - "Override": { - "Microsoft": "Debug", - "Microsoft.Hosting.Lifetime": "Debug" - } + "ConnectionStrings": { + "RedisConnection": "10.99.253.216:6379,password=ParrP@ssPtk202MMdevDvs" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Debug", + "Microsoft.Hosting.Lifetime": "Debug" + } + }, + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "log/log-.txt", + "rollingInterval": "Day" + } + } + ] }, - "WriteTo": [ - { - "Name": "File", - "Args": { - "path": "log/log-.txt", - "rollingInterval": "Day" - } - } - ] - }, "MqSettings": { "TemplateMatcher": { "HostName": "10.99.253.216" }, "TemplateGenerator": { "HostName": "10.99.253.216" },