JAVA结合WebSocket实现简单客服聊天功能

2023-05-11,,

说明:该示例只简单的实现了客服聊天功能

1、聊天记录没有保存到数据库中,一旦服务重启,消息记录将会没有,如果需要保存到数据库中,可以扩展

2、页面样式用的网上模板,样式可以自己进行修改

3、只能由用户主要发起会话,管理员无法主动进行对话

4、页面之间跳转代码没有包含在里面,请自己书写,在管理员消息列表页中,需要把该咨询的用户ID带到客服回复页面中

5、${websocket_url} 这个为项目的URL地址

效果截图:

客服回复页面

(member_admin_chat.html)

管理员消息列表页

(member_admin_chat_list.html)

用户咨询页面

(member_chat.html)

代码:

页面所需要用到的基础样式、图片,下载链接:https://www.lanzous.com/ias1kcb (这个只是自己网上下载的样式demo,可以根据自己的来)

pom.xml

          <dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>

 

或者jar包

javax.websocket-api-1.0.jar

下载地址:https://yvioo.lanzous.com/i3AXkhl3s3c

配置类

WebSocketConfig.java

package com.config;

import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;
import java.util.Set; public class WebSocketConfig implements ServerApplicationConfig {
@Override
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
return null;
} @Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) { //在这里会把含有@ServerEndpoint注解的类扫描加载进来 ,可以在这里做过滤等操作 return scanned;
}
}

  

消息DTO类(使用了lombok,这里不在多做说明)

ChatDTO.java

package com.websocket.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; /**
* @author 。
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ChatDTO { /**
* 用户ID
*/
private String userId; /**
* 用户发送信息
*/
private String message; /**
* 发送日期
* 消息时间格式(yyyy-MM-dd)
*/
private String createDate; /**
* 发送时间
* 消息时间格式(yyyy-MM-dd HH:mm:ss)
*/
private String createTime; }

  

用户DTO类

ChatUserDTO.java

package com.websocket.dto;

import com.entity.CmsUser;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; /**
* @author 。
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ChatUserDTO { /**
* 用户id
*/
private String userId; /**
* 用户名
*/
private String userName; /**
* 用户图片
*/
private String userImg; public ChatUserDTO convertUser(CmsUser user){
ChatUserDTO chatUserDTO=new ChatUserDTO();
chatUserDTO.setUserId(user.getId()+"")
.setUserName(user.getUsername())
.setUserImg(user.getUserImg());
return chatUserDTO;
}
}

  

ChatWebSocket.java

package com.websocket;

import com.service.RedisService;
import com.util.DateFormatUtils;
import com.entity.CmsUser;
import com.manager.CmsUserMng;
import com.enums.ChatTypeEnum;
import com.websocket.Constants;
import com.websocket.dto.ChatDTO;
import com.websocket.dto.ChatUserDTO;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext; import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*; /**
* @author 。
*/
@ServerEndpoint(value = "/chat_websocket")
public class ChatWebSocket { private RedisService redisService; private CmsUserMng cmsUserMng; public ChatWebSocket() {
WebApplicationContext webctx = ContextLoader.getCurrentWebApplicationContext();
this.redisService = (RedisService) webctx.getBean("redisService");
this.cmsUserMng = (CmsUserMng) webctx.getBean("cmsUserMng");
} /**
* 存储用户id
*/
public static Map userMap = new HashMap(); /**
* 聊天记录
*/
public static Map chatRecordMap = new HashMap(); /**
* 管理员列表session
*/
public static Session adminSession; /**
* 创建
*
* @param session
*/
@OnOpen
public void onOpen(Session session) {
Map<String, List<String>> requestParameterMap = session.getRequestParameterMap();
List<String> strs = requestParameterMap.get("msg");
if (strs != null && strs.size() > 0) {
String json = strs.get(0);
//从聊天集中去掉该集合
JSONObject object = JSONObject.fromObject(json);
String userId = object.getString("user_id");
String chatType = object.getString("chat_type"); /*--------------管理员列表-----------------------*/
if (ChatTypeEnum.adminListChatType.getKey().equalsIgnoreCase(chatType)) {
adminSession = session;
List list = getUserList(userMap);
//遍历所有聊天用户集合的id
chat_list_show(adminSession, list);
return; } /*--------------管理员聊天框打开-----------------------*/
if (ChatTypeEnum.adminChatType.getKey().equalsIgnoreCase(chatType)) {
//从集合中获取用户对应的数据加入信息列表中
List sessions = (List) userMap.get(userId);
if (sessions == null) {
sessions = new ArrayList();
}
sessions.add(session);
userMap.put(userId, sessions); } /*--------------用户聊天框打开-----------------------*/
if (ChatTypeEnum.userChatType.getKey().equalsIgnoreCase(chatType)) {
//判断是否建立聊天通道
List sessions = (List) userMap.get(userId);
if (sessions == null) {
sessions = new ArrayList();
}
sessions.add(session);
userMap.put(userId, sessions); } //聊天记录信息存放
List chatRecords = (List) chatRecordMap.get(userId);
if (chatRecords != null) {
chat((List<Session>) userMap.get(userId), chatRecords);
} }
} /**
* 发送消息
*
* @param json {userId:'',message:'',create_time:'',create_date:'',chat_type:'admin_list/admin_chat/user_chat'}
* admin_list:表示客服列表数据请求
* admin_chat:表示客服回复页面请求
* user_chat表示用户消息页面请求
*
*
* @throws Exception
*/
@OnMessage
public void onMessage(Session session, String json) {
JSONObject object = JSONObject.fromObject(json);
//用户ID
String userId = object.getString("user_id");
//用户发送的信息
String message = object.getString("message");
//请求类型
String chatType = object.getString("chat_type"); /*--------------管理员聊天-----------------------*/
if (ChatTypeEnum.adminChatType.getKey().equalsIgnoreCase(chatType)) {
//把管理员加入用户建立的聊天管道中 //用户聊天
//封装请求参数,时间为当前时间
ChatDTO chatDTO = new ChatDTO();
//userId=0表示是客服的回复
chatDTO.setUserId("0")
.setMessage(message)
.setCreateDate(DateFormatUtils.formatDate(new Date()))
.setCreateTime(DateFormatUtils.formatDateTime(new Date()));
//聊天记录信息存放
List chatRecords = (List) chatRecordMap.get(userId);
if (chatRecords == null) {
chatRecords = new ArrayList();
}
chatRecords.add(JSONObject.fromObject(chatDTO));
chatRecordMap.put(userId, chatRecords);
chat((List<Session>) userMap.get(userId), chatRecords); }
/*--------------用户聊天-----------------------*/
if (ChatTypeEnum.userChatType.getKey().equalsIgnoreCase(chatType)) { //封装请求参数,时间为当前时间
ChatDTO chatDTO = new ChatDTO();
chatDTO.setUserId(userId)
.setMessage(message)
.setCreateDate(DateFormatUtils.formatDate(new Date()))
.setCreateTime(DateFormatUtils.formatDateTime(new Date()));
String key = chatDTO.getUserId(); //聊天记录信息存放
List chatRecords = (List) chatRecordMap.get(key);
if (chatRecords == null) {
chatRecords = new ArrayList();
}
chatRecords.add(JSONObject.fromObject(chatDTO));
chatRecordMap.put(key, chatRecords);
chat((List<Session>) userMap.get(key), chatRecords);
if (adminSession != null) {
List list = getUserList(userMap);
//遍历所有聊天用户集合的id
chat_list_show(adminSession, list); }
} } /**
* 关闭
*/
@OnClose
public void onClose(Session session) { Map<String, List<String>> requestParameterMap = session.getRequestParameterMap();
List<String> strs = requestParameterMap.get("msg");
if (strs != null && strs.size() > 0) {
String json = strs.get(0);
JSONObject object = JSONObject.fromObject(json);
String userId = object.getString("user_id");
String chatType = object.getString("chat_type"); /*--------------管理员聊天框关闭-----------------------*/
if (ChatTypeEnum.adminChatType.getKey().equalsIgnoreCase(chatType)) { } /*--------------用户聊天框关闭-----------------------*/
if (ChatTypeEnum.userChatType.getKey().equalsIgnoreCase(chatType)) { } } } /**
* 发生错误
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
} /**
* 消息广播
*
* @param sessions
* @param messages
*/
public void chat(List<Session> sessions, List messages) {
for (Iterator it = sessions.iterator(); it.hasNext(); ) {
Session session = (Session) it.next();
try {
if (session.isOpen()) {
//当当前会话没有被关闭 发送消息
session.getBasicRemote().sendText(JSONArray.fromObject(messages) + "");
}
} catch (IOException e) {
e.printStackTrace();
}
} } /**
* 聊天列表显示
*/
public void chat_list_show(Session session, List list) {
try {
if (session.isOpen()) {
//当当前会话没有被关闭 发送消息
session.getBasicRemote().sendText(JSONArray.fromObject(list) + "");
}
} catch (IOException e) {
e.printStackTrace();
} } /**
* 通过id获取用户数据
*
* @return
*/
public List getUserList(Map userMap) {
List list = new ArrayList();
for (Object str : userMap.keySet()) {
ChatUserDTO chatUserDTO = new ChatUserDTO();
CmsUser user = cmsUserMng.findById(Integer.valueOf(str + ""));
list.add(chatUserDTO.convertUser(user));
}
return list;
} }

 聊天枚举类

ChatTypeEnum.java

 

package com.websocket;
/**
* @author 。
*/ public enum ChatTypeEnum { adminListChatType("admin_list", "管理员列表"),
adminChatType("admin_chat", "管理员聊天"),
userChatType("user_chat", "用户聊天"),
chatCountType("chat_count","消息数目"); private String key; public String value; ChatTypeEnum() {
} ChatTypeEnum(String key, String value) {
this.key = key;
this.value = value;
} public String getKey() {
return key;
} public void setKey(String key) {
this.key = key;
} public String getValue() {
return value;
} public void setValue(String value) {
this.value = value;
}
}

用户咨询页面

member_chat.html

  1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8"/>
5 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
6 <meta name="viewport"
7 content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
8 <title>客服咨询</title>
9 <link rel="stylesheet" type="text/css" href="/${res}/chat/css/chat.css"/>
10 <script src="/${res}/js/jquery.1.9.1.js"></script>
11 <script src="/${res}/chat/js/flexible.js"></script>
12 </head>
13 <body>
14
15
16 <header class="header">
17 <a class="back" href="javascript:history.back()"></a>
18 <h5 class="tit">客服</h5>
19 </header>
20 <div id="message">
21
22 </div>
23 <div id="footer">
24 <img src="/${res}/chat/images/hua.png" alt=""/>
25 <input class="my-input" type="text"/>
26 <p class="send">发送</p>
27 </div>
28 <script>
29
30 //聊天
31 var ws;
32 var obj = {
33 user_id: '${user.id}',
34 message: '',
35 chat_type: "user_chat"
36 }
37 var target = "ws:${websocket_url!}/chat_websocket?msg=" + encodeURI(JSON.stringify(obj));
38
39
40 var canSend = false;
41 $(function () {
42
43 //处理浏览器兼容性
44 if ('WebSocket' in window) {
45 ws = new WebSocket(target);
46 } else if ('MozWebSocket' in window) {
47 ws = new MozWebSocket(target);
48 } else {
49 alert('WebSocket is not supported by this browser.');
50 return;
51 }
52
53 ws.onopen = function () {
54
55 };
56 ws.onmessage = function (event) {
57 var data = JSON.parse(event.data);
58 console.log(data)
59 $('#message').html("");
60 for (var i = 0; i < data.length; i++) {
61 if (data[i].userId != '${user.id}') {
62 reply("/${res}/chat/images/touxiangm.png", data[i].message);
63 } else {
64 ask("/${res}/chat/images/touxiang.png", data[i].message);
65 }
66 }
67 };
68
69 ws.onclose = function (event) {
70 alert("连接断开,请重新刷新页面");
71 location.reload();
72 1
73 }
74 $('#footer').on('keyup', 'input', function () {
75 if ($(this).val().length > 0) {
76 $(this).next().css('background', '#114F8E').prop('disabled', true);
77 canSend = true;
78 } else {
79 $(this).next().css('background', '#ddd').prop('disabled', false);
80 canSend = false;
81 }
82 })
83 $('#footer .send').click(send)
84 $("#footer .my-input").keydown(function (e) {
85 if (e.keyCode == 13) {
86 return send();
87 }
88 });
89 })
90
91 /* 对方消息div */
92 function reply(headSrc, str) {
93 var html = "<div class='reply'><div class='msg'><img src=" + headSrc + " /><span class='name'>客服</span><p><i class='msg_input'></i>" + str + "</p></div></div>";
94 return upView(html);
95 }
96
97 /* 自己消息div */
98 function ask(headSrc, str) {
99 var html = "<div class='ask'><div class='msg'><img src=" + headSrc + " />" + "<p><i class='msg_input'></i>" + str + "</p></div></div>";
100 return upView(html);
101 }
102
103 function upView(html) {
104 var message = $('#message');
105 message.append(html);
106 var h = message.outerHeight() - window.innerHeight;
107 window.scrollTo(0, document.body.scrollHeight)
108 return;
109 }
110
111 function send() {
112 if (canSend) {
113 var input = $("#footer .my-input");
114 var val = input.val()
115 var obj = {
116 user_id: '${user.id}',
117 message: val,
118 chat_type: "user_chat"
119 }
120 ws.send(JSON.stringify(obj));
121 //ask("/${res}/chat/images/touxiangm.png", val);
122 input.val('');
123 }
124 }
125 </script>
126 </body>
127 </html>

管理员消息列表页

member_admin_chat_list.html

<!DOCTYPE html>
<html >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui">
<title>聊天列表 - ${site.name}</title>
<script src="${resSys}/jquery.js" type="text/javascript"></script>
<script src="${resSys}/front.js" type="text/javascript"></script>
<link rel="stylesheet" href="/${res}/bootstrap/css/bootstrap.css">
<script src="/${res}/bootstrap/js/bootstrap.js"></script> <!--[if lt IE 9]>
<script src="/${res}/js/html5shiv.min.js"></script>
<script src="/${res}/js/respond.min.js"></script>
<![endif]--> </head> <style>
.list-group-item span{
margin-right: 10px;
}
</style> <body>
[#include "../file/file_nav.html" /] <div>
<ul class="list-group" id="userList">
<li class="list-group-item">
<img src="/${res}/chat/images/touxiang.png" alt=""/>
<span class="badge">14</span>
Cras justo odio
</li>
</ul>
</div> </body> <script> var ws;
var obj={
user_id:'',
message:'',
chat_type:"admin_list"
}
var target="ws:${websocket_url}/chat_websocket?msg="+encodeURI(JSON.stringify(obj)); $(function () { //处理浏览器兼容性
if ('WebSocket' in window) {
ws = new WebSocket(target);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(target);
} else {
alert('WebSocket is not supported by this browser.');
return;
} ws.onmessage = function (event) {
var data=JSON.parse(event.data);
var html="";
if (data!=null&&data.length>0){
for (var i=0;i<data.length;i++){
var user=data[i];
html+="<a href='${base}/member/to_admin_chat_"+user.userId+".jspx'>"
html+="<li class='list-group-item'>";
html+="<img src='/${res}/chat/images/touxiang.png' />";
if (user.countmsg!=undefined){
html+="<span class='badge'>"+user.countmsg+"</span>"
}
html+=user.userName;
html+="</li>";
html+="</a>";
}
}else {
html="<li style='text-align: center'>没有消息</li>";
}
$("#userList").html(html); }; ws.onclose=function (event) { } })
</script>
</html>

管理员消息回复页面

member_admin_chat.html

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
<title>客服咨询</title>
<link rel="stylesheet" type="text/css" href="/${res}/chat/css/chat.css"/>
<script src="/${res}/js/jquery.1.9.1.js"></script>
<script src="/${res}/chat/js/flexible.js"></script>
</head>
<body> <header class="header">
<a class="back" href="javascript:history.back()"></a>
<h5 class="tit">客服</h5>
</header>
<div id="message"> </div>
<div id="footer">
<img src="/${res}/chat/images/hua.png" alt=""/>
<input class="my-input" type="text"/>
<p class="send">发送</p>
</div>
<script> //聊天
var ws;
var obj = {
user_id: '${user.id}',
message: '',
chat_type: "user_chat"
}
var target = "ws:${websocket_url!}/chat_websocket?msg=" + encodeURI(JSON.stringify(obj)); var canSend = false;
$(function () { //处理浏览器兼容性
if ('WebSocket' in window) {
ws = new WebSocket(target);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(target);
} else {
alert('WebSocket is not supported by this browser.');
return;
} ws.onopen = function () { };
ws.onmessage = function (event) {
var data = JSON.parse(event.data);
console.log(data)
$('#message').html("");
for (var i = 0; i < data.length; i++) {
if (data[i].userId != '${user.id}') {
reply("/${res}/chat/images/touxiangm.png", data[i].message);
} else {
ask("/${res}/chat/images/touxiang.png", data[i].message);
}
}
}; ws.onclose = function (event) {
alert("连接断开,请重新刷新页面");
location.reload();
}
$('#footer').on('keyup', 'input', function () {
if ($(this).val().length > 0) {
$(this).next().css('background', '#114F8E').prop('disabled', true);
canSend = true;
} else {
$(this).next().css('background', '#ddd').prop('disabled', false);
canSend = false;
}
})
$('#footer .send').click(send)
$("#footer .my-input").keydown(function (e) {
if (e.keyCode == 13) {
return send();
}
});
}) /* 对方消息div */
function reply(headSrc, str) {
var html = "<div class='reply'><div class='msg'><img src=" + headSrc + " /><span class='name'>客服</span><p><i class='msg_input'></i>" + str + "</p></div></div>";
return upView(html);
} /* 自己消息div */
function ask(headSrc, str) {
var html = "<div class='ask'><div class='msg'><img src=" + headSrc + " />" + "<p><i class='msg_input'></i>" + str + "</p></div></div>";
return upView(html);
} function upView(html) {
var message = $('#message');
message.append(html);
var h = message.outerHeight() - window.innerHeight;
window.scrollTo(0, document.body.scrollHeight)
return;
} function send() {
if (canSend) {
var input = $("#footer .my-input");
var val = input.val()
var obj = {user_id: '${user.id}', message: val, chat_type: "user_chat"}
ws.send(JSON.stringify(obj));
//ask("/${res}/chat/images/touxiangm.png", val);
input.val('');
}
} </script>
</body>
</html>

JAVA结合WebSocket实现简单客服聊天功能的相关教程结束。

《JAVA结合WebSocket实现简单客服聊天功能.doc》

下载本文的Word格式文档,以方便收藏与打印。