$tenantId, 'alert_type' => 'aggregation_failure', 'domain' => $domain, 'severity' => 'critical', 'title' => "[{$jobType}] 집계 실패", 'message' => mb_substr($errorMessage, 0, 500), 'current_value' => 0, 'threshold_value' => 0, 'is_read' => false, 'is_resolved' => false, 'created_at' => now(), ]); } catch (\Throwable $e) { Log::error('stat_alert 기록 실패', [ 'tenant_id' => $tenantId, 'domain' => $domain, 'error' => $e->getMessage(), ]); } } /** * 데이터 누락 알림 (특정 날짜에 통계 데이터 없음) */ public function recordMissingData(int $tenantId, string $domain, string $date): void { try { // 동일 알림 중복 방지 $exists = StatAlert::where('tenant_id', $tenantId) ->where('alert_type', 'missing_data') ->where('domain', $domain) ->where('title', 'LIKE', "%{$date}%") ->where('is_resolved', false) ->exists(); if ($exists) { return; } StatAlert::create([ 'tenant_id' => $tenantId, 'alert_type' => 'missing_data', 'domain' => $domain, 'severity' => 'warning', 'title' => "[{$domain}] {$date} 데이터 누락", 'message' => "{$date} 날짜의 {$domain} 통계 데이터가 없습니다. stat:backfill 실행을 권장합니다.", 'current_value' => 0, 'threshold_value' => 1, 'is_read' => false, 'is_resolved' => false, 'created_at' => now(), ]); } catch (\Throwable $e) { Log::error('stat_alert 기록 실패 (missing_data)', [ 'tenant_id' => $tenantId, 'domain' => $domain, 'error' => $e->getMessage(), ]); } } /** * 정합성 불일치 알림 */ public function recordMismatch(int $tenantId, string $domain, string $label, float|int $expected, float|int $actual): void { try { StatAlert::create([ 'tenant_id' => $tenantId, 'alert_type' => 'data_mismatch', 'domain' => $domain, 'severity' => 'critical', 'title' => "[{$domain}] {$label} 정합성 불일치", 'message' => "원본={$expected}, 통계={$actual}, 차이=".($actual - $expected), 'current_value' => $actual, 'threshold_value' => $expected, 'is_read' => false, 'is_resolved' => false, 'created_at' => now(), ]); } catch (\Throwable $e) { Log::error('stat_alert 기록 실패 (mismatch)', [ 'tenant_id' => $tenantId, 'domain' => $domain, 'error' => $e->getMessage(), ]); } } /** * 알림 해결 처리 */ public function resolveAlerts(int $tenantId, string $domain, string $alertType): int { return StatAlert::where('tenant_id', $tenantId) ->where('domain', $domain) ->where('alert_type', $alertType) ->where('is_resolved', false) ->update([ 'is_resolved' => true, 'resolved_at' => now(), ]); } }