From cfe970bd45eef8c14f247be4412706c723793bf7 Mon Sep 17 00:00:00 2001 From: huangdeliang Date: Wed, 2 Jun 2021 14:06:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=8F=91ws=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom/WebSocketController.java | 54 ++++++++ .../stdiet/web/server/WebSocketServer.java | 122 ++++++++++++++++++ .../java/com/stdiet/custom/utils/WsUtils.java | 8 ++ stdiet-framework/pom.xml | 17 +++ .../framework/config/SecurityConfig.java | 1 + .../framework/config/WebsocketConfig.java | 18 +++ stdiet-ui/src/main.js | 15 ++- stdiet-ui/src/utils/websocket.js | 57 ++++++++ 8 files changed, 286 insertions(+), 6 deletions(-) create mode 100644 stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java create mode 100644 stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java create mode 100644 stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java create mode 100644 stdiet-framework/src/main/java/com/stdiet/framework/config/WebsocketConfig.java create mode 100644 stdiet-ui/src/utils/websocket.js diff --git a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java new file mode 100644 index 000000000..128b8d067 --- /dev/null +++ b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java @@ -0,0 +1,54 @@ +package com.stdiet.web.controller.custom; + +import com.alibaba.fastjson.JSONObject; +import com.stdiet.common.core.controller.BaseController; +import com.stdiet.custom.utils.WsUtils; +import com.stdiet.web.server.WebSocketServer; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Controller +@EnableScheduling +@RequestMapping("/ws/api") +public class WebSocketController extends BaseController { + + @ResponseBody + @RequestMapping("/push/{cid}") + public Map pushToWeb(@PathVariable String cid, String message) { + if (message == null) { + message = "我是消息44"; + } + Map result = new HashMap(); + try { + WebSocketServer.sendInfo(message, cid); + result.put("code", 200); + result.put("msg", "success"); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + + } + + @Scheduled(fixedRate = 30000) + public void boardCast() { + try { + JSONObject heartBeat = new JSONObject(); + heartBeat.put("type", WsUtils.WS_TYPE_HEART_BEAT); + heartBeat.put("msg", "ping"); + + WebSocketServer.sendInfo(heartBeat.toJSONString(), null); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java b/stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java new file mode 100644 index 000000000..8dfbb8deb --- /dev/null +++ b/stdiet-admin/src/main/java/com/stdiet/web/server/WebSocketServer.java @@ -0,0 +1,122 @@ +package com.stdiet.web.server; + +import com.alibaba.fastjson.JSONObject; +import com.stdiet.common.core.domain.model.LoginUser; +import com.stdiet.custom.utils.WsUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.concurrent.CopyOnWriteArraySet; + +@ServerEndpoint(value = "/ws") +@Component +@Slf4j +public class WebSocketServer { + + // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 + private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet(); + //private static ConcurrentHashMap websocketList = new ConcurrentHashMap<>(); + // 与某个客户端的连接会话,需要通过它来给客户端发送数据 + private Session session; + // 接收sid + private String sid = ""; + + public static CopyOnWriteArraySet getWebSocketSet() { + return webSocketSet; + } + + /** + * 群发自定义消息 + */ + public static void sendInfo(String message, String sid) throws IOException { + log.info("推送消息到窗口" + sid + ",推送内容:" + message + "目标:" + webSocketSet.size()); + for (WebSocketServer item : webSocketSet) { + try { + // 这里可以设定只推送给这个sid的,为null则全部推送 + if (sid == null) { + item.sendMessage(message); + } else if (item.sid.equals(sid)) { + item.sendMessage(message); + } + } catch (IOException e) { + // 清理断开的连接 + webSocketSet.remove(item); + continue; + } + } + } + + // * 连接建立成功调用的方法* + @OnOpen + public void onOpen(Session session) { + this.session = session; + this.sid = String.valueOf(getUserId(session)); + + webSocketSet.add(this); // 加入set中 + + try { + JSONObject object = new JSONObject(); + object.put("type", WsUtils.WS_TYPE_SYSTEM_MESSAGE); + object.put("msg", "连接成功"); + sendMessage(object.toJSONString()); + } catch (IOException e) { + log.error("websocket IO异常"); + } + } + + @OnClose + public void onClose() { + webSocketSet.remove(this); // 从set中删除 + log.info("有一连接关闭!"); + } + + // */ + /// ** + // * 收到客户端消息后调用的方法 + // * + // * @param message 客户端发送过来的消息*//* + @OnMessage + public void onMessage(String message, Session session) { + log.info("收到来自窗口" + sid + "的信息:" + message); +// // 群发消息 +// for (WebSocketServer item : webSocketSet) { +// try { +// item.sendMessage(message); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + } + + /** + * @param session + * @param error + */ + @OnError + public void onError(Session session, Throwable error) { + log.error("发生错误"); + error.printStackTrace(); + } + + /** + * 实现服务器主动推送 + */ + public void sendMessage(String message) throws IOException { + log.info("服务器消息推送:" + message); + this.session.getBasicRemote().sendText(message); + } + + public Long getUserId(Session session) { + try { + return ((LoginUser) ((UsernamePasswordAuthenticationToken) session.getUserPrincipal()).getPrincipal()).getUser().getUserId(); + } catch (Exception e) { + return 0L; + } + } + + +} \ No newline at end of file diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java b/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java new file mode 100644 index 000000000..6d8e8f43c --- /dev/null +++ b/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java @@ -0,0 +1,8 @@ +package com.stdiet.custom.utils; + +public class WsUtils { + + public static final String WS_TYPE_HEART_BEAT = "WS_TYPE_HEART_BEAT"; + + public static final String WS_TYPE_SYSTEM_MESSAGE = "WS_TYPE_SYSTEM_MESSAGE"; +} diff --git a/stdiet-framework/pom.xml b/stdiet-framework/pom.xml index 6db2e7872..c94845c5d 100644 --- a/stdiet-framework/pom.xml +++ b/stdiet-framework/pom.xml @@ -59,6 +59,23 @@ stdiet-system + + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + \ No newline at end of file diff --git a/stdiet-framework/src/main/java/com/stdiet/framework/config/SecurityConfig.java b/stdiet-framework/src/main/java/com/stdiet/framework/config/SecurityConfig.java index af34f5aa1..07f5dc900 100644 --- a/stdiet-framework/src/main/java/com/stdiet/framework/config/SecurityConfig.java +++ b/stdiet-framework/src/main/java/com/stdiet/framework/config/SecurityConfig.java @@ -102,6 +102,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { "/custom/wxUserInfo/wx/**", "/custom/wxUserLog/wx/**", "/wx/**", +// "/ws/**", "/wap/**", "/investigate/**", "/common/customerUploadFile", diff --git a/stdiet-framework/src/main/java/com/stdiet/framework/config/WebsocketConfig.java b/stdiet-framework/src/main/java/com/stdiet/framework/config/WebsocketConfig.java new file mode 100644 index 000000000..795c5a971 --- /dev/null +++ b/stdiet-framework/src/main/java/com/stdiet/framework/config/WebsocketConfig.java @@ -0,0 +1,18 @@ +package com.stdiet.framework.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +@ConditionalOnWebApplication +public class WebsocketConfig { + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + + +} diff --git a/stdiet-ui/src/main.js b/stdiet-ui/src/main.js index 28ac40c5a..79469f5f8 100644 --- a/stdiet-ui/src/main.js +++ b/stdiet-ui/src/main.js @@ -18,12 +18,15 @@ import "./permission"; // permission control import { getDicts } from "@/api/system/dict/data"; import { getConfigKey } from "@/api/system/config"; import VueScrollTo from "vue-scrollto"; -import VueResource from "vue-resource" -import HighchartsVue from 'highcharts-vue' -import Highcharts from 'highcharts' +import VueResource from "vue-resource"; +import HighchartsVue from "highcharts-vue"; +import Highcharts from "highcharts"; +import { init } from "@/utils/websocket"; //图片导出模块 -import exportingInit from 'highcharts/modules/exporting' -exportingInit(Highcharts) +import exportingInit from "highcharts/modules/exporting"; +exportingInit(Highcharts); + +init(); import { addDateRange, @@ -111,7 +114,7 @@ Vue.use(VueScrollTo, { y: true }); -Vue.use(VueResource) +Vue.use(VueResource); new Vue({ el: "#app", diff --git a/stdiet-ui/src/utils/websocket.js b/stdiet-ui/src/utils/websocket.js new file mode 100644 index 000000000..1098bc150 --- /dev/null +++ b/stdiet-ui/src/utils/websocket.js @@ -0,0 +1,57 @@ +import { getToken } from "./auth"; + +const { protocol, hostname, origin, port } = window.location; +const wsProtocol = protocol.startsWith("https") ? "wss" : "ws"; +const url = `${wsProtocol}://${hostname}${ + port === "80" || port === "443" ? "" : `:${8091}` +}/ws`; +let ws = undefined; +let intervalRef = undefined; + +function handleOnMessageReceive(event) { + console.log({ event }); +} + +function connect() { + try { + ws = new WebSocket(url); + + ws.onopen = event => { + console.log("ws连接成功"); + intervalRef && clearInterval(intervalRef); + + document.addEventListener("message", handleOnMessageReceive); + }; + + ws.onmessage = event => { + console.log({ event }); + + const dataObj = JSON.parse(event.data || "{}"); + + if (dataObj.type && dataObj.type !== "WS_TYPE_HEART_BEAT") { + window.postMessage(dataObj, origin); + } + }; + + ws.onerror = event => { + console.log({ event }); + ws = undefined; + document.removeEventListener("message", handleOnMessageReceive); + }; + + ws.onclose = event => { + ws = undefined; + document.removeEventListener("message", handleOnMessageReceive); + }; + } catch (error) { + console.log(error); + console.log("浏览器不支持websocket"); + } +} + +export function init() { + intervalRef = setInterval(() => { + console.log("尝试连接websocket"); + !ws && connect(); + }, 10000); +}