修复房间列表在线人数不准:房间Tab每30秒自动刷新+懒清理掉线僵尸记录
This commit is contained in:
@@ -28,6 +28,8 @@ class ChatStateService
|
|||||||
{
|
{
|
||||||
$key = "room:{$roomId}:users";
|
$key = "room:{$roomId}:users";
|
||||||
Redis::hset($key, $username, json_encode($info, JSON_UNESCAPED_UNICODE));
|
Redis::hset($key, $username, json_encode($info, JSON_UNESCAPED_UNICODE));
|
||||||
|
// 写入活跃标记(90秒 TTL,心跳每次刷新;超出则被认定为掉线)
|
||||||
|
Redis::setex("room:{$roomId}:alive:{$username}", 90, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,6 +42,19 @@ class ChatStateService
|
|||||||
{
|
{
|
||||||
$key = "room:{$roomId}:users";
|
$key = "room:{$roomId}:users";
|
||||||
Redis::hdel($key, $username);
|
Redis::hdel($key, $username);
|
||||||
|
// 同时删除活跃标记
|
||||||
|
Redis::del("room:{$roomId}:alive:{$username}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新用户心跳活跃标记(心跳接口调用)。
|
||||||
|
*
|
||||||
|
* @param int $roomId 房间ID
|
||||||
|
* @param string $username 用户名
|
||||||
|
*/
|
||||||
|
public function refreshAlive(int $roomId, string $username): void
|
||||||
|
{
|
||||||
|
Redis::setex("room:{$roomId}:alive:{$username}", 90, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,6 +100,13 @@ class ChatStateService
|
|||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ($users as $username => $jsonInfo) {
|
foreach ($users as $username => $jsonInfo) {
|
||||||
|
// 过滤掌心跳已超出活跃窗口的用户(掉线超 90秒)
|
||||||
|
if (! Redis::exists("room:{$roomId}:alive:{$username}")) {
|
||||||
|
// 懒清理:顺手将僵尸记录从 hash 中移除
|
||||||
|
Redis::hdel($key, $username);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$result[$username] = json_decode($jsonInfo, true);
|
$result[$username] = json_decode($jsonInfo, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,15 +42,22 @@
|
|||||||
let _maxMsgId = 0; // 记录当前收到的最大消息 ID
|
let _maxMsgId = 0; // 记录当前收到的最大消息 ID
|
||||||
|
|
||||||
// ── Tab 切换 ──────────────────────────────────────
|
// ── Tab 切换 ──────────────────────────────────────
|
||||||
|
let _roomsRefreshTimer = null;
|
||||||
|
|
||||||
function switchTab(tab) {
|
function switchTab(tab) {
|
||||||
// 切换名单/房间 面板
|
// 切换名单/房间 面板
|
||||||
['users', 'rooms'].forEach(t => {
|
['users', 'rooms'].forEach(t => {
|
||||||
document.getElementById('panel-' + t).style.display = t === tab ? 'block' : 'none';
|
document.getElementById('panel-' + t).style.display = t === tab ? 'block' : 'none';
|
||||||
document.getElementById('tab-' + t)?.classList.toggle('active', t === tab);
|
document.getElementById('tab-' + t)?.classList.toggle('active', t === tab);
|
||||||
});
|
});
|
||||||
// 房间 Tab:拉取在线人数列表
|
// 房间 Tab:立即拉取 + 每 30 秒自动刷新在线人数
|
||||||
if (tab === 'rooms') {
|
if (tab === 'rooms') {
|
||||||
loadRoomsOnlineStatus();
|
loadRoomsOnlineStatus();
|
||||||
|
clearInterval(_roomsRefreshTimer);
|
||||||
|
_roomsRefreshTimer = setInterval(loadRoomsOnlineStatus, 30000);
|
||||||
|
} else {
|
||||||
|
clearInterval(_roomsRefreshTimer);
|
||||||
|
_roomsRefreshTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user