聊天项目(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. }
热门评论

热门文章

  1. 解密定时器的实现细节

    喜欢(566) 浏览(2327)
  2. C++ 类的继承封装和多态

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

    喜欢(594) 浏览(7077)
  4. windows环境搭建和vscode配置

    喜欢(587) 浏览(1870)
  5. slice介绍和使用

    喜欢(521) 浏览(1945)

最新评论

  1. asio多线程模型IOServicePool Lion:线程池一定要继承单例模式吗
  2. 泛型算法的定制操作 secondtonone1:lambda和bind是C11新增的利器,善于利用这两个机制可以极大地提升编程安全性和效率。
  3. 类和对象 陈宇航:支持!!!!
  4. C++ 虚函数表原理和类成员内存分布 WangQi888888:class Test{ int m; int b; }中b成员是int,为什么在内存中只占了1个字节。不应该是4个字节吗?是不是int应该改为char。这样的话就会符合图上说明的情况
  5. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。

个人公众号

个人微信