聊天项目(13) 重置密码功能

重置密码label

当我们在登录忘记密码的时候可以支持重置密码,重置密码label也要实现浮动和点击效果,以及未点击效果。所以我们复用之前的ClickedLabel,
在登录界面中升级forget_label为ClickedLabel。

  1. LoginDialog::LoginDialog(QWidget *parent) :
  2. QDialog(parent),
  3. ui(new Ui::LoginDialog)
  4. {
  5. ui->setupUi(this);
  6. connect(ui->reg_btn, &QPushButton::clicked, this, &LoginDialog::switchRegister);
  7. ui->forget_label->SetState("normal","hover","","selected","selected_hover","");
  8. ui->forget_label->setCursor(Qt::PointingHandCursor);
  9. connect(ui->forget_label, &ClickedLabel::clicked, this, &LoginDialog::slot_forget_pwd);
  10. }

点击忘记密码发送对应的信号

  1. void LoginDialog::slot_forget_pwd()
  2. {
  3. qDebug()<<"slot forget pwd";
  4. emit switchReset();
  5. }

我们在mainwindow中连接了重置密码的信号和槽

  1. //连接登录界面忘记密码信号
  2. connect(_login_dlg, &LoginDialog::switchReset, this, &MainWindow::SlotSwitchReset);

实现SlotSwitchReset

  1. void MainWindow::SlotSwitchReset()
  2. {
  3. //创建一个CentralWidget, 并将其设置为MainWindow的中心部件
  4. _reset_dlg = new ResetDialog(this);
  5. _reset_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
  6. setCentralWidget(_reset_dlg);
  7. _login_dlg->hide();
  8. _reset_dlg->show();
  9. //注册返回登录信号和槽函数
  10. connect(_reset_dlg, &ResetDialog::switchLogin, this, &MainWindow::SlotSwitchLogin2);
  11. }

ResetDialog是我们添加的界面类,新建ResetDialog界面类,界面布局如下

https://cdn.llfc.club/1712826184149.jpg

重置界面

  1. #include "resetdialog.h"
  2. #include "ui_resetdialog.h"
  3. #include <QDebug>
  4. #include <QRegularExpression>
  5. #include "global.h"
  6. #include "httpmgr.h"
  7. ResetDialog::ResetDialog(QWidget *parent) :
  8. QDialog(parent),
  9. ui(new Ui::ResetDialog)
  10. {
  11. ui->setupUi(this);
  12. connect(ui->user_edit,&QLineEdit::editingFinished,this,[this](){
  13. checkUserValid();
  14. });
  15. connect(ui->email_edit, &QLineEdit::editingFinished, this, [this](){
  16. checkEmailValid();
  17. });
  18. connect(ui->pwd_edit, &QLineEdit::editingFinished, this, [this](){
  19. checkPassValid();
  20. });
  21. connect(ui->varify_edit, &QLineEdit::editingFinished, this, [this](){
  22. checkVarifyValid();
  23. });
  24. //连接reset相关信号和注册处理回调
  25. initHandlers();
  26. connect(HttpMgr::GetInstance().get(), &HttpMgr::sig_reset_mod_finish, this,
  27. &ResetDialog::slot_reset_mod_finish);
  28. }

下面是检测逻辑

  1. bool ResetDialog::checkUserValid()
  2. {
  3. if(ui->user_edit->text() == ""){
  4. AddTipErr(TipErr::TIP_USER_ERR, tr("用户名不能为空"));
  5. return false;
  6. }
  7. DelTipErr(TipErr::TIP_USER_ERR);
  8. return true;
  9. }
  10. bool ResetDialog::checkPassValid()
  11. {
  12. auto pass = ui->pwd_edit->text();
  13. if(pass.length() < 6 || pass.length()>15){
  14. //提示长度不准确
  15. AddTipErr(TipErr::TIP_PWD_ERR, tr("密码长度应为6~15"));
  16. return false;
  17. }
  18. // 创建一个正则表达式对象,按照上述密码要求
  19. // 这个正则表达式解释:
  20. // ^[a-zA-Z0-9!@#$%^&*]{6,15}$ 密码长度至少6,可以是字母、数字和特定的特殊字符
  21. QRegularExpression regExp("^[a-zA-Z0-9!@#$%^&*]{6,15}$");
  22. bool match = regExp.match(pass).hasMatch();
  23. if(!match){
  24. //提示字符非法
  25. AddTipErr(TipErr::TIP_PWD_ERR, tr("不能包含非法字符"));
  26. return false;;
  27. }
  28. DelTipErr(TipErr::TIP_PWD_ERR);
  29. return true;
  30. }
  31. bool ResetDialog::checkEmailValid()
  32. {
  33. //验证邮箱的地址正则表达式
  34. auto email = ui->email_edit->text();
  35. // 邮箱地址的正则表达式
  36. QRegularExpression regex(R"((\w+)(\.|_)?(\w*)@(\w+)(\.(\w+))+)");
  37. bool match = regex.match(email).hasMatch(); // 执行正则表达式匹配
  38. if(!match){
  39. //提示邮箱不正确
  40. AddTipErr(TipErr::TIP_EMAIL_ERR, tr("邮箱地址不正确"));
  41. return false;
  42. }
  43. DelTipErr(TipErr::TIP_EMAIL_ERR);
  44. return true;
  45. }
  46. bool ResetDialog::checkVarifyValid()
  47. {
  48. auto pass = ui->varify_edit->text();
  49. if(pass.isEmpty()){
  50. AddTipErr(TipErr::TIP_VARIFY_ERR, tr("验证码不能为空"));
  51. return false;
  52. }
  53. DelTipErr(TipErr::TIP_VARIFY_ERR);
  54. return true;
  55. }
  56. void ResetDialog::AddTipErr(TipErr te, QString tips)
  57. {
  58. _tip_errs[te] = tips;
  59. showTip(tips, false);
  60. }
  61. void ResetDialog::DelTipErr(TipErr te)
  62. {
  63. _tip_errs.remove(te);
  64. if(_tip_errs.empty()){
  65. ui->err_tip->clear();
  66. return;
  67. }
  68. showTip(_tip_errs.first(), false);
  69. }

显示接口

  1. void ResetDialog::showTip(QString str, bool b_ok)
  2. {
  3. if(b_ok){
  4. ui->err_tip->setProperty("state","normal");
  5. }else{
  6. ui->err_tip->setProperty("state","err");
  7. }
  8. ui->err_tip->setText(str);
  9. repolish(ui->err_tip);
  10. }

获取验证码

  1. void ResetDialog::on_varify_btn_clicked()
  2. {
  3. qDebug()<<"receive varify btn clicked ";
  4. auto email = ui->email_edit->text();
  5. auto bcheck = checkEmailValid();
  6. if(!bcheck){
  7. return;
  8. }
  9. //发送http请求获取验证码
  10. QJsonObject json_obj;
  11. json_obj["email"] = email;
  12. HttpMgr::GetInstance()->PostHttpReq(QUrl(gate_url_prefix+"/get_varifycode"),
  13. json_obj, ReqId::ID_GET_VARIFY_CODE,Modules::RESETMOD);
  14. }

初始化回包处理逻辑

  1. void ResetDialog::initHandlers()
  2. {
  3. //注册获取验证码回包逻辑
  4. _handlers.insert(ReqId::ID_GET_VARIFY_CODE, [this](QJsonObject jsonObj){
  5. int error = jsonObj["error"].toInt();
  6. if(error != ErrorCodes::SUCCESS){
  7. showTip(tr("参数错误"),false);
  8. return;
  9. }
  10. auto email = jsonObj["email"].toString();
  11. showTip(tr("验证码已发送到邮箱,注意查收"), true);
  12. qDebug()<< "email is " << email ;
  13. });
  14. //注册注册用户回包逻辑
  15. _handlers.insert(ReqId::ID_RESET_PWD, [this](QJsonObject jsonObj){
  16. int error = jsonObj["error"].toInt();
  17. if(error != ErrorCodes::SUCCESS){
  18. showTip(tr("参数错误"),false);
  19. return;
  20. }
  21. auto email = jsonObj["email"].toString();
  22. showTip(tr("重置成功,点击返回登录"), true);
  23. qDebug()<< "email is " << email ;
  24. qDebug()<< "user uuid is " << jsonObj["uuid"].toString();
  25. });
  26. }

根据返回的id调用不同的回报处理逻辑

  1. void ResetDialog::slot_reset_mod_finish(ReqId id, QString res, ErrorCodes err)
  2. {
  3. if(err != ErrorCodes::SUCCESS){
  4. showTip(tr("网络请求错误"),false);
  5. return;
  6. }
  7. // 解析 JSON 字符串,res需转化为QByteArray
  8. QJsonDocument jsonDoc = QJsonDocument::fromJson(res.toUtf8());
  9. //json解析错误
  10. if(jsonDoc.isNull()){
  11. showTip(tr("json解析错误"),false);
  12. return;
  13. }
  14. //json解析错误
  15. if(!jsonDoc.isObject()){
  16. showTip(tr("json解析错误"),false);
  17. return;
  18. }
  19. //调用对应的逻辑,根据id回调。
  20. _handlers[id](jsonDoc.object());
  21. return;
  22. }

这里实现发送逻辑

  1. void ResetDialog::on_sure_btn_clicked()
  2. {
  3. bool valid = checkUserValid();
  4. if(!valid){
  5. return;
  6. }
  7. valid = checkEmailValid();
  8. if(!valid){
  9. return;
  10. }
  11. valid = checkPassValid();
  12. if(!valid){
  13. return;
  14. }
  15. valid = checkVarifyValid();
  16. if(!valid){
  17. return;
  18. }
  19. //发送http重置用户请求
  20. QJsonObject json_obj;
  21. json_obj["user"] = ui->user_edit->text();
  22. json_obj["email"] = ui->email_edit->text();
  23. json_obj["passwd"] = xorString(ui->pwd_edit->text());
  24. json_obj["varifycode"] = ui->varify_edit->text();
  25. HttpMgr::GetInstance()->PostHttpReq(QUrl(gate_url_prefix+"/reset_pwd"),
  26. json_obj, ReqId::ID_RESET_PWD,Modules::RESETMOD);
  27. }

注册、重置、登录切换

我们要实现注册、重置、登录三个界面的替换,就需要在MainWindow中添加SlotSwitchLogin2的实现

  1. //从重置界面返回登录界面
  2. void MainWindow::SlotSwitchLogin2()
  3. {
  4. //创建一个CentralWidget, 并将其设置为MainWindow的中心部件
  5. _login_dlg = new LoginDialog(this);
  6. _login_dlg->setWindowFlags(Qt::CustomizeWindowHint|Qt::FramelessWindowHint);
  7. setCentralWidget(_login_dlg);
  8. _reset_dlg->hide();
  9. _login_dlg->show();
  10. //连接登录界面忘记密码信号
  11. connect(_login_dlg, &LoginDialog::switchReset, this, &MainWindow::SlotSwitchReset);
  12. //连接登录界面注册信号
  13. connect(_login_dlg, &LoginDialog::switchRegister, this, &MainWindow::SlotSwitchReg);
  14. }

服务端响应重置

在LogicSystem的构造函数中增加注册逻辑

  1. //重置回调逻辑
  2. RegPost("/reset_pwd", [](std::shared_ptr<HttpConnection> connection) {
  3. auto body_str = boost::beast::buffers_to_string(connection->_request.body().data());
  4. std::cout << "receive body is " << body_str << std::endl;
  5. connection->_response.set(http::field::content_type, "text/json");
  6. Json::Value root;
  7. Json::Reader reader;
  8. Json::Value src_root;
  9. bool parse_success = reader.parse(body_str, src_root);
  10. if (!parse_success) {
  11. std::cout << "Failed to parse JSON data!" << std::endl;
  12. root["error"] = ErrorCodes::Error_Json;
  13. std::string jsonstr = root.toStyledString();
  14. beast::ostream(connection->_response.body()) << jsonstr;
  15. return true;
  16. }
  17. auto email = src_root["email"].asString();
  18. auto name = src_root["user"].asString();
  19. auto pwd = src_root["passwd"].asString();
  20. //先查找redis中email对应的验证码是否合理
  21. std::string varify_code;
  22. bool b_get_varify = RedisMgr::GetInstance()->Get(CODEPREFIX + src_root["email"].asString(), varify_code);
  23. if (!b_get_varify) {
  24. std::cout << " get varify code expired" << std::endl;
  25. root["error"] = ErrorCodes::VarifyExpired;
  26. std::string jsonstr = root.toStyledString();
  27. beast::ostream(connection->_response.body()) << jsonstr;
  28. return true;
  29. }
  30. if (varify_code != src_root["varifycode"].asString()) {
  31. std::cout << " varify code error" << std::endl;
  32. root["error"] = ErrorCodes::VarifyCodeErr;
  33. std::string jsonstr = root.toStyledString();
  34. beast::ostream(connection->_response.body()) << jsonstr;
  35. return true;
  36. }
  37. //查询数据库判断用户名和邮箱是否匹配
  38. bool email_valid = MysqlMgr::GetInstance()->CheckEmail(name, email);
  39. if (!email_valid) {
  40. std::cout << " user email not match" << std::endl;
  41. root["error"] = ErrorCodes::EmailNotMatch;
  42. std::string jsonstr = root.toStyledString();
  43. beast::ostream(connection->_response.body()) << jsonstr;
  44. return true;
  45. }
  46. //更新密码为最新密码
  47. bool b_up = MysqlMgr::GetInstance()->UpdatePwd(name, pwd);
  48. if (!b_up) {
  49. std::cout << " update pwd failed" << std::endl;
  50. root["error"] = ErrorCodes::PasswdUpFailed;
  51. std::string jsonstr = root.toStyledString();
  52. beast::ostream(connection->_response.body()) << jsonstr;
  53. return true;
  54. }
  55. std::cout << "succeed to update password" << pwd << std::endl;
  56. root["error"] = 0;
  57. root["email"] = email;
  58. root["user"] = name;
  59. root["passwd"] = pwd;
  60. root["varifycode"] = src_root["varifycode"].asString();
  61. std::string jsonstr = root.toStyledString();
  62. beast::ostream(connection->_response.body()) << jsonstr;
  63. return true;
  64. });

在Mysql中新增CheckEmail和UpdatePwd函数

  1. bool MysqlMgr::CheckEmail(const std::string& name, const std::string& email) {
  2. return _dao.CheckEmail(name, email);
  3. }
  4. bool MysqlMgr::UpdatePwd(const std::string& name, const std::string& pwd) {
  5. return _dao.UpdatePwd(name, pwd);
  6. }

DAO这一层写具体的逻辑, 检测邮箱是否合理

  1. bool MysqlDao::CheckEmail(const std::string& name, const std::string& email) {
  2. auto con = pool_->getConnection();
  3. try {
  4. if (con == nullptr) {
  5. pool_->returnConnection(std::move(con));
  6. return false;
  7. }
  8. // 准备查询语句
  9. std::unique_ptr<sql::PreparedStatement> pstmt(con->prepareStatement("SELECT email FROM user WHERE name = ?"));
  10. // 绑定参数
  11. pstmt->setString(1, name);
  12. // 执行查询
  13. std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
  14. // 遍历结果集
  15. while (res->next()) {
  16. std::cout << "Check Email: " << res->getString("email") << std::endl;
  17. if (email != res->getString("email")) {
  18. pool_->returnConnection(std::move(con));
  19. return false;
  20. }
  21. pool_->returnConnection(std::move(con));
  22. return true;
  23. }
  24. }
  25. catch (sql::SQLException& e) {
  26. pool_->returnConnection(std::move(con));
  27. std::cerr << "SQLException: " << e.what();
  28. std::cerr << " (MySQL error code: " << e.getErrorCode();
  29. std::cerr << ", SQLState: " << e.getSQLState() << " )" << std::endl;
  30. return false;
  31. }
  32. }

更新密码

  1. bool MysqlDao::UpdatePwd(const std::string& name, const std::string& newpwd) {
  2. auto con = pool_->getConnection();
  3. try {
  4. if (con == nullptr) {
  5. pool_->returnConnection(std::move(con));
  6. return false;
  7. }
  8. // 准备查询语句
  9. std::unique_ptr<sql::PreparedStatement> pstmt(con->prepareStatement("UPDATE user SET pwd = ? WHERE name = ?"));
  10. // 绑定参数
  11. pstmt->setString(2, name);
  12. pstmt->setString(1, newpwd);
  13. // 执行更新
  14. int updateCount = pstmt->executeUpdate();
  15. std::cout << "Updated rows: " << updateCount << std::endl;
  16. pool_->returnConnection(std::move(con));
  17. return true;
  18. }
  19. catch (sql::SQLException& e) {
  20. pool_->returnConnection(std::move(con));
  21. std::cerr << "SQLException: " << e.what();
  22. std::cerr << " (MySQL error code: " << e.getErrorCode();
  23. std::cerr << ", SQLState: " << e.getSQLState() << " )" << std::endl;
  24. return false;
  25. }
  26. }
热门评论
  • T-summers
    2024-11-27 10:55:24

    有个问题,在检查邮箱号里,不应该是遍历结果集吗,但你的实现只能循环一次就会退出。

    下面是我改的实现。

        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 true;
                }
            }
            pool_->returnConnection(std::move(con));
            return false;
        }

热门文章

  1. C++ 类的继承封装和多态

    喜欢(588) 浏览(4951)
  2. windows环境搭建和vscode配置

    喜欢(587) 浏览(2806)
  3. Linux环境搭建和编码

    喜欢(594) 浏览(12145)
  4. 解密定时器的实现细节

    喜欢(566) 浏览(3466)
  5. slice介绍和使用

    喜欢(521) 浏览(2478)

最新评论

  1. 聊天项目(9) redis服务搭建 pro_lin:redis线程池的析构函数,除了pop出队列,还要free掉redis连接把
  2. 答疑汇总(thread,async源码分析) Yagus:如果引用计数为0,则会执行 future 的析构进而等待任务执行完成,那么看到的输出将是 这边应该不对吧,std::future析构只在这三种情况都满足的时候才回block: 1.共享状态是std::async 创造的(类型是_Task_async_state) 2.共享状态没有ready 3.这个future是共享状态的最后一个引用 这边共享状态类型是“_Package_state”,引用计数即使为0也不应该block啊
  3. C++ 并发三剑客future, promise和async Yunfei:大佬您好,如果这个线程池中加入的异步任务的形参如果有右值引用,这个commit中的返回类型推导和bind绑定就会出现问题,请问实际工程中,是不是不会用到这种任务,如果用到了,应该怎么解决?
  4. Qt MVC结构之QItemDelegate介绍 胡歌-此生不换:gpt, google

个人公众号

个人微信