Chap08-Register

Chap08-Register

客户端

首先实现按下注册按钮的槽函数

void RegisterScreen::do_register_clicked()
{
    auto res= doVerify();
    if(!res){
        return;
    }
    if (securityCode->text().trimmed().isEmpty()){
        securityCode->setStyleSheet("border: 1px solid red;");
        securityCode->setToolTip("此项不能为空");
    }else{
        securityCode->setStyleSheet("");
    }

    QJsonObject j;
    j["user"] = accountEdit->text().trimmed();
    j["password"] = passwordEdit->text().trimmed();
    j["email"] = emailEdit->text().trimmed();
    j["securityCode"] = securityCode->text().trimmed();

    HttpManager::GetInstance()->PostHttp(QUrl(gate_url_prefix+"/userRegister"),
                                        j, RequestType::REG_USER,Modules::REGISTERMOD);

}

先说一下,我们修改了doVerify函数,使得showTip在doVarify内部实现,而返回一个bool,然后我们根据这个bool值来决定是否进行接下来的操作。同时内部还对password和passwordSure进行验证,确认两次输入一致。

我们需要发送user,password,email,securityCode,交给HttpManager处理。

PostHttp处理之后会触发on_http_finished信号,槽函数do_http_finished根据RequestType决触发什么信号。

void HttpManager::do_http_finished(RequestType requestType, const QString &res, ErrorCodes errorCode, Modules mod)
{
    switch(requestType)
    {
    case RequestType::GET_SECURITY_CODE:
        emit on_get_code_finished(requestType,res,errorCode);
        break;
    case RequestType::REG_USER:
        emit on_register_finished(requestType,res,errorCode);
        break;
    }
}

当有信号传进来之后,相应的槽函数处理结果

void RegisterScreen::do_register_finished(RequestType requestType,const QString&res,ErrorCodes errorCode)
{
    qDebug() << static_cast<int>(errorCode);
    qDebug() << static_cast<int>(ErrorCodes::SUCCESS);
    if(errorCode!=ErrorCodes::SUCCESS){
        showTip(0,tr("网络请求错误"));
        return;
    }
    QJsonDocument jsonDoc = QJsonDocument::fromJson(res.toUtf8());
    qDebug() << jsonDoc.toJson();
    if(jsonDoc.isEmpty()){
        showTip(0,"接收数据异常为空,无法解析");
        return;
    }
    if(!jsonDoc.isObject()){
        showTip(0,"接收数据异常,无法解析");
        return;
    }

    _handlers[requestType](jsonDoc.object());
    return;
}

后端

GateWayServer

首先在LogicSystem中注册对应路由的回调函数

RegistHandlers("/userRegister", RequestType::POST, [this](std::shared_ptr<Session> session) {
        auto body_str = beast::buffers_to_string(session->_request.body().data());
        std::cout << "receive userRegister request, body: " << body_str << std::endl;
        session->_response.set(http::field::content_type, "text/json");
        json j = json::parse(body_str);
        if (j.is_null() || j.is_discarded()) {
            std::cout << "无效json" << std::endl;
            j["error"] = ErrorCodes::ERROR_JSON;
            std::string str = j.dump(4);
            beast::ostream(session->_response.body()) << str;
            return true;
        }
        std::string code;
        bool get_code_success = RedisManager::GetInstance()->Get(EMAIL_PREFIX + j["email"].get<std::string>(), code);
        if (!get_code_success) {
            std::cout << "验证码获取失败" << std::endl;
            j["error"] = ErrorCodes::ERROR_SECURITYCODE_EXPIRED;
            std::string str = j.dump(4);
            beast::ostream(session->_response.body()) << str;
            return true;
        } else if (code != j["securityCode"].get<std::string>()) {
            std::cout << "验证码错误" << std::endl;
            j["error"] = ErrorCodes::ERROR_SECURITYCODE_NOTFOUND;
            std::string str = j.dump(4);
            beast::ostream(session->_response.body()) << str;
            return true;
        }

        // 先查找用户是否存在
        // 这里简单测试注册功能
        json jj;
        jj["error"] = ErrorCodes::SUCCESS;
        jj["email"] = j["email"].get<std::string>();
        jj["user"] = j["user"].get<std::string>();
        jj["password"] = j["password"].get<std::string>();
        jj["securityCode"] = j["securityCode"].get<std::string>();
        std::string str = jj.dump(4);
        beast::ostream(session->_response.body()) << str;
        return true;
    });

先补充一下ErrorCodes的内容

enum class ErrorCodes {
    SUCCESS = 0,
    ERROR_NETWORK = 1001,
    ERROR_JSON = 1002,
    RPCFAILED = 1003,
    ERROR_SECURITYCODE_EXPIRED = 1004,
    ERROR_SECURITYCODE_NOTFOUND = 1005
};

这个枚举需要前后端协调一致。

回调函数的内容就是根据json解析的securityCode,然后与redis查询EMAIL_PREFIX+email的value值进行比较。

比如email=xxx@163.com

查询的是后就要查询"email_xxx@163.com"添加前缀用于区分

同理nodejs处理的时候,也要设置key为email_xxx@163.com

那么查询email_xxx@163.com得到对应的securityCode.

如果没有查到,就是ERROR_SECURITYCODE_NOTFOUND,否则查到不等的话就是ERROR_SECURITYCODE_EXPIRED(过期了)。

Nodejs with Redis

首先安装redis的库

npm install ioredis

编写redis.js,通过解析config.json的host,port,password,连接redis,定义操作。

const config_module = require('./config')
const Redis = require('ioredis');

const RedisCli = new Redis(
{
    host:config_module.redis_host,
    port:config_module.redis_port,
    password:config_module.redis_passwd
});

RedisCli.on("error",function(error){
    console.log("RedisCli connection error");
    RedisCli.quit();
});

async function GetRedis(key){
    try{
        const result = await RedisCli.get(key);
        if(result === null){
            console.log("RedisCli key not found: "+key);
            return null;
        }
        console.log("RedisCli get key: "+key+" value: "+result);
        return result;
    }catch(error){
        console.log(error);
        return null;
    }
}

async function QueryRedis(key) {
    try{
        const result = await RedisCli.exists(key)
        //  判断该值是否为空 如果为空返回null
        if (result === 0) {
          console.log('result:<','<'+result+'>','This key is null...');
          return null
        }
        console.log('Result:','<'+result+'>','With this value!...');
        return result
    }catch(error){
        console.log('QueryRedis error is', error);
        return null
    }
}

async function SetRedisExpire(key,value, exptime){
    try{
        // 设置键和值
        await RedisCli.set(key,value)
        // 设置过期时间(以秒为单位)
        await RedisCli.expire(key, exptime);
        return true;
    }catch(error){
        console.log('SetRedisExpire error is', error);
        return false;
    }
}

function Quit(){
    RedisCli.quit();
}


module.exports = {GetRedis, QueryRedis, Quit, SetRedisExpire,}

那么server.js修改如下

async function GetSecurityCode(call, callback) {
    console.log("Email is:", call.request.email)
    try{
        let redis_res = await redis_module.GetRedis(const_module.email_prefix+call.request.email);
        let uniqueId = "";
        if (redis_res == null){
            uniqueId = uuidv4().toUpperCase().substring(0,4);
            let bres = await redis_module.SetRedisExpire(const_module.email_prefix+call.request.email, uniqueId, 180);
            if(!bres){
                callback(null, { email:  call.request.email,
                    error:const_module.Errors.RedisError
                });
                return;
            }
        }
        else{
            uniqueId = redis_res;
        }
        console.log("UniqueId is ", uniqueId)
                let html_str = `...`
        //发送邮件
        let mailOptions = {
            from: 'v125250@163.com',
            to: call.request.email,
            subject: '验证码',
            html: html_str,
        };

        let send_res = await emailModule.SendMail(mailOptions);
        console.log("Send Result Is ", send_res)

        if (!send_res){
            callback(null, { email:  call.request.email,
                error:const_module.Errors.Success
            }); 
        }


    }catch(error){
        console.log("Catch Error Is ", error)

        callback(null, { email:  call.request.email,
            error:const_module.Errors.Exception
        }); 
    }

}

在这里,首先在redis数据库查询是否有对应的key/value.如果没有就生成验证码加入到数据库,并且设置过期时间为3分钟。如果有(这时候就是重复发请求的情况),就直接返回。

posted @ 2025-12-24 23:16  大胖熊哈  阅读(1)  评论(0)    收藏  举报