input('period', '7d'); $from = match ($period) { 'today' => now()->startOfDay(), '30d' => now()->subDays(30), '90d' => now()->subDays(90), default => now()->subDays(7), }; $cacheKey = 'admin_analytics_' . $period; $cached = Cache::remember($cacheKey, 300, function () use ($from, $period) { return $this->buildAnalytics($from, $period); }); extract($cached); // Gerçek zamanlı veriler (cache'lenmiyor) $recentViews = PageView::with('user:id,name') ->where('created_at', '>=', $from) ->orderByDesc('id') ->limit(20) ->get(); $blockedIps = collect(); $recentBots = collect(); try { $blockedIps = DB::table('blocked_ips')->orderByDesc('blocked_at')->limit(20)->get(); $recentBots = DB::table('analytics_bot_logs')->orderByDesc('id')->limit(30)->get(); } catch (\Exception) {} return view('admin.analytics.index', compact( 'period', 'from', 'totalViews', 'uniqueVisitors', 'watchSeconds', 'aiTotal', 'newUsers', 'viewsDelta', 'todayViews', 'yesterdayViews', 'trendLabels', 'trendData', 'watchTrendData', 'hourlyData', 'topAnimes', 'topEpisodes', 'deviceStats', 'browserStats', 'pageTypeStats', 'geoStats', 'activeUsers', 'aiByType', 'aiTopQuestions', 'aiTopUsers', 'recentViews', 'referrerStats', 'directTraffic', 'botViews', 'humanViews', 'botRatio', 'botTopIps', 'botByName', 'blockedIps', 'recentBots', 'sessions', 'avgSessionTime', 'avgPages', )); } private function buildAnalytics($from, string $period): array { // ── Özet kartlar ────────────────────────────────────────────────────── $totalViews = PageView::where('created_at', '>=', $from)->count(); $uniqueVisitors = PageView::where('created_at', '>=', $from)->distinct('session_id')->count('session_id'); $watchSeconds = WatchEvent::where('created_at', '>=', $from)->sum('seconds_watched'); $aiTotal = AiQuery::where('created_at', '>=', $from)->count(); $newUsers = User::where('created_at', '>=', $from)->count(); $yesterday = now()->subDay(); $todayViews = PageView::where('created_at', '>=', now()->startOfDay())->count(); $yesterdayViews = PageView::whereBetween('created_at', [$yesterday->startOfDay(), $yesterday->endOfDay()])->count(); $viewsDelta = $yesterdayViews > 0 ? round(($todayViews - $yesterdayViews) / $yesterdayViews * 100) : 0; // ── Görüntüleme trendi (gün bazlı) ──────────────────────────────────── $viewsByDay = PageView::where('created_at', '>=', $from) ->selectRaw('DATE(created_at) as date, COUNT(*) as cnt') ->groupBy('date') ->orderBy('date') ->pluck('cnt', 'date'); $trendLabels = []; $trendData = []; $cur = clone $from; while ($cur->lte(now())) { $key = $cur->format('Y-m-d'); $trendLabels[] = $cur->format($period === 'today' ? 'H:i' : 'd M'); $trendData[] = $viewsByDay[$key] ?? 0; $cur->addDay(); } // ── Saatlik dağılım (bugün) ─────────────────────────────────────────── $hourlyRaw = PageView::where('created_at', '>=', now()->startOfDay()) ->selectRaw('HOUR(created_at) as hour, COUNT(*) as cnt') ->groupBy('hour') ->pluck('cnt', 'hour'); $hourlyData = array_map(fn($h) => $hourlyRaw[$h] ?? 0, range(0, 23)); // ── İzleme süresi trendi ───────────────────────────────────────────── $watchByDay = WatchEvent::where('created_at', '>=', $from) ->selectRaw('DATE(created_at) as date, ROUND(SUM(seconds_watched)/3600, 1) as hours') ->groupBy('date') ->orderBy('date') ->pluck('hours', 'date'); $watchTrendData = array_map(fn($k) => (float)($watchByDay[$k] ?? 0), array_keys(array_flip($trendLabels))); // ── Top 10 anime ───────────────────────────────────────────────────── $topAnimeIds = PageView::where('created_at', '>=', $from) ->whereNotNull('anime_id') ->selectRaw('anime_id, COUNT(*) as cnt') ->groupBy('anime_id') ->orderByDesc('cnt') ->limit(10) ->pluck('cnt', 'anime_id'); $topAnimes = Anime::whereIn('id', $topAnimeIds->keys()) ->get(['id', 'title', 'cover_image']) ->map(fn($a) => [ 'title' => $a->title, 'views' => $topAnimeIds[$a->id] ?? 0, 'cover' => $a->cover_url, 'slug' => $a->slug, ]) ->sortByDesc('views') ->values(); // ── Top bölümler ───────────────────────────────────────────────────── $topEpisodes = WatchEvent::where('analytics_watch_events.created_at', '>=', $from) ->selectRaw('anime_id, season_number, episode_number, episode_id, SUM(seconds_watched) as total_sec, COUNT(*) as plays, ROUND(AVG(percent_complete), 0) as avg_pct') ->groupBy('anime_id', 'season_number', 'episode_number', 'episode_id') ->orderByDesc('total_sec') ->limit(10) ->get(); $epAnimes = Anime::whereIn('id', $topEpisodes->pluck('anime_id')->unique())->pluck('title', 'id'); $topEpisodes = $topEpisodes->map(fn($e) => [ 'anime' => $epAnimes[$e->anime_id] ?? 'Bilinmiyor', 'label' => "S{$e->season_number}E{$e->episode_number}", 'plays' => $e->plays, 'hours' => round($e->total_sec / 3600, 1), 'avg_pct' => $e->avg_pct, ]); // ── Cihaz / tarayıcı / sayfa türü ──────────────────────────────────── $deviceStats = PageView::where('created_at', '>=', $from) ->selectRaw('device, COUNT(*) as cnt') ->groupBy('device') ->pluck('cnt', 'device'); $browserStats = PageView::where('created_at', '>=', $from) ->selectRaw('browser, COUNT(*) as cnt') ->groupBy('browser') ->orderByDesc('cnt') ->pluck('cnt', 'browser'); $pageTypeStats = PageView::where('created_at', '>=', $from) ->selectRaw('page_type, COUNT(*) as cnt') ->groupBy('page_type') ->orderByDesc('cnt') ->pluck('cnt', 'page_type'); // ── Coğrafi dağılım ─────────────────────────────────────────────────── $geoStats = PageView::where('created_at', '>=', $from) ->whereNotNull('city') ->selectRaw('city, country, COUNT(*) as cnt') ->groupBy('city', 'country') ->orderByDesc('cnt') ->limit(15) ->get(['city', 'country', DB::raw('COUNT(*) as cnt')]); // ── En aktif kullanıcılar ───────────────────────────────────────────── $activeUserIds = PageView::where('created_at', '>=', $from) ->whereNotNull('user_id') ->selectRaw('user_id, COUNT(*) as views, COUNT(DISTINCT DATE(created_at)) as days') ->groupBy('user_id') ->orderByDesc('views') ->limit(10) ->get(); $activeUserList = User::whereIn('id', $activeUserIds->pluck('user_id')) ->get(['id', 'name', 'email', 'created_at']) ->keyBy('id'); $activeUsers = $activeUserIds->map(fn($r) => [ 'user' => $activeUserList[$r->user_id] ?? null, 'views' => $r->views, 'days' => $r->days, ])->filter(fn($r) => $r['user']); // ── AI istatistikleri ───────────────────────────────────────────────── $aiByType = AiQuery::where('created_at', '>=', $from) ->selectRaw('query_type, COUNT(*) as cnt') ->groupBy('query_type') ->orderByDesc('cnt') ->pluck('cnt', 'query_type'); $aiTopQuestions = AiQuery::where('created_at', '>=', $from) ->where('query_type', 'chat') ->whereNotNull('query_text') ->selectRaw('query_text, COUNT(*) as cnt') ->groupBy('query_text') ->orderByDesc('cnt') ->limit(10) ->get(); $aiByUser = AiQuery::where('created_at', '>=', $from) ->whereNotNull('user_id') ->selectRaw('user_id, COUNT(*) as cnt') ->groupBy('user_id') ->orderByDesc('cnt') ->limit(5) ->get(); $aiUserList = User::whereIn('id', $aiByUser->pluck('user_id'))->pluck('name', 'id'); $aiTopUsers = $aiByUser->map(fn($r) => [ 'name' => $aiUserList[$r->user_id] ?? 'Bilinmiyor', 'cnt' => $r->cnt, ]); // ── Referrer ───────────────────────────────────────────────────────── $referrerRaw = PageView::where('created_at', '>=', $from) ->whereNotNull('referrer') ->where('referrer', '!=', '') ->selectRaw('referrer, COUNT(*) as cnt') ->groupBy('referrer') ->orderByDesc('cnt') ->limit(30) ->pluck('cnt', 'referrer'); $referrerStats = collect(); foreach ($referrerRaw as $url => $cnt) { try { $parsed = parse_url($url); $domain = $parsed['host'] ?? $url; $domain = preg_replace('/^www\./', '', $domain); } catch (\Throwable) { $domain = $url; } if ($referrerStats->has($domain)) { $referrerStats[$domain] += $cnt; } else { $referrerStats[$domain] = $cnt; } } $referrerStats = $referrerStats->sortDesc()->take(15); $directTraffic = PageView::where('created_at', '>=', $from) ->where(fn($q) => $q->whereNull('referrer')->orWhere('referrer', '')) ->count(); // ── Bot istatistikleri ──────────────────────────────────────────────── $botViews = 0; $humanViews = 0; $botRatio = 0; $botTopIps = collect(); $botByName = collect(); try { $botViews = PageView::where('created_at', '>=', $from)->where('is_bot', 1)->count(); $humanViews = PageView::where('created_at', '>=', $from)->where('is_bot', 0)->count(); $botRatio = ($botViews + $humanViews) > 0 ? round($botViews / ($botViews + $humanViews) * 100) : 0; $botTopIps = DB::table('analytics_bot_logs') ->where('created_at', '>=', $from) ->selectRaw('ip, COUNT(*) as cnt, MAX(user_agent) as ua, MAX(action) as action') ->groupBy('ip') ->orderByDesc('cnt') ->limit(15) ->get(); $botByName = DB::table('analytics_bot_logs') ->where('created_at', '>=', $from) ->selectRaw('bot_name, COUNT(*) as cnt, action') ->groupBy('bot_name', 'action') ->orderByDesc('cnt') ->limit(20) ->get(); } catch (\Exception) {} // ── Oturum istatistikleri ───────────────────────────────────────────── $sessions = collect(); $avgSessionTime = 0; $avgPages = 0; try { $avgSessionTime = (int) DB::table('analytics_sessions') ->where('started_at', '>=', $from) ->where('is_bot', 0) ->avg('total_seconds'); $avgPages = round((float) DB::table('analytics_sessions') ->where('started_at', '>=', $from) ->where('is_bot', 0) ->avg('pages_visited'), 1); $sessions = DB::table('analytics_sessions') ->where('started_at', '>=', $from) ->orderByDesc('started_at') ->limit(30) ->get(); } catch (\Exception) {} return compact( 'totalViews', 'uniqueVisitors', 'watchSeconds', 'aiTotal', 'newUsers', 'viewsDelta', 'todayViews', 'yesterdayViews', 'trendLabels', 'trendData', 'watchTrendData', 'hourlyData', 'topAnimes', 'topEpisodes', 'deviceStats', 'browserStats', 'pageTypeStats', 'geoStats', 'activeUsers', 'aiByType', 'aiTopQuestions', 'aiTopUsers', 'referrerStats', 'directTraffic', 'botViews', 'humanViews', 'botRatio', 'botTopIps', 'botByName', 'sessions', 'avgSessionTime', 'avgPages' ); } public function blockIp(Request $request) { $data = $request->validate([ 'ip' => 'required|ip', 'reason' => 'nullable|string|max:255', 'expires_at' => 'nullable|date|after:now', ]); DB::table('blocked_ips')->updateOrInsert( ['ip' => $data['ip']], [ 'reason' => $data['reason'] ?? 'Manuel engel', 'auto_blocked' => 0, 'blocked_at' => now(), 'expires_at' => $data['expires_at'] ?? null, ] ); \Illuminate\Support\Facades\Cache::forget('blocked_ip_' . $data['ip']); return back()->with('success', $data['ip'] . ' engellendi.'); } public function unblockIp(Request $request) { $ip = $request->input('ip'); DB::table('blocked_ips')->where('ip', $ip)->delete(); \Illuminate\Support\Facades\Cache::forget('blocked_ip_' . $ip); return back()->with('success', $ip . ' engeli kaldırıldı.'); } }