运用外键同步删除与最后活跃时间

This commit is contained in:
fthvgb1 2018-06-02 23:37:19 +08:00
parent 395b1c619a
commit 194c3cc259
10 changed files with 287 additions and 5 deletions

View File

@ -0,0 +1,44 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
class SyncUserActivedAt extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'larabbs:sync-user-actived-at';
/**
* The console command description.
*
* @var string
*/
protected $description = '同步最后活动时间';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
* @param User $user
* @return mixed
*/
public function handle(User $user)
{
$user->syncUserActivedAt();
$this->info('同步成功');
}
}

View File

@ -27,6 +27,7 @@ class Kernel extends ConsoleKernel
// $schedule->command('inspire')
// ->hourly();
$schedule->command('larabbs:calculate-active-user')->hourly();
$schedule->command('larabbs:sync-user-actived-at')->dailyAt('00:05');
}
/**

View File

@ -2,6 +2,7 @@
namespace App\Http;
use App\Http\Middleware\RecordLastActivedTime;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
@ -10,34 +11,50 @@ class Kernel extends HttpKernel
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
* 全局中间件,最先调用
*
* @var array
*/
protected $middleware = [
// 检测是否应用是否进入『维护模式』
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
// 检测请求的数据是否过大
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
// 对提交的请求参数进行 PHP 函数 `trim()` 处理
\App\Http\Middleware\TrimStrings::class,
// 将提交请求参数中空子串转换为 null
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
// 修正代理服务器后的服务器参数
\App\Http\Middleware\TrustProxies::class,
];
/**
* The application's route middleware groups.
*
*定义中间件组
* @var array
*/
protected $middlewareGroups = [
// Web 中间件组,应用于 routes/web.php 路由文件
'web' => [
// Cookie 加密解密
\App\Http\Middleware\EncryptCookies::class,
// 将 Cookie 添加到响应中
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
// 开启会话
\Illuminate\Session\Middleware\StartSession::class,
// 认证用户,此中间件以后 Auth 类才能生效
// \Illuminate\Session\Middleware\AuthenticateSession::class,
// 将系统的错误数据注入到视图变量 $errors 中
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
// 检验 CSRF ,防止跨站请求伪造的安全威胁
\App\Http\Middleware\VerifyCsrfToken::class,
// 处理路由绑定
\Illuminate\Routing\Middleware\SubstituteBindings::class,
RecordLastActivedTime::class,
],
// API 中间件组,应用于 routes/api.php 路由文件
'api' => [
// 使用别名来调用中间件
'throttle:60,1',
'bindings',
],
@ -45,17 +62,22 @@ class Kernel extends HttpKernel
/**
* The application's route middleware.
*
* 中间件别名设置,允许你使用别名调用中间件,例如上面的 api 中间件组调用
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
//只有登录用户才能访问,我们在控制器的构造方法中大量使用
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
// 处理路由绑定
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
// 用户授权功能
'can' => \Illuminate\Auth\Middleware\Authorize::class,
// 只有游客才能访问,在 register 和 login 请求中使用,只有未登录用户才能访问这些页面
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
// 访问节流,类似于 『1 分钟只能请求 10 次』的需求,一般在 API 中使用
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Middleware;
use Closure;
class RecordLastActivedTime
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
if (\Auth::getUser()) {
\Auth::user()->recordLastActivedAt();
}
return $response;
}
}

View File

@ -2,12 +2,18 @@
namespace App\Models;
use App\Models\Traits\ActiveUserHelper;
use App\Traits\ActiveUserHelper;
use App\Traits\LastActivedAtHelper;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Traits\HasRoles;
/**
* Class User
* @property string avatar
* @package App\Models
*/
class User extends Authenticatable
{
use Notifiable {
@ -15,6 +21,7 @@ class User extends Authenticatable
}
use HasRoles;
use ActiveUserHelper;
use LastActivedAtHelper;
/**
* The attributes that are mass assignable.
@ -34,11 +41,23 @@ class User extends Authenticatable
'password', 'remember_token',
];
/**
* @return string
*/
public function getHeaderAttribute()
{
return asset($this->avatar);
}
public function getLastActiveATAttribute($value)
{
return $this->getLastActivedAt($value);
}
/**
* @param $path
*/
public function setAvatarAttribute($path)
{
if (!starts_with($path, 'http')) {
@ -47,21 +66,34 @@ class User extends Authenticatable
$this->attributes['avatar'] = $path;
}
/**
* @param $model
* @return bool
*/
public function isAuthorOf($model)
{
return $this->id == $model->user_id;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function replies()
{
return $this->hasMany(Reply::class, 'user_id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function topics()
{
return $this->hasMany(Topic::class);
}
/**
* @param $value
*/
public function setPasswordAttribute($value)
{
if (!isset($value[59])) {
@ -70,6 +102,9 @@ class User extends Authenticatable
$this->attributes['password'] = $value;
}
/**
* @param $instance
*/
public function notify($instance)
{
if ($this->id == Auth::id()) {

View File

@ -6,7 +6,7 @@
* Time: 17:16
*/
namespace App\Models\Traits;
namespace App\Traits;
use App\Models\Reply;

View File

@ -0,0 +1,80 @@
<?php
/**
* Created by PhpStorm.
* User: xing
* Date: 2018/6/2
* Time: 22:29
*/
namespace App\Traits;
use Carbon\Carbon;
use Illuminate\Support\Facades\Redis;
/**
* Trait LastActivedAtHelper
* @property int id
* @package App\Traits
*/
trait LastActivedAtHelper
{
// 缓存相关
protected $hash_prefix = 'larabbs_last_actived_at_';
protected $field_prefix = 'user_';
public function recordLastActivedAt()
{
// 获取今天的日期
$date = Carbon::now()->toDateString();
// Redis 哈希表的命名larabbs_last_actived_at_2017-10-21
$hash = $this->hash_prefix . $date;
// 字段名称user_1
$field = $this->field_prefix . $this->id;
// 当前时间2017-10-21 08:35:15
$now = Carbon::now()->toDateTimeString();
// 数据写入 Redis ,字段已存在会被更新
Redis::hSet($hash, $field, $now);
}
public function syncUserActivedAt()
{
// 获取昨天的日期格式如2017-10-21
$yesterday_date = Carbon::yesterday()->toDateString();
// Redis 哈希表的命名larabbs_last_actived_at_2017-10-21
$hash = $this->hash_prefix . $yesterday_date;
// 从 Redis 中获取所有哈希表里的数据
$dates = Redis::hGetAll($hash);
// 遍历,并同步到数据库中
foreach ($dates as $user_id => $actived_at) {
// 会将 `user_1` 转换为 1
$user_id = str_replace($this->field_prefix, '', $user_id);
// 只有当用户存在时才更新到数据库中
if ($user = $this->find($user_id)) {
$user->last_active_at = $actived_at;
$user->save();
}
}
// 以数据库为中心的存储,既已同步,即可删除
Redis::del($hash);
}
public function getLastActivedAt($value)
{
$date = Carbon::now()->toDateString();
// Redis 哈希表的命名larabbs_last_actived_at_2017-10-21
$hash = $this->hash_prefix . $date;
$field = $this->field_prefix . $this->id;
$date = Redis::hGet($hash, $field) ?: $value;
return $date ? new Carbon($date) : $this->created_at;
}
}

View File

@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddReferences extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('topics', function (Blueprint $table) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
Schema::table('replies', function (Blueprint $table) {
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('topic_id')->references('id')->on('topics')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('topics', function (Blueprint $table) {
$table->dropForeign(['user_id']);
});
Schema::table('replies', function (Blueprint $table) {
$table->dropForeign(['user_id', 'topic_id']);
});
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddLastActivedAtToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->timestamp('last_active_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('last_active_at');
});
}
}

View File

@ -19,6 +19,8 @@
<hr>
<h4><strong>注册于</strong></h4>
<p>{{ $user->created_at->diffForHumans() }}</p>
<h4><strong>最后活跃</strong></h4>
<p title="{{ $user->last_actived_at }}">{{ $user->last_active_at->diffForHumans() }}</p>
</div>
</div>
</div>