Skip to content

Commit

Permalink
活跃用户
Browse files Browse the repository at this point in the history
  • Loading branch information
summerblue committed Mar 7, 2022
1 parent 2ef8ae4 commit 4c616e0
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 6 deletions.
26 changes: 26 additions & 0 deletions app/Console/Commands/CalculateActiveUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\User;

class CalculateActiveUser extends Command
{
// 供我们调用命令
protected $signature = 'larabbs:calculate-active-user';

// 命令的描述
protected $description = '生成活跃用户';

// 最终执行的方法
public function handle(User $user)
{
// 在命令行打印一行信息
$this->info("开始计算...");

$user->calculateAndCacheActiveUsers();

$this->info("成功生成!");
}
}
6 changes: 5 additions & 1 deletion app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
// $schedule->command('inspire')
// ->hourly();

// 一小时执行一次『活跃用户』数据生成的命令
$schedule->command('larabbs:calculate-active-user')->hourly();
}

/**
Expand Down
7 changes: 5 additions & 2 deletions app/Http/Controllers/CategoriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@
use Illuminate\Http\Request;
use App\Models\Topic;
use App\Models\Category;
use App\Models\User;

class CategoriesController extends Controller
{
public function show(Category $category, Request $request, Topic $topic)
public function show(Category $category, Request $request, Topic $topic, User $user)
{
// 读取分类 ID 关联的话题,并按每 20 条分页
$topics = $topic->withOrder($request->order)
->where('category_id', $category->id)
->with('user', 'category') // 预加载防止 N+1 问题
->paginate(20);
// 活跃用户列表
$active_users = $user->getActiveUsers();

// 传参变量话题和分类到模板中
return view('topics.index', compact('topics', 'category'));
return view('topics.index', compact('topics', 'category', 'active_users'));
}
}
7 changes: 5 additions & 2 deletions app/Http/Controllers/TopicsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use App\Models\Category;
use Illuminate\Support\Facades\Auth;
use App\Handlers\ImageUploadHandler;
use App\Models\User;

class TopicsController extends Controller
{
Expand All @@ -17,12 +18,14 @@ public function __construct()
$this->middleware('auth', ['except' => ['index', 'show']]);
}

public function index(Request $request, Topic $topic)
public function index(Request $request, Topic $topic, User $user)
{
$topics = $topic->withOrder($request->order)
->with('user', 'category') // 预加载防止 N+1 问题
->paginate(20);
return view('topics.index', compact('topics'));
$active_users = $user->getActiveUsers();

return view('topics.index', compact('topics', 'active_users'));
}

public function show(Request $request, Topic $topic)
Expand Down
117 changes: 117 additions & 0 deletions app/Models/Traits/ActiveUserHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace App\Models\Traits;

use App\Models\Topic;
use App\Models\Reply;
use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Arr;

trait ActiveUserHelper
{
// 用于存放临时用户数据
protected $users = [];

// 配置信息
protected $topic_weight = 4; // 话题权重
protected $reply_weight = 1; // 回复权重
protected $pass_days = 7; // 多少天内发表过内容
protected $user_number = 6; // 取出来多少用户

// 缓存相关配置
protected $cache_key = 'larabbs_active_users';
protected $cache_expire_in_seconds = 65 * 60;

public function getActiveUsers()
{
// 尝试从缓存中取出 cache_key 对应的数据。如果能取到,便直接返回数据。
// 否则运行匿名函数中的代码来取出活跃用户数据,返回的同时做了缓存。
return Cache::remember($this->cache_key, $this->cache_expire_in_seconds, function(){
return $this->calculateActiveUsers();
});
}

public function calculateAndCacheActiveUsers()
{
// 取得活跃用户列表
$active_users = $this->calculateActiveUsers();
// 并加以缓存
$this->cacheActiveUsers($active_users);
}

private function calculateActiveUsers()
{
$this->calculateTopicScore();
$this->calculateReplyScore();

// 数组按照得分排序
$users = Arr::sort($this->users, function ($user) {
return $user['score'];
});

// 我们需要的是倒序,高分靠前,第二个参数为保持数组的 KEY 不变
$users = array_reverse($users, true);

// 只获取我们想要的数量
$users = array_slice($users, 0, $this->user_number, true);

// 新建一个空集合
$active_users = collect();

foreach ($users as $user_id => $user) {
// 找寻下是否可以找到用户
$user = $this->find($user_id);

// 如果数据库里有该用户的话
if ($user) {

// 将此用户实体放入集合的末尾
$active_users->push($user);
}
}

// 返回数据
return $active_users;
}

private function calculateTopicScore()
{
// 从话题数据表里取出限定时间范围($pass_days)内,有发表过话题的用户
// 并且同时取出用户此段时间内发布话题的数量
$topic_users = Topic::query()->select(DB::raw('user_id, count(*) as topic_count'))
->where('created_at', '>=', Carbon::now()->subDays($this->pass_days))
->groupBy('user_id')
->get();
// 根据话题数量计算得分
foreach ($topic_users as $value) {
$this->users[$value->user_id]['score'] = $value->topic_count * $this->topic_weight;
}
}

private function calculateReplyScore()
{
// 从回复数据表里取出限定时间范围($pass_days)内,有发表过回复的用户
// 并且同时取出用户此段时间内发布回复的数量
$reply_users = Reply::query()->select(DB::raw('user_id, count(*) as reply_count'))
->where('created_at', '>=', Carbon::now()->subDays($this->pass_days))
->groupBy('user_id')
->get();
// 根据回复数量计算得分
foreach ($reply_users as $value) {
$reply_score = $value->reply_count * $this->reply_weight;
if (isset($this->users[$value->user_id])) {
$this->users[$value->user_id]['score'] += $reply_score;
} else {
$this->users[$value->user_id]['score'] = $reply_score;
}
}
}

private function cacheActiveUsers($active_users)
{
// 将数据放入缓存中
Cache::put($this->cache_key, $active_users, $this->cache_expire_in_seconds);
}
}
1 change: 1 addition & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

class User extends Authenticatable implements MustVerifyEmail
{
use Traits\ActiveUserHelper;
use HasRoles;
use HasApiTokens, HasFactory, MustVerifyEmailTrait;

Expand Down
21 changes: 20 additions & 1 deletion resources/views/topics/_sidebar.blade.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
<div class="card ">
<div class="card-body">
<a href="{{ route('topics.create') }}" class="btn btn-success w-100" aria-label="Left Align">
<i class="fas fa-pencil-alt mr-2"></i> 新建帖子
<i class="fas fa-pencil-alt mr-2"></i> 新建帖子
</a>
</div>
</div>

@if (count($active_users))
<div class="card mt-4">
<div class="card-body active-users pt-2">
<div class="text-center mt-1 mb-0 text-muted">活跃用户</div>
<hr class="mt-2">
@foreach ($active_users as $active_user)
<a class="d-flex mt-2 text-decoration-none" href="{{ route('users.show', $active_user->id) }}">
<div class="media-left media-middle me-2 ml-1">
<img src="{{ $active_user->avatar }}" width="24px" height="24px" class="media-object">
</div>
<div class="media-body">
<small class="media-heading text-secondary">{{ $active_user->name }}</small>
</div>
</a>
@endforeach
</div>
</div>
@endif

0 comments on commit 4c616e0

Please sign in to comment.