IM系统中聊天记录模块的设计与实现

  看到不少开发IM系统的朋友都想实现聊天记录存储和查询这一不可或缺的功能,这里我就把本身前段时间为傲瑞通(OrayTalk)开发聊天记录模块的经验分享出来,供须要的朋友参考下。sql

一.整体设计

1.存储位置  数据库

  从一开始咱们就打算在服务端和客户端本地同时存储聊天记录,并且,在客户端查看聊天记录时,能够选择是从本地加载、仍是从服务器加载。这样作的好处有两个: 服务器

(1)从本地加载聊天记录速度很是快。框架

(2)当更换了登陆的机器,在任何地方任什么时候刻均可以从服务器加载完整的聊天记录,记录永远不会丢失。性能

2.存储方案spa

(1)在服务端存储聊天记录固然使用咱们主流的数据库SqlServer或Mysql等。设计

(2)在客户端,咱们开始选择的是使用序列化技术,可是,考虑到当聊天记录数据量庞大时,序列化方案就不够灵活了,并且性能也跟不上。因此,最后决定使用轻量级的数据库Sqlite。code

3.ORM框架对象

  DataRabbit的最新版本增长了对Sqlite的支持,而且对不一样数据库的操做API是彻底一致的,因此咱们使用DataRabbit写了一个小组件来完成聊天记录的存储与查询等数据库访问操做。而不管是客户端仍是服务端的聊天记录存储相关的工做,都交给这个组件来完成。blog

 

二.具体实现

1.ChatMessageRecord类

  一条聊天记录基本上包含了如下几个内容:发送人、接收人、内容、时间等。而且,咱们想将两人聊天及群聊天抽象成同一个模型,因而,聊天记录的Entity类ChatMessageRecord设计成以下模样:

    public class ChatMessageRecord
    {     
        #region AutoID
        private long autoID = 0;
        /// <summary>
        /// 自增ID,编号。
        /// </summary>
        public long AutoID
        {
            get { return autoID; }
            set { autoID = value; }
        }
        #endregion

        #region SpeakerID
        private string speakerID = "";
        /// <summary>
        /// 发言人的ID。
        /// </summary>
        public string SpeakerID
        {
            get { return speakerID; }
            set { speakerID = value; }
        }
        #endregion

        #region AudienceID
        private string audienceID = "";
        /// <summary>
        /// 听众ID,能够为GroupID。
        /// </summary>
        public string AudienceID
        {
            get { return audienceID; }
            set { audienceID = value; }
        }
        #endregion

        #region OccureTime
        private DateTime occureTime = DateTime.Now;
        /// <summary>
        /// 聊天记录发生的时间。
        /// </summary>
        public DateTime OccureTime
        {
            get { return occureTime; }
            set { occureTime = value; }
        }
        #endregion

        #region ContentRtf
        private string contentRtf = "";
        /// <summary>
        /// 聊天的内容。
        /// </summary>
        public string ContentRtf
        {
            get { return contentRtf; }
            set { contentRtf = value; }
        }
        #endregion

        #region IsGroupChat
        private bool isGroupChat = false;
        /// <summary>
        /// 是否为群聊记录。
        /// </summary>
        public bool IsGroupChat
        {
            get { return isGroupChat; }
            set { isGroupChat = value; }
        }
        #endregion
    } 

    在ChatMessageRecord的定义中,聊天内容字段被设计为string类型,这是由于在OrayTalk中,聊天内容是富文本RTF格式的。若是须要,能够更改成byte[]类型,这样经过自定义的序列化操做就能够承载更复杂的聊天格式。

  最后一个字段IsGroupChat代表当前记录是否为群聊记录,若是是群聊记录,那么,AudienceID就不是好友的ID了,而是目标群组的ID。

  最后请注意:ChatMessageRecord实体与数据库中的ChatMessageRecord表是彻底映射的关系,这才使得DataRabbit的ORM数据访问成为可能。

2.ChatRecordPage类

  当咱们请求聊天记录时,因为记录数量可能很是庞大,因此,采用分页是不可避免的。咱们用ChatRecordPage来封装查询返回的一页聊天记录:

根据ChatRecordPage中的TotalCount字段,查询者能够知道符合条件的记录数是多少,如此,就能够知道总共有多少页。

3.IChatRecordPersister接口

  不管是客户端仍是服务端存储与查询聊天记录,咱们都使用同一个接口IChatRecordPersister来进行抽象:

    public interface IChatRecordPersister
    {
        /// <summary>
        /// 插入一条聊天记录(包括群聊天记录)。
        /// </summary>  
        void InsertChatMessageRecord(ChatMessageRecord record);

        
        /// <summary>
        /// 获取一页与好友的聊天记录。
        /// </summary>
        /// <param name="timeScope">日期范围</param>
        /// <param name="myID">本身的UserID</param>
        /// <param name="friendID">好友的ID</param>
        /// <param name="pageSize">页大小</param>
        /// <param name="pageIndex">页索引</param>      
        /// <returns>聊天记录页</returns>
        ChatRecordPage GetChatRecordPage(DateTimeScope timeScope, string myID, string friendID, int pageSize, int pageIndex);

        /// <summary>
        /// 获取一页群聊天记录。
        /// </summary>
        /// <param name="timeScope">日期范围</param>
        /// <param name="groupID">群ID</param>
        /// <param name="pageSize">页大小</param>
        /// <param name="pageIndex">页索引</param>     
        /// <returns>聊天记录页</returns>
        ChatRecordPage GetGroupChatRecordPage(DateTimeScope timeScope, string groupID, int pageSize, int pageIndex);        
    }

(1)插入游戏记录时,与好友聊天记录以及群聊天记录使用同一个InsertChatMessageRecord方法便可,只是在构造ChatMessageRecord对象时,字段的赋值有所区别。

(2)使用DataRabbit实现该接口时(如ChatRecordPersister类),经过属性DataBaseType来控制访问的是否为Sqlite数据库。而后在服务端使用ChatRecordPersister存取聊天记录时,就将DataBaseType设置为SqlServer;客户端则设置为Sqlite。

 

三.可能的Remoting的接口

  当咱们从服务器加载聊天记录时,能够考虑使用Remoting技术来实现,若是是这样,只须要在服务端把IChatRecordPersister接口暴露为Remoting服务,而后客户端使用这一Remoting服务进行聊天记录查询。这样一来,客户端在切换从本地加载和从服务器加载时,只须要切换IChatRecordPersister为本地ChatRecordPersister对象的引用或remoting远程引用便可。整个的代码实现将会很是简洁一致。

  到这里,关于聊天记录模块的设计与实现就介绍得差很少了,依照这样的思路,你们在本身的IM系统中增长聊天记录的功能应该是很简单的了。最后,上一张OrayTalk客户端查询聊天记录界面的截图:

      

 就到这里了,还有疑问的朋友,请给我留言,我会及时回复的。

相关文章
相关标签/搜索