运用外键同步删除与最后活跃时间
This commit is contained in:
parent
395b1c619a
commit
194c3cc259
44
app/Console/Commands/SyncUserActivedAt.php
Normal file
44
app/Console/Commands/SyncUserActivedAt.php
Normal 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('同步成功');
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ class Kernel extends ConsoleKernel
|
|||||||
// $schedule->command('inspire')
|
// $schedule->command('inspire')
|
||||||
// ->hourly();
|
// ->hourly();
|
||||||
$schedule->command('larabbs:calculate-active-user')->hourly();
|
$schedule->command('larabbs:calculate-active-user')->hourly();
|
||||||
|
$schedule->command('larabbs:sync-user-actived-at')->dailyAt('00:05');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http;
|
namespace App\Http;
|
||||||
|
|
||||||
|
use App\Http\Middleware\RecordLastActivedTime;
|
||||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
class Kernel extends HttpKernel
|
class Kernel extends HttpKernel
|
||||||
@ -10,34 +11,50 @@ class Kernel extends HttpKernel
|
|||||||
* The application's global HTTP middleware stack.
|
* The application's global HTTP middleware stack.
|
||||||
*
|
*
|
||||||
* These middleware are run during every request to your application.
|
* These middleware are run during every request to your application.
|
||||||
|
* 全局中间件,最先调用
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $middleware = [
|
protected $middleware = [
|
||||||
|
// 检测是否应用是否进入『维护模式』
|
||||||
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
|
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
|
||||||
|
// 检测请求的数据是否过大
|
||||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||||
|
// 对提交的请求参数进行 PHP 函数 `trim()` 处理
|
||||||
\App\Http\Middleware\TrimStrings::class,
|
\App\Http\Middleware\TrimStrings::class,
|
||||||
|
// 将提交请求参数中空子串转换为 null
|
||||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||||
|
// 修正代理服务器后的服务器参数
|
||||||
\App\Http\Middleware\TrustProxies::class,
|
\App\Http\Middleware\TrustProxies::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The application's route middleware groups.
|
* The application's route middleware groups.
|
||||||
*
|
*定义中间件组
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $middlewareGroups = [
|
protected $middlewareGroups = [
|
||||||
|
// Web 中间件组,应用于 routes/web.php 路由文件
|
||||||
'web' => [
|
'web' => [
|
||||||
|
// Cookie 加密解密
|
||||||
\App\Http\Middleware\EncryptCookies::class,
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
|
// 将 Cookie 添加到响应中
|
||||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||||
|
// 开启会话
|
||||||
\Illuminate\Session\Middleware\StartSession::class,
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
// 认证用户,此中间件以后 Auth 类才能生效
|
||||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
|
// 将系统的错误数据注入到视图变量 $errors 中
|
||||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
|
// 检验 CSRF ,防止跨站请求伪造的安全威胁
|
||||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
|
// 处理路由绑定
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
RecordLastActivedTime::class,
|
||||||
],
|
],
|
||||||
|
// API 中间件组,应用于 routes/api.php 路由文件
|
||||||
'api' => [
|
'api' => [
|
||||||
|
// 使用别名来调用中间件
|
||||||
'throttle:60,1',
|
'throttle:60,1',
|
||||||
'bindings',
|
'bindings',
|
||||||
],
|
],
|
||||||
@ -45,17 +62,22 @@ class Kernel extends HttpKernel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The application's route middleware.
|
* The application's route middleware.
|
||||||
*
|
* 中间件别名设置,允许你使用别名调用中间件,例如上面的 api 中间件组调用
|
||||||
* These middleware may be assigned to groups or used individually.
|
* These middleware may be assigned to groups or used individually.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $routeMiddleware = [
|
protected $routeMiddleware = [
|
||||||
|
//只有登录用户才能访问,我们在控制器的构造方法中大量使用
|
||||||
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
|
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
|
// 处理路由绑定
|
||||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
// 用户授权功能
|
||||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
|
// 只有游客才能访问,在 register 和 login 请求中使用,只有未登录用户才能访问这些页面
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
|
// 访问节流,类似于 『1 分钟只能请求 10 次』的需求,一般在 API 中使用
|
||||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
25
app/Http/Middleware/RecordLastActivedTime.php
Normal file
25
app/Http/Middleware/RecordLastActivedTime.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,18 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
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\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Spatie\Permission\Traits\HasRoles;
|
use Spatie\Permission\Traits\HasRoles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class User
|
||||||
|
* @property string avatar
|
||||||
|
* @package App\Models
|
||||||
|
*/
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
use Notifiable {
|
use Notifiable {
|
||||||
@ -15,6 +21,7 @@ class User extends Authenticatable
|
|||||||
}
|
}
|
||||||
use HasRoles;
|
use HasRoles;
|
||||||
use ActiveUserHelper;
|
use ActiveUserHelper;
|
||||||
|
use LastActivedAtHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
@ -34,11 +41,23 @@ class User extends Authenticatable
|
|||||||
'password', 'remember_token',
|
'password', 'remember_token',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function getHeaderAttribute()
|
public function getHeaderAttribute()
|
||||||
{
|
{
|
||||||
return asset($this->avatar);
|
return asset($this->avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLastActiveATAttribute($value)
|
||||||
|
{
|
||||||
|
return $this->getLastActivedAt($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $path
|
||||||
|
*/
|
||||||
public function setAvatarAttribute($path)
|
public function setAvatarAttribute($path)
|
||||||
{
|
{
|
||||||
if (!starts_with($path, 'http')) {
|
if (!starts_with($path, 'http')) {
|
||||||
@ -47,21 +66,34 @@ class User extends Authenticatable
|
|||||||
$this->attributes['avatar'] = $path;
|
$this->attributes['avatar'] = $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $model
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
public function isAuthorOf($model)
|
public function isAuthorOf($model)
|
||||||
{
|
{
|
||||||
return $this->id == $model->user_id;
|
return $this->id == $model->user_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*/
|
||||||
public function replies()
|
public function replies()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Reply::class, 'user_id');
|
return $this->hasMany(Reply::class, 'user_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*/
|
||||||
public function topics()
|
public function topics()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Topic::class);
|
return $this->hasMany(Topic::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $value
|
||||||
|
*/
|
||||||
public function setPasswordAttribute($value)
|
public function setPasswordAttribute($value)
|
||||||
{
|
{
|
||||||
if (!isset($value[59])) {
|
if (!isset($value[59])) {
|
||||||
@ -70,6 +102,9 @@ class User extends Authenticatable
|
|||||||
$this->attributes['password'] = $value;
|
$this->attributes['password'] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $instance
|
||||||
|
*/
|
||||||
public function notify($instance)
|
public function notify($instance)
|
||||||
{
|
{
|
||||||
if ($this->id == Auth::id()) {
|
if ($this->id == Auth::id()) {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Time: 17:16
|
* Time: 17:16
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Models\Traits;
|
namespace App\Traits;
|
||||||
|
|
||||||
|
|
||||||
use App\Models\Reply;
|
use App\Models\Reply;
|
80
app/Traits/LastActivedAtHelper.php
Normal file
80
app/Traits/LastActivedAtHelper.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
41
database/migrations/2018_06_02_215326_add_references.php
Normal file
41
database/migrations/2018_06_02_215326_add_references.php
Normal 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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,8 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<h4><strong>注册于</strong></h4>
|
<h4><strong>注册于</strong></h4>
|
||||||
<p>{{ $user->created_at->diffForHumans() }}</p>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user