fix(dal): при формировании групповых шорткодов не хватало include GroupType, изменен ключ кэширования

This commit is contained in:
Mikhail Kuznetsov
2025-12-24 11:25:25 +10:00
parent bcb2272943
commit b5bbc52473
6 changed files with 71 additions and 34 deletions

View File

@@ -67,6 +67,7 @@ namespace PARR.API.Controllers.V1
var query = robotConfigurationService.Get() var query = robotConfigurationService.Get()
.AsSplitQuery()
.Where(t => t.RobotCode == (int)robotCode && t.TaskStatusCode == (int)taskStatusCode); .Where(t => t.RobotCode == (int)robotCode && t.TaskStatusCode == (int)taskStatusCode);
switch (robotCode) switch (robotCode)
@@ -85,6 +86,7 @@ namespace PARR.API.Controllers.V1
.Include(t => t.Template) .Include(t => t.Template)
.ThenInclude(a => a!.Job) .ThenInclude(a => a!.Job)
.ThenInclude(t => t!.Group) .ThenInclude(t => t!.Group)
.ThenInclude(g => g.GroupType)
.Include(t => t.Template) .Include(t => t.Template)
.ThenInclude(w => w!.Job) .ThenInclude(w => w!.Job)
.ThenInclude(t => t!.Tnk) .ThenInclude(t => t!.Tnk)
@@ -93,7 +95,7 @@ namespace PARR.API.Controllers.V1
query = query query = query
.Include(t => t.Template) .Include(t => t.Template)
.ThenInclude(t => t.UnitsInTemplate); .ThenInclude(t => t!.UnitsInTemplate);
break; break;
@@ -108,6 +110,10 @@ namespace PARR.API.Controllers.V1
.ThenInclude(t => t!.Unit) .ThenInclude(t => t!.Unit)
.ThenInclude(t => t!.UnitValues) .ThenInclude(t => t!.UnitValues)
.ThenInclude(t => t.Value) .ThenInclude(t => t.Value)
.Include(t => t.Template)
.ThenInclude(a => a!.Job)
.ThenInclude(t => t!.Group)
.ThenInclude(g => g.GroupType)
.Include(t => t.Template) .Include(t => t.Template)
.ThenInclude(a => a!.Job) .ThenInclude(a => a!.Job)
.ThenInclude(t => t!.Group) .ThenInclude(t => t!.Group)
@@ -129,6 +135,7 @@ namespace PARR.API.Controllers.V1
} }
// сортируем по NextRun, чтобы те у которых дата след срабатывания ближе к текущей, выполнились скорее // сортируем по NextRun, чтобы те у которых дата след срабатывания ближе к текущей, выполнились скорее
//query = query.Where(t => t.Template.Job.GroupId == Guid.Parse("8a75030e-d2dc-4006-a773-002db36ebe27"));
query = query.OrderBy(t => t.Template!.NextRun); query = query.OrderBy(t => t.Template!.NextRun);
RobotConfiguration? task = null; RobotConfiguration? task = null;
@@ -203,7 +210,7 @@ namespace PARR.API.Controllers.V1
}; };
if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.FullDescription)) 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)) if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.ShortDescription))
robotTaskTemplateResponse.ShortDescription = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.ShortDescription, templateForShortcodes); robotTaskTemplateResponse.ShortDescription = await shortcodesService.ApplyShortcodesAsync(robotTaskTemplateResponse.ShortDescription, templateForShortcodes);
if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.Solution)) if (shortcodesService.IsAnyShortcodes(robotTaskTemplateResponse.Solution))

View File

@@ -2,7 +2,8 @@
using PARR.DAL.CacheServices; using PARR.DAL.CacheServices;
using PARR.DAL.DomainServices.Interfaces; using PARR.DAL.DomainServices.Interfaces;
using PARR.DAL.Settings; using PARR.DAL.Settings;
using System.Text.RegularExpressions; using System.Security.Cryptography;
using System.Text;
namespace PARR.DAL.DomainServices.Implementations namespace PARR.DAL.DomainServices.Implementations
{ {
@@ -23,6 +24,7 @@ namespace PARR.DAL.DomainServices.Implementations
} }
public async Task<string> GetAggregatedValueAsync( public async Task<string> GetAggregatedValueAsync(
Guid jobGroupId,
Guid unitId, Guid unitId,
string shortcode, string shortcode,
Func<Task<string>> computeIfMissing) Func<Task<string>> computeIfMissing)
@@ -30,7 +32,7 @@ namespace PARR.DAL.DomainServices.Implementations
if (string.IsNullOrEmpty(shortcode)) if (string.IsNullOrEmpty(shortcode))
throw new ArgumentException("Ключ шорткода должен быть указан.", nameof(shortcode)); throw new ArgumentException("Ключ шорткода должен быть указан.", nameof(shortcode));
var cacheKey = GetCacheKey(unitId, shortcode); var cacheKey = GetCacheKey(jobGroupId, unitId, shortcode);
try 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 var safeKey = shortcodeKey
.Trim() .Trim()
@@ -81,9 +83,13 @@ namespace PARR.DAL.DomainServices.Implementations
.Replace("/", "_") .Replace("/", "_")
.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}";
} }
} }
} }

View File

@@ -3,6 +3,7 @@
public interface IGroupedShortcodesCacheService public interface IGroupedShortcodesCacheService
{ {
Task<string> GetAggregatedValueAsync( Task<string> GetAggregatedValueAsync(
Guid jobGroupId,
Guid unitId, Guid unitId,
string shortcodeKey, string shortcodeKey,
Func<Task<string>> computeIfMissing); Func<Task<string>> computeIfMissing);

View File

@@ -124,7 +124,8 @@ namespace PARR.DAL.DomainServices.Shortcodes
var maxShortcodes = Regex.Matches(resultName, maxShortcodePattern); var maxShortcodes = Regex.Matches(resultName, maxShortcodePattern);
if (maxShortcodes.Count > 0) 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() var fieldValues = await unitInValueService.Get()
.AsNoTracking() .AsNoTracking()
//.AsSplitQuery()
.Include(uv => uv.Value) .Include(uv => uv.Value)
.Where(uv => .Where(uv =>
uv.FieldId == groupingFieldId.Value && uv.FieldId == groupingFieldId.Value &&
unitIds.Contains(uv.UnitId) && unitIds.Contains(uv.UnitId) &&
uv.Value != null && uv.Value != null &&
!string.IsNullOrWhiteSpace(uv.Value.Value)) !string.IsNullOrWhiteSpace(uv.Value.Value))
.Select(uv => new { uv.UnitId, Value = uv.Value.Value }) .Select(uv => new { uv.UnitId, Value = uv.Value!.Value })
.ToListAsync(); .ToListAsync();
valuesByUnit = fieldValues valuesByUnit = fieldValues
@@ -221,7 +223,9 @@ namespace PARR.DAL.DomainServices.Shortcodes
public bool IsAnyShortcodes(string str) 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<Match> GetShortCodes(string resultName) private static List<Match> GetShortCodes(string resultName)
@@ -359,7 +363,7 @@ namespace PARR.DAL.DomainServices.Shortcodes
return resultName; return resultName;
} }
private async Task<string> ReplaceMaxShortcodesAsync(Guid jobGroupId, Guid unitId, string input, MatchCollection maxShortcodes) private async Task<string> ReplaceMaxShortcodesAsync(Guid jobGroupId, Guid unitId, List<Guid> unitIds, string input, MatchCollection maxShortcodes)
{ {
var shortcodeToMatches = maxShortcodes var shortcodeToMatches = maxShortcodes
.Cast<Match>() .Cast<Match>()
@@ -370,18 +374,21 @@ namespace PARR.DAL.DomainServices.Shortcodes
{ {
var fullShortcode = kvp.Key; var fullShortcode = kvp.Key;
var matches = kvp.Value; 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}", logger.LogDebug("Обработка {Shortcode} для JobGroup {JobGroupId}, Template.UnitId {UnitId}, fieldName {FieldName}",
fullShortcode, jobGroupId, unitId); fullShortcode, jobGroupId, unitId, fieldName);
var mostFrequentValue = await groupedShortcodesCacheService.GetAggregatedValueAsync( var mostFrequentValue = await groupedShortcodesCacheService.GetAggregatedValueAsync(
jobGroupId,
unitId, unitId,
fullShortcode, fullShortcode,
async () => async () =>
{ {
// Логика вычисления, если кэш пуст logger.LogDebug("Кэш промахнут для {Shortcode}, начинаем вычисление.", fullShortcode);
var unitIds = await jobService.Get()
// ✅ Пытаемся загрузить UnitsInTemplate из БД
var dbUnitIds = await jobService.Get()
.Where(j => j.GroupId == jobGroupId) .Where(j => j.GroupId == jobGroupId)
.Join( .Join(
templateService.Get() templateService.Get()
@@ -396,11 +403,23 @@ namespace PARR.DAL.DomainServices.Shortcodes
.Distinct() .Distinct()
.ToListAsync(); .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; return result ?? string.Empty;
}); });
logger.LogDebug("Итоговое значение для {Shortcode}: {Value}", fullShortcode, mostFrequentValue);
foreach (var match in matches) foreach (var match in matches)
{ {
input = input.Replace(match.Value, mostFrequentValue); input = input.Replace(match.Value, mostFrequentValue);

View File

@@ -87,6 +87,7 @@ internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
// 1. Получить JobGroup и связанные Job'ы // 1. Получить JobGroup и связанные Job'ы
var jobGroup = await jobGroupService.Get() var jobGroup = await jobGroupService.Get()
.AsNoTracking() .AsNoTracking()
.Include(jg => jg.GroupType)
.Include(jg => jg.Jobs) .Include(jg => jg.Jobs)
.ThenInclude(j => j.AutoControl) .ThenInclude(j => j.AutoControl)
.Include(jg => jg.Jobs) .Include(jg => jg.Jobs)

View File

@@ -1,22 +1,25 @@
{ {
"Serilog": { "ConnectionStrings": {
"MinimumLevel": { "RedisConnection": "10.99.253.216:6379,password=ParrP@ssPtk202MMdevDvs"
"Default": "Debug", },
"Override": { "Serilog": {
"Microsoft": "Debug", "MinimumLevel": {
"Microsoft.Hosting.Lifetime": "Debug" "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": { "MqSettings": {
"TemplateMatcher": { "HostName": "10.99.253.216" }, "TemplateMatcher": { "HostName": "10.99.253.216" },
"TemplateGenerator": { "HostName": "10.99.253.216" }, "TemplateGenerator": { "HostName": "10.99.253.216" },