集群服务器项目(四)
本章我们实现群组创建、添加群组、群组聊天等功能,首先我们先看下和群组相关的表结构
allgroup表
groupuser表
群组创建
首先代码如下
// 创建群组
bool GroupModel::createGroup(Group &group)
{
// 组装sql语句
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))
{
// 主键id是自增的
group.setId(mysql_insert_id(mysql.getConnection()));
return true;
}
}
return false;
}
提供一个Group对象,然后通过对象进行创建群组,根据Group表的设计,Group对象的结构如下:
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;
string name;
string desc;
// 存储这个组里面的成员
vector<GroupUser> users;
};
加入群组
注意加入群组需要设置用户的角色
void GroupModel::addGroup(int userid, int groupid, string role)
{
// 组装sql语句
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);
}
}
其中string role就是表明以什么身份加入群组(creator normal)
群组查询
根据用户id来查询所在群组,同时查询每个群组都有哪些成员。注意一个用户可能存在多个群组内
// 查询用户所在群组信息,因为一个用户可能存在于多个群组中,这里输出的是,一个用户的哪些群组,同时每个群组有哪些成员
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;
// select a.id,a.groupname,a.groupdesc 第一个是id,第二个是groupname,第三个是groupdesc
group.setId(atoi(row[0]));
group.setName(row[1]);
group.setDesc(row[2]);
groupVec.emplace_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().emplace_back(user);
}
mysql_free_result(res);
}
}
return groupVec;
}
群组聊天
格式为:userid->groupid,因此需要根据groupid查询群组里面有哪些成员,然后向这些成员(除了自己)发送消息,同时注意群成员是否在线,若不在线,将消息存放到离线消息表中,代码如下:
// 群组聊天业务
void ChatService::groupChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
int userid = js["id"].get<int>();
int groupid = js["groupid"].get<int>();
vector<int> useridVec = _groupmodel.queryGroupUsers(userid, groupid);
lock_guard<mutex> lock(_connMutex);
for (int id : useridVec)
{
auto it = _userConnMap.find(id);
if (it != _userConnMap.end())
{
// 转发消息
it->second->send(js.dump());
}
else
{
// 存储离线群消息
_offlinemodel.insert(id, js.dump());
}
}
}
// 根据指定的groupid查询群组用户id列表,除了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);
vector<int> idVec;
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.emplace_back(atoi(row[0]));
}
mysql_free_result(res);
}
}
return idVec;
}
到此我们就完成了服务器的开发,下一节我们开始开发客户端。