1. 程式人生 > >IM系統中聊天記錄模塊的設計與實現

IM系統中聊天記錄模塊的設計與實現

人的 dex auto 由於 模型 速度 開發 構造 qlite

 看到很多開發IM系統的朋友都想實現聊天記錄存儲和查詢這一不可或缺的功能,這裏我就把自己前段時間為傲瑞通(OrayTalk)開發聊天記錄模塊的經驗分享出來,供需要的朋友參考下。

一.總體設計

1.存儲位置  

  從一開始我們就打算在服務端和客戶端本地同時存儲聊天記錄,而且,在客戶端查看聊天記錄時,可以選擇是從本地加載、還是從服務器加載。這樣做的好處有兩個:

(1)從本地加載聊天記錄速度非常快。

(2)當更換了登錄的機器,在任何地方任何時刻都可以從服務器加載完整的聊天記錄,記錄永遠不會丟失。

2.存儲方案

(1)在服務端存儲聊天記錄當然使用我們主流的數據庫SqlServer或Mysql等。

(2)在客戶端,我們開始選擇的是使用序列化技術,但是,考慮到當聊天記錄數據量龐大時,序列化方案就不夠靈活了,而且性能也跟不上。所以,最後決定使用輕量級的數據庫Sqlite。

3.ORM框架

  DataRabbit的最新版本增加了對Sqlite的支持,並且對不同數據庫的操作API是完全一致的,所以我們使用DataRabbit寫了一個小組件來完成聊天記錄的存儲與查詢等數據庫訪問操作。而無論是客戶端還是服務端的聊天記錄存儲相關的工作,都交給這個組件來完成。

二.具體實現

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客戶端查詢聊天記錄界面的截圖:

技術分享

 就到這裏了,還有疑問的朋友,請給我留言,我會及時回復的。

IM系統中聊天記錄模塊的設計與實現