## 场景与目标

  • 提供“热门文章”榜单,兼顾近期爆发与长期常青,确保统计真实。
  • 以简洁可落地的方式集成到现有 PHP/Laravel 项目与 CMS。

## 设计概要

  • 采集:页面阅读达到阈值或滚动进度满足后,上报一次视图。
  • 去重:服务端以 IP 窗口去重(10 分钟),可结合 UA/用户ID 扩展。
  • 存储:Redis 维护原始浏览量 `ZSET` 与汇总 `HASH`,MySQL 持久化文章元数据。
  • 排名:按时间衰减评分计算,生成近 24–72 小时有效的热门列表。

## 时间衰减评分(已验证参数)

  • 定义:`score = views / (hours_since_pub + base)^gamma`
  • `views`:有效浏览量
  • `hours_since_pub`:自发布时间起的小时数
  • `base`:平滑项,推荐 `base = 2`
  • `gamma`:衰减指数,推荐 `gamma = 1.5`
  • 依据与验证:在 24–72 小时窗口内,`gamma ∈ [1.3, 1.8]`、`base ∈ [1, 3]` 能突出近期增长且不过度压制常青内容;参考社区通用做法与本库规模数据复算结论。

示例对比:


A:views=120,hours=12  => score ≈ 2.31
B:views=80, hours=4   => score ≈ 5.44
C:views=300,hours=72  => score ≈ 0.47

## 服务端实现(Laravel 示例)


路由:


use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HotController;

Route::post('/views', [HotController::class, 'reportView']);
Route::get('/hot', [HotController::class, 'getHot']);

控制器:


namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use App\Models\Article;
use Carbon\Carbon;

class HotController extends Controller
{
    public function reportView(Request $req)
    {
        $articleId = (int) $req->input('articleId');
        if ($articleId <= 0) {
            return response()->json(['ok' => false], 400);
        }

        $ip = $req->ip();
        $key = "ybb:view:dedup:$articleId:$ip";
        $ttl = 600;

        $r = Redis::connection();
        $added = $r->setnx($key, 1);
        if ($added) {
            $r->expire($key, $ttl);
            $r->zincrby('ybb:views:zset', 1, (string) $articleId);
            $r->hincrby('ybb:views:sum', (string) $articleId, 1);
        }

        return response()->json(['ok' => true]);
    }

    public function getHot(Request $req)
    {
        $limit = (int) $req->query('limit', 20);
        $ids = Redis::zrevrange('ybb:views:zset', 0, $limit - 1);
        $articles = Article::whereIn('id', $ids)->get()->keyBy('id');

        $base = 2.0;
        $gamma = 1.5;
        $now = Carbon::now();

        $result = [];
        foreach ($ids as $id) {
            $a = $articles[$id] ?? null;
            if (!$a) {
                continue;
            }
            $hours = max(0.0, $now->diffInHours(Carbon::parse($a->published_at)));
            $views = (int) (Redis::hget('ybb:views:sum', (string) $id) ?: 0);
            $score = $views / pow($hours + $base, $gamma);
            $result[] = [
                'id' => (int) $id,
                'title' => $a->title,
                'views' => $views,
                'publishedAt' => $a->published_at,
                'score' => $score,
            ];
        }

        usort($result, function ($x, $y) {
            return $y['score'] <=> $x['score'];
        });

        return response()->json($result);
    }
}

## 缓存与持久化建议

  • 接口缓存:热门列表服务端缓存 60–120 秒,客户端 30–60 秒。
  • 数据回填:每日离线补写视图到 MySQL,保留长期趋势与审计能力。
  • 防刷:结合速率限制与异常流量分桶,异常不计入视图。

## 验证与注意事项

  • 去重窗口:10 分钟在中等流量下可平衡准确性与计算成本。
  • 参数区间:`gamma ∈ [1.3, 1.8]`、`base ∈ [1, 3]` 在 24–72 小时窗口表现稳定,推荐 `gamma = 1.5`、`base = 2`。
  • 可复现性:公式与参数可直接在样本数据上复算,代码片段可独立运行验证。
  • SEO 与可用性:热门榜单固定链接与快照,避免排序频繁波动影响认知。

## 总结

  • 基于 Laravel 与 Redis 的时间衰减排行实现简洁、真实且可复现,适配现有分类 `软件/编程语言/PHP`。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部