177 lines
6.1 KiB
PHP
177 lines
6.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace app\service\admin\monitor;
|
|
|
|
use app\model\system\Admin;
|
|
use app\scope\global\AccessPermissionScope;
|
|
use core\base\BaseService;
|
|
use core\jwt\JwtToken;
|
|
|
|
class OnlineUserService extends BaseService
|
|
{
|
|
public function list(array $params = []): array
|
|
{
|
|
$page = max(1, (int)($params['page'] ?? 1));
|
|
$limit = max(1, (int)($params['limit'] ?? 10));
|
|
$keyword = trim((string)($params['keyword'] ?? ''));
|
|
$ip = trim((string)($params['ip'] ?? ''));
|
|
$clientType = trim((string)($params['client_type'] ?? 'admin'));
|
|
$status = trim((string)($params['status'] ?? ''));
|
|
|
|
$jwt = new JwtToken();
|
|
$sessions = $jwt->getAllSessions($clientType !== '' ? $clientType : null);
|
|
$userIds = array_values(array_unique(array_filter(array_map(static fn (array $session): string => (string)($session['id'] ?? ''), $sessions))));
|
|
$users = $this->getUsersByIds($userIds);
|
|
|
|
$items = [];
|
|
foreach ($sessions as $session) {
|
|
$extra = $session['extra'] ?? [];
|
|
$userId = (string)($session['id'] ?? '');
|
|
$user = $users[$userId] ?? null;
|
|
$account = $extra['user_name'] ?? $user['user_name'] ?? '';
|
|
$realName = $extra['real_name'] ?? $user['real_name'] ?? '';
|
|
$sessionIp = $extra['ip'] ?? $user['login_ip'] ?? '';
|
|
$roleNames = $user['role_names'] ?? $this->formatExtraNames($extra['roles'] ?? []);
|
|
if ($roleNames === '' && (int)($extra['is_super'] ?? 0) === 1) {
|
|
$roleNames = '超级管理员';
|
|
}
|
|
|
|
if ($keyword !== '' && !str_contains($account, $keyword) && !str_contains($realName, $keyword)) {
|
|
continue;
|
|
}
|
|
|
|
if ($ip !== '' && !str_contains($sessionIp, $ip)) {
|
|
continue;
|
|
}
|
|
|
|
$sessionStatus = (string)($session['status'] ?? 'online');
|
|
if ($jwt->getBlacklistStorage()->has((string)($session['jti'] ?? ''))) {
|
|
$sessionStatus = 'offline';
|
|
}
|
|
if ($status !== '' && $sessionStatus !== $status) {
|
|
continue;
|
|
}
|
|
|
|
$createdAt = (int)($session['created_at'] ?? $user['login_time'] ?? time());
|
|
$lastActiveAt = (int)($session['last_active_at'] ?? $createdAt);
|
|
$offlineAt = isset($session['offline_at']) ? (int)$session['offline_at'] : null;
|
|
$durationEndAt = $sessionStatus === 'offline' && $offlineAt !== null ? $offlineAt : time();
|
|
$items[] = [
|
|
'jti' => $session['jti'],
|
|
'user_id' => $userId,
|
|
'account' => $account,
|
|
'real_name' => $realName,
|
|
'dept_names' => $user['dept_names'] ?? '',
|
|
'post_names' => $user['post_names'] ?? '',
|
|
'role_names' => $roleNames,
|
|
'ip' => $sessionIp,
|
|
'ip_location' => $extra['ip_location'] ?? '',
|
|
'device' => $extra['os'] ?? $this->getCurrentRequestOs(),
|
|
'browser' => $extra['browser'] ?? $this->getCurrentRequestBrowser(),
|
|
'status' => $sessionStatus,
|
|
'client_type' => $session['client_type'] ?? '',
|
|
'login_time' => $createdAt,
|
|
'last_active_at' => $lastActiveAt,
|
|
'offline_at' => $offlineAt,
|
|
'online_seconds' => max(0, $durationEndAt - $createdAt),
|
|
];
|
|
}
|
|
|
|
usort($items, static fn (array $a, array $b): int => ($b['last_active_at'] ?? 0) <=> ($a['last_active_at'] ?? 0));
|
|
|
|
$total = count($items);
|
|
$offset = ($page - 1) * $limit;
|
|
|
|
return [
|
|
'items' => array_slice($items, $offset, $limit),
|
|
'total' => $total,
|
|
];
|
|
}
|
|
|
|
public function kickout(string $jti): bool
|
|
{
|
|
return (new JwtToken())->kickoutByJti($jti);
|
|
}
|
|
|
|
protected function getUsersByIds(array $ids): array
|
|
{
|
|
if (empty($ids)) {
|
|
return [];
|
|
}
|
|
|
|
$rows = Admin::query()
|
|
->withoutGlobalScopes([AccessPermissionScope::class])
|
|
->with(['depts', 'posts', 'roles'])
|
|
->whereIn('id', $ids)
|
|
->get();
|
|
|
|
$users = [];
|
|
foreach ($rows as $row) {
|
|
$users[(string)$row->id] = [
|
|
'user_name' => $row->user_name,
|
|
'real_name' => $row->real_name,
|
|
'is_super' => $row->is_super,
|
|
'login_ip' => $row->login_ip,
|
|
'login_time' => $row->login_time,
|
|
'dept_names' => $row->depts->pluck('name')->implode(' / '),
|
|
'post_names' => $row->posts->pluck('name')->implode(' / '),
|
|
'role_names' => $row->roles->pluck('name')->implode(' / ') ?: ((int)$row->is_super === 1 ? '超级管理员' : ''),
|
|
];
|
|
}
|
|
|
|
return $users;
|
|
}
|
|
|
|
protected function formatExtraNames(array $items): string
|
|
{
|
|
$names = [];
|
|
foreach ($items as $item) {
|
|
if (is_array($item) && !empty($item['name'])) {
|
|
$names[] = $item['name'];
|
|
}
|
|
}
|
|
|
|
return implode(' / ', $names);
|
|
}
|
|
|
|
protected function getCurrentRequestBrowser(): string
|
|
{
|
|
$userAgent = request()?->header('user-agent', '') ?? '';
|
|
if (preg_match('/Edg/i', $userAgent)) {
|
|
return 'Edge';
|
|
}
|
|
if (preg_match('/Firefox/i', $userAgent)) {
|
|
return 'Firefox';
|
|
}
|
|
if (preg_match('/Chrome/i', $userAgent)) {
|
|
return 'Chrome';
|
|
}
|
|
if (preg_match('/Safari/i', $userAgent)) {
|
|
return 'Safari';
|
|
}
|
|
if (preg_match('/Opera/i', $userAgent)) {
|
|
return 'Opera';
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
protected function getCurrentRequestOs(): string
|
|
{
|
|
$userAgent = request()?->header('user-agent', '') ?? '';
|
|
if (preg_match('/win/i', $userAgent)) {
|
|
return 'Windows';
|
|
}
|
|
if (preg_match('/mac/i', $userAgent)) {
|
|
return 'Mac';
|
|
}
|
|
if (preg_match('/linux/i', $userAgent)) {
|
|
return 'Linux';
|
|
}
|
|
|
|
return '';
|
|
}
|
|
}
|