Files
analytics/app/Console/Commands/CheckAlerts.php
T
2026-03-19 18:42:19 +02:00

117 lines
3.6 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Mail\AlertTriggered;
use App\Models\Alert;
use App\Services\AnalyticsQueryService;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;
class CheckAlerts extends Command
{
protected $signature = 'analytics:check-alerts';
protected $description = 'Evaluate active alerts and trigger notifications';
public function handle(): int
{
$alerts = Alert::with('website.user')
->where('is_active', true)
->get();
$triggered = 0;
foreach ($alerts as $alert) {
$currentValue = $this->getCurrentValue($alert);
$shouldTrigger = $this->evaluate($alert, $currentValue);
if ($shouldTrigger) {
$this->trigger($alert, $currentValue);
$triggered++;
}
}
$this->info("Checked {$alerts->count()} alerts, triggered {$triggered}.");
return self::SUCCESS;
}
private function getCurrentValue(Alert $alert): float
{
$to = Carbon::today();
$from = $alert->period === 'hourly'
? Carbon::now()->subHour()
: Carbon::yesterday();
$service = AnalyticsQueryService::for($alert->website, $from, $to);
$summary = $service->summary();
return match ($alert->metric) {
'page_views' => $summary['page_views'],
'visitors' => $summary['visitors'],
'sessions' => $summary['sessions'],
'bounce_rate' => $summary['bounce_rate'],
default => 0,
};
}
private function evaluate(Alert $alert, float $currentValue): bool
{
return match ($alert->condition) {
'above' => $currentValue > $alert->threshold,
'below' => $currentValue < $alert->threshold,
'change_up' => $this->percentageChange($alert) > $alert->threshold,
'change_down' => $this->percentageChange($alert) < -$alert->threshold,
default => false,
};
}
private function percentageChange(Alert $alert): float
{
$to = Carbon::today();
$from = Carbon::yesterday();
$prevFrom = Carbon::yesterday()->subDay();
$prevTo = Carbon::yesterday();
$current = $this->getValueForPeriod($alert, $from, $to);
$previous = $this->getValueForPeriod($alert, $prevFrom, $prevTo);
if ($previous == 0) {
return $current > 0 ? 100 : 0;
}
return (($current - $previous) / $previous) * 100;
}
private function getValueForPeriod(Alert $alert, Carbon $from, Carbon $to): float
{
$service = AnalyticsQueryService::for($alert->website, $from, $to);
$summary = $service->summary();
return $summary[$alert->metric] ?? 0;
}
private function trigger(Alert $alert, float $currentValue): void
{
$conditionLabel = Alert::$conditions[$alert->condition] ?? $alert->condition;
$metricLabel = Alert::$metrics[$alert->metric] ?? $alert->metric;
$message = "{$metricLabel} {$conditionLabel} {$alert->threshold} (current: {$currentValue}) for {$alert->website->name}";
$notification = $alert->notifications()->create([
'value' => $currentValue,
'message' => $message,
]);
$alert->update(['last_triggered_at' => now()]);
if (in_array($alert->channel, ['email', 'both'])) {
Mail::to($alert->website->user)->send(new AlertTriggered($alert, $notification));
}
$this->line(" → Triggered: {$alert->name} — {$message}");
}
}