payload['tid']) ->where('is_active', true) ->first(); if (! $website) { return; } $now = $this->receivedAt; $visitor = Visitor::updateOrCreate( [ 'website_id' => $website->id, 'uid' => $this->payload['vid'], ], [ 'last_seen_at' => $now, 'first_seen_at' => $now, 'device_type' => $this->payload['device'] ?? null, 'browser' => $this->payload['browser'] ?? null, 'browser_version' => $this->payload['browser_version'] ?? null, 'os' => $this->payload['os'] ?? null, 'os_version' => $this->payload['os_version'] ?? null, 'screen_resolution' => $this->payload['sr'] ?? null, 'language' => $this->payload['lang'] ?? null, ], ); if ($visitor->wasRecentlyCreated) { $visitor->update(['first_seen_at' => $now]); $this->geolocate($visitor); } $session = AnalyticsSession::firstOrCreate( ['uid' => $this->payload['sid']], [ 'website_id' => $website->id, 'visitor_id' => $visitor->id, 'started_at' => $now, 'entry_page' => $this->payload['path'] ?? $this->payload['url'] ?? null, 'ip_address' => $this->ip, 'referrer_url' => $this->payload['referrer'] ?? null, 'referrer_domain' => $this->payload['ref_domain'] ?? null, 'utm_source' => $this->payload['utm_source'] ?? null, 'utm_medium' => $this->payload['utm_medium'] ?? null, 'utm_campaign' => $this->payload['utm_campaign'] ?? null, 'utm_term' => $this->payload['utm_term'] ?? null, 'utm_content' => $this->payload['utm_content'] ?? null, ], ); if ($this->payload['type'] === 'pageview') { PageView::create([ 'website_id' => $website->id, 'visitor_id' => $visitor->id, 'analytics_session_id' => $session->id, 'url' => $this->payload['url'] ?? '', 'path' => $this->payload['path'] ?? '/', 'title' => $this->payload['title'] ?? null, 'referrer' => $this->payload['referrer'] ?? null, 'time_on_page' => $this->payload['time_on_page'] ?? null, 'viewed_at' => $now, ]); } if ($this->payload['type'] === 'event') { Event::create([ 'website_id' => $website->id, 'visitor_id' => $visitor->id, 'analytics_session_id' => $session->id, 'name' => $this->payload['event_name'] ?? 'unknown', 'category' => $this->payload['event_category'] ?? null, 'action' => $this->payload['event_action'] ?? null, 'label' => $this->payload['event_label'] ?? null, 'value' => $this->payload['event_value'] ?? null, 'properties' => $this->payload['event_properties'] ?? null, 'occurred_at' => $now, ]); } // Calculate duration, ensuring it's never negative (handle out-of-order events) // Use max() to ensure we use the latest timestamp for ended_at $endedAt = $session->ended_at ? max($now, $session->ended_at) : $now; $duration = max(0, (int) $endedAt->diffInSeconds($session->started_at, false)); $session->update([ 'ended_at' => $endedAt, 'duration' => $duration, 'exit_page' => $this->payload['path'] ?? $this->payload['url'] ?? $session->exit_page, 'pages_count' => $session->pageViews()->count(), 'is_bounce' => $session->pageViews()->count() <= 1, ]); $session->refresh(); $goalService->evaluate($website, $visitor, $session, $this->payload, $now); } private function geolocate(Visitor $visitor): void { try { $position = Location::get($this->ip); if ($position) { $visitor->update(array_filter([ 'country' => $position->countryCode ?? null, 'region' => $position->regionName ?? null, 'city' => $position->cityName ?? null, ])); } } catch (\Throwable) { // Geolocation is best-effort; never fail the job over it } } }