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客户端查询聊天记录界面的截图:

      

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

 

时间: 2024-09-26 10:13:33

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

嵌入式系统中的模块动态加载技术

摘要 提出一种适用于嵌入式系统的模块动态加载技术,设计实现简单,占用资源少,开销小,并且成功运用于DeltaOS.可提高系统的灵活性和扩属性.介招加载与动态链接的原理和应用情况,解释相关术语,描述基本设计思路:详细说明该技术的核心.即模块声明.调用库.两级重定位表,最后给出结论. 关键词 模块 动态加栽 嵌入式系统DeltaOS 引 言随着电子技术的飞速发展,嵌人式设备应用越来越广泛,复杂度也越来越高.这使得硬件和软件设计比例发生了很大变化,软件开发的比重越来越大.然而传统嵌入式开发过程中需要将

(3)MEF插件系统中通信机制的设计和实现

文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 一般的WinForm中通过C#自带的Event机制便能很好的实现事件的注册和分发,但是,在插件系统中却不能这么简单的直接用已有的类来完成.一个插件本不包含另外一个插件,它们均是独立解耦的,实现插件和插件间的通信还需要我们设计出一个事件引擎来完成这个需求. 目前很多高级语言中基本都实现了观察者模式,并进行了自己的包装.比如C#中的delegate和event组合

大规模定制企业CRM系统中数据仓库的应用设计

一.大规模定制客户智能http://www.aliyun.com/zixun/aggregation/8302.html">数据仓库技术 1.数据的抽取.由于数据仓库是一个独立的数据环境,它需要通过抽取过程将数据从联机事务处理系统.外部数据源.脱机的数据存储介质中导入到数据仓库.数据抽取在技术上主要涉及互连.复制.增量,转换.调度和监控等几个方面. 2.数据的存储和管理.数据仓库遇到的第一个问题是对大量数据的存储和管理.这里所涉及的数据量比传统事务处理大得多且随时间的推移而累积.数据仓库的

MongoDB在呼叫中心系统中的应用

MongoDB在呼叫中心系统中的应用 席晓筱,詹舒波 MongoDB是一个可扩展,高性能,无模式,基于文档存储的非关系型数据管理系统.它的面向文档存储的特点使得MongoDB可以支持松散结构数据的存储:弱一致性特点使得MongoDB可以保证更快速的用户访问速度:高性能特点使得MongoDB可以更好的支持大数据量的处理.呼叫中心的不断发展对数据存储系统提出了新要求:大数据量+松散数据结构+高访问速度,因此引入非关系数据库MongoDB作为呼叫中心部分应用数据的存储系统十分合适.本文首先对Mongo

基于Swing开发系统中简化代码和对象生命周期管理的设计方法

在基于 Java 开发的电信级系统中,会有大量的 GUI 界面设计工作,但众所周知 Java 的目前的 IDE 解决方案对 Swing 界面开发支持的友好性不尽如人意,要做出友好的界面还是要耗费大量的时间,对有些模块可能比业务逻辑的工作量还要大.所以,现在对于 GUI 界面比较多的系统中,很多公司都会用到界面引擎和 XML 方式来自动生成界面,优点在于: 1.使用 XML 文档描述界面,通过界面生成引擎来解释 XML 文档并最终产生显示的界面.这使得开发界面更加容易,界面风格更加一致,维护更加方

如何处理Win8系统XAMPP中Apache模块无效的问题

目前使用最广泛Web服务器是Apache,如果系统默认占用了80端口,Win8系统在XAMPP中就会出现Apache模块无效的现象,导致apache无法打开.以下小编为您详细介绍一下如何解决Apache模块无效的方法! 1.以管理员权限运行c:windowssystem32cmd.exe C:WINDOWSsystem32>net stop http HTTP Service 服务已成功停止 2.C:WINDOWSsystem32>netstat -ano | findstr 0.0.0.0:

设计-考勤系统中考勤排班和结果计算

问题描述 考勤系统中考勤排班和结果计算 对于考勤系统中的班次,出现跨天的情况,在这种情况下,如何设计表结构,如何计算考勤结果 解决方案 可以把日期转换成浮点数来计算.日期浮点数是这样的数:如果两个时间差1天,那么就差1,如果差1小时,就差1/24,以此类推.不用理会跨天还是跨月.它永远和时间差对应. 解决方案二: 你用分钟数表示排班的起至时间,设置的时候就可以超过24:00 了.转换成实际时间,就用当天0点 + 开始分钟数-当天0点 + 结束分钟数,得到的时段自然就跨天了. 解决方案三: 可以设

PostgreSQL 在铁老大订单系统中的schemaless设计和性能压测

标签 PostgreSQL , UDF , schemaless , 自动建表 , 自动分区 , 订单查询 , 用户订单查询 , 席别订单查询 背景 数据的流动孕育数据生态,在很多大型系统中,业务系统之间的数据流动是很常见的事情. 例如铁大哥的订单数据的流动,可能带动很多围绕订单的业务需求.比如说订单查询: 1.按用户查询,返回用户30天内的所有订单. 2.按坐席和乘车日期查询,返回这个坐席的售出记录,看看有没有退票.有没有中途票可以加塞等. 以预售30天为例, 假设有20000趟车,20节车厢

集成框架-最近打算做一个大的框架把其它系统都包含在我们系统中,请问有啥好的设计或者实现案例吗?

问题描述 最近打算做一个大的框架把其它系统都包含在我们系统中,请问有啥好的设计或者实现案例吗? 主要功能是 我这个项目作为整个系统的入口,所以我们考虑使用单点登录.最好是能做的对外看所有被集成到子项目和我们框架看上去像一个整体! 解决方案 http://blog.csdn.net/hb0746/article/details/8517208 解决方案二: 看上去是一个整体的关键是统一界面,用相同的页眉页脚和相同的css样式.至于单点登录,这个有很成熟的方案. 解决方案三: 日志系统框架的设计与实