网页版在线客服实现代码
第一次写,感觉很乱,后面直接上代码吧,代码也有注释,想看思路的朋友可以看前面。有什么建议和意见欢迎砸过来。
功能:
1、客服需登录进入客服页面。用户无需登录,进入用户页面,直接获取sessionId作为id值。
2、用户进入页面并且发送消息时,客服才会获取到该用户,并在左侧列表显示。
3、点击用户名即可切换聊天对象,正在聊天的用户,用户名为选中状态。
4、每条消息的时间显示在本条消息上方(水平居中)。消息时间间隔不足1分钟,则此不显示时间。
5、每次读取的消息均为最新消息。
6、客服未与其正在聊天的用户发送新消息时应有新消息提示。
7、用户关闭浏览器或长时间无回应时,将用户置为下线。
客服界面将清空与下线用户的聊天窗口,且下线用户不显示在左侧列表中。
8、用户长时间无回应置为下线后,刷新可继续在线与客服聊天。
9、发送:1)输入框未输入消息时,提示“发送内容不能为空”。
2)未选择用户时,提示“请选择用户”。
数据库表:
三张表:客服(user),用户(customer),聊天记录(cs_chat_record)。
客服表主要字段:id,name,password,online,currentPeople;
用户表主要字段:id,name,online,headImg;
聊天记录表主要字段:id,userId,customerId,userContent,customerContent,time;
主要思路:
1、用户进入页面:查看当前用户有没有正在被客服人员接待
①正在被接待:设置用户为在线状态(因为刷新会有下线操作)。
②未被接待:获取所有在线客服每个人正在接待的用户人数,并给该用户分配一个当前接待人数少的客服。
修改此客服正在接待的人数,将用户添加到用户表中(设置为在线状态)。
将用户和客服均存到session中。
2、客服进入页面:常规验证。
4、用户和客服发送消息时,都先将信息保存到数据库。
5、用户和客服界面,都是一秒刷新一次获取最新消息。
6、客服页面:当前正在沟通的用户列表,每秒获取一次,将不在线的用户在列表清除。
7、长时间无回复的用户置为下线,用到数据库定时器。
8、用户关闭浏览器置为下线,用到的是window.onbeforeunload方法。
9、新消息提醒:保存用户消息将用户头像设置成有红点的,获取消息时将用户头像设置为无红点的。
知识点:
1、获取用户id:getRequest().getSession().getId();
2、获取最新聊天记录时,聊天记录表的id是在前端存储的,一定不要在后台用全局变量存储,方便用户刷新页面时再次获取之前所有聊天记录。
3、c:forEach标签只能在服务器端使用,在页面加载完成后需要用js的for循环。
4、用户列表的显示是foreach循环得到的,为显示用户名的label设置class属性,用户点击切换用户时使用。
列表用户正在聊天的用户用户名设置为选中状态:$("div#"+id).css('background','#00ffff');
其他用户取消选中状态:$(".CustomerBG").css('background','#ffffff');
( ps:这里不是很懂,所以给div设置了两个属性,给背景色和取消背景色用到的不是一个属性。)
5、循环得到的用户名:<label id='labelId' class='checkLabel' style='font-size: 12pt;'data_id='" +cId +"'>"
1)点击用户名触发的事件,此方法在$(function(){})外写,能监听js中拼接的节点(此客服系统用的此方法):
$(document).off("click", ".checkLabel").on("click", ".checkLabel", function(){
//点击用户名之后要执行的内容
});
2)此方法在$(function(){})内写,不能监听到js中拼接的节点,
$(".checkLabel").click(function(){
//要执行的内容
})
attr:1)$(this).attr("data_id");拿到自定义的属性的值。
2)$(this).attr("data_id",“value”);给自定义的属性赋值。
6、清空div中的所有内容:
$("#divId").empty();
$("#divId").html("");
7、地址栏不能拼接时间参数,时间格式有空格,地址以空格为终点。
8、给myclass的span节点的内容加样式。
<div class="myclass" ><span>样式体现在这</span></div>
.myclass span{
//要写的样式
}
9、数据库定时器的格式
DELIMITER $$
ALTER DEFINER=`root`@`localhost` EVENT `test` ON SCHEDULE EVERY 1 MINUTE STARTS '2018-04-26 18:40:32' ON COMPLETION NOT PRESERVE ENABLE DO BEGIN
//要执行的SQL语句
END$$
DELIMITER ;
10、将30分钟内未发送消息的用户置为下线(觉得不是很容易写,半个多小时才写出来,记录一下)
UPDATE customer SET online=0
WHERE id IN
(SELECT customerId FROM
(SELECT customerId,MAX(`time`) maxTime FROM cs_chat_record GROUP BY customerId) temp
WHERE maxTime <CURRENT_TIMESTAMP - INTERVAL 30 MINUTE);
未解决的问题:
1、每秒一次获取用户列表,页面会有闪烁。
2、火狐浏览器只在关闭页面时执行window.onbeforeunload方法,关闭整个浏览器时不执行。
3、“发送内容不为空”和“请选择用户”的判断都是在后台做的,感觉应该在前端做。
主要代码:
客服页:
<!DOCTYPE html>
<%@ page language="java" pageEncoding="utf-8"%>
<%@ include file="/platform/common/jsp/taglibs.jsp"%>
<html>
<head><title>客服中心</title><meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><!--微信不缓存东西 start--><meta http-equiv="Pragma" content="no-cache" /><meta http-equiv="Expires" content="0" /><style type="text/css">.myclass{width:100%;font-size: 12pt;padding-bottom: 10px;}.myclass span{width:100px;background-color: #66b3ff;border: 1px;}</style><link rel="stylesheet" type="text/css" href="${path}/module/cs/css/easyui.css"><script type="text/javascript" src="${path}/module/cs/js/jquery.min.js"></script><script type="text/javascript" src="${path}/module/cs/js/jquery.easyui.min.js"></script><script type="text/javascript">var oldId = 1;//计算时间间隔function computeTime(oldTime){date = new Date(oldTime);var date2 = date.getTime()-datetemp.getTime();//一分钟内再次发消息,则不显示发送消息的时间if(date2>60000){var flagH="";var flagM="";var hour = date.getHours();var min= date.getMinutes();//解决小时和分钟小于10,数字前面不显示0的情况if(hour<10)flagH="0";else flagH="";if(min<10)flagM=":0";else flagM=":";//发送内容时间$("#contentCS").append("<div style='text-align:center ;'>"+flagH+hour+flagM+min+"</div>")datetemp=date;}}function getAllRecord(){$.ajax({url : "${path}/cs/getAllChatRecord?idTemp=user_id&id="+id+"&oldId="+oldId,type : "POST",dataType:"json",data :null,success : function(data) {if (data.success) {//将聊天信息显示到页面上for ( var i in data.obj) {var csChatRecord = data.obj[i];if(csChatRecord.userContent != null && csChatRecord.userContent != ''){computeTime(csChatRecord.time); $("#contentCS").append("<div class='myclass' style='text-align:right ;'><span>" +csChatRecord.userContent+"</span></div>"); }if(csChatRecord.customerContent != null && csChatRecord.customerContent != ''){computeTime(csChatRecord.time); $("#contentCS").append("<div class='myclass'><span>" +csChatRecord.customerContent+"</span></div>");}oldId=csChatRecord.id;}} else {alert(data.msg);}} , error:function(result){alert("服务器丢了");} });}//获取左侧正在沟通的用户列表function getCustomerList(){$("#westList").html("");$.ajax({url : "${path}/cs/getCustomerList",type : "POST",dataType:"json",success : function(data) {if (data.success) {var onlineFlag=1;//用户下线,如果客服人员正在与此人沟通,那么清空聊天页面for ( var i in data.obj) {var customer = data.obj[i];if(id==customer.id){onlineFlag=0;}}if(onlineFlag==1){$("#contentCS").html("");id="";}//获得用户列表,显示到页面上for ( var i in data.obj) {var customer = data.obj[i];cId=customer.id;var cHeadImg=customer.headImg;var cName=customer.name;if(customer.online >0){$("#westList").append("<div id="+cId+" class='CustomerBG'><img alt='加载中' src='${path }/module/cs/images/"+cHeadImg+"' height='40px' width='40px' >"+"<label id='labelId' class='checkLabel' style='font-size: 12pt;'data_id='" +cId+"'>"+cName+"</div>");}//防止列表刷新将选中的用户置为未选中状态if(cId==id){$("div#"+cId).css('background','#00ffff');}}} else {alert(data.msg);}} , error:function(result){alert("服务器丢了");} });}var datetemp=new Date(0);//用于计算时间差的中间变量var date;$(function(){//获取用户列表getCustomerList();//定时刷新setInterval('getCustomerList()',1000);//消息存入数据库$("#send").click(function() {$.ajax({url : "${path}/cs/saveUserChatRecord?id="+id,type : "POST",dataType:"json",/* 向后端传输的数据 */ data :$("#ff").serialize(),success : function(data) {if (!data.success) {alert(data.msg);} $("#textId").textbox('clear');} , error:function(result){alert("发送失败");} });});});var id = "";$(document).off("click", ".checkLabel").on("click", ".checkLabel", function(){oldId = 1;datetemp=new Date(0);id = $(this).attr("data_id");$(".CustomerBG").css('background','#ffffff');$("div#"+id).css('background','#00ffff');$("#contentCS").html("");$("#contentCS").append("<div class='myclass'>当前正在沟通的用户:"+$(this).text()+"</div>");//从数据库读取消息getAllRecord();// 定时刷新组件,读取数据库信息 setInterval('getAllRecord()',1000);});</script>
</head>
<body><div id="cc" class="easyui-layout" style="width:600px;height:500px;" fit="true"> <!-- <div data-options="region:'north'" style="height:10%"></div> --> <div data-options="region:'south'" style="height:30%;"> <form id="ff" style="height:100%;width:100%" > <div> <input id="textId" class="easyui-textbox" name="userContent" style="height:120px;width:100%;border: 0;" multiline="true" prompt="说点什么吧..." /> </div> <div style="float:right" > <button type="button" id="send" >发送</button></div> </form> </div><!-- <div data-options="region:'east'" style="width:10%;"></div> --><div id="westList" data-options="region:'west'" style="width:20%;">正在沟通的用户<button type="button" id="list" style="display: none"></button></div> <div id="contentCS" data-options="region:'center'" style="padding:5px;" ></div> </div>
</body>
</html>
用户页:
<!DOCTYPE html>
<%@ page language="java" pageEncoding="utf-8"%>
<%@ include file="/platform/common/jsp/taglibs.jsp"%>
<html>
<head><title>客服中心</title><meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><!--微信不缓存东西 start--><meta http-equiv="Pragma" content="no-cache" /><meta http-equiv="Expires" content="0" /><style type="text/css">.myclass{width:100%;font-size: 12pt;padding-bottom: 10px;}span{width:100px;background-color: #66b3ff;border: 1px;}</style><link rel="stylesheet" type="text/css" href="${path}/module/cs/css/easyui.css"><script type="text/javascript" src="${path}/module/cs/js/jquery.min.js"></script><script type="text/javascript" src="${path}/module/cs/js/jquery.easyui.min.js"></script><script type="text/javascript">var oldId = 1;//计算时间间隔function computeTime(oldTime){date = new Date(oldTime);var date2 = date.getTime()-datetemp.getTime();//一分钟内再次发消息,则不显示发送消息的时间if(date2>60000){var flagH="";var flagM="";var hour = date.getHours();var min= date.getMinutes();//解决小时和分钟小于10,数字前面不显示0的情况if(hour<10)flagH="0";else flagH="";if(min<10)flagM=":0";else flagM=":";//发送内容时间$("#content").append("<div style='text-align:center ;'>"+flagH+hour+flagM+min+"</div>")datetemp=date;}}function getAllRecord(){$.ajax({url : "${path}/cs/getAllChatRecord?idTemp=customer_id&oldId="+oldId,type : "POST",dataType:"json",success : function(data) {if (data.success) {for ( var i in data.obj) {var csChatRecord = data.obj[i];if(csChatRecord.userContent != null && csChatRecord.userContent != ''){computeTime(csChatRecord.time); //发送的内容$("#content").append("<div class='myclass'><span>" +csChatRecord.userContent+"</span></div>");}if(csChatRecord.customerContent != null && csChatRecord.customerContent != ''){computeTime(csChatRecord.time); $("#content").append("<div class='myclass' style='text-align:right ;'><span>" +csChatRecord.customerContent+"</span></div>");}oldId=csChatRecord.id;}} else {alert(data.msg);}} , error:function(result){alert("获取所有消息失败");} });}var datetemp=new Date(0);//用于计算时间差的中间变量var date;$(function(){getAllRecord();/* 定时刷新组件,读取数据库信息 */setInterval('getAllRecord()',1000);/* 定时刷新组件,读取时间 */$("#send").click(function() {$.ajax({url : "${path}/cs/saveCustomerChatRecord",type : "POST",dataType:"json",/* 向后端传输的数据 */ data :$("#ff").serialize(),success : function(data) {if (data.success) {$("#textId").textbox('clear');} else {alert(data.msg);$("#textId").textbox('clear');}} , error:function(result){alert("发送失败");} });});});window.onbeforeunload = function() { $.ajax({url : "${path}/cs/customerOffline",type : "POST",});} </script>
</head>
<body><div id="cc" class="easyui-layout" style="width:600px;height:400px;" fit="true"> <div data-options="region:'north'" style="height:15%"><h1 >客服中心</h1> </div> <div data-options="region:'south'" style="height:25%;"> <form id="ff" style="height:100%;width:100%" > <div> <input id="textId" class="easyui-textbox" name="customerContent" style="height:100px;width:100%" multiline="true" prompt="说点什么吧..." /> </div> <div style="float:right" > <button type="button" id="send">发送</button></div> </form> </div><!-- <div data-options="region:'east'" style="width:10%;"></div> <div data-options="region:'west'" style="width:10%;"></div> --> <div id="content" data-options="region:'center'" style="padding:5px;" ></div> </div>
</body>
</html>
controller:
@Controller
@RequestMapping(value = "/cs")
public class CSController extends SpringController{@Autowiredprivate CSService csService;@Autowiredprivate UserService userService;/*** 将用户发送的消息保存到数据库* @param userContent* @return*/@RequestMapping(value = "/saveCustomerChatRecord",method=RequestMethod.POST)@ResponseBodypublic AjaxJson saveCustomerChatRecord(@RequestParam(value = "customerContent", required = false) String customerContent) {AjaxJson ajaxJson =new AjaxJson();CSChatRecord csChatRecord = new CSChatRecord();try {User user = (User) getRequest().getSession().getAttribute("user");Customer customer = (Customer) getRequest().getSession().getAttribute("customer");//获取当前时间 String datetime = DateUtil.getCurrDateSecondString();//封装csChatRecord对象csChatRecord.setUserId(user.getId());csChatRecord.setCustomerId(customer.getId());csChatRecord.setTime(datetime);customerContent = customerContent.trim();if(customerContent != null && !customerContent.equals("")){csChatRecord.setCustomerContent(customerContent);//将csChatRecord对象保存到数据库csService.saveCustomerChatRecord(csChatRecord);}else{ajaxJson.setSuccess(false);ajaxJson.setMsg("发送内容不能为空");}customer.setHeadImg("C_new.jpg");csService.updateHeadImgState(customer);return ajaxJson;}catch (Exception e) {e.printStackTrace();ajaxJson.setSuccess(false);ajaxJson.setMsg("服务器跑丢了。。");return ajaxJson;}}/*** 将客服发送的消息保存到数据库* @param userContent* @return*/@RequestMapping(value = "/saveUserChatRecord",method=RequestMethod.POST)@ResponseBodypublic AjaxJson saveUserChatRecord(@RequestParam(value = "userContent", required = false) String userContent,@RequestParam(value = "id", required = false) String id) {AjaxJson ajaxJson = new AjaxJson();CSChatRecord csChatRecord = new CSChatRecord();try {if(id==null || id==""){ajaxJson.setSuccess(false);ajaxJson.setMsg("请选择用户");return ajaxJson;}User user = (User) getRequest().getSession().getAttribute("user");//获取当前时间String datetime = DateUtil.getCurrDateSecondString();//封装csChatRecord对象csChatRecord.setUserId(user.getId());csChatRecord.setCustomerId(id);csChatRecord.setTime(datetime);userContent = userContent.trim();if(userContent != null && !userContent.equals("")){csChatRecord.setUserContent(userContent);//将csChatRecord对象保存到数据库csService.saveUserChatRecord(csChatRecord);}else{ajaxJson.setSuccess(false);ajaxJson.setMsg("发送内容不能为空");}return ajaxJson;} catch (Exception e) {e.printStackTrace();return ajaxJson;}}/*** 从数据库读取所有聊天记录,展示在页面上* 客服和用户公用同一个方法,* idTemp:为user_id时表示客服,为customer_id时表示用户* id:客服读消息时用到的用户id* oldId:上一次读取新消息的记录,保证每次读取的都是最新消息* @return*/@RequestMapping(value = "/getAllChatRecord")@ResponseBodypublic AjaxJson getAllChatRecord(@RequestParam(value = "idTemp", required = false) String idTemp,@RequestParam(value = "id", required = false) String id,@RequestParam(value = "oldId", required = false) int oldId) {AjaxJson ajaxJson =new AjaxJson();CSChatRecord csChatRecord = new CSChatRecord();User user = (User) getRequest().getSession().getAttribute("user");Customer customer = new Customer();try {//判断是用户页面还是客服页面读取数据库消息if(idTemp.equals("user_id")){//客服csChatRecord.setCustomerId(id);customer.setId(id);customer.setHeadImg("C.jpg");csService.updateHeadImgState(customer);}else if(idTemp.equals("customer_id")){customer = (Customer) getRequest().getSession().getAttribute("customer");csChatRecord.setCustomerId(customer.getId());}csChatRecord.setUserId(user.getId());csChatRecord.setId(oldId);List<CSChatRecord> alllist = csService.getAllChatRecord(csChatRecord);if(alllist.size()>0){ajaxJson.setObj(alllist);}return ajaxJson;} catch (Exception e) {e.printStackTrace();return ajaxJson;}}/*** 客服界面* @return*/@RequestMapping(value = "/goUser")public String goUser() {try {return "module/cs/jsp/weChat_user";} catch (Exception e) {e.printStackTrace();return "module/weChat/jsp/weChat_404";}}/*** 用户界面* @return*/@RequestMapping(value = "/goCustomer")public String goCustomer() {int temp = 0;User userTemp = new User();Customer customer = new Customer();try {Customer customer2 = (Customer) getRequest().getSession().getAttribute("customer");if(customer2 == null){//获取所有在线客服每个人正在接待的用户人数List<User> userList = userService.getAllUserState();userTemp = userList.get(0);temp = userTemp.getCurrentPeople();for (User user : userList) {if(user.getCurrentPeople()<temp){temp = user.getCurrentPeople();userTemp = user;}}//修改当前客服接待人数,currentPeople+1userService.updateCurrentPeople(userTemp);String session = getRequest().getSession().getId();customer.setId(session);//设置当前用户的在线状态customer.setOnline(1);int number = (int) ((Math.random()*100)+1);customer.setName("用户"+number);csService.insertCustomer(customer);getRequest().getSession().setAttribute("customer", customer);getRequest().getSession().setAttribute("user", userTemp);}else{customer2.setOnline(1);csService.updateCustomerOnline(customer2);}return "module/cs/jsp/weChat_customer";} catch (Exception e) {e.printStackTrace();return "module/weChat/jsp/weChat_404";}}/*** 用户下线*/@RequestMapping(value = "/customerOffline",method=RequestMethod.POST)@ResponseBodypublic void customerOffline() {Customer customer = (Customer) getRequest().getSession().getAttribute("customer");customer.setOnline(0);customer.setHeadImg("C.jpg");csService.updateCustomerOnline(customer);}/*** 获取当前正在沟通的用户* @return*/@RequestMapping(value = "/getCustomerList",method=RequestMethod.POST)@ResponseBodypublic AjaxJson getCustomerList() {User user = (User) getRequest().getSession().getAttribute("user");AjaxJson ajaxJson = new AjaxJson();List<CSChatRecord> customerList = csService.getCustomerList(user.getId());if(customerList.size()>0)ajaxJson.setObj(customerList);return ajaxJson;}
}
网页版在线客服实现代码相关推荐
- 宝塔实测-PHP网页版在线客服系统源码
大家好啊,我是测评君,欢迎来到web测评. 本期给大家带来一套PHP网页版在线客服系统源码. 运行环境 服务器宝塔面板 PHP 7.2 Mysql 5.6 Linux Centos7以上 文字安装教程 ...
- ASP.NET版在线客服系统源码 带服务端
代码分网站,服务端,客端三部份组成. 不是外面那种纯网页的在线客服源码跟这没的比. 演示功能参考www.qchat.cn 转载于:https://www.cnblogs.com/sxsoft/arch ...
- JavaScript在线客服留言代码
下载地址 JavaScript在线客服留言代码,仿百度商桥客服系统网页特效代码. dd:
- ThinkPHP核心多商户版在线客服程序
简介: ThinkPHP核心多商户版在线客服对接适用场景[PC+WAP+公众号] 源码分享 运行环境: 服务器系统:Linux + Centos7.x + 宝塔 亲测环境:Nginx 1.18.0 + ...
- pc端_移动端_H5_ QQ在线客服链接代码
强烈推荐: http://wpa.qq.com/msgrd?v=3&uin=1846189021&site=qq&menu=yes PC端:QQ在线客服链接代码 <a h ...
- QQ在线客服JS代码,自适应漂浮在网页右侧
漂浮在网页右侧的QQ在线客服代码,自适应浏览器调试,当你拖动滚动条的时候它会自动计算始终漂浮于某一位置,而且是动画特效,很不错,你也可以把它改成一个漂浮广告,只用换掉表格里的内容就可以了. <h ...
- 开源版-在线客服系统源码_网页聊天室源码_webim
2019独角兽企业重金招聘Python工程师标准>>> WoLive是一款在线客服系统源码,支持PC Web和移动端,只需嵌入一段js代码即可快速接入.购买后可私有化部署,WoLiv ...
- 在线客服系统代码安装 (附移动版APP下载)
GOFLY,一套可私有化部署的免费开源客服系统,独立部署后,无服务器限制,无域名限制,无坐席限制 网站只需嵌入一段js或跳转直连地址即可快速接入客服,访客端支持电脑.手机页面自适应,全渠道支持,这个项 ...
- 防黑运营版在线客服系统源码/多商户机器人/自助注册客服系统源码/im即时通讯聊天系统源码
☑️ 编号:ym213 ☑️ 品牌:thinkPHP ☑️ 语言:php ☑️ 大小:99MB ☑️ 类型:在线客服系统源码 ☑️ 支持:im即时通讯
- 在线客服系统代码_h5客服_对接公众号_支持APP_支持多语言
前言 客服系统比较常见,主流的还是采用三方SDK接入,这些SDK的实现方式大都采用长连接,性能要求比较高,费用也偏高.我们在此的目标是开发一个属于自己的客服系统,完全的无依赖第三方,完全自己控制. 一 ...
最新文章
- html 简单机器人对话页面,简单的js聊天机器人框架BotUI
- pdb+ipdb 调试 Python代码
- nofollow标签_网站nofollow标签的应用 - 最蜘蛛池博客
- domain logic approaches
- disk genius_如何预约Apple Store商店或Genius Bar
- MySQL分布式ID_分布式唯一ID系列(3)——数据库自增ID机制适合做分布式ID吗
- 【工具】Xshell安装注册以及简单属性配置
- Android 应用开发(38)TableLayout(表格布局)
- Linux init详解(转)
- html字体样式美化,css美化文字做法详解总结
- Vista 自动激活工具(最新 最权威 所有版本 可升级)
- Prolog编程求解图搜索问题
- 【笔试】中移物联网重庆公司
- time datetime 总结
- UNIX网络编程卷1 回射客户程序 TCP客户程序设计范式
- Google推出拼音输入法了!
- 方法: 跳转App Store更新你应用的URL究竟该怎么写
- 【paper吐槽】【SelfSupervised Learning】Self-Supervised Image Restoration with Blurry and Noisy Pairs
- 《杜拉拉升职记》的体会-勤奋的重要性
- 快排的优化策略(3种快排4种优化)