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

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

          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

pom.xml

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

 

或者jar包

tomcat7-websocket.jar

websocket-api.jar 

配置类

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;
    }


}

  

 用户咨询页面

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>
原文地址:https://www.cnblogs.com/pxblog/p/12596111.html