spring boot+vue实现H5聊天室客服功能
h5效果图
vue效果图
功能实现
spring boot
+webSocket
实现- 官方地址 https://docs.spring.io/spring-framework/docs/5.0.8.RELEASE/spring-framework-reference/web.html#websocket
maven 配置文件
4.0.0
org.springframework.boot
spring-boot-dependencies
2.2.0.RELEASE
org.example
webChat
1.0-SNAPSHOT
1.8
org.springframework.boot
spring-boot-starter-thymeleaf
com.alibaba
fastjson
1.2.78
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-websocket
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
webSocket
配置
package com.example.webchat.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
/**
* @author Mr.Fang
* @title: WebSocketConfig
* @Description: web socket 配置
* @date 2021/11/14 13:12
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "myHandler/") // 访问路径
.addInterceptors(new WebSocketHandlerInterceptor()) // 配置拦截器
.setAllowedOrigins("*"); // 跨域
}
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192); // 例如消息缓冲区大小、空闲超时等
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
消息处理类
package com.example.webchat.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.webchat.pojo.DataVo;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Mr.Fang
* @title: MyHandler
* @Description: 消息处理类
* @date 2021/11/14 13:12
*/
public class MyHandler extends AbstractWebSocketHandler {
private static int onlineCount = 0;
// 线程安全
private static Map userMap = new ConcurrentHashMap<>(); // 用户
private static Map adminMap = new ConcurrentHashMap<>(); // 客服
/**
* @Description: 连接成功之后
* @param session
* @return void
* @Author Mr.Fang
* @date 2021/11/14 13:15
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws IOException {
addOnlineCount(); // 当前用户加 1
System.out.println(session.getId());
Map map = session.getAttributes();
Object token = map.get("token");
Object admin = map.get("admin");
DataVo dataVo = new DataVo();
dataVo.setCode(9001).setMsg("连接成功");
if (Objects.nonNull(admin)) {
adminMap.put(session.getId(), session); // 添加客服
} else {
// 分配客服
userMap.put(session.getId(), session); // 添加当前用户
distribution(dataVo);
}
dataVo.setId(session.getId());
System.out.println("用户连接成功:" + admin);
System.out.println("用户连接成功:" + token);
System.out.println("在线用户:" + getOnlineCount());
this.sendMsg(session, JSONObject.toJSONString(dataVo));
}
/**
* @param vo
* @return void
* @Description: 分配客服
* @Author Mr.Fang
* @date 2021/11/14 13:13
*/
private void distribution(DataVo vo) {
if (adminMap.size() != 0) {
Random random = new Random();
int x = random.nextInt(adminMap.size());
Set values = adminMap.keySet();
int j = 0;
for (String str : values) {
if (j == x) {
vo.setRecId(str);
System.out.println("分配ID:" + str);
break;
}
j++;
}
}
}
/**
* @param session
* @param message
* @return void
* @Description: 收发消息
* @Author Mr.Fang
* @date 2021/11/14 13:13
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.print("用户ID:" + session.getId());
String payload = message.getPayload();
System.out.println("接受到的数据:" + payload);
DataVo dataVo = JSON.toJavaObject(JSON.parseObject(payload), DataVo.class); // json 转对象
if (Objects.isNull(dataVo.getRecId()) || dataVo.getRecId().equals("")) { // 用户客服为空 分配客服
WebSocketSession socketSession = adminMap.get(session.getId());
if (Objects.isNull(socketSession)) {
this.distribution(dataVo);
}
}
if (dataVo.getCode() == 9002) {
if (Objects.nonNull(dataVo.getRecId())) { // user -> admin
WebSocketSession socketSession = adminMap.get(dataVo.getRecId());
dataVo.setSelfId(session.getId()).setRecId("");
this.sendMsg(socketSession, JSONObject.toJSONString(dataVo));
} else if (Objects.nonNull(dataVo.getSelfId())) { // admin ->user
WebSocketSession socketSession = userMap.get(dataVo.getSelfId());
dataVo.setRecId(session.getId()).setSelfId("");
this.sendMsg(socketSession, JSONObject.toJSONString(dataVo));
}
}
}
/**
* @param session
* @param msg
* @return void
* @Description: 发送消息
* @Author Mr.Fang
* @date 2021/11/14 13:14
*/
private void sendMsg(WebSocketSession session, String msg) throws IOException {
session.sendMessage(new TextMessage(msg));
}
/**
* @Description: 断开连接之后
* @param session
* @param status
* @return void
* @Author Mr.Fang
* @date 2021/11/14 13:14
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
subOnlineCount(); // 当前用户加 1
adminMap.remove(session.getId());
userMap.remove(session.getId());
System.out.println("用户断开连接token:" + session.getId());
System.out.println("用户断开连接admin:" + session.getId());
System.out.println("在线用户:" + getOnlineCount());
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
/**
* @Description: 在线用户 +1
* @return void
* @Author Mr.Fang
* @date 2021/11/14 13:16
*/
public static synchronized void addOnlineCount() {
MyHandler.onlineCount++;
}
/**
* @Description: 在线用户 -1
* @return void
* @Author Mr.Fang
* @date 2021/11/14 13:16
*/
public static synchronized void subOnlineCount() {
MyHandler.onlineCount--;
}
}
配置拦截器
package com.example.webchat.config;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Objects;
/**
* @author Mr.Fang
* @title: WebSocketHandlerInterceptor
* @Description: 拦截器
* @date 2021/11/14 13:12
*/
public class WebSocketHandlerInterceptor extends HttpSessionHandshakeInterceptor {
/**
* @param request
* @param response
* @param wsHandler
* @param attributes
* @return boolean
* @Description: 握手之前
* @Author Mr.Fang
* @date 2021/11/14 13:18
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpServletRequest re = servletRequest.getServletRequest();
Object token = re.getParameter("token");
Object admin = re.getParameter("admin");
if (Objects.isNull(token)) {
return false;
}
re.getSession().setAttribute("admin", admin);
re.getSession().setAttribute("token", token);
return super.beforeHandshake(request, response, wsHandler, attributes);
}
/**
* @param request
* @param response
* @param wsHandler
* @param ex
* @return boolean
* @Description: 握手之后
* @Author Mr.Fang
* @date 2021/11/14 13:18
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {
super.afterHandshake(request, response, wsHandler, ex);
}
}
h5服务端
服务端
服务端发送消息
客户端
客户端
客户端发送消息
vue
连接webSocket
{{item.content}}
{{item.content}}
发送
源码地址 https://gitee.com/bxmms/web-chat.git