deca81c2d7
CsvReconcileJob — hourly резерв-канал приёма лидов через CSV-экспорт поставщика: - Cache::lock 600s (overlap protection). - Окно [now-25h, now] (запас 1ч над hourly cron). - INSERT supplier_csv_reconcile_log status='running' → 'ok' | 'drift_alert' | 'failed'. - Missing vids → INSERT supplier_leads (platform extracted из project, source='csv_recovery', recovered_from_csv_at=now) + dispatch RouteSupplierLeadJob. - Drift > 5% → CsvDriftAlertMail на services.supplier.alert_email. - UNIQUE-vid conflict → log + skip (idempotency). - На SupplierTransientException/любой Throwable → status='failed', error_message, rethrow. CsvDriftAlertMail + blade-template emails/csv_drift_alert. routes/console.php — Schedule::job(new CsvReconcileJob)->hourly(). config/services.php — supplier.alert_email default 'ops@liderra.ru'. 6 integration tests (CsvReconcileJobTest) + Schedule registration test (через Http::fake + Bus::fake + Mail::fake + SharesSupplierPdo trait для cross-connection). Parallel-test race fix: putSupplierSession() вызывается прямо перед SUT, потому что Sync/Cleanup tests'ы в afterEach делают forget('supplier:session'), а в --parallel режиме воркеры делят Redis DB+prefix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
16 lines
872 B
PHP
16 lines
872 B
PHP
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head><meta charset="UTF-8"><title>CSV drift alert</title></head>
|
||
<body style="font-family: Arial, sans-serif;">
|
||
<h3>Расхождение CSV-сверки с базой Лидерры</h3>
|
||
<p>Окно: <strong>{{ $windowStart->format('Y-m-d H:i') }} — {{ $windowEnd->format('Y-m-d H:i') }}</strong></p>
|
||
<ul>
|
||
<li>Всего строк в CSV: <strong>{{ $totalCsvRows }}</strong></li>
|
||
<li>Пропущено webhook'ом (recovered): <strong>{{ $missingCount }}</strong></li>
|
||
<li>Восстановлено в БД: <strong>{{ $recoveredCount }}</strong></li>
|
||
<li>Drift ratio: <strong>{{ number_format($driftRatio * 100, 2, ',', ' ') }}%</strong> (порог 5%)</li>
|
||
</ul>
|
||
<p>Подробности — в таблице <code>supplier_csv_reconcile_log.id = {{ $reconcileLogId }}</code>.</p>
|
||
</body>
|
||
</html>
|