Files
YwxAppThink/app/backend/controller/Spider.php
T

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());
}
}
}