重置密码label
当我们在登录忘记密码的时候可以支持重置密码,重置密码label也要实现浮动和点击效果,以及未点击效果。所以我们复用之前的ClickedLabel, 在登录界面中升级forget_label为ClickedLabel。
LoginDialog::LoginDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::LoginDialog)
{
ui->setupUi(this);
connect(ui->reg_btn, &QPushButton::clicked, this, &LoginDialog::switchRegister);
ui->forget_label->SetState("normal","hover","","selected","selected_hover","");
ui->forget_label->setCursor(Qt::PointingHandCursor);
connect(ui->forget_label, &ClickedLabel::clicked, this, &LoginDialog::slot_forget_pwd);
}
点击忘记密码发送对应的信号
void LoginDialog::slot_forget_pwd()
{
qDebug()<<"slot forget pwd";
emit switchReset();
}
我们在mainwindow中连接了重置密码的信号和槽
//连接登录界面忘记密码信号
connect(_login_dlg, &LoginDialog::switchReset, this, &MainWindow::SlotSwitchReset);
实现SlotSwitchReset
void MainWindow::SlotSwitchReset()
{
//创建一个CentralWidget, 并将其设置为MainWindow的中心部件
_reset_dlg = new ResetDialog(this);
_reset_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
setCentralWidget(_reset_dlg);
_login_dlg->hide();
_reset_dlg->show();
//注册返回登录信号和槽函数
connect(_reset_dlg, &ResetDialog::switchLogin, this, &MainWindow::SlotSwitchLogin2);
}
ResetDialog是我们添加的界面类,新建ResetDialog界面类,界面布局如下
重置界面
#include "resetdialog.h"
#include "ui_resetdialog.h"
#include <QDebug>
#include <QRegularExpression>
#include "global.h"
#include "httpmgr.h"
ResetDialog::ResetDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ResetDialog)
{
ui->setupUi(this);
connect(ui->user_edit,&QLineEdit::editingFinished,this,[this](){
checkUserValid();
});
connect(ui->email_edit, &QLineEdit::editingFinished, this, [this](){
checkEmailValid();
});
connect(ui->pwd_edit, &QLineEdit::editingFinished, this, [this](){
checkPassValid();
});
connect(ui->varify_edit, &QLineEdit::editingFinished, this, [this](){
checkVarifyValid();
});
//连接reset相关信号和注册处理回调
initHandlers();
connect(HttpMgr::GetInstance().get(), &HttpMgr::sig_reset_mod_finish, this,
&ResetDialog::slot_reset_mod_finish);
}
下面是检测逻辑
bool ResetDialog::checkUserValid()
{
if(ui->user_edit->text() == ""){
AddTipErr(TipErr::TIP_USER_ERR, tr("用户名不能为空"));
return false;
}
DelTipErr(TipErr::TIP_USER_ERR);
return true;
}
bool ResetDialog::checkPassValid()
{
auto pass = ui->pwd_edit->text();
if(pass.length() < 6 || pass.length()>15){
//提示长度不准确
AddTipErr(TipErr::TIP_PWD_ERR, tr("密码长度应为6~15"));
return false;
}
// 创建一个正则表达式对象,按照上述密码要求
// 这个正则表达式解释:
// ^[a-zA-Z0-9!@#$%^&*]{6,15}$ 密码长度至少6,可以是字母、数字和特定的特殊字符
QRegularExpression regExp("^[a-zA-Z0-9!@#$%^&*]{6,15}$");
bool match = regExp.match(pass).hasMatch();
if(!match){
//提示字符非法
AddTipErr(TipErr::TIP_PWD_ERR, tr("不能包含非法字符"));
return false;;
}
DelTipErr(TipErr::TIP_PWD_ERR);
return true;
}
bool ResetDialog::checkEmailValid()
{
//验证邮箱的地址正则表达式
auto email = ui->email_edit->text();
// 邮箱地址的正则表达式
QRegularExpression regex(R"((\w+)(\.|_)?(\w*)@(\w+)(\.(\w+))+)");
bool match = regex.match(email).hasMatch(); // 执行正则表达式匹配
if(!match){
//提示邮箱不正确
AddTipErr(TipErr::TIP_EMAIL_ERR, tr("邮箱地址不正确"));
return false;
}
DelTipErr(TipErr::TIP_EMAIL_ERR);
return true;
}
bool ResetDialog::checkVarifyValid()
{
auto pass = ui->varify_edit->text();
if(pass.isEmpty()){
AddTipErr(TipErr::TIP_VARIFY_ERR, tr("验证码不能为空"));
return false;
}
DelTipErr(TipErr::TIP_VARIFY_ERR);
return true;
}
void ResetDialog::AddTipErr(TipErr te, QString tips)
{
_tip_errs[te] = tips;
showTip(tips, false);
}
void ResetDialog::DelTipErr(TipErr te)
{
_tip_errs.remove(te);
if(_tip_errs.empty()){
ui->err_tip->clear();
return;
}
showTip(_tip_errs.first(), false);
}
显示接口
void ResetDialog::showTip(QString str, bool b_ok)
{
if(b_ok){
ui->err_tip->setProperty("state","normal");
}else{
ui->err_tip->setProperty("state","err");
}
ui->err_tip->setText(str);
repolish(ui->err_tip);
}
获取验证码
void ResetDialog::on_varify_btn_clicked()
{
qDebug()<<"receive varify btn clicked ";
auto email = ui->email_edit->text();
auto bcheck = checkEmailValid();
if(!bcheck){
return;
}
//发送http请求获取验证码
QJsonObject json_obj;
json_obj["email"] = email;
HttpMgr::GetInstance()->PostHttpReq(QUrl(gate_url_prefix+"/get_varifycode"),
json_obj, ReqId::ID_GET_VARIFY_CODE,Modules::RESETMOD);
}
初始化回包处理逻辑
void ResetDialog::initHandlers()
{
//注册获取验证码回包逻辑
_handlers.insert(ReqId::ID_GET_VARIFY_CODE, [this](QJsonObject jsonObj){
int error = jsonObj["error"].toInt();
if(error != ErrorCodes::SUCCESS){
showTip(tr("参数错误"),false);
return;
}
auto email = jsonObj["email"].toString();
showTip(tr("验证码已发送到邮箱,注意查收"), true);
qDebug()<< "email is " << email ;
});
//注册注册用户回包逻辑
_handlers.insert(ReqId::ID_RESET_PWD, [this](QJsonObject jsonObj){
int error = jsonObj["error"].toInt();
if(error != ErrorCodes::SUCCESS){
showTip(tr("参数错误"),false);
return;
}
auto email = jsonObj["email"].toString();
showTip(tr("重置成功,点击返回登录"), true);
qDebug()<< "email is " << email ;
qDebug()<< "user uuid is " << jsonObj["uuid"].toString();
});
}
根据返回的id调用不同的回报处理逻辑
void ResetDialog::slot_reset_mod_finish(ReqId id, QString res, ErrorCodes err)
{
if(err != ErrorCodes::SUCCESS){
showTip(tr("网络请求错误"),false);
return;
}
// 解析 JSON 字符串,res需转化为QByteArray
QJsonDocument jsonDoc = QJsonDocument::fromJson(res.toUtf8());
//json解析错误
if(jsonDoc.isNull()){
showTip(tr("json解析错误"),false);
return;
}
//json解析错误
if(!jsonDoc.isObject()){
showTip(tr("json解析错误"),false);
return;
}
//调用对应的逻辑,根据id回调。
_handlers[id](jsonDoc.object());
return;
}
这里实现发送逻辑
void ResetDialog::on_sure_btn_clicked()
{
bool valid = checkUserValid();
if(!valid){
return;
}
valid = checkEmailValid();
if(!valid){
return;
}
valid = checkPassValid();
if(!valid){
return;
}
valid = checkVarifyValid();
if(!valid){
return;
}
//发送http重置用户请求
QJsonObject json_obj;
json_obj["user"] = ui->user_edit->text();
json_obj["email"] = ui->email_edit->text();
json_obj["passwd"] = xorString(ui->pwd_edit->text());
json_obj["varifycode"] = ui->varify_edit->text();
HttpMgr::GetInstance()->PostHttpReq(QUrl(gate_url_prefix+"/reset_pwd"),
json_obj, ReqId::ID_RESET_PWD,Modules::RESETMOD);
}
注册、重置、登录切换
我们要实现注册、重置、登录三个界面的替换,就需要在MainWindow中添加SlotSwitchLogin2的实现
//从重置界面返回登录界面
void MainWindow::SlotSwitchLogin2()
{
//创建一个CentralWidget, 并将其设置为MainWindow的中心部件
_login_dlg = new LoginDialog(this);
_login_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
setCentralWidget(_login_dlg);
_reset_dlg->hide();
_login_dlg->show();
//连接登录界面忘记密码信号
connect(_login_dlg, &LoginDialog::switchReset, this, &MainWindow::SlotSwitchReset);
//连接登录界面注册信号
connect(_login_dlg, &LoginDialog::switchRegister, this, &MainWindow::SlotSwitchReg);
}
服务端响应重置
在LogicSystem的构造函数中增加注册逻辑
//重置回调逻辑
RegPost("/reset_pwd", [](std::shared_ptr<HttpConnection> connection) {
auto body_str = boost::beast::buffers_to_string(connection->_request.body().data());
std::cout << "receive body is " << body_str << std::endl;
connection->_response.set(http::field::content_type, "text/json");
Json::Value root;
Json::Reader reader;
Json::Value src_root;
bool parse_success = reader.parse(body_str, src_root);
if (!parse_success) {
std::cout << "Failed to parse JSON data!" << std::endl;
root["error"] = ErrorCodes::Error_Json;
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
}
auto email = src_root["email"].asString();
auto name = src_root["user"].asString();
auto pwd = src_root["passwd"].asString();
//先查找redis中email对应的验证码是否合理
std::string varify_code;
bool b_get_varify = RedisMgr::GetInstance()->Get(CODEPREFIX + src_root["email"].asString(), varify_code);
if (!b_get_varify) {
std::cout << " get varify code expired" << std::endl;
root["error"] = ErrorCodes::VarifyExpired;
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
}
if (varify_code != src_root["varifycode"].asString()) {
std::cout << " varify code error" << std::endl;
root["error"] = ErrorCodes::VarifyCodeErr;
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
}
//查询数据库判断用户名和邮箱是否匹配
bool email_valid = MysqlMgr::GetInstance()->CheckEmail(name, email);
if (!email_valid) {
std::cout << " user email not match" << std::endl;
root["error"] = ErrorCodes::EmailNotMatch;
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
}
//更新密码为最新密码
bool b_up = MysqlMgr::GetInstance()->UpdatePwd(name, pwd);
if (!b_up) {
std::cout << " update pwd failed" << std::endl;
root["error"] = ErrorCodes::PasswdUpFailed;
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
}
std::cout << "succeed to update password" << pwd << std::endl;
root["error"] = 0;
root["email"] = email;
root["user"] = name;
root["passwd"] = pwd;
root["varifycode"] = src_root["varifycode"].asString();
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
});
在Mysql中新增CheckEmail和UpdatePwd函数
bool MysqlMgr::CheckEmail(const std::string& name, const std::string& email) {
return _dao.CheckEmail(name, email);
}
bool MysqlMgr::UpdatePwd(const std::string& name, const std::string& pwd) {
return _dao.UpdatePwd(name, pwd);
}
DAO这一层写具体的逻辑, 检测邮箱是否合理
bool MysqlDao::CheckEmail(const std::string& name, const std::string& email) {
auto con = pool_->getConnection();
try {
if (con == nullptr) {
pool_->returnConnection(std::move(con));
return false;
}
// 准备查询语句
std::unique_ptr<sql::PreparedStatement> pstmt(con->prepareStatement("SELECT email FROM user WHERE name = ?"));
// 绑定参数
pstmt->setString(1, name);
// 执行查询
std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
// 遍历结果集
while (res->next()) {
std::cout << "Check Email: " << res->getString("email") << std::endl;
if (email != res->getString("email")) {
pool_->returnConnection(std::move(con));
return false;
}
pool_->returnConnection(std::move(con));
return true;
}
}
catch (sql::SQLException& e) {
pool_->returnConnection(std::move(con));
std::cerr << "SQLException: " << e.what();
std::cerr << " (MySQL error code: " << e.getErrorCode();
std::cerr << ", SQLState: " << e.getSQLState() << " )" << std::endl;
return false;
}
}
更新密码
bool MysqlDao::UpdatePwd(const std::string& name, const std::string& newpwd) {
auto con = pool_->getConnection();
try {
if (con == nullptr) {
pool_->returnConnection(std::move(con));
return false;
}
// 准备查询语句
std::unique_ptr<sql::PreparedStatement> pstmt(con->prepareStatement("UPDATE user SET pwd = ? WHERE name = ?"));
// 绑定参数
pstmt->setString(2, name);
pstmt->setString(1, newpwd);
// 执行更新
int updateCount = pstmt->executeUpdate();
std::cout << "Updated rows: " << updateCount << std::endl;
pool_->returnConnection(std::move(con));
return true;
}
catch (sql::SQLException& e) {
pool_->returnConnection(std::move(con));
std::cerr << "SQLException: " << e.what();
std::cerr << " (MySQL error code: " << e.getErrorCode();
std::cerr << ", SQLState: " << e.getSQLState() << " )" << std::endl;
return false;
}
}