mirror of
https://gitee.com/Zhaoxin59/my-chat_-client.git
synced 2026-02-13 16:41:48 +08:00
refactor: 大规模调整项目目录结构,将ChatClient和ChatServer整合为Monorepo结构,并分为两个独立文件夹:chatclient/ 和 chatserver/。更新了ChatClient的CMakeLists.txt配置以适配新结构。
This commit is contained in:
66
ChatClient/include/addfrienddialog.h
Normal file
66
ChatClient/include/addfrienddialog.h
Normal file
@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "debug.h"
|
||||
#include "model/data.h"
|
||||
|
||||
using model::UserInfo;
|
||||
|
||||
/////////////////////////////////////////
|
||||
//表示一个好友搜索的结果
|
||||
/////////////////////////////////////////
|
||||
class FriendResultItem : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FriendResultItem(const UserInfo& userInfo);
|
||||
void clickAddBtn();
|
||||
|
||||
private:
|
||||
const UserInfo& userInfo;
|
||||
|
||||
QPushButton* addBtn;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////
|
||||
//整个搜索好友的窗口
|
||||
/////////////////////////////////////////
|
||||
class AddFriendDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AddFriendDialog(QWidget *parent);
|
||||
|
||||
//初始化结果区域
|
||||
void initResultArea();
|
||||
|
||||
//往窗口中新增一个好友搜索的结果
|
||||
void addResult(const UserInfo& userInfo);
|
||||
|
||||
//清空界面上所有的好友搜索结果
|
||||
void clear();
|
||||
|
||||
//
|
||||
void setSearchKey(const QString& searcheKey);
|
||||
|
||||
void clickSearchBtn();
|
||||
void clickSearchDone();
|
||||
|
||||
private:
|
||||
//整个窗口的网格布局
|
||||
QGridLayout* layout;
|
||||
|
||||
//
|
||||
QLineEdit* searchEdit;
|
||||
|
||||
//保存搜索好友的结果
|
||||
QWidget* resultContainer;
|
||||
};
|
||||
72
ChatClient/include/choosefrienddialog.h
Normal file
72
ChatClient/include/choosefrienddialog.h
Normal file
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QHBoxLayout>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QPushButton>
|
||||
#include <QPainter>
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
class ChooseFriendDialog;
|
||||
|
||||
////////////////////////////////////////////////
|
||||
/// 选择好友窗口中的一个 元素/好友项
|
||||
////////////////////////////////////////////////
|
||||
class ChooseFriendItem : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChooseFriendItem(ChooseFriendDialog* owner, const QString& userId, const QIcon& avatar, const QString& name, bool checked);
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void enterEvent(QEnterEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
|
||||
QString& getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
QCheckBox* getCheckBox() {
|
||||
return checkBox;
|
||||
}
|
||||
|
||||
private:
|
||||
bool isHover = false;
|
||||
|
||||
QCheckBox* checkBox;
|
||||
QPushButton* avatarBtn;
|
||||
QLabel* nameLabel;
|
||||
ChooseFriendDialog* owner; //记录了哪个QWidget持有这个Item,此处应该是ChooseFriendDialog
|
||||
|
||||
QString userId; //记录成员id用于删除唯一指定的成员Item
|
||||
};
|
||||
|
||||
class ChooseFriendDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ChooseFriendDialog(QWidget *parent, const QString& userId);
|
||||
|
||||
void initLeft(QHBoxLayout* layout);
|
||||
void initRight(QHBoxLayout* layout);
|
||||
void initData();
|
||||
|
||||
void addFriend(const QString& userId, const QIcon& avatar, const QString& name, bool checked);
|
||||
void addSelectedFriend(const QString& userId, const QIcon& avatar, const QString& name);
|
||||
void deleteSelectedFriend(const QString& userId);
|
||||
|
||||
void clickOkBtn();
|
||||
QList<QString> generateMemberList();
|
||||
private:
|
||||
QWidget* totalContainer;
|
||||
QWidget* selectedContainer;
|
||||
|
||||
//当前选择窗口是点击哪个用户弹出的
|
||||
QString userId;
|
||||
};
|
||||
|
||||
14
ChatClient/include/debug.h
Normal file
14
ChatClient/include/debug.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
#define TEST_UI 0
|
||||
|
||||
#define TEST_GROUP_SESSION_DETAIL 0
|
||||
|
||||
#define TEST_SKIP_LOGIN 0
|
||||
|
||||
#define TEST_NETWORK 0
|
||||
|
||||
#define DEPOLY 1
|
||||
|
||||
#endif // DEBUG_H
|
||||
37
ChatClient/include/groupsessiondetailwidget.h
Normal file
37
ChatClient/include/groupsessiondetailwidget.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <qscrollarea.h>
|
||||
#include <qscrollbar.h>
|
||||
#include <qgridlayout.h>
|
||||
#include <qpushbutton.h>
|
||||
#include <qgridlayout.h>
|
||||
#include "sessiondetailwidget.h"
|
||||
#include "debug.h"
|
||||
|
||||
class AvatarItem;
|
||||
|
||||
class GroupSessionDetailWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GroupSessionDetailWidget(QWidget* parent);
|
||||
|
||||
void initData();
|
||||
void initMembers(const QString& chatSessionId);
|
||||
|
||||
void addMember(AvatarItem* avatarItem);
|
||||
|
||||
private:
|
||||
QGridLayout* glayout;
|
||||
QLabel* groupNameLabel;
|
||||
|
||||
//表示当前所要添加的AvatarItem处在的行和列
|
||||
int curRow = 0;
|
||||
int curCol = 1;
|
||||
};
|
||||
108
ChatClient/include/historymessagewidget.h
Normal file
108
ChatClient/include/historymessagewidget.h
Normal file
@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QRadioButton>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QDateTimeEdit>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "debug.h"
|
||||
#include "model/data.h"
|
||||
|
||||
using model::Message;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// 表示一个历史消息元素
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class HistoryItem : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
HistoryItem() {}
|
||||
|
||||
static HistoryItem* makeHistoryItem(const Message& message);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// 展示历史消息窗口
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class HistoryMessageWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HistoryMessageWidget(QWidget *parent);
|
||||
|
||||
//在窗口中添加一个历史消息
|
||||
void addHistoryMessage(const Message& message);
|
||||
|
||||
//清空窗口中所有的历史消息
|
||||
void clear();
|
||||
|
||||
void clickSearchBtn();
|
||||
void clickSearchBtnDone();
|
||||
|
||||
private:
|
||||
void initScrollArea(QGridLayout* layout);
|
||||
|
||||
QRadioButton* keyRadioBtn;
|
||||
QRadioButton* timeRadioBtn;
|
||||
QLineEdit* searchEdit;
|
||||
|
||||
QDateTimeEdit* begTimeEdit;
|
||||
QDateTimeEdit* endTimeEdit;
|
||||
|
||||
//持有所有的历史消息结果的容器对象
|
||||
QWidget* container;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// 展示图片历史消息
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class ImageButton : public QPushButton
|
||||
{
|
||||
public:
|
||||
ImageButton(const QString& fileId, const QByteArray& content);
|
||||
void updateUI(const QString& fileId, const QByteArray& content);
|
||||
|
||||
private:
|
||||
QString fileId;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// 展示文件历史消息
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class FileLabel : public QLabel {
|
||||
public:
|
||||
FileLabel(const QString& fileId, const QString& filename);
|
||||
void getContentDone(const QString& fileId, const QByteArray& fileContent);
|
||||
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
QString fileId;
|
||||
QByteArray content;
|
||||
QString filename;
|
||||
bool loadDone = false;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
/// 展示语音历史消息
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class SpeechLabel : public QLabel {
|
||||
public:
|
||||
SpeechLabel(const QString& fileId);
|
||||
|
||||
void getContentDone(const QString& fileId, const QByteArray& content);
|
||||
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
|
||||
private:
|
||||
QString fileId;
|
||||
QByteArray content;
|
||||
bool loadDone = false;
|
||||
};
|
||||
38
ChatClient/include/loginwidget.h
Normal file
38
ChatClient/include/loginwidget.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "phoneloginwidget.h"
|
||||
#include "verifycodewidget.h"
|
||||
#include "toast.h"
|
||||
|
||||
class LoginWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LoginWidget(QWidget *parent);
|
||||
|
||||
void switchMode();
|
||||
|
||||
void clickSubmitBtn();
|
||||
void userLoginDone(bool ok, const QString& reason);
|
||||
void userRegisterDone(bool ok, const QString& reason);
|
||||
|
||||
private:
|
||||
bool isLoginMode = true;
|
||||
QLabel* titleLabel;
|
||||
|
||||
QLineEdit* usernameEdit;
|
||||
QLineEdit* passwordEdit;
|
||||
QLineEdit* verifyCodeEdit;
|
||||
VerifyCodeWidget* verifyCodeWidget;
|
||||
|
||||
QPushButton* submitBtn;
|
||||
QPushButton* phoneModeBtn;
|
||||
QPushButton* switchModeBtn;
|
||||
};
|
||||
114
ChatClient/include/mainwidget.h
Normal file
114
ChatClient/include/mainwidget.h
Normal file
@ -0,0 +1,114 @@
|
||||
#ifndef MAINWIDGET_H
|
||||
#define MAINWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QMainWindow>
|
||||
|
||||
#include "debug.h"
|
||||
#include "messageeditarea.h"
|
||||
#include "messageshowarea.h"
|
||||
#include "selfinfowidget.h"
|
||||
#include "sessionfriendarea.h"
|
||||
#include "groupsessiondetailwidget.h"
|
||||
#include "addfrienddialog.h"
|
||||
#include "model/datacenter.h"
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class MainWidget;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MainWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
//对于单例模式最关键的部分是限制别人创建实例
|
||||
static MainWidget* instance;
|
||||
|
||||
public:
|
||||
static MainWidget* getInstance();
|
||||
|
||||
public:
|
||||
MainWidget(QWidget *parent = nullptr);
|
||||
~MainWidget();
|
||||
|
||||
private:
|
||||
Ui::MainWidget *ui;
|
||||
//窗口左侧部分
|
||||
QWidget* windowLeft;
|
||||
//窗口中间部分
|
||||
QWidget* windowMid;
|
||||
//窗口右侧部分
|
||||
QWidget* windowRight;
|
||||
//用户头像
|
||||
QPushButton* userAvatar;
|
||||
//会话标签页按钮
|
||||
QPushButton* sessionTabBtn;
|
||||
//好友标签页按钮
|
||||
QPushButton* friendTabBtn;
|
||||
//好友申请标签页按钮
|
||||
QPushButton* applyTabBtn;
|
||||
|
||||
//用户搜索框
|
||||
QLineEdit* searchEdit;
|
||||
//添加好友按钮
|
||||
QPushButton* addFriendBtn;
|
||||
|
||||
//
|
||||
SessionFriendArea* sessionFriendArea;
|
||||
|
||||
//显示会话标题
|
||||
QLabel* sessionTitleLabel;
|
||||
|
||||
//显示会话详情按钮
|
||||
QPushButton* extraBtn;
|
||||
|
||||
//消息展示区
|
||||
MessageShowArea* messageShowArea;
|
||||
|
||||
//消息编辑区
|
||||
MessageEditArea* messageEditArea;
|
||||
|
||||
enum ActiveTab {
|
||||
SESSION_LIST,
|
||||
FRIEND_LIST,
|
||||
APPLY_LIST
|
||||
};
|
||||
|
||||
ActiveTab activeTab = SESSION_LIST;
|
||||
|
||||
public:
|
||||
void initMainWindow();
|
||||
void initLeftWindow();
|
||||
void initMidWindow();
|
||||
void initRightWindow();
|
||||
|
||||
void initSignalSlot();
|
||||
void initWebSocket();
|
||||
|
||||
void switchTabToSession();
|
||||
void switchTabToFriend();
|
||||
void switchTabToApply();
|
||||
|
||||
void loadSessionList();
|
||||
void loadFriendList();
|
||||
void loadApplyList();
|
||||
|
||||
void updateFriendList();
|
||||
void updateChatSessionList();
|
||||
void updateApplyList();
|
||||
|
||||
void loadRecentMessage(const QString& chatSessionId);
|
||||
void updateRecentMessage(const QString& chatSessionId);
|
||||
|
||||
//点击好友项后,切换到会话列表的总函数,上方的switchTabToSession只是其中的一个环节
|
||||
void switchSession(const QString& userId);
|
||||
|
||||
MessageShowArea* getMessageShowArea();
|
||||
};
|
||||
#endif // MAINWIDGET_H
|
||||
56
ChatClient/include/messageeditarea.h
Normal file
56
ChatClient/include/messageeditarea.h
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef MESSAGEEDITAREA_H
|
||||
#define MESSAGEEDITAREA_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QScrollBar>
|
||||
//#include <QGraphicsDropShadowEffect>
|
||||
//#include <qpropertyanimation.h>
|
||||
//#include <QEvent>
|
||||
|
||||
#include "historymessagewidget.h"
|
||||
#include "model/data.h"
|
||||
|
||||
using namespace model;
|
||||
|
||||
//编辑消息的区域
|
||||
class MessageEditArea : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MessageEditArea(QWidget *parent = nullptr);
|
||||
|
||||
void initSignalSlot();
|
||||
void sendTextMessage();
|
||||
void addSelfMessage(MessageType messageType, const QByteArray& content, const QString& extraInfo);
|
||||
void addOtherMessage(const model::Message& message);
|
||||
//花式按钮事件
|
||||
//bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
|
||||
void clickSendImageBtn();
|
||||
void clickSendFileBtn();
|
||||
|
||||
void soundRecordPressed();
|
||||
void soundRecordReleased();
|
||||
|
||||
void soundSpeech(const QString& path);
|
||||
private:
|
||||
QPushButton* sendImageBtn;
|
||||
QPushButton* sendFileBtn;
|
||||
QPushButton* sendSpeechBtn;
|
||||
QPushButton* showHistoryBtn;
|
||||
QPlainTextEdit* textEdit;
|
||||
QPushButton* sendTextButton;
|
||||
QLabel* tipLabel;
|
||||
|
||||
//花式按钮
|
||||
//QGraphicsDropShadowEffect* shadowEffect;
|
||||
|
||||
|
||||
signals:
|
||||
};
|
||||
|
||||
#endif // MESSAGEEDITAREA_H
|
||||
120
ChatClient/include/messageshowarea.h
Normal file
120
ChatClient/include/messageshowarea.h
Normal file
@ -0,0 +1,120 @@
|
||||
#ifndef MESSAGESHOWAREA_H
|
||||
#define MESSAGESHOWAREA_H
|
||||
|
||||
#include <QScrollArea>
|
||||
#include <QWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QScrollBar>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QFontMetrics>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
|
||||
|
||||
#include "model/data.h"
|
||||
#include "debug.h"
|
||||
|
||||
//.h文件中,不宜使用namespace xxx
|
||||
using model::Message;
|
||||
|
||||
////////////////////////////////////////////
|
||||
/// 表示消息展示区
|
||||
////////////////////////////////////////////
|
||||
class MessageShowArea : public QScrollArea
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MessageShowArea();
|
||||
|
||||
//头插
|
||||
void addFrontMessage(bool isLeft, const Message& message);
|
||||
//尾插
|
||||
void addMessage(bool isLeft, const Message& message);
|
||||
|
||||
//清空
|
||||
void clear();
|
||||
|
||||
//滚动到末尾
|
||||
void scrollToEnd();
|
||||
|
||||
private:
|
||||
QWidget* container;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
/// 表示一个消息元素
|
||||
/// 我们可能考虑要同时支持文本,文件,图片,视频,语音
|
||||
////////////////////////////////////////////
|
||||
class MessageItem : public QWidget {
|
||||
public:
|
||||
//isLeft表示当前的这个消息是否是左侧的消息
|
||||
MessageItem(bool isLeft);
|
||||
|
||||
//通过工厂方法创建MessageItem实例
|
||||
static MessageItem* makeMessageItem(bool isLeft, const Message& message);
|
||||
|
||||
//添加工厂函数
|
||||
static QWidget* makeTextMessageItem(bool isLeft, const QString& message);
|
||||
static QWidget* makeImageMessageItem(bool isLeft, const QString& fileId, const QByteArray& content);
|
||||
static QWidget* makeFileMessageItem(bool isLeft, const Message& message);
|
||||
static QWidget* makeSpeechMessageItem(bool isLeft, const Message& message);
|
||||
|
||||
private:
|
||||
bool isLeft;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
/// 创建类表示“文本消息”正文部分
|
||||
//也让这个类表示文件消息
|
||||
////////////////////////////////////////////
|
||||
class MessageContentLabel : public QWidget {
|
||||
public:
|
||||
MessageContentLabel(const QString& text, bool isLeft, model::MessageType messageType, const QString& fileId,
|
||||
const QByteArray& content);
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
void updateUI(const QString& fileId, const QByteArray& fileContent);
|
||||
void saveAsFile(const QByteArray& content);
|
||||
|
||||
void playDone();
|
||||
|
||||
void contextMenuEvent(QContextMenuEvent* event) override;
|
||||
void speechConverTextDone(const QString& fileId, const QString& text);
|
||||
|
||||
private:
|
||||
QLabel* label;
|
||||
bool isLeft;
|
||||
|
||||
model::MessageType messageType;
|
||||
QString fileId;
|
||||
QByteArray content;
|
||||
QString fileName;
|
||||
|
||||
bool loadContentDone = false;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
/// 创建类表示“图片消息”部分
|
||||
////////////////////////////////////////////
|
||||
class MessageImageLabel : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MessageImageLabel(const QString& fileId, const QByteArray& content, bool isLeft);
|
||||
|
||||
void updateUI(const QString& fileId, const QByteArray& content);
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
QPushButton* imageBtn;
|
||||
|
||||
QString fileId; //该图片在服务器对应的文件id
|
||||
QByteArray content; //图片的二进制数据
|
||||
bool isLeft;
|
||||
};
|
||||
|
||||
#endif // MESSAGESHOWAREA_H
|
||||
318
ChatClient/include/model/data.h
Normal file
318
ChatClient/include/model/data.h
Normal file
@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
#include <QUuid>
|
||||
|
||||
#include "base.qpb.h"
|
||||
#include "gateway.qpb.h"
|
||||
#include "user.qpb.h"
|
||||
#include "friend.qpb.h"
|
||||
#include "file.qpb.h"
|
||||
#include "notify.qpb.h"
|
||||
#include "speech_recognition.qpb.h"
|
||||
#include "message_storage.qpb.h"
|
||||
#include "message_transmit.qpb.h"
|
||||
|
||||
// 创建命名空间
|
||||
namespace model {
|
||||
///////////////////////////
|
||||
//工具函数,后续很多模块可能会用到
|
||||
///////////////////////////
|
||||
|
||||
//获取仅当前的源文件名
|
||||
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) {
|
||||
//先把时间戳转换为datetime对象
|
||||
QDateTime datetime = QDateTime::fromSecsSinceEpoch(timestamp);
|
||||
//把datetime对象转化为格式化的时间
|
||||
return datetime.toString("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 ok = file.open(QFile::ReadOnly);
|
||||
if(!ok) {
|
||||
qDebug() << "文件打开失败!!!";
|
||||
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 ok = file.open(QFile::WriteOnly);
|
||||
if(!ok) {
|
||||
qDebug() << "文件打开失败!!!";
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(content);
|
||||
file.flush();
|
||||
file.close();
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// 用户信息
|
||||
///////////////////////////
|
||||
class UserInfo {
|
||||
public:
|
||||
//初始化,避免一些随机值可能的负面影响
|
||||
QString userId = ""; //用户编号
|
||||
QString nickname = ""; //用户昵称
|
||||
QString description = ""; //用户签名
|
||||
QString phone = ""; //手机号码
|
||||
QIcon avatar; //用户头像
|
||||
|
||||
//从protobuffer的UserInfo对象,转换为当前代码的UserInfo对象
|
||||
void load(const bite_im::UserInfo& userInfo) {
|
||||
this->userId = userInfo.userId();
|
||||
this->nickname = userInfo.nickname();
|
||||
this->phone = userInfo.phone();
|
||||
this->description = userInfo.description();
|
||||
if (userInfo.avatar().isEmpty()) {
|
||||
//使用默认的头像即可
|
||||
this->avatar = QIcon(":resource/image/defaultAvatar.png");
|
||||
}
|
||||
else {
|
||||
this->avatar = makeIcon(userInfo.avatar());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////
|
||||
//消息信息
|
||||
///////////////////////////
|
||||
enum MessageType {
|
||||
TEXT_TYPE, //文本消息
|
||||
IMAGE_TYPE, //图片消息
|
||||
FILE_TYPE, //文件消息
|
||||
SPEECH_TYPE //语音消息
|
||||
};
|
||||
|
||||
class Message {
|
||||
public:
|
||||
QString messageId = ""; //消息的编号
|
||||
QString chatSessionId = ""; //消息所属会话的编号
|
||||
QString time = ""; //消息的时间,通过格式化的方式来表示
|
||||
MessageType messageType = TEXT_TYPE; //消息类型
|
||||
UserInfo sender; //发送者信息
|
||||
QByteArray content; //消息的正文内容
|
||||
QString fileId = ""; //文件的身份标识,当为文件,图片,语音,视频有效,当为文本则为""
|
||||
QString fileName = ""; //文件名称
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void load(const bite_im::MessageInfo& messageInfo) {
|
||||
this->messageId = messageInfo.messageId();
|
||||
this->chatSessionId = messageInfo.chatSessionId();
|
||||
this->time = formatTime(messageInfo.timestamp());
|
||||
this->sender.load(messageInfo.sender());
|
||||
|
||||
//设置消息的类型
|
||||
auto type = messageInfo.message().messageType();
|
||||
if (type == bite_im::MessageTypeGadget::MessageType::STRING) {
|
||||
this->messageType = TEXT_TYPE;
|
||||
this->content = messageInfo.message().stringMessage().content().toUtf8();
|
||||
}
|
||||
else if (type == bite_im::MessageTypeGadget::MessageType::IMAGE) {
|
||||
this->messageType = IMAGE_TYPE;
|
||||
if (messageInfo.message().imageMessage().hasImageContent()) {
|
||||
this->content = messageInfo.message().imageMessage().imageContent();
|
||||
}
|
||||
if (messageInfo.message().imageMessage().hasFileId()) {
|
||||
this->fileId = messageInfo.message().imageMessage().fileId();
|
||||
}
|
||||
}
|
||||
else if (type == bite_im::MessageTypeGadget::MessageType::FILE) {
|
||||
this->messageType = FILE_TYPE;
|
||||
if (messageInfo.message().fileMessage().hasFileContents()) {
|
||||
this->content = messageInfo.message().fileMessage().fileContents();
|
||||
}
|
||||
if (messageInfo.message().fileMessage().hasFileId()) {
|
||||
this->fileId = messageInfo.message().fileMessage().fileId();
|
||||
}
|
||||
this->fileName = messageInfo.message().fileMessage().fileName();
|
||||
}
|
||||
else if (type == bite_im::MessageTypeGadget::MessageType::SPEECH) {
|
||||
this->messageType = SPEECH_TYPE;
|
||||
if (messageInfo.message().speechMessage().hasFileContents()) {
|
||||
this->content = messageInfo.message().speechMessage().fileContents();
|
||||
}
|
||||
if (messageInfo.message().speechMessage().hasFileId()) {
|
||||
this->fileId = messageInfo.message().speechMessage().fileId();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 错误的类型, 啥都不做了, 只是打印一个日志
|
||||
LOG() << "非法的消息类型! type=" << type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static QString makeId() {
|
||||
//Uuid这东西背后也是一套算法,能够生成“全球唯一的身份标识”
|
||||
//Qt对UUID也有封装
|
||||
//{75eb37e5-5af5-4bfb-8d0e-5261038a9107}(16进制的整数)
|
||||
return "M" + QUuid::createUuid().toString().mid(25, 12);
|
||||
}
|
||||
|
||||
static Message makeTextMessage(const QString& chatSessionId,const UserInfo& sender, const QByteArray& content) {
|
||||
Message message;
|
||||
//此处需要确保生成的messageId是唯一的
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////
|
||||
//会话信息
|
||||
///////////////////////////
|
||||
class ChatSessionInfo {
|
||||
public:
|
||||
QString chatSessionId = ""; //会话编号
|
||||
QString chatSessionName = ""; //会话名字(单聊或群聊)
|
||||
Message lastMessage; //表示会话的最新消息
|
||||
QIcon avatar; //会话的头像(单聊或群聊)
|
||||
QString userId = ""; //(单聊为对方的id,群聊为"")
|
||||
|
||||
|
||||
void load(const bite_im::ChatSessionInfo& chatSessionInfo) {
|
||||
this->chatSessionId = chatSessionInfo.chatSessionId();
|
||||
this->chatSessionName = chatSessionInfo.chatSessionName();
|
||||
if (chatSessionInfo.hasSingleChatFriendId()) {
|
||||
this->userId = chatSessionInfo.singleChatFriendId();
|
||||
}
|
||||
if (chatSessionInfo.hasPrevMessage()) {
|
||||
lastMessage.load(chatSessionInfo.prevMessage());
|
||||
}
|
||||
if (chatSessionInfo.hasAvatar() && !chatSessionInfo.avatar().isEmpty()) {
|
||||
//如果有头像,则设置这个头像
|
||||
this->avatar = makeIcon(chatSessionInfo.avatar());
|
||||
}
|
||||
else {
|
||||
//如果没有,则会根据是单聊还是群聊,使用不同的默认头像
|
||||
if (userId != "") {
|
||||
//单聊
|
||||
this->avatar = QIcon(":/resource/image/defaultAvatar.png");
|
||||
}
|
||||
else {
|
||||
//群聊
|
||||
this->avatar = QIcon(":/resource/image/groupAvatar.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} //end namespace model
|
||||
265
ChatClient/include/model/datacenter.h
Normal file
265
ChatClient/include/model/datacenter.h
Normal file
@ -0,0 +1,265 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <qstandardpaths.h>
|
||||
#include <QDir>
|
||||
#include <QJsonObject>
|
||||
|
||||
//#include <QList>
|
||||
|
||||
#include "data.h"
|
||||
#include "../network/netclient.h"
|
||||
|
||||
namespace model
|
||||
{
|
||||
|
||||
class DataCenter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static DataCenter* getInstance();
|
||||
|
||||
~DataCenter();
|
||||
/// <summary>
|
||||
/// 计算两个整数的和。
|
||||
/// </summary>
|
||||
/// <param name="a">第一个加数</param>
|
||||
/// <param name="b">第二个加数</param>
|
||||
/// <returns>返回 a + b 的结果</returns>
|
||||
/*int add(int a, int b) {
|
||||
return a + b;
|
||||
}*/
|
||||
|
||||
private:
|
||||
DataCenter();
|
||||
|
||||
static DataCenter* instance;
|
||||
|
||||
//列出DataCenter中要组织管理的所有数据
|
||||
|
||||
//当前客户端登录到服务器对应的登录会话Id
|
||||
QString loginSessionId;
|
||||
|
||||
//当前的用户信息
|
||||
UserInfo* myself = nullptr;
|
||||
|
||||
//好友列表
|
||||
QList<UserInfo>* friendList = nullptr;
|
||||
|
||||
//会话列表
|
||||
QList<ChatSessionInfo>* chatSessionList = nullptr;
|
||||
|
||||
//记录当前选中的会话是哪个
|
||||
QString currentChatSessionId = "";
|
||||
|
||||
//记录每个会话中,都有哪些成员
|
||||
QHash<QString, QList<UserInfo>>* memberList = nullptr;//unordered_map
|
||||
|
||||
//待处理的好友申请列表
|
||||
QList<UserInfo>* applyList = nullptr;
|
||||
|
||||
//每个会话最近消息的列表,key为chatSessionId,value为消息列表
|
||||
QHash<QString, QList<Message>>* recentMessages = nullptr;
|
||||
|
||||
//存储每个会话,表示未读消息的数量
|
||||
QHash<QString, int>* unreadMessageCount = nullptr;
|
||||
|
||||
// 用户的好友搜索结果
|
||||
QList<UserInfo>* searchUserResult = nullptr;
|
||||
|
||||
//保存一个历史消息搜索结果
|
||||
QList<Message>* searchMessageResult = nullptr;
|
||||
|
||||
//短信(邮箱)验证码的验证Id
|
||||
QString currentVerifyCodeId = "";
|
||||
|
||||
//让dataCenter持有Netclient实例
|
||||
network::NetClient netClient;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// 初始化数据文件
|
||||
/// </summary>
|
||||
void initDataFile();
|
||||
|
||||
//存储数据到文件中
|
||||
void saveDataFile();
|
||||
|
||||
//从数据文件中加载数据到内存
|
||||
void loadDataFile();
|
||||
|
||||
//清空未读消息数目
|
||||
void clearUnread(const QString& chatSessionId);
|
||||
|
||||
//增加未读消息的数目
|
||||
void addUnread(const QString& chatSessionId);
|
||||
|
||||
//获取未读消息的数目
|
||||
int getUnread(const QString& chatSessionId);
|
||||
|
||||
//获取到当前的登录会话Id
|
||||
const QString& getLoginSessionId() const{
|
||||
return loginSessionId;
|
||||
}
|
||||
|
||||
//验证网络的连通性
|
||||
void ping() { netClient.ping(); }
|
||||
|
||||
//针对netclient中的websocket进行初始化
|
||||
void initWebsocket();
|
||||
|
||||
//通过网络获取到用户的个人信息
|
||||
void getMyselfAsync();
|
||||
|
||||
UserInfo* getMyselfsync();
|
||||
//
|
||||
void resetMyself(std::shared_ptr<bite_im::GetUserInfoRsp> resp);
|
||||
|
||||
//通过网络获取好友列表
|
||||
QList<UserInfo>* getFriendList();
|
||||
void getFriendListAsync();
|
||||
void resetFriendList(std::shared_ptr<bite_im::GetFriendListRsp> resp);
|
||||
|
||||
//获取会话列表
|
||||
QList<ChatSessionInfo>* getChatSessionList();
|
||||
void getChatSessionListAsync();
|
||||
void resetChatSessionList(std::shared_ptr<bite_im::GetChatSessionListRsp> resp);
|
||||
|
||||
//获取好友申请列表
|
||||
QList<UserInfo>* getApplyList();
|
||||
void getApplyListAsync();
|
||||
void resetApplyList(std::shared_ptr<bite_im::GetPendingFriendEventListRsp> resp);
|
||||
|
||||
//获取最近的消息列表
|
||||
void getRecnetMessageListAsync(const QString& chatSessionId, bool updateUI);
|
||||
QList<Message>* getRecentMessageList(const QString& chatSessionId);
|
||||
void resetRecentMessageList(const QString& chatSessionId, std::shared_ptr<bite_im::GetRecentMsgRsp> resp);
|
||||
|
||||
//发送消息给服务器
|
||||
void sendTextMessageAsync(const QString& chatSessionId, const QString& content);
|
||||
void sendImageMessageAsync(const QString& chatSessionId, const QByteArray& content);
|
||||
void sendFileMessageAsync(const QString& chatSessionId, const QString& fileName, const QByteArray& content);
|
||||
void sendSpeechMessageAsync(const QString& chatSessionId, const QByteArray& content);
|
||||
|
||||
//修改用户昵称
|
||||
void changeNicknameAsync(const QString& nickname);
|
||||
void resetNickname(const QString& nickname);
|
||||
|
||||
//修改用户签名
|
||||
void changeDescriptionAsync(const QString& desc);
|
||||
void resetDescription(const QString& desc);
|
||||
|
||||
//获取邮箱验证码
|
||||
void getVerifyCodeAsync(const QString& email);
|
||||
void resetVerifyCodeId(const QString& verifyCodeId);
|
||||
|
||||
//获取verifyCodeId
|
||||
const QString& getVerifyCodeId() const;
|
||||
|
||||
//修改邮箱号码
|
||||
void changePhoneAsync(const QString& email, const QString& verifyCodeId, const QString& verifyCode);
|
||||
void resetPhone(const QString& email);
|
||||
|
||||
//修改头像
|
||||
void changeAvatarAsync(const QByteArray& imageBytes);
|
||||
void resetAvatar(const QByteArray& avatar);
|
||||
|
||||
//删除好友
|
||||
void deleteFriendAsync(const QString& userId);
|
||||
void removeFriend(const QString& userId);
|
||||
void addFriendApplyAsync(const QString& userId);
|
||||
|
||||
//发送同意好友申请操作
|
||||
void acceptFriendApplyAsync(const QString& userId);
|
||||
UserInfo removeFromApplyList(const QString& userId);
|
||||
|
||||
//拒绝好友申请操作
|
||||
void rejectFriendApplyAsync(const QString& userId);
|
||||
|
||||
//创建群聊
|
||||
void createGroupChatSessionAsync(const QList<QString>& userIdList);
|
||||
|
||||
//获取会话成员列表
|
||||
void getMemberListAsync(const QString& chatSessionId);
|
||||
QList<UserInfo>* getMemberList(const QString& chatSessionId);
|
||||
void resetMemberList(const QString& chatSessionId, const QList<bite_im::UserInfo>& memberList);
|
||||
|
||||
//搜索用户
|
||||
void searchUserAsync(const QString& searchKey);
|
||||
QList<UserInfo>* getSearchUserResult();
|
||||
void resetSearchUserResult(const QList<bite_im::UserInfo>& userList);
|
||||
|
||||
//搜索历史消息
|
||||
void searchMessageAsync(const QString& searchKey);
|
||||
void searchMessageByTimeAsync(const QDateTime& begTime, const QDateTime& endTime);
|
||||
QList<Message>* getSearchMessageReuslt();
|
||||
void resetSearchMessageResult(const QList<bite_im::MessageInfo>& msgList);
|
||||
|
||||
//登录注册
|
||||
void userLoginAsync(const QString& username, const QString& password);
|
||||
void resetLoginSessionId(const QString& loginSessionId);
|
||||
void userRegisterAsync(const QString& username, const QString& password);
|
||||
void phoneLoginAsync(const QString& phone, const QString& verifyCode);
|
||||
void phoneRegisterAsync(const QString& phone, const QString& verifyCode);
|
||||
|
||||
//获取单个文件
|
||||
void getSingleFileAsync(const QString& fileId);
|
||||
|
||||
//语音转文字
|
||||
void speechConvertTextAsync(const QString& fileId, const QByteArray& content);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
///辅助函数
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//根据会话id查询会话信息
|
||||
ChatSessionInfo* findChatSessionById(const QString& chatSessionId);
|
||||
//根据用户ID查询会话信息
|
||||
ChatSessionInfo* findChatSessionByUserId(const QString& userId);
|
||||
//把指定的会话信息,放到列表头部
|
||||
void topCurrentChatSessionId(const ChatSessionInfo& chatSessionInfo);
|
||||
//根据用户id查询好友信息
|
||||
UserInfo* findFriendById(const QString& userId);
|
||||
|
||||
//设置/获取当前选中的会话
|
||||
void setCurrentChatSessionId(const QString& chatSessionId);
|
||||
const QString& getCurrentSessionId();
|
||||
|
||||
//添加消息到DataCenter中
|
||||
void addMessage(const Message& message);
|
||||
signals:
|
||||
//自定义信号
|
||||
void getMyselfDone();
|
||||
void getFriendListDone();
|
||||
void getChatSessionListDone();
|
||||
void getApplyListDone();
|
||||
void getRecentMessageListDone(const QString& chatSessionId);
|
||||
void getRecentMessageListDoneNoUI(const QString& chatSessionId);
|
||||
void sendMessageDone(MessageType messageType, const QByteArray& content, const QString& extraInfo);
|
||||
void updateLastMessage(const QString& chatSessionId);
|
||||
void receiveMessageDone(const Message& lastMessage);
|
||||
void changeNicknameDone();
|
||||
void changeDescriptionDone();
|
||||
void getVerifyCodeDone(bool ok);
|
||||
void changePhoneDone();
|
||||
void changeAvatarDone();
|
||||
void deleteFriendDone();
|
||||
void clearCurrentSession();
|
||||
void addFriendApplyDone();
|
||||
void receiveFriendApplyDone();
|
||||
void acceptFriendApplyDone();
|
||||
void rejectFriendApplyDone();
|
||||
void receiveFriendProcessDone(const QString& nickname, bool agree);
|
||||
void createGroupChatSessionDone();
|
||||
void receiveSessionCreateDone();
|
||||
void getMemberListDone(const QString& chatSessionId);
|
||||
void searchUserDone();
|
||||
void searchMessageDone();
|
||||
void userLoginDone(bool ok, const QString& reason);
|
||||
void userRegisterDone(bool ok, const QString& reason);
|
||||
void phoneLoginDone(bool ok, const QString& reason);
|
||||
void phoneRegisterDone(bool ok, const QString& reason);
|
||||
void getSingleFileDone(const QString& fileId, const QByteArray& fileContent);
|
||||
void speechConvertTextDone(const QString& fileId, const QString& text);
|
||||
};
|
||||
} //end namespace model
|
||||
132
ChatClient/include/network/netclient.h
Normal file
132
ChatClient/include/network/netclient.h
Normal file
@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QWebSocket>
|
||||
#include <QProtobufSerializer>
|
||||
#include <qnetworkreply.h>
|
||||
#include <QUuid>
|
||||
|
||||
#include "../model/data.h"
|
||||
|
||||
//此处为了避免“循环包含”的问题,就需要使用前置声明的方法
|
||||
// 代替包含头文件
|
||||
namespace model {
|
||||
class DataCenter;
|
||||
} //end namespace model
|
||||
|
||||
class model::DataCenter;
|
||||
|
||||
namespace network {
|
||||
class NetClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
//定义重要的常量,ip暂时使用本地的回环ip,端口号暂定的8000和8001
|
||||
const QString HTTP_URL = "http://127.0.0.1:8000";
|
||||
const QString WEBSOCKET_URL = "ws://127.0.0.1:8001/ws";
|
||||
|
||||
public:
|
||||
NetClient(model::DataCenter* dataCenter);
|
||||
|
||||
//验证网络的联通性
|
||||
void ping();
|
||||
|
||||
//初始化websocket
|
||||
void initWebSocket();
|
||||
|
||||
//针对websocket的处理
|
||||
void handleWsResponse(const bite_im::NotifyMessage& notifyMessage);
|
||||
void handleWsMessage(const model::Message& message);
|
||||
void handleWsRemoveFriend(const QString& userId);
|
||||
void handleWsAddfriendApply(const model::UserInfo& userInfo);
|
||||
void handleWsAddFriendProcess(const model::UserInfo& userInfo, bool agree);
|
||||
void handleWsSessionCreate(const model::ChatSessionInfo& chatSessionInfo);
|
||||
|
||||
//发送身份认证请求
|
||||
void sendAuth();
|
||||
|
||||
//生成请求的Id
|
||||
static QString makeRequestId();
|
||||
|
||||
//封装发送请求的逻辑
|
||||
QNetworkReply* sendHttpRequest(const QString& apiPath, const QByteArray& body);
|
||||
|
||||
//封装处理响应的逻辑(判定HTTP正确性,反序列化,判断业务的正确性)
|
||||
//由于不同的api返回的pb对象结构不同,为了让一个函数能够处理多种不同的类型,需要使用模板
|
||||
//后面两个是输出型参数,用于表示这次的操作是成功还是失败
|
||||
template <typename T>
|
||||
std::shared_ptr<T> handleHttpResponse(QNetworkReply* httpResp, bool* ok, QString* reason) {
|
||||
//判定HTTP层面上
|
||||
if (httpResp->error() != QNetworkReply::NoError) {
|
||||
*ok = false;
|
||||
*reason = httpResp->errorString();
|
||||
httpResp->deleteLater();
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
|
||||
//说明并没有出错, 那就获取到响应的body
|
||||
QByteArray respBody = httpResp->readAll();
|
||||
|
||||
//针对body反序列化
|
||||
std::shared_ptr<T> respObj = std::make_shared<T>();
|
||||
respObj->deserialize(&serializer, respBody);
|
||||
|
||||
//判定业务的结构是否正确
|
||||
if (!respObj->success()) {
|
||||
*ok = false;
|
||||
*reason = respObj->errmsg();
|
||||
httpResp->deleteLater();
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
|
||||
//释放httpResp对象
|
||||
httpResp->deleteLater();
|
||||
*ok = true;
|
||||
return respObj;
|
||||
}
|
||||
|
||||
void getMyself(const QString& loginSessionId);
|
||||
void getFriendList(const QString& loginSessionId);
|
||||
void getChatSessionList(const QString& loginSessionId);
|
||||
void getApplyList(const QString& loginSessionId);
|
||||
void getRecentMessageList(const QString& loginSessionId, const QString& chatSessionId, bool updateUI);
|
||||
void sendMessage(const QString& loginSessionId, const QString& chatSessionId, model::MessageType messageType,
|
||||
const QByteArray& content, const QString& extraInfo);
|
||||
void receiveMessage(const QString& chatSessionId);
|
||||
void changeNickname(const QString& loginSessionId, const QString& nickname);
|
||||
void changeDescription(const QString& loginSessionId, const QString& desc);
|
||||
void getVerifyCode(const QString& email);
|
||||
void changeEmail(const QString& loginSessionId, const QString& email, const QString& verifyCodeId, const QString& verifyCode);
|
||||
void changeAvatar(const QString& loginSessionId, const QByteArray& avatar);
|
||||
void deleteFriend(const QString& loginSessionId, const QString& userId);
|
||||
void addFriendApply(const QString& loginSessionId, const QString& userId);
|
||||
void acceptFriendApply(const QString& loginSessionId, const QString& userId);
|
||||
void rejectFriendApply(const QString& loginSessionId, const QString& userId);
|
||||
void createGroupChatSession(const QString& loginSessionId, const QList<QString>& userIdList);
|
||||
void getMemberList(const QString& loginSessionId, const QString& chatSessionId);
|
||||
void searchUser(const QString& loginSessionId, const QString& searchKey);
|
||||
void searchMessage(const QString& loginSessionId, const QString& chatSessionId, const QString& searchKey);
|
||||
void searchMessageByTime(const QString& loginSessionId, const QString& chatSessionId, const QDateTime& begTime, const QDateTime& endTime);
|
||||
void userLogin(const QString& username, const QString& password);
|
||||
void userRegister(const QString& username, const QString& password);
|
||||
void phoneLogin(const QString& phone, const QString& verifyCodeId, const QString& verifyCode);
|
||||
void phoneRegister(const QString& phone, const QString& verifyCodeId, const QString& verifyCode);
|
||||
void getSingleFile(const QString& loginSessionId, const QString& fileId);
|
||||
void speechConvertText(const QString& loginSessionId, const QString& fileId, const QByteArray& content);
|
||||
|
||||
private:
|
||||
model::DataCenter* dataCenter;
|
||||
|
||||
//http客户端
|
||||
QNetworkAccessManager httpClient;
|
||||
|
||||
//websocket客户端
|
||||
QWebSocket websocketClient;
|
||||
|
||||
//序列化器
|
||||
QProtobufSerializer serializer;
|
||||
signals:
|
||||
};
|
||||
} //end namespace network
|
||||
40
ChatClient/include/phoneloginwidget.h
Normal file
40
ChatClient/include/phoneloginwidget.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "loginwidget.h"
|
||||
|
||||
class PhoneLoginWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PhoneLoginWidget(QWidget *parent);
|
||||
|
||||
void sendVerifyCode();
|
||||
void sendVerifyCodeDone(bool ok);
|
||||
|
||||
void clickSubmitBtn();
|
||||
void phoneLoginDone(bool ok, const QString& reason);
|
||||
void phoneRegisterDone(bool ok, const QString& reason);
|
||||
|
||||
void countDown();
|
||||
void switchMode();
|
||||
|
||||
private:
|
||||
QLineEdit* phoneEdit;
|
||||
QPushButton* sendVerifyCodeBtn;
|
||||
QLineEdit* verifyCodeEdit;
|
||||
QLabel* titleLabel;
|
||||
QPushButton* submitBtn;
|
||||
QPushButton* switchModeBtn;
|
||||
|
||||
bool isLoginMode = true;
|
||||
QString currentPhone = ""; // 记录是使用哪个手机号发送的验证码
|
||||
QTimer* timer;
|
||||
int leftTime = 30;
|
||||
};
|
||||
68
ChatClient/include/selfinfowidget.h
Normal file
68
ChatClient/include/selfinfowidget.h
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <qlineedit.h>
|
||||
#include <QPushButton>
|
||||
#include <QCursor>
|
||||
#include <QGridLayout>
|
||||
#include <QTimer>
|
||||
#include <QFileDialog>
|
||||
#include "debug.h"
|
||||
#include "toast.h"
|
||||
|
||||
class SelfInfoWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SelfInfoWidget(QWidget *parent);
|
||||
//~SelfInfoWidget();
|
||||
|
||||
void initSingnalSlots();
|
||||
|
||||
void clickNameSubmitBtn();
|
||||
void clickNameSubmitBtnDone();
|
||||
|
||||
void clickDescSubmitBtn();
|
||||
void clickDescSubmitBtnDone();
|
||||
void clickGetVerifyCodeBtn();
|
||||
void clickPhoneSubmitBtn();
|
||||
void clickPhoneSubmitBtnDone();
|
||||
void clickAvatarBtn();
|
||||
void clickAvatarBtnDone();
|
||||
|
||||
private:
|
||||
QGridLayout* layout;
|
||||
|
||||
QPushButton* avatarBtn;
|
||||
QLabel* idTag; //序号标签
|
||||
QLabel* idLabel; //序号
|
||||
|
||||
QLabel* nameTag;//昵称标签
|
||||
QLabel* nameLabel;//名字
|
||||
QLineEdit* nameEdit;//编辑昵称
|
||||
QPushButton* nameModifyBtn;//修改昵称
|
||||
QPushButton* nameSubmitBtn;//提交修改
|
||||
|
||||
QLabel* descTag;//签名标签
|
||||
QLabel* descLabel;//签名
|
||||
QLineEdit* descEdit;//编辑签名
|
||||
QPushButton* descModifyBtn;//修改签名
|
||||
QPushButton* descSubmitBtn;//提交修改
|
||||
|
||||
QLabel* phoneTag;//电话标签
|
||||
QLabel* phoneLabel;//电话号码
|
||||
QLineEdit* phoneEdit;//编辑电话
|
||||
QPushButton* phoneModifyBtn;//修改电话
|
||||
QPushButton* phoneSubmitBtn;//提交修改
|
||||
|
||||
QLabel* verifyCodeTag;//显示验证码
|
||||
QLineEdit* verifyCodeEdit;//输入验证码
|
||||
QPushButton* getVerifyCodeBtn;//获取验证码按钮
|
||||
|
||||
//要修改新的手机号码
|
||||
QString emailToChange;
|
||||
int leftTime = 30;
|
||||
};
|
||||
51
ChatClient/include/sessiondetailwidget.h
Normal file
51
ChatClient/include/sessiondetailwidget.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QFontMetrics>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "debug.h"
|
||||
#include "model/data.h"
|
||||
#include "choosefrienddialog.h"
|
||||
|
||||
using namespace model;
|
||||
|
||||
///////////////////////////////////////
|
||||
///表示 一个头像 + 一个名字的组合控件
|
||||
///////////////////////////////////////
|
||||
class AvatarItem : public QWidget {
|
||||
public:
|
||||
AvatarItem(const QIcon& avatar, const QString& name);
|
||||
|
||||
QPushButton* getAvatar() {
|
||||
return avatarBtn;
|
||||
}
|
||||
|
||||
private:
|
||||
QPushButton* avatarBtn;
|
||||
QLabel* nameLabel;
|
||||
};
|
||||
|
||||
///////////////////////////////////////
|
||||
///表示“单聊会话详情”窗口
|
||||
///////////////////////////////////////
|
||||
class SessionDetailWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SessionDetailWidget(QWidget *parent, const UserInfo& userInfo);
|
||||
|
||||
void clickDeleteFriendBtn();
|
||||
|
||||
private:
|
||||
UserInfo userInfo;
|
||||
|
||||
QPushButton* deleteFriendBtn;
|
||||
};
|
||||
141
ChatClient/include/sessionfriendarea.h
Normal file
141
ChatClient/include/sessionfriendarea.h
Normal file
@ -0,0 +1,141 @@
|
||||
#ifndef SESSIONFRIENDAREA_H
|
||||
#define SESSIONFRIENDAREA_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
// #include <iostream>
|
||||
#include <QStyleOption>
|
||||
#include <QPainter>
|
||||
#include <QEnterEvent>
|
||||
#include "model/data.h"
|
||||
#include <QLabel>
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
/// 滚动区域的Item的类型
|
||||
////////////////////////////////////////
|
||||
enum ItemType{
|
||||
SessionItemType,
|
||||
FriendItemType,
|
||||
ApplyItemType
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
/// 整个滚动区域的实现
|
||||
////////////////////////////////////////
|
||||
class SessionFriendArea : public QScrollArea
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SessionFriendArea(QWidget *parent = nullptr);
|
||||
|
||||
//清空区域内所有的Item
|
||||
void clear();
|
||||
|
||||
//添加一个Item
|
||||
void addItem(ItemType itemType, const QString& id, const QIcon& avatar, const QString& name, const QString& text);
|
||||
|
||||
//选中某一个指定的item,通过index下标来访问item
|
||||
void clickItem(int index);
|
||||
|
||||
private:
|
||||
//后续向container中的layout添加元素,就会有QScrollArea的滚动
|
||||
QWidget* container;
|
||||
|
||||
signals:
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
/// 滚动区域的Item的实现
|
||||
////////////////////////////////////////
|
||||
|
||||
class SessionFriendItem : public QWidget {
|
||||
//可以使当前的类使用信号与槽相关的操作
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SessionFriendItem(QWidget* owner, const QIcon& avatar, const QString& name, const QString& text);
|
||||
|
||||
//通过显式绘制控件的基础样式,解决了自定义控件因未正确处理Qt样式表机制导致的QSS不生效问题
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void enterEvent(QEnterEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
|
||||
void select();
|
||||
|
||||
//Active实现Item被点击后的业务逻辑
|
||||
virtual void active();
|
||||
|
||||
private:
|
||||
//owner 就是指向了 SessionFriendArea
|
||||
QWidget* owner;
|
||||
|
||||
//表示当前的Item是否是选中的状态(选中时其背景色会有所不同)
|
||||
bool selected = false;
|
||||
|
||||
protected:
|
||||
QLabel* messageLabel;
|
||||
};
|
||||
|
||||
////////////////////////////////////////
|
||||
/// 会话Item的实现
|
||||
////////////////////////////////////////
|
||||
class SessionItem : public SessionFriendItem {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SessionItem(QWidget* owner, const QString& chatSessionId, const QIcon& avatar,
|
||||
const QString& name, const QString& lastmessage);
|
||||
|
||||
void active() override;
|
||||
|
||||
void updateLastMessage(const QString& chatSessionId);
|
||||
private:
|
||||
//当前会话Id
|
||||
QString chatSessionId;
|
||||
|
||||
//最后一条消息的文本预览
|
||||
QString text;
|
||||
};
|
||||
|
||||
////////////////////////////////////////
|
||||
/// 好友Item的实现
|
||||
////////////////////////////////////////
|
||||
class FriendItem : public SessionFriendItem {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FriendItem(QWidget* owner, const QString& userId, const QIcon& avatar, const QString& name, const QString& description);
|
||||
void active() override;
|
||||
private:
|
||||
// 好友的用户Id
|
||||
QString userId;
|
||||
};
|
||||
|
||||
////////////////////////////////////////
|
||||
/// 好友申请Item的实现
|
||||
////////////////////////////////////////
|
||||
class ApplyItem : public SessionFriendItem {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ApplyItem(QWidget* owner, const QString& userId, const QIcon& avatar, const QString& name);
|
||||
void acceptFriendApply();
|
||||
void rejectFriendApply();
|
||||
|
||||
void active() override;
|
||||
private:
|
||||
//好友申请Id
|
||||
QString userId;
|
||||
};
|
||||
|
||||
#endif // SESSIONFRIENDAREA_H
|
||||
59
ChatClient/include/soundrecorder.h
Normal file
59
ChatClient/include/soundrecorder.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef SOUNDRECORDER_H
|
||||
#define SOUNDRECORDER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStandardPaths>
|
||||
#include <QFile>
|
||||
#include <QAudioSource>
|
||||
#include <QAudioSink>
|
||||
#include <QMediaDevices>
|
||||
|
||||
class SoundRecorder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
const QString RECORD_PATH = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/sound/tmpRecord.pcm";
|
||||
const QString PLAY_PATH = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/sound/tmpPlay.pcm";
|
||||
|
||||
public:
|
||||
static SoundRecorder* getInstance();
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
/// 录制语音语音
|
||||
/////////////////////////////////////////////////
|
||||
// 开始录制
|
||||
void startRecord();
|
||||
// 停止录制
|
||||
void stopRecord();
|
||||
|
||||
private:
|
||||
static SoundRecorder* instance;
|
||||
explicit SoundRecorder(QObject *parent = nullptr);
|
||||
|
||||
QFile soundFile;
|
||||
QAudioSource* audioSource;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
/// 播放语音
|
||||
/////////////////////////////////////////////////
|
||||
public:
|
||||
// 开始播放
|
||||
void startPlay(const QByteArray& content);
|
||||
// 停止播放
|
||||
void stopPlay();
|
||||
|
||||
private:
|
||||
QAudioSink *audioSink;
|
||||
QMediaDevices *outputDevices;
|
||||
QAudioDevice outputDevice;
|
||||
QFile inputFile;
|
||||
|
||||
signals:
|
||||
// 录制完毕后发送这个信号
|
||||
void soundRecordDone(const QString& path);
|
||||
// 播放完毕发送这个信号
|
||||
void soundPlayDone();
|
||||
|
||||
};
|
||||
|
||||
#endif // SOUNDRECORDER_H
|
||||
22
ChatClient/include/toast.h
Normal file
22
ChatClient/include/toast.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QApplication>
|
||||
#include <QScreen>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
|
||||
class Toast : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//此处并不需要指定父窗口,全局通知的父窗口就是桌面
|
||||
Toast(const QString& text);
|
||||
|
||||
//并不需要手动的来new这个对象,而是通过showMessage来弹出
|
||||
static void showMessage(const QString& text);
|
||||
|
||||
};
|
||||
46
ChatClient/include/userinfowidget.h
Normal file
46
ChatClient/include/userinfowidget.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QGridLayout>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "model/data.h"
|
||||
#include "model/datacenter.h"
|
||||
|
||||
//using model::Message;
|
||||
using namespace model;
|
||||
|
||||
class UserInfoWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UserInfoWidget(const UserInfo& userInfo, QWidget *parent);
|
||||
void initSingleSlot();
|
||||
|
||||
void clickDeleteFriendBtn();
|
||||
void clickApplyBtn();
|
||||
|
||||
private:
|
||||
////保存对应的Message对象,暂时先放在这里
|
||||
//Message message;
|
||||
const UserInfo& userInfo;
|
||||
|
||||
QPushButton* avatarBtn;
|
||||
QLabel* idTag;
|
||||
QLabel* idLabel;
|
||||
|
||||
QLabel* nameTag;
|
||||
QLabel* nameLabel;
|
||||
|
||||
QLabel* phoneTag;
|
||||
QLabel* phoneLabel;
|
||||
|
||||
QPushButton* applyBtn;
|
||||
QPushButton* sendMessageBtn;
|
||||
QPushButton* deleteFriendBtn;
|
||||
};
|
||||
32
ChatClient/include/verifycodewidget.h
Normal file
32
ChatClient/include/verifycodewidget.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
class VerifyCodeWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VerifyCodeWidget(QWidget *parent = nullptr);
|
||||
|
||||
//通过这个函数生成随机的验证码字符串
|
||||
QString generateVerifyCode();
|
||||
|
||||
//刷新并把当前验证码显示到界面上
|
||||
void refreshVerifyCode();
|
||||
|
||||
//检验验证码是否匹配
|
||||
bool checkVerifyCode(const QString& verifyCode);
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
//随机数生成器
|
||||
QRandomGenerator randomGenerator;
|
||||
|
||||
//保存验证码的值
|
||||
QString verifyCode = "";
|
||||
signals:
|
||||
};
|
||||
Reference in New Issue
Block a user