JavaWeb聊天(Redis+环信) 一、发送接收消息、聊天记录拉取

8 篇文章 0 订阅
5 篇文章 1 订阅

公司有需求做一个聊天功能。 APP端,跟网页端互相聊天

android端直接嵌入了环信提供的DEMO。聊天记录。都是存储在本地自己进行维护。

所以本次只需要维护网页端的聊天记录~还有接收发送的消息就好啦。


好啦~人狠话不多。看效果吧!


总结一下要实现的功能点

1、发送与接收文字、表情、图片、地址消息、自定义消息  --》拉取聊天记录 (三天内的)

2、消息来了。外层菜单的红点提示,未读消息

3、redis中的聊天记录存储3天。 3天以后数据将插入到DB中


(下图为效果)




1、发送与接收文字、表情、图片、地址消息、自定义消息   (拉取聊天记录->三天内的)


本次无论是回调,还是发送,都将聊天的JSON字符串放入redis中。进行保存。


结构设计:

先看一个 环信webim接收到的回调数据结构 (用文本的举例)


{
  "id": "449906829204391972",
  "type": "chat",
  "from": "iambuyer_30",
  "to": "jjcceshi",
  "data": "文本数据",
  "ext": {},
  "sourceMsg": "文本数据",
  "error": false,
  "errorText": "",
  "errorCode": ""
}


设计思路:

其实将2个人的聊天记录(上述的这个结构)存在redis中。

采用List结构即可。

KEY的话。可以使用2个人的ID作为KEY,比如

user_20 

user_21

这2个用户。  key可以使用  chat||user_20&&user_21


意思就是无论回调,还是发送消息

使用ajax的方法,调用接口,将该JSON体扔进当前聊天双方所属的聊天内容中即可。


废话不多说。继续上代码。

先嵌入环信的WEB_IM 引入官方提示的资源文件

<script src="${rc.contextPath}/resources/webIm/webim.config.js"></script>
<script src="${rc.contextPath}/resources/webIm/strophe-1.2.8.min.js"></script>
<script src="${rc.contextPath}/resources/webIm/websdk-1.4.13.js"></script>
<script src="${rc.contextPath}/resources/webIm/adapter.js"></script>
<script src="${rc.contextPath}/resources/webIm/webrtc-1.4.12.js"></script>

<script>
    var jsRootPath = '${rc.contextPath}';
    //定义一个自己的ID。通过session取
    var thisId = '$!session.getAttribute("LOGIN_USER").ssoUserId';
    var thisHeadImg = '$!session.getAttribute("LOGIN_USER").headImg';

</script>

<script type='text/javascript' src='${rc.contextPath}/resources/webIm/iambuyerKorEasemob.js'></script>


自定义一些逻辑控制变量

var EASEMOBTYPE = new Object;
EASEMOBTYPE.txt='txt';
EASEMOBTYPE.img='img';
EASEMOBTYPE.audio='audio';
EASEMOBTYPE.loc='loc';
EASEMOBTYPE.prodLink='prodLink';
EASEMOBTYPE.prod='prod';

var EASEMOBWEBSHOWTYPE = new Object;
EASEMOBWEBSHOWTYPE.me='mine';
EASEMOBWEBSHOWTYPE.you='user';


var IAMBUYER_PREFIX = "iambuyer_";



//定义一个自己的ID。通过session取 //ID 以及 头像拿到了菜单html中
/*var thisId = '$!session.getAttribute("LOGIN_USER").ssoUserId';
var thisHeadImg = '$!session.getAttribute("LOGIN_USER").headImg';*/
thisId = 202;
//定义对方的ID 表示当前聊天的人是谁
var toUserId = "";
var toUserHeadImg = "";

//聊天窗口的ID
var chatMsgContentDiv = "chatMsgContentDiv";
//记录游标
var index = 0;


//记录自己
var me = "mine";
var you= "user";






写一下环信的登录、各种回调。


var conn = {};
conn = new WebIM.connection({
    isMultiLoginSessions: WebIM.config.isMultiLoginSessions,
    https: typeof WebIM.config.https === 'boolean' ? WebIM.config.https : location.protocol === 'https:',
    url: WebIM.config.xmppURL,
    isAutoLogin: true,
    heartBeatWait: WebIM.config.heartBeatWait,
    autoReconnectNumMax: WebIM.config.autoReconnectNumMax,
    autoReconnectInterval: WebIM.config.autoReconnectInterval,
    apiUrl: WebIM.config.apiURL
});

// tips: ie8 support  fileInputId should match with the file in the document
WebIM.flashUpload = UploadShim({fileInputId: 'image'}, conn).flashUpload;


// listern,添加回调函数
conn.listen({
    onOpened: function (message) {          //连接成功回调,连接成功后才可以发送消息
        //如果isAutoLogin设置为false,那么必须手动设置上线,否则无法收消息
        // 手动上线指的是调用conn.setPresence(); 在本例中,conn初始化时已将isAutoLogin设置为true
        // 所以无需调用conn.setPresence();
        console.log("%c [opened] 连接已成功建立", "color: green")
    },
    onTextMessage: function (message) {

        message['msgType'] = EASEMOBTYPE.txt;

        // 在此接收和处理消息,根据message.type区分消息来源,私聊或群组或聊天室
        if(message.ext.isProId != undefined || message.ext.isProId == true ){
            message['msgType'] = EASEMOBTYPE.prod;
        }

        if(message.ext.isPro != undefined || message.ext.isPro == true ){
            message['msgType'] = EASEMOBTYPE.prodLink;
        }

        resolveEasemobJson(message,thisId,true);
        insertRedis(message,message['msgType']);
        //扔未读消息
        insertRedisCount(message.from);

    },  //收到文本消息
    onEmojiMessage: function (message) {
        // 当为WebIM添加了Emoji属性后,若发送的消息含WebIM.Emoji里特定的字符串,connection就会自动将
        // 这些字符串和其它文字按顺序组合成一个数组,每一个数组元素的结构为{type: 'emoji(或者txt)', data:''}
        // 当type='emoji'时,data表示表情图像的路径,当type='txt'时,data表示文本消息
        console.log('Emoji');
        console.log(JSON.stringify(message));
        var data = message.data;
        for (var i = 0, l = data.length; i < l; i++) {
            console.log(data[i]);
        }

        message['msgType'] = EASEMOBTYPE.txt;
        resolveEasemobJson(message,thisId,true);
        insertRedis(message,EASEMOBTYPE.txt);
        //扔未读消息
        insertRedisCount(message.from);




    },   //收到表情消息
    onPictureMessage: function (message) {


        console.log(message);
        console.log(JSON.stringify(message));
        console.log('Picture');

        var options = {url: message.url};
        options.onFileDownloadComplete = function () {
            // 图片下载成功
            console.log('Image download complete!');
            console.log(message.url);

            message['msgType'] = EASEMOBTYPE.img;

            resolveEasemobJson(message,thisId,true);
            insertRedis(message,EASEMOBTYPE.img);
            //扔未读消息
            insertRedisCount(message.from);
        };
        options.onFileDownloadError = function () {
            // 图片下载失败
            console.log('Image download failed!');
        };
        WebIM.utils.download.call(conn, options);       // 意义待查

    }, //收到图片消息
    onCmdMessage: function (message) {
        console.log('CMD');
    },     //收到命令消息
    onAudioMessage: function (message) {
        console.log("Audio");
        console.log(JSON.stringify(message));
    },   //收到音频消息
    onLocationMessage: function (message) {
        console.log(JSON.stringify(message));
        console.log("Location");

        message['msgType'] = EASEMOBTYPE.loc;
        resolveEasemobJson(message,thisId,true);
        insertRedis(message,EASEMOBTYPE.loc);
        //扔未读消息
        insertRedisCount(message.from);
    },//收到位置消息


    onOnline: function () {
        console.log('onLine');
    },                  //本机网络连接成功
    onOffline: function () {
        console.log('offline');
    },                 //本机网络掉线
    onError: function (message) {
        console.log('Error');
        console.log(message);
        console.log(JSON.stringify(message));
        if (message && message.type == 1) {
            console.warn('连接建立失败!请确认您的登录账号是否和appKey匹配。')
        }
    }           //失败回调

});

现在开始贴一下。 回调中的resolveEasemobJson方法

主要作用就是。无论发送,还是收到消息。将该消息解析到当前聊天的窗口中去

//解析环信字符串append到html中
//初始化、回调通用
//tempfalg 追加到哪里
function resolveEasemobJson(easemobJson,thisUserId,tempfalg){
    console.log(JSON.stringify(easemobJson));
    var msgType = easemobJson.msgType;
    var from = easemobJson.from;
    var to = easemobJson.to;

    var falg =false;
    //判断是不是当前聊天窗口的人。如果不是 不显示。
    if(IAMBUYER_PREFIX+toUserId  == to || IAMBUYER_PREFIX+toUserId  == from){
        falg =true;
    }

    if(falg){
        var isWho;

        if(from == IAMBUYER_PREFIX+thisUserId){
            isWho = EASEMOBWEBSHOWTYPE.me;
        }else{
            isWho = EASEMOBWEBSHOWTYPE.you;
        }
        //文本消息
        if(EASEMOBTYPE.txt == msgType){
            var sourceMsg = easemobJson.sourceMsg;
            sourceMsg = samilToImg(sourceMsg);
            sendText(isWho,sourceMsg,undefined,tempfalg);
        }else if(EASEMOBTYPE.img == msgType){//图片消息
            var url = easemobJson.url;
            sendImg(isWho,url,undefined,tempfalg)
        }else if(EASEMOBTYPE.auto == msgType){//语音消息

        }else if(EASEMOBTYPE.loc == msgType){//地址消息
            var addr = easemobJson.addr;
            var lat = easemobJson.lat;
            var lng = easemobJson.lng;
            sendLoc(isWho,undefined,addr,lat,lng,tempfalg);

        }else if(EASEMOBTYPE.prodLink == msgType){自定义的消息

            var proId = easemobJson.ext.proId;
            var proPice = easemobJson.ext.price;
            var proName = easemobJson.ext.productName;
            var proImg = easemobJson.ext.proImg;
            sendPro(isWho,proName,proPice,proId,proImg,undefined,tempfalg);
        }

    }


    //延时一秒。展示未读
    setTimeout("getAllUnMsgCount()",1000);

    //延时一秒。展示未读
    setTimeout("initUserList(false)",1000);

}

根据上述代码。可以看见其实就是根据json聊天数据的类型。区分是什么样的数据。

调用不同的方法。 动态的拼接JS 生成在html中展示给用户

下面在贴一个sendText发送文本的方法  

        //文本消息封装
        //true 追加到后面 false 追加到前面
        function sendText(IsWho,txt,headImg,falg){

            var html ="";

            headImg = setHeadImg(IsWho,headImg);

            var className  = "";
            if(IsWho == EASEMOBWEBSHOWTYPE.me){
                className = "layim-chat-"+EASEMOBWEBSHOWTYPE.me;
            }

            html +='<li class="'+className+'">';
            html +='    <div class="layim-chat-user">';
            html +='        <img src="'+headImg+'">';
            html +='    </div>';
            html +='    <div class="layim-chat-text">'+txt+'</div>';
            html +='</li>';
            console.log("----追加文本");
            console.log(html);

            appendOrPrepend(falg,html);

        }

从上面可以看到还有一个 appendOrPrepend方法。 这个方法其实就是获取更多聊天记录。 每次从redis中会取10条聊天记录出来。  从最上面的变量定义有一个index 这个index其实就是定义游标。 记录一下   聊天记录取到哪了。

其实每次点击一下聊天列表中的用户头像。就是把 toUserId进行一次替换。 游标也会进行清空。 现在帖一下初始化用户列表


   //用户沟通列表初始化
        //falg 需要不需要初始化聊天窗口
        function initUserList(falg){
            jQuery.ajax({
                url : "selectChatByUserId/"+thisId,
                type : 'get',
                contentType : 'application/json;charset=UTF-8',
                success : function(data) {

                    $("#chatListDiv").empty();

                    var html ="";

                      console.log("-------初始化用户列表----")
                      console.log(JSON.stringify(data));
                      for(var i = 0 ; i < data.content.length ; i ++){

                      var userHeadImg = data.content[i].userHeadImg;
                      var userId = data.content[i].userId;
                      var endTime = data.content[i].endTime;
                      var userName = data.content[i].userName;
                      var chatContent = data.content[i].chatContent;

                      var unMsgCount = data.content[i].unMsgCount;

                      //初始化聊天窗口以及沟通记录
                      if(falg){
                          if(i==0){
                              initChatWin(userId,userHeadImg);
                          }
                      }

                      endTime = timeToDateStr(endTime);

                      if(userHeadImg == undefined || userHeadImg == '' || userHeadImg == null || userHeadImg == 'null'){
                          userHeadImg = "../resources/img/caigoushang_headImg.png";
                      }

                      html+='    <li class="clearfix"  ';

                      if(userId == toUserId){
                          html +='style = "background-color: #02c2a2;" ';
                      }

                      html+='οnclick="onClikeChatUser('+userId+',\''+userHeadImg+'\')">';
                      html+='    <div class="chatUser layui-col-md12" style="display: inline-block">';
                      html+='    <img src="'+userHeadImg+'" alt="" class="layui-circle" width="40">';
                      html+='    <span class="chatUserName layui-col-md6 nowrap">'+userName+'</span>';
                      html+='    <span class="layui-col-md6 chatUserListTime nowrap">'+endTime+'</span>';
                      html+='<span class="layui-col-md11 nowrap chatAboutUser">'+chatContent+'</span>';
                      html+='    <div class="layui-row chatUserInfo">';
                      html+='    <span class="layui-col-md10 nowrap" style="visibility: hidden" > 1   </span>';
                      /*未读*/
                      html+='    <span class="layui-col-md2">';
                      if(unMsgCount > 0){
                          html+='       <span class="layui-badge">'+unMsgCount+'</span>';
                      }
                      html+='    </span>';
                      html+='    </div>';
                      html+='    </div>';
                      html+='    </li>';

                  }
                  $("#chatListDiv").append(html);
                },
                error : function() {
                    alert("FAIL!");
                }
            });
        }


用户列表这个方法很多地方都会调用

1、初始化的时候调用。

2、来消息作为刷新用户列表顺序以及增加未读量的时候调用

3、发送消息作为用户列表顺序刷新调用


下面继续贴代码。贴一下。 initChatWin()这个方法主要是初始化聊天窗口。其实就是去后台取聊天记录生成html出来

  //初始化聊天窗口
        function initChatWin(temptoUserId,temptoUserheadImg){

            toUserId = temptoUserId;

            toUserHeadImg = temptoUserheadImg;

            //初始化聊天记录
            jQuery.ajax({
                url : "getChatRecordCount?toId="+toUserId+"&fromId="+thisId,
                type : 'get',
                contentType : 'application/json;charset=UTF-8',
                success : function(data) {
                    if(data.ret == 200){

                        var length = data.content;
                        //第一次初始化
                        /*if(index == -1){*/
                        index = length;

                        /*}*/
                        jQuery.ajax({
                            url : "getChatRecord?toId="+toUserId+"&fromId="+thisId+"&index="+index,
                            type : 'get',
                            contentType : 'application/json;charset=UTF-8',
                            success : function(data) {
                                //递减游标
                                declinePage();

                                //上来先清空
                                $("#"+chatMsgContentDiv).empty();
                                if(data.ret == 200){
                                    var contents = data.content.data;
                                    for(var i = 0 ; i < contents.length; i++){
                                        //装载
                                        resolveEasemobJson(contents[i],thisId,true);
                                    }

                                }else{
                                    layer.msg("暂无聊天记录");
                                }
                            }
                        })
                    }else{
                        layer.msg("暂无聊天记录");
                    }
                }
            })

            //初始化聊天框中的产品信息
            jQuery.ajax({
                url : "selectChatProByUserId/"+toUserId+"/"+thisId,
                type : 'get',
                contentType : 'application/json;charset=UTF-8',
                success : function(data) {

                    //上来先清空
                    $("#chatWinProInfo").empty();
                    $("#chatWinProList").empty();

                    if(data.ret == 200){
                        var contents = data.content;
                        var html = "";
                        if(contents.length >= 1){
                            html += '<img src="'+contents[0].loopImg001+'" alt="" class="chatProductTopInfoImg">';
                            html += '    <span class="chatProductNumTopInfo nowrap layui-col-md12">商品编码:'+contents[0].productNum+'</span>';
                            html += '<span class="nowrap chatProductTitleTopInfo layui-col-md12">'+contents[0].productName+'</span>';
                            html += '<span class="chatProductPriceTopInfo layui-col-md12 nowrap">'+contents[0].productMinPrice+'-'+contents[0].productMaxPrice+'</span>';
                            html += '<div class="layui-row chatProductCreateTime">';
                            html += '    <span class="layui-col-md4 nowrap">发布者:'+contents[0].userName+'</span>';
                            html += '    <span class="layui-col-md8 nowrap">发布时间:'+contents[0].createTime+'</span>';
                            html += '</div>';
                            $("#chatWinProInfo").append(html);
                        }

                        var listHtml = "";
                        for(var i = 0 ; i < contents.length; i++){
                            //装载
                            listHtml +='<div class="chatProductTopInfo historyProductItem layui-col-md12" style="display: inline-block">';
                            listHtml +='    <img src="'+contents[i].loopImg001+'" alt="" class="chatProductTopInfoImg">';
                            listHtml +='    <span class="chatProductNumTopInfo nowrap layui-col-md12">商品编码:'+contents[i].productNum+'</span>';
                            listHtml +='    <span class="nowrap chatProductTitleTopInfo layui-col-md12">'+contents[i].productName+'</span>';
                            listHtml +='    <span class="chatProductPriceTopInfo layui-col-md12 nowrap">'+contents[i].productMinPrice+'-'+contents[i].productMaxPrice+'</span>';
                            listHtml +='    <div class="layui-row">';

                            //price,proId,proImg,productName
                            var price = contents[i].productMinPrice+'-'+contents[i].productMaxPrice;
                            listHtml +='        <button class="layui-btn ContinueChat" οnclick="recordCount("'+price + '","'+ contents[i].loopImg001 + '","'+ contents[i].productName +'",'+ contents[i].proId +','+ toUserId +')">继续沟通</button>';
                            listHtml +='    </div>';
                            listHtml +='</div>';
                        };
                        $("#chatWinProList").append(listHtml);
                    }
                }
            })
        }

该方法主要就是初始化聊天列表的时候。需要递减游标。 首先呢。咱们把总的聊天长度取出来。 然后赋值给游标。 这样就可以通过这个数据去后台取聊天记录啦。 

比如有20个聊天记录

传递到后台的记录是 20. 后台从redis中。拿到  10-19下标的数据。 生成出来就好啦。 

大家看一下 这里生成的时候调用了 resolveEasemobJson方法。

这就是数据统一的好处。 将JSON解析到html统一调用resolveEasemobJson方法


贴一个递减游标的方法

     //递减游标
        function declinePage(){
            //减去分页
            index = index-10;
            if(index < 0){
                index= 0;
            }
        }

追加的方法。其实就是。。初始化在后面。, 获取聊天记录呢。是在前面


    //追加
        //falg true 追加到后面, 追加到前面
        //查看聊天记录 或者 发送聊天使用。
        function appendOrPrepend(falg,html){
            if(falg){
                $("#"+chatMsgContentDiv).append(html);
                scrollDown();
            }else{
                $("#"+chatMsgContentDiv).prepend(html);
            }
        }


这里追加到后面有一个样式控制。就是每次追加。让滚动条保持在最下面

    //控制滚动条保持在最下面
        function scrollDown(){
            setTimeout("timeOutScrollDown()",200)
        }

        function timeOutScrollDown(){

            var scrollHeight = $('#chatMsgContentDivScroll').prop("scrollHeight");
            $('#chatMsgContentDivScroll').scrollTop((scrollHeight+200));
            scrollHeight = $('#chatMsgContentDivScroll').prop("scrollHeight");
        }


在贴出获取分页的方法

其实这个就是获取聊天记录后。倒着循环。装载到html的前面。

        //获取分页聊天记录
        function getChatPageList(){
            jQuery.ajax({
                url : "getChatRecord?toId="+toUserId+"&fromId="+thisId+"&index="+index,
                type : 'get',
                contentType : 'application/json;charset=UTF-8',
                success : function(data) {
                    //递减游标
                    declinePage();
                    if(data.ret == 200){
                        var contents = data.content.data;
                        //倒着循环
                        for(var i = contents.length ; i >= 0; i--){
                            //装载
                            resolveEasemobJson(contents[i-1],thisId,false);
                        }
                    }else{
                        layer.msg("暂无聊天记录");
                    }
                }
            })
        }


以上代码呢。其实就是可以做到了。

初始化用户列表, 接收android推送过来的环信的消息并展示啦。


接下来。贴一下发送的前端逻辑吧。 


// 私聊发送文本消息,发送表情同发送文本消息,只是会在对方客户端将表情文本进行解析成图片
var sendPrivateText = function (content,toId) {
    var id = conn.getUniqueId();
    var msg = new WebIM.message('txt', id);
    msg.set({
        msg: content,                       // 消息内容
        to: IAMBUYER_PREFIX+toId,                          // 接收消息对象
        roomType: false,
        success: function (id, serverMsgId) {
            console.log("send private text Success");
            console.log(serverMsgId);
            console.log(id);
            var easemobJSON = convertTxt(serverMsgId,toId,msg.value);
            resolveEasemobJson(easemobJSON,thisId,true);
        }
    });
    msg.body.chatType = 'singleChat';
    conn.send(msg.body);
    console.log(JSON.stringify(msg));
};


// 私聊发送图片消息
var sendPrivateImg = function (imgId,to,size) {
    var id = conn.getUniqueId();
    var msg = new WebIM.message('img', id);
    var input = document.getElementById(imgId);               // 选择图片的input
    var file = WebIM.utils.getFileUrl(input);                   // 将图片转化为二进制文件
    var allowType = {
        'jpg': true,
        'gif': true,
        'png': true,
        'bmp': true
    };

    var option = {
        apiUrl: WebIM.config.apiURL,
        file: file,
        to: IAMBUYER_PREFIX+to,
        roomType: false,
        chatType: 'singleChat',
        onFileUploadError: function () {
            console.log('onFileUploadError');
        },
        onFileUploadComplete: function () {
            console.log('onFileUploadComplete');
        },
        success: function (id, serverMsgId) {
            console.log('Success');
            console.log(serverMsgId);
            console.log(id);
            console.log(JSON.stringify(msg));

            var url = msg.body.body.url;
            var filename = msg.body.body.filename;
            var secret = msg.body.body.secret;

            var easemobJSON =  convertImg(serverMsgId,to,filename,url,secret,size);
            resolveEasemobJson(easemobJSON,thisId,true);
        },
    };
    // for ie8
    try {
        if (!file.filetype.toLowerCase() in allowType) {
            console.log('file type error')
            return
        }
    } catch (e) {
        option.flashUpload = WebIM.flashUpload
    }
    msg.set(option);
    conn.send(msg.body);
    console.log("111");
    console.log(JSON.stringify(msg));
    console.log("222");
};

发送我贴了2个。 一个是txt , 一个是发送img


这里需要注意的就是 

convertTxt 这个方法。 

其实呢。 环信webIM这块做的就比较尴尬。 它回调跟发送生成出来的JSON不一致,因为咱们采用了他回调的JSON格式进行处理。 所以需要将发送的JSON转成咱们使用的JSON格式

  //因为环信发送 跟 回调的JS不一致
        //我们需要将每一条聊天记录扔到redis中。所以将发送的记录转换成可以扔的数据
        function convertTxt(id,to,sourceMsg){

            var easemobJson =
            {
                "id": id,
                "type": "chat",
                "from": IAMBUYER_PREFIX + thisId,
                "to": IAMBUYER_PREFIX + to,
                "ext": {

                },
                "sourceMsg": sourceMsg,
                "error": false,
                "errorText": "",
                "errorCode": ""
            };

            insertRedis(easemobJson,EASEMOBTYPE.txt);
            return easemobJson;
        }

        //因为环信发送 跟 回调的JS不一致
        //我们需要将每一条聊天记录扔到redis中。所以将发送的记录转换成可以扔的数据
        function convertImg(id,to,filename,url,secret,size){
            var easemobJson =
                {
                    "id": id,
                    "type": "chat",
                    "from": IAMBUYER_PREFIX + thisId,
                    "to": IAMBUYER_PREFIX + to,
                    "url": url,
                    "secret": secret,
                    "filename": filename,
                    "file_length": size,
                    "width": 0,
                    "height": 0,
                    "filetype": "",
                    "accessToken": "",
                    "ext": {

                    },
                    "error": false,
                    "errorText": "",
                    "errorCode": ""
                }

            insertRedis(easemobJson,EASEMOBTYPE.img);
            return easemobJson;
        }

这样处理一下就好了!!


我在这里说明一下表情的转换。下面是表情图片的定义

var smileS =
    {
        '\\[Smile\\]':'ee_1.png',
        '\\[Happy\\]':'ee_2.png',
        '\\[Blink\\]':'ee_3.png',
        '\\[surprised\\]':'ee_4.png',
        '\\[Tongue\\]':'ee_5.png',
        '\\[Sunglasses\\]':'ee_6.png',
        '\\[anger\\]':'ee_7.png',
        '\\[purse\\]':'ee_8.png',
        '\\[Shy\\]':'ee_9.png',
        '\\[Unhappy\\]':'ee_10.png',
        '\\[weep\\]':'ee_11.png',
        '\\[daze\\]':'ee_12.png',
        '\\[Snowman\\]':'ee_13.png',
        '\\[Curse\\]':'ee_14.png',
        '\\[doctor\\]':'ee_15.png',
        '\\[pout\\]':'ee_16.png',
        '\\[sweets\\]':'ee_17.png',
        '\\[sleepy\\]':'ee_18.png',
        '\\[Pie\\]':'ee_19.png',
        '\\[Shut\\]':'ee_20.png',
        '\\[Whisper\\]':'ee_21.png',
        '\\[Frown\\]':'ee_22.png',
        '\\[Lookdown\\]':'ee_23.png',
        '\\[heart\\]':'ee_24.png',
        '\\[Heartbroken\\]':'ee_25.png',
        '\\[Moon\\]':'ee_26.png',
        '\\[Stars\\]':'ee_27.png',
        '\\[sunlight\\]':'ee_28.png',
        '\\[Rainbow\\]':'ee_29.png',
        '\\[colour\\]':'ee_30.png',
        '\\[Kiss\\]':'ee_31.png',
        '\\[Redlips\\]':'ee_32.png',
        '\\[Rose\\]':'ee_33.png',
        '\\[Goldleaf\\]':'ee_34.png',
        '\\[Fabulous\\]':'ee_35.png'
    };

     //表情  图片转表情
        function imgToSamil(htmlmsg){
            //处理第一层 删除除img标签以外的所有标签
            var regex = /<\/?((?!img).)*?\/?>/g;
            htmlmsg = htmlmsg.replace(regex,"");
            htmlmsg=htmlmsg.replace(/ /ig,'');//去掉 
            //处理第二层 将img转换成固定的符号
            for (var smile in smileS) {
                var key = smile.replace("\\","").replace("\\","");
                var val = smileS[smile];
                var head='<\\/?((img).*?)((';
                var food=').*?)\\/?>';
                /*var ImgRegex = /<\/?((img).*?)((ee_1.png).*?)\/?>/g;*/
                var ImgRegex = new RegExp(head+val+food,'g');
                htmlmsg = htmlmsg.replace(ImgRegex,key);
            }
            return htmlmsg;
        }

        //表情  表情转图片
        function samilToImg(htmlmsg){
            //将表情转换成图片
            for (var smile in smileS) {
                var key = smile;
                var val = smileS[smile];
                /*var ImgRegex = /<\/?((img).*?)((ee_1.png).*?)\/?>/g;*/

                if(htmlmsg.indexOf(key.replace("\\","").replace("\\","")) != -1){
                    // 包含
                    /*var ImgRegex = new RegExp(key,'gm');
                    console.log(key);
                    console.log(val);
                    htmlmsg = htmlmsg.replace(ImgRegex,val);*/

                    console.log(key);
                    console.log(val);

                    var imgHtml = '<img src="../resources/webIm/smile/'+val+'" />'
                    htmlmsg = htmlmsg.replace(new RegExp(key,'gm'),imgHtml);

                }
            }
            return htmlmsg;
        }


resolveEasemobJson中会使用到samilToImg 将表情字符串转换成图片


在自己给对方发送消息的时候会使用到 讲图片转成表情

    //发送消息
        function sendChatMsg(){
            var htmlmsg =  $("#msgBox").html();
            sendPrivateText(imgToSamil(htmlmsg),toUserId);
            $("#msgBox").empty();
            $("#msgBox").focus();
        }

思路其实就是:我发送的时候呢。 会先设置到待发送框中。  然后点击发送会获取数据。 这时。需要将 img标签转换成 固定的表情字符。 然后调用resolveEasemobJson生成到聊天框中。 生成的时候在转换回来。 

这样转换其实是因为要给android推送消息。 推送的消息一定是表情字符而不是 img图片。


在做这里的时候 说明一下 

htmlmsg.indexOf的效率比 match 方法高很多。 我在跑match的时候经常循环的时候浏览器会挂掉。

直接用replace 进行循环也会挂掉。

所以这里先用indexOf判断。如果存在在进行替换。这样才OK


好的!兄弟们。。你们如果能看到这。。我谢谢你们。

因为。。。咱们终于!!


要开始贴后端代码了!!!


-------------------------------------------------------------------------------

先来保存聊天记录的控制层

  @RequestMapping("/saveChatRecord")
    public void saveChatRecord(HttpServletRequest request , @RequestBody EasemobChatWebMsgVO easemobChatWebMsgVO){
        taskExecutor.execute(new EasemobChatThread(easemobService,easemobChatWebMsgVO));
    }

这里呢。我单起了一个线程异步的去放聊天记录。这样前端是异步。后端也是异步。用户基本无感知。

逻辑层代码

public  void saveChatRecordRedis(EasemobChatWebMsgVO easemobChatWebMsgVO){
		String to = easemobChatWebMsgVO.getTo();
		String from = easemobChatWebMsgVO.getFrom();
		//key 使用 toID&&fromID 组成
		String key =IamBuyerRedisKey.getRedisChatKey1(to,from);
		String key1 =IamBuyerRedisKey.getRedisChatKey2(to,from);



		//定义标量确定是否放入缓存成功
		boolean falg = false;

		//正反拼查询ID是否存在
		if(redisUtil.hasKey(key)){
			//存在就扔进缓存中
			synchronized (this) {
				redisUtil.lSet(key, easemobChatWebMsgVO,EXPIRY_TIME);
				falg = true;
			}
		}else{
			if(redisUtil.hasKey(key1)){
				synchronized (this) {
					redisUtil.lSet(key1, easemobChatWebMsgVO,EXPIRY_TIME);
					falg = true;
				}
			}
		}
		//没扔进缓存中定义变量扔进缓存中
		if(!falg){
			//防止同一时间创建集合进行覆盖
			synchronized (this){
				redisUtil.lSet(key,easemobChatWebMsgVO,EXPIRY_TIME);
			}
		}
	}

KEY的拼接规则我就不说了。。 大家自己根据自己的喜好拼接吧。。


在贴出一个获取聊天记录的吧!

	@Override
	public List<Object> getChatRecord(Integer toId, Integer fromId, Integer index) {

		List<Object> list = new ArrayList<>();

		//key 使用 toID&&fromID 组成
		String key = IamBuyerRedisKey.getRedisChatKey1(IamBuyerRedisKey.IMABUYER_CHAT+toId,IamBuyerRedisKey.IMABUYER_CHAT+fromId);
		String key1 = IamBuyerRedisKey.getRedisChatKey2(IamBuyerRedisKey.IMABUYER_CHAT+toId,IamBuyerRedisKey.IMABUYER_CHAT+fromId);
		long statrIndex = (index-LENGTH);
		long endIndex = (index-1);

		if(statrIndex < 0){
			statrIndex = 0;
		}

		if(endIndex < 0){
			endIndex = 0;
		}

		System.out.println("本次查询游标为:"+statrIndex + "-" + endIndex);

		//正反拼查询ID是否存在
		if(redisUtil.hasKey(key)){
			//取出集合
			list = redisUtil.lGet(key, statrIndex, endIndex);
		}else{
			if(redisUtil.hasKey(key1)){
					list = redisUtil.lGet(key1,statrIndex,endIndex);
			}
		}
		return list;
	}

这个比较简单啦。就是根据前端传递过来的游标值进行redis中查询数据。 


继续贴代码。在贴出一个获取聊天列表的代码

	//获取聊天列表 以及聊天列表的最后一句话
	@Override
	public List<ChatUserVO> selectChatByUserId(Integer userId) {

		List<ChatUserVO> chatUserVOS = commRecordMapper.selectChatList(userId);
		//循环装载最后一条聊天记录 以及 聊天时间
		for (ChatUserVO chatUserVO : chatUserVOS) {

			String userHeadImg = chatUserVO.getUserHeadImg();
			if(!StringUtils.isEmpty(userHeadImg)){
				try {
					chatUserVO.setUserHeadImg(UploadUtil.getHttpFilePath(userHeadImg));
				} catch (Exception e) {
					e.printStackTrace();
				}

			}

			String fromId = IamBuyerRedisKey.IMABUYER_CHAT + chatUserVO.getThisId();
			String toId =   IamBuyerRedisKey.IMABUYER_CHAT + chatUserVO.getUserId();

			String redisChatKey1 = IamBuyerRedisKey.getRedisChatKey1(fromId, toId);
			String redisChatKey2 = IamBuyerRedisKey.getRedisChatKey2(fromId, toId);
			//未读消息
			String redisChatCountKey1 = IamBuyerRedisKey.getRedisChatCountKey1(fromId, toId);
			String redisChatCountKey2 = IamBuyerRedisKey.getRedisChatCountKey2(fromId, toId);

			//有数据的key 聊天的key
			String redisChatkey = "";

			//未读消息的key
			String redisCahtCountKey = "";

			//最后一条记录
			String chatEndContent = "";

			//最后一条记录的时间
			long chatEndTime = 0;

			//正反拼查询ID是否存在
			if(redisUtil.hasKey(redisChatKey1)){
				//存在就把缓存中的最后一条记录取出来
				redisChatkey = redisChatKey1;
			}else{
				if(redisUtil.hasKey(redisChatKey2)){
					//存在就把缓存中的最后一条记录取出来
					redisChatkey = redisChatKey2;
				}
			}

			//正反拼查询ID是否存在   未读消息
			if(redisUtil.hasKey(redisChatCountKey1)){
				redisCahtCountKey = redisChatCountKey1;
			}else{
				if(redisUtil.hasKey(redisChatCountKey2)){
					redisCahtCountKey = redisChatCountKey2;
				}
			}

			//装载聊天记录
			if(!"".equals(redisChatkey)){
				long listSize = redisUtil.lGetListSize(redisChatkey);
				Object o = redisUtil.lGetIndex(redisChatkey, listSize - 1);
				if(o instanceof  EasemobChatWebMsgVO){
					EasemobChatWebMsgVO easemobChatWebMsgVO = (EasemobChatWebMsgVO) o;
					//装载最后一条的时间
					chatEndTime = easemobChatWebMsgVO.getExt().getTimestamp();
					//装载数据类型
					String msgType = easemobChatWebMsgVO.getMsgType();

					if(msgType.equals(KorChatTypeEnum.TXT.getDesc())){
						chatEndContent =easemobChatWebMsgVO.getSourceMsg();
					}else if(msgType.equals(KorChatTypeEnum.IMG.getDesc())){
						chatEndContent ="图片消息";
					}else if(msgType.equals(KorChatTypeEnum.LOC.getDesc())){
						chatEndContent ="地址消息";
					}else if(msgType.equals(KorChatTypeEnum.AUDIO.getDesc())){
						chatEndContent ="语音消息";
					}else if(msgType.equals(KorChatTypeEnum.PROD_LINK.getDesc())){
						chatEndContent ="商品连接消息";
					}else if(msgType.equals(KorChatTypeEnum.PROD.getDesc())){
						chatEndContent ="商品链接消息";
					}
				}
			}

			//装载未读消息
			if(!"".equals(redisCahtCountKey)){
                AtomicLong unMsgCount = (AtomicLong) redisUtil.get(redisCahtCountKey);
				chatUserVO.setUnMsgCount(unMsgCount.longValue());
			}

			if("".equals(chatEndContent)){
				chatEndContent = "三天内暂无记录";
			}
			chatUserVO.setChatContent(chatEndContent);
			if(chatEndTime != 0 ){
				//对比继续沟通的时间 跟 最后一条聊天记录的时间。 谁大 。 谁大就用谁的
				if(chatEndTime > chatUserVO.getEndTime().getTime()){
					chatUserVO.setEndTime(new Date(chatEndTime));
				}
			}
		}

		//排序
		Collections.sort(chatUserVOS, new ChatUserVO());

		return chatUserVOS;
	}

这里因为有我的业务逻辑存在。我简单说明一下

我会先从数据库中取出来用户沟通的记录。 获取一个初始的聊天列表。然后我会根据这2个人的ID拼接出redis中存储聊天记录的key进行查询聊天记录。 把最后一条聊天记录放到集合中。。 最后按照最后的聊天时间进行排序。这个集合。




好啦。以上的代码的思路就可以实现出来

发送与接收文字、表情、图片、地址消息、自定义消息  --》拉取聊天记录 (三天内的)


今天写累了。 明天继续写







  • 14
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值