create([ 'run_id' => $run?->id, 'rule_id' => $rule?->id, 'severity' => $severity, 'type' => $type, 'message' => $message, 'context' => $context, 'is_resolved' => false, ]); $recipient = $rule?->alert_email ?: config('crawler.default_alert_email'); if (is_string($recipient) && $recipient !== '') { try { Mail::raw($this->buildEmailBody($alert), static function ($mail) use ($recipient, $severity): void { $mail->to($recipient) ->subject(sprintf('[Crawler][%s] 采集告警', strtoupper($severity->value))); }); } catch (\Throwable $exception) { Log::warning('Crawler alert email failed', [ 'alert_id' => $alert->id, 'error' => $exception->getMessage(), ]); } } return $alert; } private function buildEmailBody(CrawlAlert $alert): string { $lines = [ '采集告警通知', sprintf('等级: %s', $alert->severity?->value ?? 'unknown'), sprintf('类型: %s', $alert->type), sprintf('信息: %s', $alert->message), sprintf('规则ID: %s', (string) ($alert->rule_id ?? '-')), sprintf('运行ID: %s', (string) ($alert->run_id ?? '-')), sprintf('时间: %s', (string) $alert->created_at), ]; if (is_array($alert->context) && $alert->context !== []) { $lines[] = '上下文:'; $lines[] = json_encode($alert->context, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) ?: '{}'; } return implode("\n", $lines); } }