diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/constant/MessageConstants.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/constant/MessageConstants.java index cefec0238..f35a4a273 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/constant/MessageConstants.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/constant/MessageConstants.java @@ -31,4 +31,9 @@ public interface MessageConstants { * 心跳响应标识 */ String PONG = "pong"; + + /** + * 同一 token 的新连接替换旧连接时发送给旧连接的控制消息。 + */ + String KICKED = "kicked"; } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java index 3b0a0ec43..df6365e5c 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java @@ -65,6 +65,7 @@ public class SseEmitterSessionManager implements PushSessionManager { // 关闭已存在的SseEmitter,防止超过最大连接数 SseEmitter oldEmitter = emitters.remove(token); if (oldEmitter != null) { + sendKickedMessage(oldEmitter); oldEmitter.complete(); } @@ -103,6 +104,21 @@ public class SseEmitterSessionManager implements PushSessionManager { return emitter; } + /** + * 通知旧连接已被同 token 新连接替换。 + * + * @param emitter 旧 SSE 连接 + */ + private void sendKickedMessage(SseEmitter emitter) { + try { + emitter.send(SseEmitter.event() + .name("message") + .data(MessageConstants.KICKED)); + } catch (Exception ignore) { + // 旧连接可能已断开,忽略通知失败 + } + } + /** * 断开指定用户的 SSE 连接 * diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java index 2c8f2251b..1ba699d7a 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.utils.ThreadUtils; import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.push.constant.MessageConstants; import org.dromara.common.push.dto.PushDTO; import org.dromara.common.push.properties.MessageProperties; import org.dromara.common.redis.utils.RedisUtils; @@ -61,6 +62,7 @@ public class WebSocketSessionManager implements PushSessionManager { Map sessions = USER_TOKEN_SESSIONS.computeIfAbsent(userId, key -> new ConcurrentHashMap<>()); // 移除并关闭旧的同token会话,避免重复连接 WebSocketSession oldSession = sessions.remove(token); + sendKickedMessage(oldSession); closeSession(oldSession, CloseStatus.NORMAL); // 存储新会话 sessions.put(token, session); @@ -129,6 +131,18 @@ public class WebSocketSessionManager implements PushSessionManager { toRemoveUsers.forEach(USER_TOKEN_SESSIONS::remove); } + /** + * 通知旧连接已被同 token 新连接替换。 + * + * @param session 旧 WebSocket 会话 + */ + private void sendKickedMessage(WebSocketSession session) { + if (session == null || !session.isOpen()) { + return; + } + sendMessage(session, MessageConstants.KICKED); + } + /** * 订阅消息通道 * 注册消息消费者,监听Redis消息推送