feat(aihitRelationshipsSyncer): Добавлена реализация MinRelationshipsThresholdPct для защиты базы данных от потери связей ЭК

This commit is contained in:
Mikhail Kuznetsov
2026-07-14 14:55:49 +10:00
parent 4c80e247f5
commit 497d241caf

View File

@@ -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<RelationshipsSyncService> logger;
private readonly IUnitRepository unitService;
private readonly WorkerSettings workerSettings;
private readonly SettingsFromDb _settingsFromDb;
public RelationshipsSyncService(
ILogger<RelationshipsSyncService> logger,
IUnitRepository unitService,
WorkerSettings workerSettings
SettingsFromDb settingsFromDb
)
{
this.logger = logger;
this.unitService = unitService;
this.workerSettings = workerSettings;
_settingsFromDb = settingsFromDb;
}
public async Task SyncAsync(List<AihitData> 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<AihitData> data)
{
if (data == null || data.Count == 0)