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