网站客服聊天

1. 点击好友弹框之后,要给服务器发消息,进入组Group.Group原理在上一篇已经介绍了,这里不再赘述。

2. 点击发送消息到后台,后台在传送回来

3. htmlappend到相应元素上,demo已经实现了,我们把代码拿过来用就可以了

4. 模拟用户登录,点击发送聊天

  在做上述工作之前,还是要做许多准备工作的。我们分析一下界面元素

  好的,可以看到,一个消息里面有消息发送时间(addtime,用户名(username,用户头像(userphoto),用户消息体(msgcontent),除此之外还需要用户id,聊天id,以及组名(groupname.以此我先在后台建立模型。

namespace LayIM.Model

{

    public enum CSMessageType

    {

        System = 1,//系统消息,出错,参数错误等消息

        Custom = 2 //普通消息,对话,或者群组消息    }

}

namespace LayIM.Model

{

    public class CSChatMessage

    {

        public CSChatMessage() {

            addtime = DateTime.Now.ToString("HH:mm:ss");

        }

        /// <summary>

        /// 消息来源

        /// </summary>

        public CSUser fromuser { get; set; }

        public CSUser touser { get; set; }

        /// <summary>

        /// 消息内容

        /// </summary>

        public string msg { get; set; }

        /// <summary>

        /// 消息发送时间

        /// </summary>

        public string addtime { get; set; }

        /// <summary>

        /// 消息类型

        /// </summary>

        public CSMessageType msgtype { get; set; }

 

        public object other { get; set; }

    }

}

namespace LayIM.Model

{

    public class SingalRUser

    {

        protected string _groupName { get; set; }

        private string _connectionId { get; set; }

        /// <summary>

        /// 用户当前所在组

        /// </summary>

        public string groupname

        {

            get

            {

                return this._groupName;

            }

        }

        /// <summary>

        /// 用户当前所在connectionid

        /// </summary>

        public string connectionid

        {

            get

            {

                return this._connectionId;

            }

        }

        public SingalRUser(string groupName, string connectionId)

        {

            _groupName = groupName;

            _connectionId = connectionId;

        }

        public SingalRUser() { }

    }

    /// <summary>

    /// 用户Model

    /// </summary>

    public class CSUser : SingalRUser

    {

        public CSUser(string groupName, string connectionId) :

            base(groupName, connectionId)

        {

        }

        /// <summary>

        /// 用户id

        /// </summary>

        public int userid { get; set; }

        /// <summary>

        /// 用户昵称

        /// </summary>

        public string username { get; set; }

        /// <summary>

        /// 用户头像

        /// </summary>

        public string photo { get; set; }

    }

}

ok,很简单的几个modelCSUser为用户,CSChatMessage为消息体。那么,如果想让两个用户联通,我们需要得到他们所在的组,即经常说到的 userID1+userID2,生成组名代码如下:(主要保证两个用户的组名唯一性就可,方法随意)

    /// <summary>

        /// 根据两个用户ID得到对应的组织名称

        /// </summary>

        /// <param name="sendid">发送人(主动联系人)</param>

        /// <param name="receiveid">接收人(被动联系人)</param>

        /// <returns></returns>

        public static string GetGroupName(string sendid, string receiveid)

        {

            /*

                排序的目的就是为了保证,无论谁连接服务器,都能得到正确的组织ID

            */

            int compareResult = string.Compare(sendid, receiveid);

            if (compareResult > 0) {

                //重新排序 如果sendid>receiveid

                return string.Format("G{0}{1}", receiveid, sendid);

            }

            return string.Format("G{0}{1}", sendid, receiveid);

        }

现在groupName也有了,我们回到 CustomServiceHub 类中来。添加用户加入组的方法,这个方法什么时候调用呢,就是当你点击某个用户头像弹出聊天框的时候调用。

/// <summary>

        /// 人对人聊天 连接服务器

        /// </summary>

        /// <param name="sendid">发送人</param>

        /// <param name="receiveid">接收人</param>

        /// <returns></returns>

        public Task ClientToClient(string sendid, string receiveid)

        {

            if (sendid == null || receiveid == null) { throw new ArgumentNullException("sendid or receiveid can't be null"); }

            //获取组名

            string groupName = MessageUtils.GetGroupName(sendid, receiveid);

            //将当前用户添加到此组织内            Groups.Add(CurrentUserConnectionId, groupName);

            //构建系统连接成功消息

            var msg = MessageUtils.GetSystemMessage(groupName, MessageConfig.ClientToClientConnectedSucceed, new { currentid = sendid, receiveid = receiveid });

            //将消息推送到当前组 (AB聊天的组) 同样调用receiveMessage方法

            return Clients.Caller.receiveMessage(msg);

        }

  里面有些代码是我封装的,大体看清思路就可以了。下面去读一下layim.js里的源代码,找到弹出用户窗口那一段。

 //弹出聊天窗

        config.chatings = 0;

        node.list.on('click', '.xxim_childnode', function () {

            var othis = $(this);

            //当前登录用户id

            var currentid = config.user.id;

            //取得被点击的用户id

            var receiveid = othis.data('id');

            //调用signalR封装的方法,连接服务器,将发送人id,接收人id传给后台,当前用户加入组            csClient.server.ctoc(currentid, receiveid);

            xxim.popchatbox(othis);

        });

  在看一下csClient到底做了什么

 ctoc: function (sid, rid) {

                //调用hubclientToClient方法

                if (!chat.isConnected(rid)) {

                    //如果没有连接过,进行连接

                    console.log("用户 " + rid + "没有连接过...");

                    _this.proxy.proxyCS.server.clientToClient(sid, rid);

                } else {

                    console.log("用户 " + rid + "已经连接过了,不需要连接了...");

                }

            },

  这里呢,我另外加了个js对象缓存,防止每次点击都要重复连接数据库,当然,页面刷新之后缓存消失,需要重新 连。到这里我们点击一下,看看效果。

  好,从上图可以看到,服务器返回了成功的消息,并且,groupname也是按照顺序生成的。这个消息有什么用呢,其实对于客户端是没有什么效果的,如果想要提示用户连接成功或者提示对方是否在线可以用到,这里我不在扩展,只是为了打印看是否连接成功,当连接成功之后呢,用户就会存在组 G1000010003中了,这时候你发消息如果对面没有连接的话,他是看不见的。连接成功之后,就要做发消息功能了。继续回到 CustomServiceHub 类,添加发送消息方法:

/// <summary>

        /// 发送消息 ,服务器接收的是CSChatMessage实体,他包含发送人,接收人,消息内容等信息

        /// </summary>

        /// <param name="msg"></param>

        /// <returns></returns>        public Task ClientSendMsgToClient(CSChatMessage msg)

        {

            var groupName = MessageUtils.GetGroupName(msg.fromuser.userid.ToString(), msg.touser.userid.ToString());

            /*

            中间处理一下消息直接转发给(A,B所在组织,即聊天窗口)

            */

            msg.msgtype = CSMessageType.Custom;//消息类型为普通消息

            return Clients.Group(groupName).receiveMessage(msg);

        }

  可以看到,同样是用到了receiveMessage方法,不过这里呢,调用的Clients.Group(groupName)也就是说,发送的这条消息职能在这个组内的人才能看到,那么组里就两个人,是不是就实现了11 聊天呢,离线留言也支持哦。消息发送成功之后,其实不管对方在不在线,我们都可以做一下本地处理,为了演示消息发送效果,我们不用本地js在发送的时候直接拼接到页面上,而是client端接收到消息体之后再处理,这样会看出消息延时效果。(扩展:假如发送的消息很慢的话,就可以在消息体旁边加一个等待的小菊花,提示发送成功,失败等。)好,我直接将layim里模拟消息处理的代码拿出来了,我们看详细代码。

   handleCustomMsg: function (result) {

            var log = {};

            //接收人

            var keys = 'one' + result.touser.userid;

            //发送人

            var keys1 = 'one' + result.fromuser.userid;

            //这里一定要注意,这个keys是会变的,也就是说,如果只取一个的话,会造成 log.imarea[0]undefined的情况,至于为什么会变,看看代码好好思考一下吧

            log.imarea = $('#layim_area' + keys);//layim_areaone0

            if (!log.imarea.length) {

                log.imarea = $('#layim_area' + keys1);//layim_areaone0            }

            //拼接html模板

            log.html = function (param, type) {

                return '<li class="' + (type === 'me' ? 'layim_chateme' : '') + '">'

                    + '<div class="layim_chatuser">'

                        + function () {

                            if (type === 'me') {

                                return '<span class="layim_chattime">' + param.time + '</span>'

                                       + '<span class="layim_chatname">' + param.name + '</span>'

                                       + '<img src="' + param.face + '" >';

                            } else {

                                return '<img src="' + param.face + '" >'

                                       + '<span class="layim_chatname">' + param.name + '</span>'

                                       + '<span class="layim_chattime">' + param.time + '</span>';

                            }

                        }()

                    + '</div>'

                    + '<div class="layim_chatsay">' + param.content + '<em class="layim_zero"></em></div>'

                + '</li>';

            };

            //上述代码还是layim里的代码,只不过拼接html的时候,参数采用signalR返回的参数

            var type = result.fromuser.userid == currentUser.id ? "me" : "";//如果发送人的id==当前用户的id,那么这条消息类型为me

            //拼接html 直接调用layim里的代码            log.imarea.append(log.html({

                time: result.addtime,

                name: result.fromuser.username,

                face: result.fromuser.photo,

                content: result.msg

            }, type));

            //滚动条处理

            log.imarea.scrollTop(log.imarea[0].scrollHeight);

        },

  好了, 代码也都处理完了,这里呢有个小插曲,我们怎么确定当前用户是谁呢?由于我写的是死数据,所以我就采用随机生成的方法,然后将用户保存到 localStorage里面了,这样当用户再次打开页面,还是会取到第一次的用户,这里呢不多做介绍了。

 /*

        获取随机一个用户

        当用户第一次登陆就获取,然后存到本地localStorage中模拟用户,之后再登录就直接从缓存里面取

        */

        function getRandomUser() {

            var userKey = "SIGNALR_USER";

            var user = local.get(userKey);

            if (user) { return JSON.parse(user);}

            var userids = [];

            var usernames = ["痴玉", "书筠", "诗冬", "飞枫", "盼玉", "靖菡", "宛雁", "之卉", "凡晴", "书枫", "沛梦"];

            var userphotos = [];

            //添加id,用户头像数组

            for (var i = 0; i < 9; i++) {

                userids.push(10000 + i);

                userphotos.push("/photos/00" + i.toString() + ".jpg");

            }

            //取一个random值,自动生成当前用户

            var random = Math.random().toString().substr(3, 1);

            if (random > 8) { random = 8; }

            var user = {

                name: usernames[random],

                photo: userphotos[random],

                id:userids[random]

            };

            local.set(userKey, JSON.stringify(user));

            return user;

        }

        /*本地存储*/

        var local = {

            get: function (key) {

                return localStorage.getItem(key);

            },

            set: function (key, value) {

                localStorage.setItem(key, value);

            }

        }

  当然里面有好多需要注意的细节没有给大家讲,具体的可以看详细代码,思路基本已经出来了。我在重复一遍吧:第一,点击用户,连接服务器,当前用户加入对应的组。第二,发送消息,调用server端的方法,将消息发送出去后,在推送到组里面去,第三,客户端接收到消息之后,加到html页面上,就这么简单。还有一个细节,注意消息在左边还是右边。

  演示一下吧:模拟第一个用户登录。(谷歌浏览器)

  好,很好听的名字:飞枫,id10003,下面第二个用户登录,(QQ浏览器)

id10001,名字为 书筠。那么我们先用第一个用户点击  书筠 头像 打开聊天窗口,然后在用第二个用户点击 飞枫头像打开聊天窗口(由于没有做历史记录,所以离线留言功能暂时不支持,只支持在线)

  打开之后,唉,单身的我只能模拟两个人聊天玩了。。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值