yunzer/app/service/VisitStatsService.php
2025-05-19 21:51:21 +08:00

133 lines
4.1 KiB
PHP

<?php
namespace app\service;
use think\facade\Cache;
use think\facade\Request;
use think\facade\Db;
class VisitStatsService
{
// Redis实例
protected $redis;
// 键名前缀
protected $prefix = 'stats:';
public function __construct()
{
// 获取Redis处理器
$this->redis = Cache::store('redis')->handler();
}
/**
* 记录访问并更新统计数据
*/
public function recordVisit(string $page = 'home', string $userId = null): array
{
$date = date('Y-m-d');
$hour = date('H');
$userId = $userId ?? Request::ip();
// 使用管道提高性能
$pipe = $this->redis->multi();
// 总访问量(PV)
$pipe->incr($this->prefix.'total_visits');
// 每日访问量
$pipe->incr($this->prefix.'daily:'.$date);
// 页面统计
$pipe->zIncrBy($this->prefix.'page_views', 1, $page);
// UV统计(使用HyperLogLog节省内存)
$pipe->pfAdd($this->prefix.'uv:'.$date, [$userId]);
// 时段统计
$pipe->hIncrBy($this->prefix.'hourly:'.$date, $hour, 1);
// 执行所有命令
$result = $pipe->exec();
// 更新数据库统计
$this->updateDailyStats($date, [
'total_visits' => $result[0],
'daily_visits' => $result[1],
'unique_visitors' => $this->getUniqueVisitors($date)
]);
return [
'total' => $result[0],
'daily' => $result[1],
'page' => $result[2],
'uv' => $result[3],
'hourly'=> $result[4]
];
}
/**
* 更新每日统计数据
*/
protected function updateDailyStats(string $date, array $stats)
{
// 获取其他统计数据
$otherStats = [
'total_users' => Db::name('users')->where('delete_time', null)->count(),
'new_users' => Db::name('users')->whereDay('create_time', $date)->count(),
'total_articles' => Db::name('articles')->where('delete_time', null)->count(),
'daily_articles' => Db::name('articles')->whereDay('create_time', $date)->count(),
'article_views' => Db::name('articles')->whereDay('update_time', $date)->sum('views'),
'total_resources' => Db::name('resources')->where('delete_time', null)->count(),
'daily_resources' => Db::name('resources')->whereDay('create_time', $date)->count(),
'resource_downloads' => Db::name('resources')->whereDay('update_time', $date)->sum('downloads')
];
// 合并统计数据
$stats = array_merge($stats, $otherStats);
// 更新或插入统计数据
Db::name('daily_stats')->insertOrUpdate([
'date' => $date,
'total_users' => $stats['total_users'],
'new_users' => $stats['new_users'],
'total_visits' => $stats['total_visits'],
'daily_visits' => $stats['daily_visits'],
'unique_visitors' => $stats['unique_visitors'],
'total_articles' => $stats['total_articles'],
'daily_articles' => $stats['daily_articles'],
'article_views' => $stats['article_views'],
'total_resources' => $stats['total_resources'],
'daily_resources' => $stats['daily_resources'],
'resource_downloads' => $stats['resource_downloads']
], ['date']);
}
/**
* 获取总访问量
*/
public function getTotalVisits(): int
{
return (int)$this->redis->get($this->prefix.'total_visits');
}
/**
* 获取当日访问量
*/
public function getDailyVisits(string $date = null): int
{
$date = $date ?? date('Y-m-d');
return (int)$this->redis->get($this->prefix.'daily:'.$date);
}
/**
* 获取独立访客数(UV)
*/
public function getUniqueVisitors(string $date = null): int
{
$date = $date ?? date('Y-m-d');
return $this->redis->pfCount($this->prefix.'uv:'.$date);
}
/**
* 获取热门页面