核心数据结构分析开发

ops/2024/10/19 15:43:06/

核心数据结构分析开发

前言

        在上一集我们已经完成了对客户端环境的搭建。这一集我们将正式开始对客户端代码的编写。基于MVC软件设计模式中,我们先来考虑我们的M(Model),所以我们这一集就先分析并开发关键的类。

分析

在整一个即时通讯系统中,我们最关键的就是数据。其中包括以下三类数据:

  • 用户信息
  • 会话信息
  • 消息信息

其中用户信息毋庸置疑,包括用户编号、用户昵称、用户签名、手机号码、用户头像等相关信息。

其次,会话信息是指两人之间以及群聊的会话的信息。最后,消息信息就是包括但不限于文本、语音、文件、图片等信息。

分析之后我们会发现非常的简单,那么我就开始着手开始完成这三大信息类的代码编写吧!

用户信息

UserInfo

/*** @brief 消息信息*/
class UserInfo{
public:QString userId = "";     //用户编号QString nickname = "";   //用户昵称QString description = "";//用户签名QString phone = "";      //手机号码QIcon avatar;            //用户头像
};

这里的userId使用的是字符串类型,如果是单节点的MySQL数据库我们是可以选择使用int类型,并让这个参数在MySQL当中作为自增主键,但在分布式系统下,这个做法就可能欠缺考虑了。在分布式系统当中,事物的一致性和完整性是一个挑战,数据的一致性也是一个巨大的挑战,在分布式系统中,我们通常会选择使用分布式ID生成器来生成userId,而不是依赖于MySQL的自增主键。

会话信息

ChatSessionInfo

/*** @brief 会话信息*/
class ChatSessionInfo{
public:QString chatSessionId = "";   //会话编号QString chatSessionName = ""; //会话名字,如果是单聊,就是对方的昵称,如果是群聊,那就是群聊名Message lastMessage;          //表示最新消息QIcon avatar;                 //会话头像,如果是单聊,就是对方的头像,如果是群聊,则是群聊头像QString userId = "";          //表示单聊,就是对方的昵称,如果是群聊,则先设置为"",后续通过其他方式把完整的用户id列表拿到
};

这里的Message是消息信息,所以大家不要感到沮丧,下一个就是他!

同样chatSessionId不选择使用int类型!

我们平时使用的微信与QQ,每一个会话都含有对方的昵称,对方的头像以及最后的一次消息。

消息信息

        消息信息就是最复杂的,它包括四种不同的消息类型,我们这里会使用一个枚举去表示他的消息类型。

MessageType

/*** @brief 消息信息*/
enum MessageType{TEXT_TYPE,  //文本消息IMAGE_TYPE, //图片消息FILE_TYPE,  //文件消息SPEECH_TYPE //语音消息
};

Message

/*** @brief 消息信息*/
class Message{
public:QString messageId = "";       //消息编号QString chatSessionId = "";   //消息所属会话编号QString time = "";            //消息的时间,通过格式化时间的方式来表示,eg:10-27 10:02:00MessageType messageType;      //消息类型UserInfo sender;              //发送者信息QByteArray content;           //消息的正文内容QString fileId = "";          //文件的身份标识,消息类型是文件,图片,语音的时候才有效,文本消息时为空QString fileName = "";        //文件名称,只有当消息类型是文件时才生效,其他时候为空
}

由于我们这个消息类型有四种,所以我们就需要四种不同的构造函数!

但是我们发现了一个问题,如果使用构造函数,我们貌似无法构成重载!

我们的构造函数的形参是几乎一样的!

所以我们就要引入一个新的设计思路------工厂模式

我们直接来看代码!

makeMessage

static Message makeMessage(MessageType messageType, const QString& chatSessionId, const UserInfo& sender,const QByteArray& content, const QString& extraInfo){if(messageType == TEXT_TYPE){return makeTextMessage(chatSessionId,sender,content);}else if(messageType == IMAGE_TYPE){return makeImageMessage(chatSessionId,sender,content);}else if(messageType == FILE_TYPE){return makeFileMessage(chatSessionId,sender,content,extraInfo);}else if(messageType == SPEECH_TYPE){return makeSpeechMessage(chatSessionId,sender,content);}else{//触发未知消息类型return Message();}}

我们这里判断完消息类型之后就可以进入各自的方法进行构造出一个自己的消息实例。

makeTextMessage

static Message makeTextMessage(const QString& chatSessionId, const UserInfo& sender, const QByteArray& content){Message message;//确保唯一性message.messageId = makeId();message.chatSessionId = chatSessionId;message.sender = sender;//生成格式化时间message.time = formatTime(getTime());message.content = content;message.messageType = TEXT_TYPE;message.fileId = "";message.fileName = "";return message;}

makeImageMessage

static Message makeImageMessage(const QString& chatSessionId, const UserInfo& sender, const QByteArray& content){Message message;message.messageId = makeId();message.chatSessionId = chatSessionId;message.sender = sender;message.time = formatTime(getTime());message.content = content;message.messageType = IMAGE_TYPE;message.fileId = "";message.fileName = "";return message;}

makeFileMessage

static Message makeFileMessage(const QString& chatSessionId, const UserInfo& sender, const QByteArray& content, const QString& fileName){Message message;message.messageId = makeId();message.chatSessionId = chatSessionId;message.sender = sender;message.time = formatTime(getTime());message.content = content;message.messageType = FILE_TYPE;message.fileId = "";message.fileName = fileName;return message;}

makeSpeechMessage

static Message makeSpeechMessage(const QString& chatSessionId, const UserInfo& sender, const QByteArray& content){Message message;message.messageId = makeId();message.chatSessionId = chatSessionId;message.sender = sender;message.time = formatTime(getTime());message.content = content;message.messageType = SPEECH_TYPE;message.fileId = "";message.fileName = "";return message;}

这就是工厂模式!能完美的解决我们构造函数不能够重载的好方法。

但是这里也要说一下,他并不是完美的设计模式,缺点是,每增加一个产品类,就需要增加一个具体的工厂类,这可能会导致系统中类的个数急剧增加,在一定程度上增加了系统的复杂度。

工具方法

讲完了三个最重要的类,我们也要把相关的工具也落实到位。

/*** @brief 工具函数*/
//获取文件名称
static inline QString getFileName(const QString& path){QFileInfo fileInfo(path);return fileInfo.fileName();
}
/*** 宏日志*/
#define TAG QString("[%1:%2]").arg(model::getFileName(__FILE__),QString::number(__LINE__))
#define LOG() qDebug().noquote() << TAG//格式化时间
static inline QString formatTime(int64_t timestamp){QDateTime dateTime = QDateTime::fromSecsSinceEpoch(timestamp);return dateTime.toString("YYYY-MM-dd HH:mm:ss");
}
//获取时间戳
static inline int64_t getTime(){return QDateTime::currentSecsSinceEpoch();
}
//QByteArray转成QIcon
static inline QIcon makeIcon(const QByteArray& byteArray){QPixmap pixmap;pixmap.loadFromData(byteArray);QIcon icon(pixmap);return icon;
}
//读写文件
//读取所有的二进制内容=>QByteArray
static inline QByteArray loadFileToByteArray(const QString& path){QFile file(path);bool success = file.open(QFile::ReadOnly);if(!success){LOG() << "文件打开失败";return QByteArray();}QByteArray content = file.readAll();file.close();return content;
}
//QByteArray=>文件
static inline void writeByteArrayToFile(const QString& path,const QByteArray& content){QFile file(path);bool success = file.open(QFile::WriteOnly);if(!success){LOG() << "文件打开失败";return;}file.write(content);file.flush();//刷新缓冲区file.close();
}

完整代码

#pragma once#include <QString>
#include <QIcon>
#include <QUuid>
#include <QDateTime>
#include <QFile>
#include <QDebug>
#include <QFileInfo>/*** 关于命名空间,约定:* 如果代码所在的文件,就是在项目的顶层目录中,直接使用全局命名空间,不手动指定。* 如果代码所在的文件,在某个子目录当中,指定一个和目录名字相同的命名空间。*/// 创建一个命名空间namespace model{
/*** @brief 工具函数*/
//获取文件名称
static inline QString getFileName(const QString& path){QFileInfo fileInfo(path);return fileInfo.fileName();
}
/*** 宏日志*/
#define TAG QString("[%1:%2]").arg(model::getFileName(__FILE__),QString::number(__LINE__))
#define LOG() qDebug().noquote() << TAG//格式化时间
static inline QString formatTime(int64_t timestamp){QDateTime dateTime = QDateTime::fromSecsSinceEpoch(timestamp);return dateTime.toString("YYYY-MM-dd HH:mm:ss");
}
//获取时间戳
static inline int64_t getTime(){return QDateTime::currentSecsSinceEpoch();
}
//QByteArray转成QIcon
static inline QIcon makeIcon(const QByteArray& byteArray){QPixmap pixmap;pixmap.loadFromData(byteArray);QIcon icon(pixmap);return icon;
}
//读写文件
//读取所有的二进制内容=>QByteArray
static inline QByteArray loadFileToByteArray(const QString& path){QFile file(path);bool success = file.open(QFile::ReadOnly);if(!success){LOG() << "文件打开失败";return QByteArray();}QByteArray content = file.readAll();file.close();return content;
}
//QByteArray=>文件
static inline void writeByteArrayToFile(const QString& path,const QByteArray& content){QFile file(path);bool success = file.open(QFile::WriteOnly);if(!success){LOG() << "文件打开失败";return;}file.write(content);file.flush();//刷新缓冲区file.close();
}/*** @brief 用户信息*/
class UserInfo{
public:QString userId = "";     //用户编号QString nickname = "";   //用户昵称QString description = "";//用户签名QString phone = "";      //手机号码QIcon avatar;            //用户头像
};
/*** @brief 消息信息*/
enum MessageType{TEXT_TYPE,  //文本消息IMAGE_TYPE, //图片消息FILE_TYPE,  //文件消息SPEECH_TYPE //语音消息
};/*** @brief 消息信息*/
class Message{
public:QString messageId = "";       //消息编号QString chatSessionId = "";   //消息所属会话编号QString time = "";            //消息的时间,通过格式化时间的方式来表示,eg:10-27 10:02:00MessageType messageType;      //消息类型UserInfo sender;              //发送者信息QByteArray content;           //消息的正文内容QString fileId = "";          //文件的身份标识,消息类型是文件,图片,语音的时候才有效,文本消息时为空QString fileName = "";        //文件名称,只有当消息类型是文件时才生效,其他时候为空//此处 extraInfo目前只是在消息类型是文件消息时,作为"文件名"补充static Message makeMessage(MessageType messageType, const QString& chatSessionId, const UserInfo& sender,const QByteArray& content, const QString& extraInfo){if(messageType == TEXT_TYPE){return makeTextMessage(chatSessionId,sender,content);}else if(messageType == IMAGE_TYPE){return makeImageMessage(chatSessionId,sender,content);}else if(messageType == FILE_TYPE){return makeFileMessage(chatSessionId,sender,content,extraInfo);}else if(messageType == SPEECH_TYPE){return makeSpeechMessage(chatSessionId,sender,content);}else{//触发未知消息类型return Message();}}private:// 生成唯一的messageIdstatic QString makeId(){return "Message" + QUuid::createUuid().toString().sliced(25,12);}static Message makeTextMessage(const QString& chatSessionId, const UserInfo& sender, const QByteArray& content){Message message;//确保唯一性message.messageId = makeId();message.chatSessionId = chatSessionId;message.sender = sender;//生成格式化时间message.time = formatTime(getTime());message.content = content;message.messageType = TEXT_TYPE;message.fileId = "";message.fileName = "";return message;}static Message makeImageMessage(const QString& chatSessionId, const UserInfo& sender, const QByteArray& content){Message message;message.messageId = makeId();message.chatSessionId = chatSessionId;message.sender = sender;message.time = formatTime(getTime());message.content = content;message.messageType = IMAGE_TYPE;message.fileId = "";message.fileName = "";return message;}static Message makeFileMessage(const QString& chatSessionId, const UserInfo& sender, const QByteArray& content, const QString& fileName){Message message;message.messageId = makeId();message.chatSessionId = chatSessionId;message.sender = sender;message.time = formatTime(getTime());message.content = content;message.messageType = FILE_TYPE;message.fileId = "";message.fileName = fileName;return message;}static Message makeSpeechMessage(const QString& chatSessionId, const UserInfo& sender, const QByteArray& content){Message message;message.messageId = makeId();message.chatSessionId = chatSessionId;message.sender = sender;message.time = formatTime(getTime());message.content = content;message.messageType = SPEECH_TYPE;message.fileId = "";message.fileName = "";return message;}
};/*** @brief 会话信息*/
class ChatSessionInfo{
public:QString chatSessionId = "";   //会话编号QString chatSessionName = ""; //会话名字,如果是单聊,就是对方的昵称,如果是群聊,那就是群聊名Message lastMessage;          //表示最新消息QIcon avatar;                 //会话头像,如果是单聊,就是对方的头像,如果是群聊,则是群聊头像QString userId = "";          //表示单聊,就是对方的昵称,如果是群聊,则先设置为"",后续通过其他方式把完整的用户id列表拿到
};}

那么这一集我们就完成了关键类的编写以及相关的工具方法的编写,后面,我们就要开始落实主窗口,那么期待我们的下一集。


http://www.ppmy.cn/ops/105038.html

相关文章

【Oracle点滴积累】Oracle 19c安装Critical Patch Update for January 2024

广告位招租&#xff01; 知识无价&#xff0c;人有情&#xff0c;无偿分享知识&#xff0c;希望本条信息对你有用&#xff01; 今天和大家分享如何为Oracle 19c(未启用RMAN的单实例)安装Critical Patch Update(Patch Number:35943157)&#xff0c;本指引不包含Roll Back部分&a…

【Python机器学习】NLP词中的数学——主题建模

目录 齐普夫定律 相关度排序 工具 其他工具 Okapi BM25 在文档向量中&#xff0c;词计数是有用的&#xff0c;但是纯词计数&#xff0c;即使按照文档长度进行归一化处理&#xff0c;也不能告诉我们太多该词在当前文档相对于语料库中其他文档的重要度信息。如果能弄清楚这些…

经纬恒润天津工厂荣获2024年第一批天津市数字化车间

在数字化转型的浪潮中&#xff0c;天津市工业与信息化局本月正式公布了2024年第一批数字化车间和智能工厂名单&#xff0c;经纬恒润天津工厂凭借其卓越的创新能力与先进的智能制造技术成功入选&#xff0c;荣膺"天津市数字化车间"称号&#xff0c;标志着经纬恒润在数…

微服务组件----网关

小编目前大一&#xff0c;刚开始着手学习微服务的相关知识&#xff0c;小编会把它们整理成知识点发布出来。我认为同为初学者&#xff0c;我把我对知识点的理解以这种代码加观点的方式分享出来不仅加深了我的理解&#xff0c;或许在某个时候对你也有所帮助&#xff0c;同时也欢…

稀疏镜像在OpenHarmony上的应用

一、稀疏镜像升级背景 常用系统镜像格式为原始镜像&#xff0c;即RAW格式。镜像体积比较大&#xff0c;在烧录固件或者升级固件时比较耗时&#xff0c;而且在移动设备升级过程时比较耗费流量。为此&#xff0c;将原始镜像用稀疏描述&#xff0c;可以大大地缩减镜像体积&#x…

macos 使用port安装mariadb/mysql数据库服务器

在mac下安装mariadb/mysql数据库服务器的方式 有多种,可以直接下载官方安装包安装,或者使用port, brew 这类macos下的专业包管理工具安装, 推荐使用port 包管理工具来安装mysql数据库服务器. 使用方法如下: 先使用port search xxx查找要安装的软件都有哪些安装包 命令: por…

儿童护眼大路灯怎么选择?5款儿童护眼落地灯分享

儿童护眼大路灯怎么选择&#xff1f;网上有很多与护眼大路灯相关的热门话题&#xff0c;比如“护眼大路灯怎么选”、“儿童护眼大路灯伤眼”等。由此可见&#xff0c;护眼大路灯虽然是当前公认最高效的照明工具&#xff0c;但却一直备受争议。究其根源&#xff0c;是市面上充斥…

30个值得收藏的国家超清三维地图

我们在《值得收藏的28个省会城市三维地形图》一文中&#xff0c;分享了精美的全国28个省会/首府城市的三维地形渲染图。 现在我们又精选30个国家的三维地形图分享给大家&#xff0c;你可以在文末查看该数据的领取方法。 30个国家三维地形图 本次一共整理30个国家的三维地图分…