114 lines
4.2 KiB
PHP
114 lines
4.2 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
namespace App\Jobs;
|
|||
|
|
|
|||
|
|
use App\Models\PositionDutyLog;
|
|||
|
|
use App\Models\Sysparam;
|
|||
|
|
use App\Models\User;
|
|||
|
|
use App\Services\ChatStateService;
|
|||
|
|
use App\Services\RoomBroadcastService;
|
|||
|
|
use Illuminate\Bus\Queueable;
|
|||
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|||
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|||
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|||
|
|
use Illuminate\Queue\SerializesModels;
|
|||
|
|
use Illuminate\Support\Facades\DB;
|
|||
|
|
use Illuminate\Support\Facades\Redis;
|
|||
|
|
|
|||
|
|
class ProcessUserLeave implements ShouldQueue
|
|||
|
|
{
|
|||
|
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|||
|
|
|
|||
|
|
public function __construct(
|
|||
|
|
public int $roomId,
|
|||
|
|
public User $user,
|
|||
|
|
public float $leaveTime
|
|||
|
|
) {}
|
|||
|
|
|
|||
|
|
public function handle(ChatStateService $chatState, RoomBroadcastService $broadcast): void
|
|||
|
|
{
|
|||
|
|
// 获取该用户最后一次进入房间的时间
|
|||
|
|
$lastJoinTime = (float) Redis::get("room:{$this->roomId}:join_time:{$this->user->username}");
|
|||
|
|
|
|||
|
|
// 如果最后一次加入的时间 > 当前离线任务产生的时间,说明用户又刷新重新进来了
|
|||
|
|
if ($lastJoinTime >= $this->leaveTime) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 1. 从 Redis 删除该用户
|
|||
|
|
$chatState->userLeave($this->roomId, $this->user->username);
|
|||
|
|
|
|||
|
|
// 记录退出时间和退出信息
|
|||
|
|
$this->user->update([
|
|||
|
|
'out_time' => now(),
|
|||
|
|
'out_info' => '正常退出了房间',
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
// 关闭该用户尚未结束的在职登录记录(结算在线时长)
|
|||
|
|
$this->closeDutyLog($this->user->id);
|
|||
|
|
|
|||
|
|
// 2. 发送离场播报
|
|||
|
|
$superLevel = (int) Sysparam::getValue('superlevel', '100');
|
|||
|
|
|
|||
|
|
if ($this->user->user_level >= $superLevel) {
|
|||
|
|
// 管理员离场:系统公告
|
|||
|
|
$leaveMsg = [
|
|||
|
|
'id' => $chatState->nextMessageId($this->roomId),
|
|||
|
|
'room_id' => $this->roomId,
|
|||
|
|
'from_user' => '系统公告',
|
|||
|
|
'to_user' => '大家',
|
|||
|
|
'content' => "👋 管理员 【{$this->user->username}】 已离开聊天室。",
|
|||
|
|
'is_secret' => false,
|
|||
|
|
'font_color' => '#b91c1c',
|
|||
|
|
'action' => 'admin_welcome',
|
|||
|
|
'welcome_user' => $this->user->username,
|
|||
|
|
'sent_at' => now()->toDateTimeString(),
|
|||
|
|
];
|
|||
|
|
} else {
|
|||
|
|
[$leaveText, $color] = $broadcast->buildLeaveBroadcast($this->user);
|
|||
|
|
$leaveMsg = [
|
|||
|
|
'id' => $chatState->nextMessageId($this->roomId),
|
|||
|
|
'room_id' => $this->roomId,
|
|||
|
|
'from_user' => '进出播报',
|
|||
|
|
'to_user' => '大家',
|
|||
|
|
'content' => "<span style=\"color: {$color}; font-weight: bold;\">{$leaveText}</span>",
|
|||
|
|
'is_secret' => false,
|
|||
|
|
'font_color' => $color,
|
|||
|
|
'action' => 'system_welcome',
|
|||
|
|
'welcome_user' => $this->user->username,
|
|||
|
|
'sent_at' => now()->toDateTimeString(),
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将播报存入 Redis 历史及广播
|
|||
|
|
$chatState->pushMessage($this->roomId, $leaveMsg);
|
|||
|
|
broadcast(new \App\Events\UserLeft($this->roomId, $this->user->username))->toOthers();
|
|||
|
|
broadcast(new \App\Events\MessageSent($this->roomId, $leaveMsg))->toOthers();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 关闭该用户尚未结束的在职登录记录(结算在线时长)
|
|||
|
|
*/
|
|||
|
|
private function closeDutyLog(int $userId): void
|
|||
|
|
{
|
|||
|
|
// 将今日开放日志关闭并结算实际时长
|
|||
|
|
PositionDutyLog::query()
|
|||
|
|
->where('user_id', $userId)
|
|||
|
|
->whereNull('logout_at')
|
|||
|
|
->whereDate('login_at', today())
|
|||
|
|
->update([
|
|||
|
|
'logout_at' => now(),
|
|||
|
|
'duration_seconds' => DB::raw('GREATEST(0, TIMESTAMPDIFF(SECOND, login_at, NOW()))'),
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
// 关闭历史遗留的跨天未关闭日志(login_at 非今日)
|
|||
|
|
PositionDutyLog::query()
|
|||
|
|
->where('user_id', $userId)
|
|||
|
|
->whereNull('logout_at')
|
|||
|
|
->whereDate('login_at', '!=', today())
|
|||
|
|
->update([
|
|||
|
|
'logout_at' => DB::raw('DATE_ADD(DATE(login_at), INTERVAL "23:59:59" HOUR_SECOND)'),
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
}
|