UserLogin.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. <?php
  2. namespace App\Models;
  3. use App\Helpers\LoginClientInfo;
  4. use Carbon\Carbon;
  5. class UserLogin extends BaseModel
  6. {
  7. protected $table = 'user_login';
  8. protected $fillable = [
  9. 'user_id',
  10. 'login_ip',
  11. 'country',
  12. 'browser',
  13. 'os',
  14. 'status',
  15. 'logout_time',
  16. 'online_duration',
  17. 'user_agent',
  18. ];
  19. // 登录日志需要展示创建时间(登录时间)
  20. protected $hidden = [];
  21. protected $appends = ['status_text'];
  22. public const STATUS_FAIL = 0;
  23. public const STATUS_SUCCESS = 1;
  24. /**
  25. * 写入登录日志
  26. *
  27. * @param string|int $userId 用户ID(member_id / user_id)
  28. * @param int $status 1成功 0失败
  29. * @param string|null $loginIp
  30. * @param string|null $userAgent
  31. */
  32. public static function addData($userId, int $status = self::STATUS_SUCCESS, ?string $loginIp = null, ?string $userAgent = null): self
  33. {
  34. $info = LoginClientInfo::fromRequest($loginIp, $userAgent);
  35. return self::create([
  36. 'user_id' => (string)$userId,
  37. 'login_ip' => $info['login_ip'],
  38. 'country' => $info['country'],
  39. 'browser' => $info['browser'],
  40. 'os' => $info['os'],
  41. 'status' => $status,
  42. 'logout_time' => null,
  43. 'online_duration' => null,
  44. 'user_agent' => $info['user_agent'],
  45. ]);
  46. }
  47. /**
  48. * 标记登出:更新最近一条未登出的成功登录记录
  49. */
  50. public static function markLogout($userId): void
  51. {
  52. $log = self::where('user_id', (string)$userId)
  53. ->where('status', self::STATUS_SUCCESS)
  54. ->whereNull('logout_time')
  55. ->orderByDesc('id')
  56. ->first();
  57. if (!$log) {
  58. return;
  59. }
  60. $logoutAt = Carbon::now('Asia/Shanghai');
  61. $loginAt = Carbon::parse($log->getRawOriginal('created_at') ?? $log->created_at)
  62. ->setTimezone('Asia/Shanghai');
  63. $seconds = max(0, $logoutAt->getTimestamp() - $loginAt->getTimestamp());
  64. $minutes = (int)ceil($seconds / 60);
  65. $log->logout_time = $logoutAt->format('Y-m-d H:i:s');
  66. $log->online_duration = $minutes;
  67. $log->save();
  68. }
  69. public function getStatusTextAttribute(): string
  70. {
  71. $status = (int)($this->attributes['status'] ?? self::STATUS_SUCCESS);
  72. return $status === self::STATUS_SUCCESS ? '登录成功' : '登录失败';
  73. }
  74. public function getLogoutTimeAttribute($value): ?string
  75. {
  76. if (empty($value) || $value === '0000-00-00 00:00:00') {
  77. return null;
  78. }
  79. return Carbon::parse($value)->setTimezone('Asia/Shanghai')->format('Y-m-d H:i:s');
  80. }
  81. // 获取用户未登录天数(最后第二次未登录的天数)
  82. public static function getNotLoginDays($memberId)
  83. {
  84. $list = self::where('user_id', $memberId)
  85. ->where('status', self::STATUS_SUCCESS)
  86. ->orderByDesc('id')
  87. ->limit(2)
  88. ->get()
  89. ->toArray();
  90. if (count($list) < 2) {
  91. return 0;
  92. }
  93. if (date('Y-m-d', strtotime($list[0]['created_at'])) != date('Y-m-d')) {
  94. return 0;
  95. }
  96. $diff = strtotime($list[0]['created_at']) - strtotime($list[1]['created_at']);
  97. $days = ceil($diff / 86400);
  98. return abs($days);
  99. }
  100. }