chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace ywxapp\middleware;
use think\facade\Db;
use ywxapp\model\BaseModel;
use ywxapp\library\SpiderDetect;
/**
* 全站搜索蜘蛛统计中间件(全局注册于 app/middleware.php
*
* 设计要点:
* - 仅命中蜘蛛 UA 才落库,普通用户请求零额外查询;
* - 落库在响应生成之后($next 之后),不阻塞蜘蛛抓取响应;
* - 明细写 spider_log,按日聚合 upsert 到 spider_stat(报表免全表扫描);
* - 任何数据库异常静默吞掉(统计绝不能影响业务),缺表时经 BaseModel 自愈引擎 自愈一次;
* - install.sql 为唯一事实源,此处 DDL 仅为老库运行时兜底。
*
* @author ywxapp <admin@ywxapp.cn>
*/
class SpiderStat
{
public function handle($request, \Closure $next)
{
$response = $next($request);
try {
$ua = (string) $request->header('user-agent', '');
$spider = SpiderDetect::detect($ua);
if ($spider !== null) {
$this->record($request, $response, $spider, $ua);
}
} catch (\Throwable $e) {
// 统计失败绝不影响正常响应
}
return $response;
}
/**
* 落库:明细 + 按日聚合
*/
protected function record($request, $response, string $spider, string $ua): void
{
$prefix = config('database.connections.mysql.prefix', 'wxapp_');
$logTable = $prefix . 'spider_log';
$statTable = $prefix . 'spider_stat';
$data = [
'spider' => $spider,
'url' => mb_substr((string) $request->url(), 0, 500),
'ip' => mb_substr((string) $request->ip(), 0, 45),
'app' => mb_substr((string) (app('http')->getName() ?: ''), 0, 20),
'user_agent' => mb_substr($ua, 0, 500),
'http_code' => method_exists($response, 'getCode') ? (int) $response->getCode() : 200,
'create_at' => time(),
];
try {
$this->insert($logTable, $statTable, $data, $spider);
} catch (\Throwable $e) {
// 表可能不存在(老库未升级):自愈一次后重试
$this->ensureTables($logTable, $statTable);
$this->insert($logTable, $statTable, $data, $spider);
}
}
/**
* 写明细 + 聚合 upsert
*/
protected function insert(string $logTable, string $statTable, array $data, string $spider): void
{
Db::table($logTable)->insert($data);
// 按日聚合:主键(stat_date, spider),存在则计数+1
Db::execute(
"INSERT INTO `{$statTable}` (`stat_date`, `spider`, `count`) VALUES (?, ?, 1) "
. "ON DUPLICATE KEY UPDATE `count` = `count` + 1",
[date('Y-m-d'), $spider]
);
}
/**
* 缺表自愈(DDL 与 public/install/install.sql 保持一致)
*/
protected function ensureTables(string $logTable, string $statTable): void
{
BaseModel::ensureTable($logTable, "CREATE TABLE IF NOT EXISTS `{$logTable}` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`spider` varchar(20) NOT NULL DEFAULT '' COMMENT '蜘蛛标识',
`url` varchar(500) NOT NULL DEFAULT '' COMMENT '抓取URL',
`ip` varchar(45) NOT NULL DEFAULT '' COMMENT '来源IP',
`app` varchar(20) NOT NULL DEFAULT '' COMMENT '应用名',
`user_agent` varchar(500) NOT NULL DEFAULT '' COMMENT 'User-Agent',
`http_code` smallint unsigned NOT NULL DEFAULT '200' COMMENT '响应状态码',
`create_at` int NOT NULL DEFAULT '0' COMMENT '抓取时间',
PRIMARY KEY (`id`),
KEY `idx_spider_time` (`spider`,`create_at`),
KEY `idx_create_at` (`create_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='搜索蜘蛛抓取日志'");
BaseModel::ensureTable($statTable, "CREATE TABLE IF NOT EXISTS `{$statTable}` (
`stat_date` date NOT NULL COMMENT '统计日期',
`spider` varchar(20) NOT NULL DEFAULT '' COMMENT '蜘蛛标识',
`count` int unsigned NOT NULL DEFAULT '0' COMMENT '抓取次数',
PRIMARY KEY (`stat_date`,`spider`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='搜索蜘蛛按日统计'");
}
}