mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-21 10:35:57 +08:00
pr:增加webSocket支持
This commit is contained in:
parent
b4cf1dbb6d
commit
ce8c038653
5
pom.xml
5
pom.xml
@ -83,6 +83,11 @@
|
||||
<!-- 依赖声明 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot的依赖配置-->
|
||||
<dependency>
|
||||
|
||||
@ -17,6 +17,11 @@
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 通用工具-->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
|
||||
150
ruoyi-demo/src/main/java/com/ruoyi/websocket/WebSocket.java
Normal file
150
ruoyi-demo/src/main/java/com/ruoyi/websocket/WebSocket.java
Normal file
@ -0,0 +1,150 @@
|
||||
package com.ruoyi.websocket;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.enums.UserType;
|
||||
import com.ruoyi.common.helper.LoginHelper;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.server.PathParam;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/*
|
||||
*@WebSocket
|
||||
*@author ye
|
||||
*@create 2023/3/30 9:45
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ServerEndpoint("/webSocket/{token}")
|
||||
public class WebSocket {
|
||||
/*
|
||||
* 存储session集合
|
||||
* */
|
||||
private static ConcurrentHashMap<Long, Session> sessionMap = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 存储用户集合
|
||||
*/
|
||||
private static ConcurrentHashMap<Long, LoginUser> userMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam(value = "token") String token){
|
||||
System.out.println("【webSocket连接成功】,token为:" + token);
|
||||
LoginUser loginUser = LoginHelper.getLoginUser(token);
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
log.error("token失效或无法解析");
|
||||
}
|
||||
setMap(session, loginUser);
|
||||
|
||||
}
|
||||
|
||||
private void setMap(Session session, LoginUser loginUser) {
|
||||
//获取用户id
|
||||
Long userId = loginUser.getUserId();
|
||||
//存储会话到会话集合
|
||||
sessionMap.put(userId, session);
|
||||
//存储用户信息到用户集合
|
||||
userMap.put(userId, loginUser);
|
||||
//获取会话大小 即在线人数
|
||||
int size = sessionMap.size();
|
||||
log.warn("用户连接:{},昵称:{},当前在线人数为:{}", userId, loginUser.getUsername(), size);
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(Session session){
|
||||
System.out.println("【webSocket退出成功】" );
|
||||
removeMap(session);
|
||||
}
|
||||
|
||||
private void removeMap(Session session) {
|
||||
Long userId = getUserIdBySession(session);
|
||||
if (ObjectUtil.isNull(userId)) {
|
||||
return;
|
||||
}
|
||||
sessionMap.remove(userId);
|
||||
userMap.remove(userId);
|
||||
}
|
||||
|
||||
private Long getUserIdBySession(Session session) {
|
||||
for (Long userId : sessionMap.keySet()) {
|
||||
if (sessionMap.get(userId).getId().equals(session.getId())) {
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Session session, Throwable throwable){
|
||||
|
||||
System.out.println("error:");
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(Session session, String message){
|
||||
System.out.println("【webSocket接收成功】内容为:" + message);
|
||||
LoginUser loginUser = getUserBySession(session);
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
return;
|
||||
}
|
||||
if (UserType.SYS_USER.getUserType().equals(loginUser.getUserType())) {
|
||||
//系统用户
|
||||
handlePCMsg(loginUser, message);
|
||||
}else {
|
||||
//app
|
||||
handleAPPMsg(loginUser, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleAPPMsg(LoginUser loginUser, String message) {
|
||||
log.info("APP用户:{},消息:", loginUser.getUsername(), message);
|
||||
}
|
||||
|
||||
private void handlePCMsg(LoginUser loginUser, String message) {
|
||||
log.info("系统用户:{},消息:", loginUser.getUsername(), message);
|
||||
}
|
||||
|
||||
|
||||
private LoginUser getUserBySession(Session session) {
|
||||
Long userId = getUserIdBySession(session);
|
||||
if (ObjectUtil.isNull(userId)) {
|
||||
return null;
|
||||
}
|
||||
return userMap.get(userId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送自定义消息
|
||||
*/
|
||||
public static void sendInfo(String message, Long toUserId) {
|
||||
log.info("发送消息到:{},消息内容:{}", toUserId, message);
|
||||
if (ObjectUtil.isNull(toUserId) || StringUtils.isBlank(message)) {
|
||||
log.error("消息体不完整");
|
||||
return;
|
||||
}
|
||||
if (sessionMap.contains(toUserId)) {
|
||||
try {
|
||||
sendMessage(sessionMap.get(toUserId), message);
|
||||
} catch (Exception e) {
|
||||
log.error("发送给用户{}的消息出错", toUserId);
|
||||
}
|
||||
}else {
|
||||
//用户不在线
|
||||
log.error("用户:{}不在线", toUserId);
|
||||
//后续处理
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendMessage(Session session, String message) throws IOException {
|
||||
session.getBasicRemote().sendText(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.ruoyi.websocket;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
/*
|
||||
*@开启WebSocket支持
|
||||
*@author ye
|
||||
*@create 2023/3/30 9:43
|
||||
*/
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div class="navbar">
|
||||
|
||||
<WebSocket></WebSocket>
|
||||
|
||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/>
|
||||
@ -56,6 +59,7 @@ import SizeSelect from '@/components/SizeSelect'
|
||||
import Search from '@/components/HeaderSearch'
|
||||
import RuoYiGit from '@/components/RuoYi/Git'
|
||||
import RuoYiDoc from '@/components/RuoYi/Doc'
|
||||
import WebSocket from './WebSocket'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -66,7 +70,8 @@ export default {
|
||||
SizeSelect,
|
||||
Search,
|
||||
RuoYiGit,
|
||||
RuoYiDoc
|
||||
RuoYiDoc,
|
||||
WebSocket
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
|
||||
108
ruoyi-ui/src/layout/components/WebSocket/index.vue
Normal file
108
ruoyi-ui/src/layout/components/WebSocket/index.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
name: "WebSocket",
|
||||
data() {
|
||||
return {
|
||||
webSocket: undefined,
|
||||
wsUri: undefined,
|
||||
lockReconnect: false,//是否真正建立连接
|
||||
timeout: 60 * 1000,//60秒一次心跳
|
||||
timeoutObj:undefined,//时间间隔对象
|
||||
serverTimeoutObj:undefined,//心跳倒计时对象
|
||||
timeoutNum:undefined,//重连对象
|
||||
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initWebSocket();
|
||||
},
|
||||
methods: {
|
||||
initWebSocket() {
|
||||
this.initWebSocketUri();
|
||||
//建立连接
|
||||
if (typeof (WebSocket) == "undefined") {
|
||||
console.log("你的浏览器不支持WebSocket");
|
||||
}else {
|
||||
this.webSocket = new WebSocket(this.wsUri);
|
||||
console.log("this.websocket", this.webSocket);
|
||||
this.webSocket.onopen = this.webSocketOnopen;
|
||||
this.webSocket.onerror = this.webSocketOnerror;
|
||||
this.webSocket.onmessage = this.webSocketOnmessage;
|
||||
this.webSocket.onclose = this.webSocketOnclose;
|
||||
}
|
||||
},
|
||||
initWebSocketUri() {
|
||||
this.wsUri = "ws://127.0.0.1:8080/webSocket/" + getToken();
|
||||
},
|
||||
webSocketOnopen() {
|
||||
console.log("连接成功");
|
||||
|
||||
this.start();
|
||||
},
|
||||
webSocketOnerror() {
|
||||
console.log("出错")
|
||||
this.reconnect();
|
||||
},
|
||||
webSocketOnmessage(res) {
|
||||
console.log("接收消息",res)
|
||||
|
||||
this.reset();
|
||||
},
|
||||
webSocketOnclose() {
|
||||
console.log("关闭连接")
|
||||
this.reconnect();
|
||||
},
|
||||
reset() {
|
||||
this.timeoutObj && clearTimeout(this.timeoutObj);
|
||||
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
|
||||
//重启心跳
|
||||
this.start();
|
||||
},
|
||||
//开启心跳
|
||||
start() {
|
||||
this.timeoutObj && clearTimeout(this.timeoutObj);
|
||||
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
|
||||
|
||||
this.timeoutObj= setTimeout(() => {
|
||||
if (this.webSocket.readyState === 1) {
|
||||
this.webSocketSend("heartCheck");
|
||||
}else {
|
||||
this.reconnect();
|
||||
}
|
||||
this.serverTimeoutObj = setTimeout(() => {
|
||||
this.webSocket.close();
|
||||
}, this.timeout);
|
||||
|
||||
}, this.timeout);
|
||||
},
|
||||
webSocketSend(msg) {
|
||||
this.webSocket.send(msg)
|
||||
},
|
||||
reconnect() {
|
||||
if (this.lockReconnect) {
|
||||
return;
|
||||
}
|
||||
this.lockReconnect = true;
|
||||
this.timeoutNum && clearTimeout(this.timeoutNum);
|
||||
this.timeoutNum = setTimeout(() => {
|
||||
this.initWebSocket();
|
||||
this.lockReconnect = false;
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user