option('date') ? (string) $this->option('date') : now()->subDay()->toDateString(); $rows = DB::table('feed_events') ->selectRaw('algo_version, source') ->selectRaw("SUM(CASE WHEN event_type = 'feed_impression' THEN 1 ELSE 0 END) AS impressions") ->selectRaw("SUM(CASE WHEN event_type = 'feed_click' THEN 1 ELSE 0 END) AS clicks") ->selectRaw("SUM(CASE WHEN event_type = 'feed_click' AND dwell_seconds IS NOT NULL AND dwell_seconds < 5 THEN 1 ELSE 0 END) AS dwell_0_5") ->selectRaw("SUM(CASE WHEN event_type = 'feed_click' AND dwell_seconds >= 5 AND dwell_seconds < 30 THEN 1 ELSE 0 END) AS dwell_5_30") ->selectRaw("SUM(CASE WHEN event_type = 'feed_click' AND dwell_seconds >= 30 AND dwell_seconds < 120 THEN 1 ELSE 0 END) AS dwell_30_120") ->selectRaw("SUM(CASE WHEN event_type = 'feed_click' AND dwell_seconds >= 120 THEN 1 ELSE 0 END) AS dwell_120_plus") ->whereDate('event_date', $date) ->groupBy('algo_version', 'source') ->get(); foreach ($rows as $row) { $algoVersion = (string) $row->algo_version; $source = (string) $row->source; $impressions = (int) ($row->impressions ?? 0); $clicks = (int) ($row->clicks ?? 0); $saves = $this->countSavesForGroup($date, $algoVersion, $source); $ctr = $impressions > 0 ? $clicks / $impressions : 0.0; $saveRate = $clicks > 0 ? $saves / $clicks : 0.0; DB::table('feed_daily_metrics')->updateOrInsert( [ 'metric_date' => $date, 'algo_version' => $algoVersion, 'source' => $source, ], [ 'impressions' => $impressions, 'clicks' => $clicks, 'saves' => $saves, 'ctr' => $ctr, 'save_rate' => $saveRate, 'dwell_0_5' => (int) ($row->dwell_0_5 ?? 0), 'dwell_5_30' => (int) ($row->dwell_5_30 ?? 0), 'dwell_30_120' => (int) ($row->dwell_30_120 ?? 0), 'dwell_120_plus' => (int) ($row->dwell_120_plus ?? 0), 'updated_at' => now(), 'created_at' => now(), ] ); } $this->info("Aggregated feed analytics for {$date}."); return self::SUCCESS; } private function countSavesForGroup(string $date, string $algoVersion, string $source): int { /** @var Collection $clickedPairs */ $clickedPairs = DB::table('feed_events') ->select('user_id', 'artwork_id') ->whereDate('event_date', $date) ->where('event_type', 'feed_click') ->where('algo_version', $algoVersion) ->where('source', $source) ->groupBy('user_id', 'artwork_id') ->get(); if ($clickedPairs->isEmpty()) { return 0; } $saves = 0; foreach ($clickedPairs as $pair) { $hasSave = DB::table('user_discovery_events') ->whereDate('event_date', $date) ->where('user_id', (int) $pair->user_id) ->where('artwork_id', (int) $pair->artwork_id) ->where('algo_version', $algoVersion) ->whereIn('event_type', ['favorite', 'download']) ->exists(); if ($hasSave) { $saves++; } } return $saves; } }