175 lines
5.8 KiB
PHP
175 lines
5.8 KiB
PHP
<?php
|
|
/*
|
|
* @Author: YwxApp <ywx@ywxapp.cn>
|
|
* @Date: 2026-07-28 00:00:00
|
|
* @Description: 搜索蜘蛛统计
|
|
* @FilePath: \ywxapp_dev\app\backend\controller\Spider.php
|
|
* @CustomString: Copyright (c) 2026 YwxApp
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
namespace app\backend\controller;
|
|
|
|
use think\facade\Db;
|
|
use ywxapp\controller\BackendBase;
|
|
use ywxapp\library\SpiderDetect;
|
|
|
|
/**
|
|
* 搜索蜘蛛统计
|
|
*
|
|
* 数据来源:全局中间件 ywxapp\middleware\SpiderStat 写入的
|
|
* spider_log(明细)与 spider_stat(按日聚合)两张表。
|
|
*
|
|
* @author ywxapp <admin@ywxapp.cn>
|
|
*/
|
|
class Spider extends BackendBase
|
|
{
|
|
protected $noNeedLogin = [];
|
|
protected $noNeedVerify = [];
|
|
|
|
/**
|
|
* 抓取明细列表(页面 + Ajax)
|
|
*/
|
|
public function index()
|
|
{
|
|
if ($this->request->isAjax()) {
|
|
$page = $this->request->param('page/d', 1);
|
|
$limit = $this->request->param('limit/d', 15);
|
|
$spider = $this->request->param('spider', '');
|
|
$ip = $this->request->param('ip', '');
|
|
$url = $this->request->param('url', '');
|
|
$date = $this->request->param('date', ''); // YYYY-MM-DD
|
|
|
|
$query = Db::name('spider_log');
|
|
if ($spider !== '') {
|
|
$query->where('spider', $spider);
|
|
}
|
|
if ($ip !== '') {
|
|
$query->where('ip', 'like', $ip . '%');
|
|
}
|
|
if ($url !== '') {
|
|
$query->where('url', 'like', '%' . $url . '%');
|
|
}
|
|
if ($date !== '' && ($ts = strtotime($date)) !== false) {
|
|
$query->whereBetween('create_at', [$ts, $ts + 86399]);
|
|
}
|
|
|
|
try {
|
|
$list = $query->order('id', 'desc')
|
|
->paginate(['page' => $page, 'list_rows' => $limit]);
|
|
$items = $list->items();
|
|
$labels = SpiderDetect::labels();
|
|
foreach ($items as &$item) {
|
|
$item['spider_text'] = $labels[$item['spider']] ?? $item['spider'];
|
|
$item['time_text'] = date('Y-m-d H:i:s', (int) $item['create_at']);
|
|
}
|
|
unset($item);
|
|
$this->result->setCount($list->total())->success($items);
|
|
} catch (\Throwable $e) {
|
|
// 表尚未创建(还没有蜘蛛来访过):返回空列表而非报错
|
|
$this->result->setCount(0)->success([]);
|
|
}
|
|
}
|
|
|
|
return $this->fetch();
|
|
}
|
|
|
|
/**
|
|
* 汇总统计:各蜘蛛 今日/7日/30日/总量 + 近30日逐日趋势(图表用)
|
|
*/
|
|
public function stats()
|
|
{
|
|
$labels = SpiderDetect::labels();
|
|
$today = date('Y-m-d');
|
|
$d7 = date('Y-m-d', strtotime('-6 days'));
|
|
$d30 = date('Y-m-d', strtotime('-29 days'));
|
|
|
|
$summary = [];
|
|
$trend = ['dates' => [], 'series' => []];
|
|
|
|
try {
|
|
$rows = Db::name('spider_stat')
|
|
->where('stat_date', '>=', $d30)
|
|
->select()
|
|
->toArray();
|
|
$totalRows = Db::name('spider_stat')
|
|
->field('spider, SUM(`count`) AS total')
|
|
->group('spider')
|
|
->select()
|
|
->toArray();
|
|
|
|
// 各蜘蛛汇总
|
|
$map = [];
|
|
foreach ($totalRows as $r) {
|
|
$map[$r['spider']] = [
|
|
'spider' => $r['spider'],
|
|
'label' => $labels[$r['spider']] ?? $r['spider'],
|
|
'today' => 0,
|
|
'week' => 0,
|
|
'month' => 0,
|
|
'total' => (int) $r['total'],
|
|
];
|
|
}
|
|
foreach ($rows as $r) {
|
|
$key = $r['spider'];
|
|
if (! isset($map[$key])) {
|
|
continue;
|
|
}
|
|
$c = (int) $r['count'];
|
|
$map[$key]['month'] += $c;
|
|
if ($r['stat_date'] >= $d7) {
|
|
$map[$key]['week'] += $c;
|
|
}
|
|
if ($r['stat_date'] === $today) {
|
|
$map[$key]['today'] += $c;
|
|
}
|
|
}
|
|
usort($map, fn ($a, $b) => $b['total'] <=> $a['total']);
|
|
$summary = array_values($map);
|
|
|
|
// 近30日逐日趋势(每蜘蛛一条线)
|
|
$dates = [];
|
|
for ($i = 29; $i >= 0; $i--) {
|
|
$dates[] = date('Y-m-d', strtotime("-{$i} days"));
|
|
}
|
|
$trend['dates'] = $dates;
|
|
$bySpider = [];
|
|
foreach ($rows as $r) {
|
|
$bySpider[$r['spider']][$r['stat_date']] = (int) $r['count'];
|
|
}
|
|
foreach ($summary as $s) {
|
|
$key = $s['spider'];
|
|
$line = [];
|
|
foreach ($dates as $d) {
|
|
$line[] = $bySpider[$key][$d] ?? 0;
|
|
}
|
|
$trend['series'][] = ['name' => $s['label'], 'data' => $line];
|
|
}
|
|
} catch (\Throwable $e) {
|
|
// 表未创建:返回空数据
|
|
}
|
|
|
|
$this->result->success(['summary' => $summary, 'trend' => $trend]);
|
|
}
|
|
|
|
/**
|
|
* 清理明细日志(保留最近 N 天,聚合表不动,历史报表不受影响)
|
|
*/
|
|
public function clear()
|
|
{
|
|
if (! ($this->request->isAjax() && $this->request->isPost())) {
|
|
$this->result->error('请求方式错误', 405);
|
|
}
|
|
$days = $this->request->param('days/d', 30);
|
|
$days = max(1, min(365, $days));
|
|
try {
|
|
$count = Db::name('spider_log')
|
|
->where('create_at', '<', time() - $days * 86400)
|
|
->delete();
|
|
$this->result->success('', "已清理 {$count} 条 {$days} 天前的明细日志");
|
|
} catch (\Throwable $e) {
|
|
$this->result->error('清理失败: ' . $e->getMessage());
|
|
}
|
|
}
|
|
}
|