[集群聊天服务器]----(八)群组类、群组操作接口以及业务模块之创建群组,加入群组以及群组聊天

接着上文关于[集群聊天服务器]----(七)业务模块之一对一聊天、添加好友函数、好友类以及离线消息类的剖析。本章将对创建群组,加入群组以及群组聊天业务进行剖析。

群类

类似于User类,构建了Group类

#ifndef GROUP_H
#define GROUP_H

#include "groupuser.hpp"
#include <string>
#include <vector>
using namespace std;

//User表的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=desc;}  //组功能描述
    vector<GroupUser> &getUsers() {return this->users;}


private:
    int id;
    string name;
    string desc;
    vector<GroupUser> users; //组成员
};

#endif
  • 群的id初始默认为-1,设置/获取群id,群名称以及群用途函数

GroupUser类

GroupUser为群组用户,继承了User类,多了一个role角色信息,从User类直接继承,复用User的其他信息

void setRole(string role) {this->role = role;}
string getRole() {return this->role;}

//派生类的特殊变量 role角色
string role;

群组信息的操作接口

// 创建群组
bool createGroup(Group &group);

// 加入群组
void addGroup(int userid, int groupid, string role);

// 查询用户所在群组信息
vector<Group> queryGroups(int userid);

// 根据指定的groupid查询群组用户id列表,除userid自己,主要用户群聊业务给群组其它成员群发消息
vector<int> queryGroupUsers(int userid, int groupid);

创建群组函数

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()));
            return true;
        }
    }
    return false;
}
  • 组装sql语句,根据群名称,群作用在allgroup表插入相关消息;
  • 连接数据库,并进行更新,并返回群id

加入群组函数

void 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);
    }
}
  • 组装sql语句,根据群id,用户id,以及群内角色在allgroup表插入相关消息;
  • 连接数据库,并进行更新,并返回群id

查询用户所在群组信息

vector<Group> GroupModel::queryGroups(int userid)
{
    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 ;
            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);
            }

            mysql_free_result(res);
        }
        
    }
    return groupVec;
}
  • 组装sql语句,根据用户id在allgroup表和groupuser表中进联合查找,寻找用户所在的组;
  • 根据sql语句,调用MySQL::query()语句进行查找好友,然后调用mysql_fetch_row()函数,查找对应的行,row是MYSQL_ROW类型,可以根据下标找到对应的值,并把用户对应的组Group类放入groupVec中;
  • 根据groupVec中,对组内的成员姓名,id以及状态和群内角色进行查找,对应的GroupUser类进行返回
  • 注意释放资源

查找群内除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.push_back(atoi(row[0]));
            }

            mysql_free_result(res);
        }
    }
    return idVec;
}
  • 组装sql语句,根据userid在groupuser表查找其余用户id;
  • 根据sql语句,调用MySQL::query()语句进行查找好友,然后调用mysql_fetch_row()函数,查找对应的行,row是MYSQL_ROW类型,可以根据下标找到对应的值,并把用户放入idVec中返回;
  • 注意释放资源

创建群组

当客户端输入msgid=CREATE_GROUP_MSG时,ChatService::ChatService()中会回调ChatService::createGroup()函数进行处理:

void ChatService::createGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    string name = js["groupname"];
    string desc = js["groupdesc"];

    // 存储新创建的群组信息
    Group group(-1, name, desc);
    if (_groupModel.createGroup(group))
    {
        // 存储群组创建人信息
        _groupModel.addGroup(userid, group.getId(), "creator");
    }
}
  • 通过 JSON 对象反序列结果,寻找用户id userid对应的值,groupname以及groupdesc
  • 创建Group类,并调用 GroupModel::createGroup()创建群,角色为creator;
  • 根据创建的群组,设置群id

加入群组

当客户端输入msgid=ADD_GROUP_MSG时,ChatService::ChatService()中会回调ChatService::addGroup()函数进行处理:

void ChatService::addGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    _groupModel.addGroup(userid, groupid, "normal");

}
  • 根据userid以及要加入的groupid,调用GroupModel::addGroup()函数加入群,角色默认为normal;

群组聊天

当客户端输入msgid=GROUP_CHAT_MSG时,ChatService::ChatService()中会回调ChatService::groupChat()函数进行处理:

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())
        {
            // id 在线 转发消息 服务器主动推送消息给toid用户
            it->second->send(js.dump());
        }
        else
        {
            User user = _userModel.query(id);
            //在另一台电脑上
            if (user.getState() == "online")
            {
                _redis.publish(id, js.dump());
            }
            else
            {
                // id 不在线 存储离线消息
                _offlineMsgModel.insert(id, js.dump());
            }        
        }
    }
}
  • 通过 JSON 对象反序列结果,寻找用户id以及群id对应的值,找到用户想要对话的群,并在_groupModel中进行查找群内其余成员,并放入useridVec中;
  • _userConnMap查找用户是否存在;
  • 如果用户在一台主机并且处于在线状态,就发送想要发送的消息;
  • 如果不在同一台主机,根据id调用_userModel在user表中进行查看对方是否在线,如果在线就通过redis发布消息
  • 不在线就存储其离线消息

好了~ 关于群组类、群组操作接口以及业务模块之创建群组,加入群组以及群组聊天的剖析就到此结束了,服务端的开发也到此结束了,接下来我们会剖析客户端的实现,下一节见~

  • 21
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值