'admin.openapi.stats.index', 'show' => 'admin.openapi.stats.show', ] )] class StatController extends AdminController { protected OpenApiStatRepository $repository; public function __construct(OpenApiStatRepository $repository) { $this->repository = $repository; } /** * 统计概览页面 * * @param Content $content * @return Content */ public function index(Content $content): Content { return $content ->title('OpenAPI统计分析') ->description('API调用统计和分析') ->body($this->grid()); } /** * 统计详情页面 * * @param mixed $id * @param Content $content * @return Content */ public function show($id, Content $content): Content { return $content ->title('统计详情') ->description('查看详细统计信息') ->body($this->detail($id)); } /** * 构建数据表格 * * @return Grid */ protected function grid(): Grid { return Grid::make($this->repository->query()->with('app'), function (Grid $grid) { $grid->column('id', 'ID')->sortable(); $grid->column('app.name', '应用名称')->display(function ($name) { return $name ?: '未知应用'; }); $grid->column('app_id', '应用ID'); $grid->column('date', '统计日期')->display(function ($date) { return $date ? $date->format('Y-m-d') : ''; }); $grid->column('hour', '统计小时')->display(function ($hour) { return $hour !== null ? sprintf('%02d:00', $hour) : '全天'; }); $grid->column('endpoint', '接口端点')->limit(30); $grid->column('request_count', '请求次数')->sortable()->totalRow(); $grid->column('success_count', '成功次数')->sortable()->totalRow(); $grid->column('error_count', '错误次数')->sortable()->totalRow(); $grid->column('success_rate', '成功率')->display(function () { return $this->success_rate . '%'; })->label('success'); $grid->column('avg_response_time', '平均响应时间')->display(function ($time) { return $this->formatted_avg_response_time; }); $grid->column('rate_limit_hits', '限流次数')->sortable()->totalRow(); $grid->column('unique_ips', '唯一IP数')->sortable(); // 筛选器 $grid->filter(function (Grid\Filter $filter) { $filter->equal('app_id', '应用ID')->select( OpenApiApp::pluck('name', 'app_id') ); $filter->between('date', '统计日期')->date(); $filter->equal('hour', '统计小时')->select( array_combine(range(0, 23), array_map(function($h) { return sprintf('%02d:00', $h); }, range(0, 23))) ); $filter->like('endpoint', '接口端点'); $filter->between('request_count', '请求次数'); $filter->scope('today', '今日')->where('date', now()->toDateString()); $filter->scope('yesterday', '昨日')->where('date', now()->subDay()->toDateString()); $filter->scope('this_week', '本周')->whereBetween('date', [ now()->startOfWeek()->toDateString(), now()->endOfWeek()->toDateString() ]); $filter->scope('this_month', '本月')->whereBetween('date', [ now()->startOfMonth()->toDateString(), now()->endOfMonth()->toDateString() ]); }); // 工具栏 $grid->tools(function (Grid\Tools $tools) { $tools->append(' 统计仪表板 '); }); // 排序 $grid->model()->orderBy('date', 'desc')->orderBy('hour', 'desc'); // 禁用操作 $grid->disableCreateButton(); $grid->disableEditButton(); $grid->disableDeleteButton(); $grid->disableBatchActions(); // 行操作 $grid->actions(function (Grid\Displayers\Actions $actions) { $actions->disableEdit(); $actions->disableDelete(); }); }); } /** * 构建详情页面 * * @param mixed $id * @return Show */ protected function detail($id): Show { return Show::make($id, $this->repository->query()->with('app'), function (Show $show) { $show->field('id', 'ID'); $show->field('app.name', '应用名称'); $show->field('app_id', '应用ID'); $show->field('date', '统计日期'); $show->field('hour', '统计小时')->as(function ($hour) { return $hour !== null ? sprintf('%02d:00', $hour) : '全天'; }); $show->field('endpoint', '接口端点'); $show->divider('请求统计'); $show->field('request_count', '总请求次数'); $show->field('success_count', '成功次数'); $show->field('error_count', '错误次数'); $show->field('success_rate', '成功率')->as(function () { return $this->success_rate . '%'; })->label('success'); $show->divider('响应时间统计'); $show->field('avg_response_time', '平均响应时间')->as(function () { return $this->formatted_avg_response_time; }); $show->field('max_response_time', '最大响应时间')->as(function ($time) { return $time < 1000 ? round($time, 2) . 'ms' : round($time / 1000, 2) . 's'; }); $show->field('min_response_time', '最小响应时间')->as(function ($time) { return $time < 1000 ? round($time, 2) . 'ms' : round($time / 1000, 2) . 's'; }); $show->divider('其他统计'); $show->field('rate_limit_hits', '限流命中次数'); $show->field('unique_ips', '唯一IP数量'); $show->field('error_details', '错误详情')->json(); $show->field('created_at', '创建时间'); $show->field('updated_at', '更新时间'); // 禁用操作 $show->disableEditButton(); $show->disableDeleteButton(); }); } /** * 统计仪表板 * * @param Content $content * @return Content */ public function dashboard(Content $content): Content { return $content ->title('OpenAPI统计仪表板') ->description('API调用统计概览') ->row(function ($row) { // 今日统计卡片 $row->column(3, $this->todayStatsCard()); $row->column(3, $this->yesterdayStatsCard()); $row->column(3, $this->weekStatsCard()); $row->column(3, $this->monthStatsCard()); }) ->row(function ($row) { // 趋势图表 $row->column(6, $this->requestTrendChart()); $row->column(6, $this->errorRateChart()); }) ->row(function ($row) { // 热门接口和应用 $row->column(6, $this->topEndpointsCard()); $row->column(6, $this->topAppsCard()); }); } /** * 今日统计卡片 * * @return string */ protected function todayStatsCard(): string { $stats = OpenApiStats::today()->selectRaw(' SUM(request_count) as total_requests, SUM(success_count) as total_success, SUM(error_count) as total_errors, AVG(avg_response_time) as avg_time ')->first(); $successRate = $stats->total_requests > 0 ? round(($stats->total_success / $stats->total_requests) * 100, 2) : 0; return view('admin::widgets.info-box', [ 'style' => 'info', 'title' => '今日统计', 'number' => number_format($stats->total_requests ?? 0), 'description' => "成功率: {$successRate}%", 'icon' => 'fa-calendar-day', ])->render(); } /** * 昨日统计卡片 * * @return string */ protected function yesterdayStatsCard(): string { $stats = OpenApiStats::yesterday()->selectRaw(' SUM(request_count) as total_requests, SUM(success_count) as total_success, SUM(error_count) as total_errors ')->first(); $successRate = $stats->total_requests > 0 ? round(($stats->total_success / $stats->total_requests) * 100, 2) : 0; return view('admin::widgets.info-box', [ 'style' => 'warning', 'title' => '昨日统计', 'number' => number_format($stats->total_requests ?? 0), 'description' => "成功率: {$successRate}%", 'icon' => 'fa-calendar-minus', ])->render(); } /** * 本周统计卡片 * * @return string */ protected function weekStatsCard(): string { $stats = OpenApiStats::thisWeek()->selectRaw(' SUM(request_count) as total_requests, SUM(success_count) as total_success, SUM(error_count) as total_errors ')->first(); $successRate = $stats->total_requests > 0 ? round(($stats->total_success / $stats->total_requests) * 100, 2) : 0; return view('admin::widgets.info-box', [ 'style' => 'success', 'title' => '本周统计', 'number' => number_format($stats->total_requests ?? 0), 'description' => "成功率: {$successRate}%", 'icon' => 'fa-calendar-week', ])->render(); } /** * 本月统计卡片 * * @return string */ protected function monthStatsCard(): string { $stats = OpenApiStats::thisMonth()->selectRaw(' SUM(request_count) as total_requests, SUM(success_count) as total_success, SUM(error_count) as total_errors ')->first(); $successRate = $stats->total_requests > 0 ? round(($stats->total_success / $stats->total_requests) * 100, 2) : 0; return view('admin::widgets.info-box', [ 'style' => 'primary', 'title' => '本月统计', 'number' => number_format($stats->total_requests ?? 0), 'description' => "成功率: {$successRate}%", 'icon' => 'fa-calendar-alt', ])->render(); } /** * 请求趋势图表 * * @return string */ protected function requestTrendChart(): string { // 获取最近7天的数据 $data = OpenApiStats::whereBetween('date', [ now()->subDays(6)->toDateString(), now()->toDateString() ])->selectRaw(' date, SUM(request_count) as total_requests, SUM(success_count) as total_success, SUM(error_count) as total_errors ')->groupBy('date')->orderBy('date')->get(); return view('admin::widgets.chart', [ 'title' => '请求趋势(最近7天)', 'type' => 'line', 'data' => $data, ])->render(); } /** * 错误率图表 * * @return string */ protected function errorRateChart(): string { // 获取最近7天的错误率数据 $data = OpenApiStats::whereBetween('date', [ now()->subDays(6)->toDateString(), now()->toDateString() ])->selectRaw(' date, SUM(request_count) as total_requests, SUM(error_count) as total_errors ')->groupBy('date')->orderBy('date')->get()->map(function ($item) { $item->error_rate = $item->total_requests > 0 ? round(($item->total_errors / $item->total_requests) * 100, 2) : 0; return $item; }); return view('admin::widgets.chart', [ 'title' => '错误率趋势(最近7天)', 'type' => 'bar', 'data' => $data, ])->render(); } /** * 热门接口卡片 * * @return string */ protected function topEndpointsCard(): string { $endpoints = OpenApiStats::today()->selectRaw(' endpoint, SUM(request_count) as total_requests ')->groupBy('endpoint') ->orderBy('total_requests', 'desc') ->limit(10) ->get(); return view('admin::widgets.table', [ 'title' => '今日热门接口', 'headers' => ['接口', '请求次数'], 'data' => $endpoints->map(function ($item) { return [$item->endpoint, number_format($item->total_requests)]; }), ])->render(); } /** * 热门应用卡片 * * @return string */ protected function topAppsCard(): string { $apps = OpenApiStats::today() ->join('openapi_apps', 'openapi_stats.app_id', '=', 'openapi_apps.app_id') ->selectRaw(' openapi_apps.name, SUM(openapi_stats.request_count) as total_requests ')->groupBy('openapi_apps.name') ->orderBy('total_requests', 'desc') ->limit(10) ->get(); return view('admin::widgets.table', [ 'title' => '今日热门应用', 'headers' => ['应用名称', '请求次数'], 'data' => $apps->map(function ($item) { return [$item->name, number_format($item->total_requests)]; }), ])->render(); } }