|
|
@@ -0,0 +1,115 @@
|
|
|
+package com.supervision.WebSocket;
|
|
|
+
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.supervision.common.core.domain.model.LoginUser;
|
|
|
+import com.supervision.framework.web.service.TokenService;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import javax.websocket.*;
|
|
|
+import javax.websocket.server.PathParam;
|
|
|
+import javax.websocket.server.ServerEndpoint;
|
|
|
+import java.io.*;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+
|
|
|
+/**
|
|
|
+ * WebSocket 实时推送服务(兼容 JDK 1.8)
|
|
|
+ * 每个用户登录后建立独立连接,通过 Token 鉴权
|
|
|
+ */
|
|
|
+@Component
|
|
|
+@ServerEndpoint("/websocket/{token}")
|
|
|
+public class EventWebSocket {
|
|
|
+
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(EventWebSocket.class);
|
|
|
+
|
|
|
+ /** 保存所有在线用户连接 */
|
|
|
+ private static final Map<Long, Session> USER_SESSION_MAP = new ConcurrentHashMap<>();
|
|
|
+
|
|
|
+ private Long userId;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 连接建立成功调用的方法
|
|
|
+ * */
|
|
|
+ @OnOpen
|
|
|
+ public void onOpen(Session session, @PathParam("token") String token) {
|
|
|
+ logger.info("🟢 WebSocket 请求到达,----------连接成功");
|
|
|
+ logger.info("🟢 WebSocket 请求到达,----------session: {}", session);
|
|
|
+ logger.info("🟢 WebSocket 请求到达,----------token: {}", token);
|
|
|
+ try {
|
|
|
+ TokenService tokenService = SpringContextHolder.getBean(TokenService.class);
|
|
|
+ LoginUser loginUser = tokenService.getLoginUserByToken(token);
|
|
|
+ if (loginUser == null) {
|
|
|
+ session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "Invalid token"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.userId = loginUser.getUserId();
|
|
|
+ USER_SESSION_MAP.put(userId, session);
|
|
|
+ logger.info("✅ 用户 " + loginUser.getUsername() + " 已连接 WebSocket");
|
|
|
+ } catch (Exception e) {
|
|
|
+ try {
|
|
|
+ session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "Auth failed"));
|
|
|
+ } catch (IOException ignored) {}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @OnMessage
|
|
|
+ public void onMessage(String message, Session session) {
|
|
|
+ if ("ping".equalsIgnoreCase(message)) {
|
|
|
+ session.getAsyncRemote().sendText("pong");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @OnClose
|
|
|
+ public void onClose() {
|
|
|
+ if (userId != null) {
|
|
|
+ USER_SESSION_MAP.remove(userId);
|
|
|
+ logger.info("❌ 用户 " + userId + " 已断开 WebSocket");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @OnError
|
|
|
+ public void onError(Session session, Throwable error) {
|
|
|
+ System.err.println("WebSocket 错误:" + error.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 广播消息给所有在线用户 */
|
|
|
+ public static void broadcast(String type, Object data) {
|
|
|
+ String msg = JSON.toJSONString(new WebSocketMessage(type, data));
|
|
|
+ for (Session session : USER_SESSION_MAP.values()) {
|
|
|
+ if (session.isOpen()) {
|
|
|
+ session.getAsyncRemote().sendText(msg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 向特定用户发送消息 */
|
|
|
+ public static void sendToUser(Long userId, Object data) {
|
|
|
+ Session session = USER_SESSION_MAP.get(userId);
|
|
|
+ if (session != null && session.isOpen()) {
|
|
|
+ session.getAsyncRemote().sendText(JSON.toJSONString(data));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 简单封装的消息结构 */
|
|
|
+ private static class WebSocketMessage {
|
|
|
+ private String type;
|
|
|
+ private Object data;
|
|
|
+
|
|
|
+ public WebSocketMessage(String type, Object data) {
|
|
|
+ this.type = type;
|
|
|
+ this.data = data;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getType() {
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Object getData() {
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|