IM系統中聊天記錄模塊的設計與實現
看到很多開發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系統中聊天記錄模塊的設計與實現