基於融雲api開發客服聊天功能
以下所有內容均為本人自己開發總結的經驗,如有雷同,不勝榮幸!
最近公司在做一款app,app裡面涉及到與客服聊天功能。
於是公司就接入了融雲api聊天系統。
app端(前端)聊天由專業的ios andorid開發人員完成,我負責開發後臺客服聊天即可。
目前使用的是ssm 4.0框架,jdk1.8
框架就自行搭建了。能執行就行。下面就詳細聊聊接入融雲,並開發聊天。
注:融雲是收費的。各位看官如果是使用收費模式,則不需要觀看該文章,因為我是自行開發的。
涉及功能:1對1聊天,聊天記錄儲存,會話列表,未讀訊息,是否線上(這些功能屬於後臺,前端的我不管。)
1:1vs1 聊天,(從app端看來是使用者對客服1vs1,從客服對app端則是1vs n)(n:即多個使用者,所以這邊會有會話聊天列表)
因為只有1對1聊天是免費的。所以除了1對1聊天,其他功能全部本地開發,並未使用融雲api。
融雲api配置我就不說了。網上一大堆。隨便看看就ok。
配置只有一點要注意:
這個要放進來。這個是融雲的聊天格式,
不論是前端還是後端。使用者聊天都必須使用token 這是融雲要求的。所以獲得token是服務端完成的。
如果上面的圖片沒有放入到專案中,那麼你服務端獲取token是失敗的。
這個坑是摸索半天,看了半天的原始碼才搞通
String userToken = UserExample.returnToken(users.getUsers_only_md5(), usersInfo.getUsers_info_nick(),RongYunKeyEnum.APPKEY.getValue(), RongYunKeyEnum.APPSECRET.getValue()); req.setAttribute("userToken", userToken);
returnToken 是我在 UserExample 類裡自己封裝了方法,當引數傳進來而已。自己隨便寫就行。
服務端token寫好了。頁面端需要2個值, token 和appKey 用request 傳作用域就行
記得返回到聊天的頁面來。
融雲是用id 作為聊天的唯一物件。所以我這邊用自己資料庫的唯一列作為傳遞到融雲的id值。所以我傳遞到頁面上,方面2人聊天。
我這邊解釋下,我是本地跟本地聊天。並未涉及到和app聊天。所以我要區分,該登入是使用者還是客服。可以用2個登入方法登入頁面來實現。(identity:區分身份(users/manager))
<script type="text/javascript"> $(document).keyup(function(event){ if(event.keyCode ==13){ privateMessage(); } }); var instance = null; var userInfo = { appKey : "${appKey}", token : "${userToken}" }; function showTips(data){ var dom = document.getElementById("show"); dom.style.display = "block"; if(data){ dom.innerHTML += "<li>" + data + "</li>"; }else{ dom.innerHTML = ""; } } var callbacks = { getInstance : function(_instance){ instance = _instance; }, receiveNewMessage : function(message){ // 判斷訊息型別 showTips("新訊息,型別為:" + message.messageType); // showResult("新訊息",message,start);
console.log("messageUId:" + message.messageUId + ", messageId:" + message.messageId); console.log(message); }, getCurrentUser : function(userInfo){ userId = userInfo.userId; $("#login").val(userId); //afterConnected(); } }; init(userInfo, callbacks); </script>
頁面上這段js 用來初始化融雲 或者說使用者是否登入成功了。可以F12檢視的。
聊天:就是。我發訊息給你,你收到訊息,你發訊息給我,我收到訊息。
var recallMessage = null, clearMessage; function markMessage(message){ recallMessage = message; clearMessage = message; }
// 訊息監聽器 RongIMClient.setOnReceiveMessageListener({ // 接收到的訊息 onReceived: function (message) { // 判斷訊息型別 console.log(message.content.content); if("附加資訊" == message.content.extra){ //這邊要修改成 //是否跟當前使用者在聊天 //判斷使用者來增加未讀數量還是顯示在當前了聊天框中 //所以一下該function需要進行修改。 //getNewMsgUser(); } //有新訊息進來的時候,先進入資料庫執行,該使用者是否能接收訊息, //如果不能接收,則進行未讀訊息數量修改, //否則直接進入聊天介面 var htmls = "<div class=\"leftd\">"+ "<div style=\"float:left;\">"+ "<img src=\"../img/20180917141606.png\" style=\"width:40px;height:40px;\"/>"+ "</div>"+ "<div class=\"speech left\" style=\"margin-left:61px;\">"+message.content.content+"</div>"+ "</div>"; $("#liaotian").append(htmls); $("#getMessage").val(message.content.content); //message接受到的訊息(包含傳送的資訊,也可以在extra中新增要傳遞的值,如:時間等) $('#liaotian').scrollTop($('#liaotian')[0].scrollHeight); } });
該方法是用來接收有人發訊息過來。中間有一段註釋的內容。我還在開發中,你們也可以自己想想,文字描述很清晰
//私聊 function privateMessage(){ var sendMsg = $("#sendMessage").val(); if("" == sendMsg){ alert("請輸入聊天內容"); $('#sendMessage').focus(); return; } var conversationtype = RongIMLib.ConversationType.PRIVATE; // 私聊 var targetId = $("#toUserId").val(); // 目標 Id var msg = new RongIMLib.TextMessage({content:sendMsg,extra:"附加資訊"}); var conversationtype = RongIMLib.ConversationType.PRIVATE; // 單聊,其他會話選擇相應的訊息型別即可。 var pushData = "your pushInfo";//這個暫時還沒研究 RongIMClient.getInstance().sendMessage(conversationtype, targetId, msg, { onSuccess: function (message) { //傳送成功後,將該條傳送的訊息記錄到資料庫中 //如果資料庫插入成功,則進行頁面的傳送訊息append到html聊天框裡。 var htmls = "<div class=\"rightd\">"+ "<div style=\"float:right;\">"+ "<img src=\"../img/20180917141606.png\" style=\"width:40px;height:40px;\"/>"+ "</div>"+ "<div class=\"speech right\" style=\"margin-right:61px;\">"+message.content.content+"</div>"+ "</div>"; $("#liaotian").append(htmls); var sendMsg = $("#sendMessage").val(""); console.log(message.content.content); $('#liaotian').scrollTop($('#liaotian')[0].scrollHeight); }, onError: function (errorCode,message) { console.log(message.content.content); } }, false, pushData ); }
該方法就是我傳送訊息,傳送聊天內容等
//獲取會話列表 function findMsgUser(){ var currUserId = '${currUserId}'; $.get("${ctx}/webMessageUserList/selectWebMessageUserList.htmls",{only_md5:'${currUserId}'},function(data){ var objList = eval(data); var htmls = ""; if(objList.code == 20000){ for(i=0;i<objList.entity.length;i++){ var b = objList.entity[i].users_only_md5; if(objList.entity[i].users_only_md5 != currUserId){ htmls += "<div style=\"width:180px;height:60px;\" onclick=\"pushUser('"+objList.entity[i].users_only_md5+"')\">"+ "<img alt=\"使用者頭像\" width=\"40px\" height=\"40px\" src=\""+objList.entity[i].users_info_photo+"\">"+ "<label>"+objList.entity[i].users_info_nick+"</label>"+ " <label style=\"color:red;\">"+objList.entity[i].web_message_user_list_read_count+"</label></div>"; }else{ htmls += "<div style=\"width:180px;height:60px;\" onclick=\"pushMaster('"+objList.entity[i].master_only_md5+"')\">"+ "<img alt=\"使用者頭像\" width=\"40px\" height=\"40px\" src=\""+objList.entity[i].users_info_photo+"\">"+ "<label>"+objList.entity[i].users_info_nick+"</label>"+ " <label style=\"color:red;\">"+objList.entity[i].web_message_user_list_read_count+"</label></div>"; } } }else{ var htmls = "<div style=\"width:180px;height:60px;\">"+ "<img alt=\"使用者頭像\" width=\"40px\" height=\"40px\" src=\"${ctx}/img/20180917141606.png\">"+ "<label>暫無會話使用者</label></div>"; } $("#newMsgUser").html(htmls); }); }
該方法是獲取會話列表。因為融雲的會話列表是收費的。所以我自己寫一個會話列表:
需要建立資料庫,然後將客服與使用者聊天建立一條會話資料,作為聊天的列表模式,不需要重複建立,需要自己判斷,
怎麼建立這個資料呢。首先。客服是不能主動找未聊天過的 使用者聊!!!!即只有app端先發起客服聊天,客服才有資格去回覆。
所以會話列表的資料建立,應該有app端發起時,在介面處去建立會話列表資料。
:目前這一塊我還沒有寫。所以自己建立的幾個臨時的資料,
而且我這邊是本地跟本地聊,即開啟2個瀏覽器去聊。所以暫時還沒去寫,不過,並不難。
//客服跟使用者聊天 function pushUser(users_only_md5){ if(toUserId != users_only_md5){ toUserId = users_only_md5; hisHtmls = ""; $("#liaotian").html(hisHtmls); $("#toUserId").val(users_only_md5); getHisMsgUser(users_only_md5); } }
//使用者跟客服聊天 function pushMaster(master_only_md5){ if(toUserId != master_only_md5){ toUserId = master_only_md5; hisHtmls = ""; $("#liaotian").html(hisHtmls); $("#toUserId").val(master_only_md5); getHisMsgMaster(master_only_md5); } }
有以上2個方法,是因為我用2個瀏覽器去聊天的,所以要區分開使用者和客服。
//獲取跟該使用者的歷史聊天記錄 function getHisMsgUser(users_only_md5){ if(regNull(hisHtmls)){ $.get("${ctx}/webMessage/selectWebMessageUsers.htmls", { users_only_md5:users_only_md5, master_only_md5:'${currUserId}' }, function(data){ var obj = eval(data); pushHTML(obj.entity,"users"); }); } }
//獲取跟該客服的歷史聊天記錄 function getHisMsgMaster(master_only_md5){ if(regNull(hisHtmls)){ $.get("${ctx}/webMessage/selectWebMessageUsers.htmls", { users_only_md5:'${currUserId}', master_only_md5:master_only_md5 }, function(data){ var obj = eval(data); pushHTML(obj.entity,"master"); }); } }
以上2個方法的初衷,主要取決於是誰先點。使用者介面點客服聊天,和客服介面點使用者聊天。
//將獲得的聊天記錄佈局到html中 function pushHTML(list,obj){ if("error" == list){ hisHtmls = "暫無歷史聊天記錄"; }else{ list = eval(list); for(i=list.length-1;i>=0;i--){ if(obj != "users"){ if(regNull(list[i].user_content)){ //管理員傳送 hisHtmls += "<div class=\"leftd\">"+ "<div style=\"float:left;\">"+ "<img src=\"../img/20180917141606.png\" style=\"width:40px;height:40px;\"/>"+ "</div>"+ "<div class=\"speech left\" style=\"margin-right:61px;\">"+list[i].master_content+"</div>"+ "</div>"; }else{ //使用者傳送 hisHtmls += "<div class=\"rightd\">"+ "<div style=\"float:right;\">"+ "<img src=\"../img/20180917141606.png\" style=\"width:40px;height:40px;\"/>"+ "</div>"+ "<div class=\"speech right\" style=\"margin-right:61px;\">"+list[i].user_content+"</div>"+ "</div>"; } }else{ if(regNull(list[i].user_content)){ //管理員傳送 hisHtmls += "<div class=\"rightd\">"+ "<div style=\"float:right;\">"+ "<img src=\"../img/20180917141606.png\" style=\"width:40px;height:40px;\"/>"+ "</div>"+ "<div class=\"speech right\" style=\"margin-right:61px;\">"+list[i].master_content+"</div>"+ "</div>"; }else{ //使用者傳送 hisHtmls += "<div class=\"leftd\">"+ "<div style=\"float:left;\">"+ "<img src=\"../img/20180917141606.png\" style=\"width:40px;height:40px;\"/>"+ "</div>"+ "<div class=\"speech left\" style=\"margin-right:61px;\">"+list[i].user_content+"</div>"+ "</div>"; } } } } $("#liaotian").append(hisHtmls); //該jquery是為了讓滾動條一直居底部 $('#liaotian').scrollTop($('#liaotian')[0].scrollHeight); }
<script type="text/javascript"> /**初始化頁面所載入的資料*/ //獲取會話列表 findMsgUser(); </script>
<body> 登入使用者ID: <input type="text" id="login" value="${currUserId}" readonly="readonly"/> 目標ID: <input type="text" id="toUserId" value="" readonly="readonly"/> <input type="text" id="identity" value="${identity}" readonly="readonly"/></p> <div style="width:610px;height:500px;"> <div id="liaotian" style="border: 1px solid red;width:400px;height:500px;overflow-x: hidden;overflow-y: auto;float:left;"> </div> <div id="newMsgUser" style="border:1px solid red;width:200px;height:500px;float:right;overflow-x: hidden;overflow-y: auto;"> </div> </div> </p> <div style="width:400px;height:50px"> <div style="width:300px;height:49px;float: left;"> <textarea id="sendMessage" rows="" cols="" style="width: 320px;height:49px"></textarea> </div> <div style="width:80px;height:49px;float:right;"> <input type="button" id="push" value="傳送" style="width:80px;height:49px;" onclick="privateMessage()" /> </div> </div> </body>
<script src="${ctx}/rong/RongIMLib-2.3.3.min.js"></script> <script src="//cdn.ronghub.com/RongEmoji-2.2.4.min.js"></script> <script src="//cdn.ronghub.com/RongIMVoice-2.2.4.min.js"></script> <script src="${ctx}/rong/init.js"></script> <script src="${ctx}/js/jquery-2.1.4.min.js"></script> <script src="${ctx}/common/common.js"></script> <style type="text/css"> div.speech { margin: 10px 0; padding: 8px; table-layout: fixed; word-break: break-all; position: relative; background: -webkit-gradient(linear, 50% 0%, 50% 100%, from(#ffffff), color-stop(0.1, #ececec), color-stop(0.5, #dbdbdb), color-stop(0.9, #dcdcdc), to(#8c8c8c)); border: 1px solid #989898; -webkit-border-radius: 8px; -moz-border-radius: 8px; border-radius: 8px; }
div.speech:before { content: ''; position: absolute; width: 0; height: 0; left: 15px; top: -20px; border: 10px solid; border-color: transparent transparent #989898 transparent; }
div.speech:after { content: ''; position: absolute; width: 0; height: 0; left: 17px; top: -16px; border: 8px solid; border-color: transparent transparent #ffffff transparent; }
div.speech.right { box-shadow: -2px 2px 5px #CCC; margin-right: 10px; width: 75%; float: right; background: -webkit-gradient(linear, 50% 0%, 50% 100%, from(#e4ffa7), color-stop(0.1, #bced50), color-stop(0.4, #aed943), color-stop(0.8, #a7d143), to(#99BF40)); }
div.speech.right:before { content: ''; position: absolute; width: 0; height: 0; top: 9px; bottom: auto; left: auto; right: -10px; border-width: 9px 0 9px 10px; border-color: transparent #989898; }
div.speech.right:after { content: ''; position: absolute; width: 0; height: 0; top: 10px; bottom: auto; left: auto; right: -8px; border-width: 8px 0 8px 9px; border-color: transparent #bced50; }
div.speech.left { box-shadow: 2px 2px 2px #CCCCCC; margin-left: 10px; width: 75%; float: left; background: -webkit-gradient(linear, 50% 0%, 50% 100%, from(#ffffff), color-stop(0.1, #eae8e8), color-stop(0.4, #E3E3E3), color-stop(0.8, #DFDFDF), to(#D9D9D9)); }
div.speech.left:before { content: ''; position: absolute; width: 0; height: 0; top: 9px; bottom: auto; left: -10px; border-width: 9px 10px 9px 0; border-color: transparent #989898; }
div.speech.left:after { content: ''; position: absolute; width: 0; height: 0; top: 10px; bottom: auto; left: -8px; border-width: 8px 9px 8px 0; border-color: transparent #eae8e8; }
.leftimg { float: left; margin-top: 10px; }
.rightimg { float: right; margin-top: 10px; }
.leftd { clear: both; float: left; }
.rightd { clear: both; float: right; }
.clear { clear: both; }
.speed .left { float: left; }
.speed .right { float: right; } </style> <script type="text/javascript"> //記錄常量 var hisHtmls = ""; var toUserId = ""; </script>
頁面的樣式不是很好看,講究用用。
還有好幾個功能。目前正在開發中,等開發好了之後在更新上來。
以上的功能,目前已完成聊天,會話列表,聊天記錄等。但是聊天記錄還想還沒儲存到本地資料庫,
之後開發 聊天記錄存,未讀數量,是否線上。等功能。
等寫完在更新吧。
希望以上一點小小的功能 能與大家一起學習,一起分享。
該文件的程式碼順序我並沒有去整理,我是一段擷取一段註釋來描述該功能。如果看官需要複製程式碼,請注意下html的順序,
理論上,程式碼是沒有問題。但是需要修改一些部分引數,因為我是連資料庫的。所以你們自行建立的資料庫表字段,需要放到該頁面上來。替換了 即可正常聊天。