feat(aihitRelationshipsSyncer): Добавлена реализация MinRelationshipsThresholdPct для защиты базы данных от потери связей ЭК
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user