聊天服务器中,不仅要实现不同用户之间聊天,还要实现群聊。
今天我们就一起来实现一下群组相关的业务吧~
一、群组业务介绍
1.1 数据库表及对应类
底层数据库中,除了有用户表User、好友表Friend、离线消息表OfflineMessage表,还有群组及群组用户表
群组表Allgroup存放所有群组的群号,群组名及群组描述
群组用户表GroupUser存放群号及对应群内组员编号和群内组员角色
为了操作群组表,我们同User类对象一致,定义Group类
在include/server/model创建group.hpp头文件,定义Group类
#ifndef GROUP_H
#define GROUP_H
#include "groupuser.hpp"
#include <string>
#include <vector>
using namespace std;
// Group表的ORM类
class Group
{
public:
Group(int id = -1, string name = "", string desc = "")
{
this->id = id;
this->name = name;
this->desc = desc;
}
void setId(int id) { this->id = id; }
void setName(string name) { this->name = name; }
void setDesc(string desc) { this->desc = desc; }
int getId() { return this->id; }
string getName() { return this->name; }
string getDesc() { return this->desc; }
vector<GroupUser> &getUsers() { return this->users; }
private:
int id; // 群组id
string name; // 群组名称
string desc; // 群组描述
vector<GroupUser> users; // 群组用户数组
};
#endif
其中GroupUser类继承自User类(毕竟群组成员也是用户),users代表对应群组的群组成员列表
在include/server/model创建groupuser.hpp头文件,定义GroupUser类
#ifndef GROUPUSER_H
#define GROUPUSER_H
#include "user.hpp"
// 群组用户,多了一个role角色信息,从User类直接继承,复用User的其它信息
class GroupUser : public User
{
public:
void setRole(string role) { this->role = role; }
string getRole() { return this->role; }
private:
string role; //群组用户角色,分为creator 与 normal
};
#endif
在User类基础上,GroupUser多了一个属性,名为群组角色,分为创建者creator与普通群成员
在include/server/model创建groupmodel.hpp头文件,定义GroupModel类操作群组
#ifndef GROUPMODEL_H
#define GROUPMODEL_H
#include "group.hpp"
#include <string>
#include <vector>
using namespace std;
// 维护群组信息的操作接口方法
class GroupModel
{
public:
// 创建群组
bool createGroup(Group &group);
// 加入群组
bool addGroup(int userid, int groupid, string role);
// 查询用户所在群组信息
vector<Group> queryGroups(int userid);
//群聊,除用户userid自身
vector<int> queryGroupUsers(int userid, int groupid);
};
#endif
其中有创建群组,加入群组,查询用户所在群组信息及群聊函数
在src/server/model创建groupmodel.cpp源文件,实现GroupModel类
类似于向数据库中插入用户,这里向数据库中插入群组,即创建群组
// 创建群组
bool GroupModel::createGroup(Group &group)
{
char sql[1024] = {0};
sprintf(sql, "insert into allgroup(groupname, groupdesc) values('%s', '%s')",
group.getName().c_str(), group.getDesc().c_str()); // 向群组表中插入群组名和群组描述信息
MySQL mysql;
if (mysql.connect())
{
if (mysql.update(sql))
{
group.setId(mysql_insert_id(mysql.getConnection())); // 数据库自动生成id号,设置群组id
return true;
}
}
return false;
}
群组创建好后,成员欲加入群组,实现函数
// 加入群组
bool GroupModel::addGroup(int userid, int groupid, string role)
{
char sql[1024] = {0};
sprintf(sql, "insert into groupuser values(%d, %d, '%s')", groupid, userid, role.c_str());
MySQL mysql;
if (mysql.connect())
{
mysql.update(sql);
return true;
}
return false;
}
一个用户可以属于多个群组,一个群组同样包含多个用户,是多对多的关系
因此,我们要
1. 先根据userid在groupuser表中查询出该用户所属的群组信息
2. 再根据群组信息,查询属于该群组的所有用户的userid,并且和user表进行多表联合查询,查出用户的详细信息,反馈至对应群组的用户表users中
// 查询用户所在群组信息
vector<Group> GroupModel::queryGroups(int userid)
{
/*
1. 先根据userid在groupuser表中查询出该用户所属的群组信息
2. 再根据群组信息,查询属于该群组的所有用户的userid,并且和user表进行多表联合查询,查出用户的详细信息
*/
char sql[1024] = {0};
sprintf(sql, "select a.id,a.groupname,a.groupdesc from allgroup a inner join \
groupuser b on a.id = b.groupid where b.userid=%d",
userid); // 多表联合查询,获取该用户所属的所有群组信息
vector<Group> groupVec; // 定义数组存放群组对象
MySQL mysql;
if (mysql.connect())
{
MYSQL_RES *res = mysql.query(sql);
if (res != nullptr)
{
MYSQL_ROW row;
// 查出userid所有的群组信息
while ((row = mysql_fetch_row(res)) != nullptr)
{
Group group;
group.setId(atoi(row[0]));
group.setName(row[1]);
group.setDesc(row[2]);
groupVec.push_back(group);
}
mysql_free_result(res);
}
}
// 查询群组的用户信息
for (Group &group : groupVec)
{
sprintf(sql, "select a.id,a.name,a.state,b.grouprole from user a \
inner join groupuser b on b.userid = a.id where b.groupid=%d",
group.getId()); // 多表联合查询,获取不同群组中所有用户信息,并添加至对应群组的用户数组中
MYSQL_RES *res = mysql.query(sql);
if (res != nullptr)
{
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)) != nullptr)
{
GroupUser user;
user.setId(atoi(row[0]));
user.setName(row[1]);
user.setState(row[2]);
user.setRole(row[3]);
group.getUsers().push_back(user); // 调用getUsers函数获取群组用户数组对象
}
mysql_free_result(res);
}
}
return groupVec;
}
群组聊天类似于点对点聊天,点对点向指定id用户发送消息即可,离线存储离线消息表中
群组聊天发送给所属群内所有用户,除自身userid之外,类似操作如下
// 群聊,除用户userid自身
vector<int> GroupModel::queryGroupUsers(int userid, int groupid)
{
char sql[1024] = {0};
sprintf(sql, "select userid from groupuser where groupid = %d and userid != %d", groupid, userid); // 获取群组中除自身userid之外的所有用户id
vector<int> idVec; // 定义数组存放群组用户id
MySQL mysql;
if (mysql.connect())
{
MYSQL_RES *res = mysql.query(sql);
if (res != nullptr)
{
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)) != nullptr)
{
idVec.push_back(atoi(row[0]));
}
mysql_free_result(res);
}
}
return idVec;
}
因此,groupmodel.cpp源文件如下:
#include "groupmodel.hpp"
#include "db.hpp"
#include <vector>
// 创建群组
bool GroupModel::createGroup(Group &group)
{
char sql[1024] = {0};
sprintf(sql, "insert into allgroup(groupname, groupdesc) values('%s', '%s')",
group.getName().c_str(), group.getDesc().c_str()); // 向群组表中插入群组名和群组描述信息
MySQL mysql;
if (mysql.connect())
{
if (mysql.update(sql))
{
group.setId(mysql_insert_id(mysql.getConnection())); // 数据库自动生成id号,设置群组id
return true;
}
}
return false;
}
// 加入群组
bool GroupModel::addGroup(int userid, int groupid, string role)
{
char sql[1024] = {0};
sprintf(sql, "insert into groupuser values(%d, %d, '%s')", groupid, userid, role.c