141 lines
7.1 KiB
PHP
141 lines
7.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\User;
|
|
use App\Models\UserActivityLog;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class UserAnalyticsController extends Controller
|
|
{
|
|
public function index(Request $request)
|
|
{
|
|
$tab = $request->input('tab', 'overview'); // overview | bots | activity | country
|
|
$country = $request->input('country');
|
|
$period = (int) $request->input('period', 30); // days
|
|
$from = now()->subDays($period);
|
|
|
|
// ── Overview stats ────────────────────────────────────────────────────
|
|
$totalReal = User::where('role', '!=', 'admin')->count();
|
|
$newReal = User::where('role', '!=', 'admin')->where('created_at', '>=', $from)->count();
|
|
$active30 = DB::table('analytics_pageviews')
|
|
->where('is_bot', 0)->where('created_at', '>=', $from)
|
|
->distinct('user_id')->whereNotNull('user_id')->count('user_id');
|
|
$botViews = DB::table('analytics_pageviews')
|
|
->where('is_bot', 1)->where('created_at', '>=', $from)->count();
|
|
$realViews = DB::table('analytics_pageviews')
|
|
->where('is_bot', 0)->where('created_at', '>=', $from)->count();
|
|
|
|
// ── Daily new users (chart) ───────────────────────────────────────────
|
|
$dailyNew = DB::table('users')
|
|
->selectRaw('DATE(created_at) as day, COUNT(*) as cnt')
|
|
->where('role', '!=', 'admin')
|
|
->where('created_at', '>=', $from)
|
|
->groupBy('day')->orderBy('day')
|
|
->pluck('cnt', 'day');
|
|
|
|
// ── Country breakdown ─────────────────────────────────────────────────
|
|
$countriesQuery = DB::table('analytics_pageviews')
|
|
->selectRaw('country, COUNT(*) as views, COUNT(DISTINCT user_id) as users')
|
|
->where('is_bot', 0)
|
|
->where('created_at', '>=', $from)
|
|
->whereNotNull('country')
|
|
->groupBy('country')
|
|
->orderByDesc('views');
|
|
if ($country) $countriesQuery->where('country', $country);
|
|
$countries = $countriesQuery->limit(50)->get();
|
|
|
|
// ── Bot analysis ──────────────────────────────────────────────────────
|
|
$botStats = DB::table('analytics_bot_logs')
|
|
->selectRaw('bot_name, action, COUNT(*) as cnt')
|
|
->where('created_at', '>=', $from)
|
|
->groupBy('bot_name', 'action')
|
|
->orderByDesc('cnt')
|
|
->limit(30)->get();
|
|
|
|
$topBotIps = DB::table('analytics_bot_logs')
|
|
->selectRaw('ip, COUNT(*) as cnt')
|
|
->where('created_at', '>=', $from)
|
|
->groupBy('ip')
|
|
->orderByDesc('cnt')
|
|
->limit(20)->get();
|
|
|
|
$blockedIps = DB::table('blocked_ips')
|
|
->orderByDesc('blocked_at')
|
|
->limit(30)->get();
|
|
|
|
// ── User activity log ─────────────────────────────────────────────────
|
|
$actQuery = UserActivityLog::with('user:id,name,username,avatar')
|
|
->where('created_at', '>=', $from);
|
|
if ($country) $actQuery->where('country', $country);
|
|
if ($request->input('user_id')) $actQuery->where('user_id', $request->input('user_id'));
|
|
if ($request->input('action')) $actQuery->where('action', $request->input('action'));
|
|
$actQuery->orderByDesc('created_at');
|
|
$actLogs = $actQuery->paginate(50)->withQueryString();
|
|
|
|
// ── Top active users ──────────────────────────────────────────────────
|
|
$topUsers = DB::table('user_activity_logs')
|
|
->selectRaw('user_id, COUNT(*) as actions')
|
|
->where('is_bot', 0)->whereNotNull('user_id')
|
|
->where('created_at', '>=', $from)
|
|
->groupBy('user_id')->orderByDesc('actions')
|
|
->limit(10)->get();
|
|
$topUserIds = $topUsers->pluck('user_id');
|
|
$topUserMap = User::whereIn('id', $topUserIds)->get()->keyBy('id');
|
|
|
|
// ── Action breakdown ──────────────────────────────────────────────────
|
|
$actionBreakdown = DB::table('user_activity_logs')
|
|
->selectRaw('action, COUNT(*) as cnt')
|
|
->where('is_bot', 0)
|
|
->where('created_at', '>=', $from)
|
|
->groupBy('action')->orderByDesc('cnt')
|
|
->get();
|
|
|
|
// ── Device breakdown ──────────────────────────────────────────────────
|
|
$deviceBreakdown = DB::table('analytics_pageviews')
|
|
->selectRaw('device, COUNT(*) as cnt')
|
|
->where('is_bot', 0)->where('created_at', '>=', $from)
|
|
->groupBy('device')->orderByDesc('cnt')->get();
|
|
|
|
return view('admin.analytics.users', compact(
|
|
'tab', 'period', 'country',
|
|
'totalReal', 'newReal', 'active30', 'botViews', 'realViews',
|
|
'dailyNew', 'countries', 'botStats', 'topBotIps', 'blockedIps',
|
|
'actLogs', 'topUsers', 'topUserMap', 'actionBreakdown', 'deviceBreakdown'
|
|
));
|
|
}
|
|
|
|
public function userDetail(Request $request, User $user)
|
|
{
|
|
$period = (int) $request->input('period', 30);
|
|
$from = now()->subDays($period);
|
|
|
|
$logs = UserActivityLog::where('user_id', $user->id)
|
|
->where('created_at', '>=', $from)
|
|
->orderByDesc('created_at')
|
|
->paginate(50)->withQueryString();
|
|
|
|
$actBreakdown = DB::table('user_activity_logs')
|
|
->selectRaw('action, COUNT(*) as cnt')
|
|
->where('user_id', $user->id)->where('created_at', '>=', $from)
|
|
->groupBy('action')->orderByDesc('cnt')->get();
|
|
|
|
$pageviews = DB::table('analytics_pageviews')
|
|
->where('user_id', $user->id)->where('created_at', '>=', $from)
|
|
->orderByDesc('created_at')->limit(100)->get();
|
|
|
|
$watchEvents = DB::table('analytics_watch_events as we')
|
|
->join('episodes as e', 'e.id', '=', 'we.episode_id')
|
|
->join('animes as a', 'a.id', '=', 'we.anime_id')
|
|
->selectRaw('we.created_at, a.title as anime_title, e.episode_number, we.percent_complete, we.seconds_watched')
|
|
->where('we.user_id', $user->id)->where('we.created_at', '>=', $from)
|
|
->orderByDesc('we.created_at')->limit(50)->get();
|
|
|
|
return view('admin.analytics.user-detail', compact(
|
|
'user', 'logs', 'actBreakdown', 'pageviews', 'watchEvents', 'period'
|
|
));
|
|
}
|
|
}
|