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' => "{$leaveText}", '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)'), ]); } }