登录验证
从json
对象中获取用户ID和密码,并在数据库中查询获取用户信息。
int id = js["id"].get<int>();
std::string password = js["password"];
如果用户已经登录过,即state == "online"
,则返回错误信息。
if (user.getId() == id && user.getPassword() == password)
{
if (user.getState() == "online")
{
// 该用户已经登录,不能重复登录
json response;
response["msgid"] = LOGIN_MSG_ACK;
response["errno"] = 2;
response["errmsg"] = "this account is using, input another!";
conn->send(response.dump());
}
else
{
// ...
}
}
登录成功后,需要在用户表_userConnMap<ID, TcpConnection>
中记录新登录的用户。考虑到多线程对此map
进行操作,我们需要使用互斥锁。
// 登录成功,记录用户连接信息
// 需要考虑线程安全问题 onMessage会在不同线程中被调用
{
lock_guard<mutex> lock(_connMutex);
_userConnMap.insert({id, conn});
}
lock_guard
遵守RALL
手法,初始化即上锁,销毁即解锁。这里用临时作用域让锁得颗粒度变小,保证性能。
查询离线消息
我们还需要查询该用户离线时是否有离线消息,如果有离线消息是要取出放到vector<string>
容器中得。我们可以使用 JSON 直接与 STL 容器进行操作。
// 查询该用户是否有离线消息
std::vector<std::string> vec = _offlineMsgModel.query(id);
if (!vec.empty())
{
response["offlinemsg"] = vec;
// 读取该用户的离线消息后,将该用户离线消息删除掉
_offlineMsgModel.remove(id);
}
显示好友列表
我们还需要显示这个登录用户得好友列表,在 MySQL 中记录着每个用户 ID 的好友名单。我们需要取出来,并且加入到 JSON 对象中。
std::vector<User> userVec = _friendModel.query(id);
if (!userVec.empty())
{
std::vector<std::string> vec;
for (auto& user : userVec)
{
json js;
js["id"] = user.getId();
js["name"] = user.getName();
js["state"] = user.getState();
vec.push_back(js.dump());
}
response["friends"] = vec;
}
conn->send(response.dump());
登录模块完整代码
#include "chatservice.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h>
/**
* 登录业务
* 从json得到用户id
* 从数据中获取此id的用户,判断此用户的密码是否等于json获取到的密码
* 判断用户是否重复登录
* {"msgid":1,"id":13,"password":"123456"}
* {"errmsg":"this account is using, input another!","errno":2,"msgid":2}
*/
void ChatService::loginHandler(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
LOG_DEBUG << "do login service!";
int id = js["id"].get<int>();
std::string password = js["password"];
User user = _userModel.query(id);
if (user.getId() == id && user.getPassword() == password)
{
if (user.getState() == "online")
{
// 该用户已经登录,不能重复登录
json response;
response["msgid"] = LOGIN_MSG_ACK;
response["errno"] = 2;
response["errmsg"] = "this account is using, input another!";
conn->send(response.dump());
}
else
{
// 登录成功,记录用户连接信息
// 需要考虑线程安全问题 onMessage会在不同线程中被调用
{
lock_guard<mutex> lock(_connMutex);
_userConnMap.insert({id, conn});
}
// id用户登录成功后,向redis订阅channel(id)
// _redis.subscribe(id);
// 登录成功,更新用户状态信息 state offline => online
user.setState("online");
_userModel.updateState(user);
json response;
response["msgid"] = LOGIN_MSG_ACK;
response["errno"] = 0;
response["id"] = user.getId();
response["name"] = user.getName();
// 查询该用户是否有离线消息
std::vector<std::string> vec = _offlineMsgModel.query(id);
if (!vec.empty())
{
response["offlinemsg"] = vec;
// 读取该用户的离线消息后,将该用户离线消息删除掉
_offlineMsgModel.remove(id);
}
else
{
LOG_INFO << "无离线消息";
}
std::vector<User> userVec = _friendModel.query(id);
if (!userVec.empty())
{
std::vector<std::string> vec;
for (auto& user : userVec)
{
json js;
js["id"] = user.getId();
js["name"] = user.getName();
js["state"] = user.getState();
vec.push_back(js.dump());
}
response["friends"] = vec;
}
conn->send(response.dump());
}
}
}
用户登录模块测试
可以看到返回的错误码:errno = 0
表示成功,并在里面返回了好友列表的JSON
信息。
数据库查看:可以看到zhang san
是登录的状态了