作者: 乔克斯
查看: 4156|回复: 7

[教程] 【ESF通讯公司】IM系统中聊天记录模块的设计与实现

[复制链接]
乔克斯 发表于 2014-7-23 01:12:34 | 显示全部楼层 |阅读模式
查看: 4156|回复: 7
  看到很多开发IM系统的朋友都想实现聊天记录存储和查询这一不可或缺的功能,这里我就把自己前段时间为傲瑞通(OrayTalk)开发聊天记录模块的经验分享出来,供需要的朋友参考下。
一.总体设计
1.存储位置  
从一开始我们就打算在服务端和客户端本地同时存储聊天记录,而且,在客户端查看聊天记录时,可以选择是从本地加载、还是从服务器加载。这样做的好处有两个:
(1)从本地加载聊天记录速度非常快。
(2)当更换了登录的机器,在任何地方任何时刻都可以从服务器加载完整的聊天记录,记录永远不会丢失。
2.存储方案
(1)在服务端存储聊天记录当然使用我们主流的数据库SqlServer或Mysql等。
(2)在客户端,我们开始选择的是使用序列化技术,但是,考虑到当聊天记录数据量庞大时,序列化方案就不够灵活了,而且性能也跟不上。所以,最后决定使用轻量级的数据库Sqlite。
3.ORM框架
  DataRabbit的最新版本增加了对Sqlite的支持,并且对不同数据库的操作API是完全一致的,所以我们使用DataRabbit写了一个小组件来完成聊天记录的存储与查询等数据库访问操作。而无论是客户端还是服务端的聊天记录存储相关的工作,都交给这个组件来完成。
二.具体实现
1.ChatMessageRecord类
  一条聊天记录基本上包含了以下几个内容:发送人、接收人、内容、时间等。并且,我们想将两人聊天及群聊天抽象成同一个模型,于是,聊天记录的Entity类ChatMessageRecord设计成如下模样:
[C#] 纯文本查看 复制代码
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来封装查询返回的一页聊天记录:
161658198814608.png
根据ChatRecordPage中的TotalCount字段,查询者可以知道符合条件的记录数是多少,如此,就可以知道总共有多少页。
3.IChatRecordPersister接口
  无论是客户端还是服务端存储与查询聊天记录,我们都使用同一个接口IChatRecordPersister来进行抽象:
[C#] 纯文本查看 复制代码
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客户端查询聊天记录界面的截图:
161715119437585.png

评分

参与人数 3金钱 +4 收起 理由
owksmx593 + 1 感谢分享,LZ辛苦了~
CSdmin + 1 赞一个!
xiaobo + 2 可惜的是,这个软件没有源码

查看全部评分

回复 论坛版权

使用道具 举报

原始 发表于 2014-7-28 11:14:33 | 显示全部楼层
涨姿势了,好贴顶一个!
tzhweb 发表于 2014-12-27 20:41:18 | 显示全部楼层
涨姿势了
回复

使用道具 举报

thinhils 发表于 2014-12-29 17:30:15 | 显示全部楼层
不錯不錯,下來研究研究
WaterAsh 发表于 2015-4-27 15:53:45 | 显示全部楼层
涨姿势了!
回复

使用道具 举报

xgslym 发表于 2015-11-5 00:26:41 | 显示全部楼层
来试试,非常感谢
wuling35 发表于 2015-11-8 01:23:29 | 显示全部楼层
我来看看
回复

使用道具 举报

ligyste 发表于 2019-11-10 16:39:56 | 显示全部楼层
不错啊,谢谢
您需要登录后才可以回帖 登录 | 加入CSkin博客

本版积分规则

QQ|申请友链|小黑屋|手机版|Archiver|CSkin ( 粤ICP备13070794号

Powered by Discuz! X3.2  © 2001-2013 Comsenz Inc.  Designed by ARTERY.cn
GMT+8, 2024-3-29 15:36, Processed in 0.618357 second(s), 33 queries , Gzip On.

快速回复 返回顶部 返回列表