diff --git a/PARR.AIHITRelationshipsSyncer/Services/Implementations/RelationshipsSyncService.cs b/PARR.AIHITRelationshipsSyncer/Services/Implementations/RelationshipsSyncService.cs index 25e6074b..3b6bfd4f 100644 --- a/PARR.AIHITRelationshipsSyncer/Services/Implementations/RelationshipsSyncService.cs +++ b/PARR.AIHITRelationshipsSyncer/Services/Implementations/RelationshipsSyncService.cs @@ -2,9 +2,9 @@ using Microsoft.Extensions.Logging; using PARR.AIHITRelationshipsSyncer.Models; using PARR.AIHITRelationshipsSyncer.Services.Interfaces; -using PARR.AIHITRelationshipsSyncer.Settings; using PARR.Core.Repositories.Interfaces.Unit; using PARR.Domain.Entities.Unit; +using PARR.Domain.Settings; namespace PARR.AIHITRelationshipsSyncer.Services.Implementations { @@ -12,25 +12,56 @@ namespace PARR.AIHITRelationshipsSyncer.Services.Implementations { private readonly ILogger logger; private readonly IUnitRepository unitService; - private readonly WorkerSettings workerSettings; + private readonly SettingsFromDb _settingsFromDb; public RelationshipsSyncService( ILogger logger, IUnitRepository unitService, - WorkerSettings workerSettings + SettingsFromDb settingsFromDb ) { this.logger = logger; this.unitService = unitService; - this.workerSettings = workerSettings; + _settingsFromDb = settingsFromDb; } public async Task SyncAsync(List aihitdata) { + // 1. Сначала фильтруем и нормализуем входящий мусор var validPairs = ValidateInput(aihitdata); + + // Если пришел пустой список, то и проверять порог нет смысла (лог уже записан внутри) if (!validPairs.Any()) return; + // 2. FAIL-FAST: Быстро узнаем общее число связей в БД без выкачивания самих данных + int previousCount = await unitService.Get() + .SelectMany(u => u.ChildUnits) + .CountAsync(); + + // 3. SAFEGUARD COMPLIANCE: Проверяем защитный порог падения данных + if (previousCount > 0) + { + // 1. Считаем в decimal с абсолютной точностью + decimal exactPercentage = ((decimal)validPairs.Count / previousCount) * 100; + + // 2. Округляем до 1 знака после запятой (например, 96.98% -> 97.0%) + // Это защитит от ложных срабатываний из-за пары недостающих связей на больших объемах + decimal currentPercentage = Math.Round(exactPercentage, 1, MidpointRounding.AwayFromZero); + + // 3. Строгое сравнение (<) гарантирует пропуск при ровно 97% и работу "0" как выключателя + if (currentPercentage < _settingsFromDb.MinRelationshipsThresholdPct) + { + logger.LogWarning( + "Синхронизация отменена: количество полученных связей ниже порогового значения! " + + "Получено: {CurrentCount} ({CurrentPercentage:F1}%), ожидалось >= {Threshold}% от прошлого объема ({PreviousCount}).", + validPairs.Count, currentPercentage, _settingsFromDb.MinRelationshipsThresholdPct, previousCount); + + return; + } + } + + // 4. HAPPY PATH: Если проверка пройдена, выполняем тяжелую работу var sourceUnitNames = ExtractUnitNames(validPairs); var existingUnits = await GetOrCreateUnitsAsync(sourceUnitNames); @@ -42,6 +73,7 @@ namespace PARR.AIHITRelationshipsSyncer.Services.Implementations await FinalCommitAsync(toRemove.Count, toAdd.Count); } + private List<(string Parent, string Child)> ValidateInput(List data) { if (data == null || data.Count == 0)