物联网实战--平台篇之(六)应用管理后台

目录

一、应用数据库

二、登录记忆

三、新建应用

四、获取应用列表

五、重命名应用


 本项目的交流QQ群:701889554

物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html

物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html

本项目资源文件https://download.csdn.net/download/ypp240124016/89301894

sqlite数据库查看软件 https://download.csdn.net/download/ypp240124016/89302020

一、应用数据库

        在账户数据库中,除了账户表,还有个应用表,它的函数列表和示例内容如下两图所示,主要也是包含了建表、插入和更新等操作。

//建立应用表
bool AccountSqlite::createAppListTable(void)
{
    QString str_query = QString::asprintf("CREATE TABLE If Not Exists app_id_list ("
                                          "id INTEGER NOT NULL,"
                                          "app_id bigint NOT NULL UNIQUE,"
                                          "app_name char(30) DEFAULT NULL,"
                                          "creator char(30) DEFAULT NULL,"
                                          "create_time timestamp DEFAULT (datetime(\'now\',\'localtime\')),"
                                          "PRIMARY KEY (id)"
                                          ")"
                                         );


    if(runSqlQuery(str_query)==false)
    {
        qDebug("createAppLisTable error!");
        return false;
    }
    return true;
}

//添加应用ID
bool AccountSqlite::addAppIDToList(u32 app_id, QString creator)
{
    QString str_query = QString::asprintf("INSERT INTO app_id_list (app_id, app_name, creator) VALUES ( %u, \"%s\" , \"%s\" )", app_id, "新应用",creator.toUtf8().data());

    if( runSqlQuery(str_query))
    {
        qDebug("addAppIDToList ok!");

        AccountNodeStruct tag_account;
        tag_account.account.clear();
        selectAccountByName(creator, tag_account);
        if(tag_account.account.isEmpty()==false)
        {
            int nSize=tag_account.appList.size();
            for(int i=0; i<nSize; i++)
            {
                if(tag_account.appList.at(i)==app_id)
                {
                    return true;
                }
            }
            tag_account.appList.append(app_id);
            updateAccountAppList(creator, tag_account.appList);//更新应用列表
        }
        return true;
    }

    qDebug("addAppIDToList failed!");
    return false;
}

//更新应用名称
bool AccountSqlite::updateAppName(u32 app_id, QString app_name)
{
    QString str_query = QString::asprintf("UPDATE app_id_list SET app_name=\"%s\"   WHERE app_id=%u",
                                          app_name.toUtf8().data(), app_id);
//    qDebug()<<str_query;
    if( runSqlQuery(str_query))
    {
        qDebug("updateAppName ok!");
        return true;
    }
    return false;
}

//获取应用名称
QString AccountSqlite::selectAppName(u32 app_id)
{
    QString app_name="";
    QString str_query = QString::asprintf("select app_name from app_id_list where app_id=%u", app_id);
    if( runSqlQuery(str_query))
    {
        while(m_sqlQuery.next())
        {
            app_name=m_sqlQuery.value(0).toString();
            break;
        }
        m_sqlQuery.finish();
    }
    return app_name;
}

//查询应用节点信息
bool AccountSqlite::selectAppInfo(u32 app_id, AppNodeStruct &app_node)
{
    QString str_query = QString::asprintf("select app_id, app_name, creator, create_time from app_id_list where app_id=%u", app_id);
//    qDebug()<<str_query;
    if( runSqlQuery(str_query))
    {
        while(m_sqlQuery.next())
        {
            int ptr=0;
            app_node.appID=m_sqlQuery.value(ptr++).toUInt();
            app_node.appName=m_sqlQuery.value(ptr++).toString();
            app_node.creator=m_sqlQuery.value(ptr++).toString();
            app_node.createTime=m_sqlQuery.value(ptr++).toString();
            m_sqlQuery.finish();
            return true;
        }
        m_sqlQuery.finish();
    }
    return false;
}


//获取最大的应用ID
u32 AccountSqlite::selectMaxAppID(void)
{
    u32 max_app=0;
    QString str_query = QString::asprintf("SELECT app_id  FROM  app_id_list ORDER BY app_id DESC limit 10");
    if(runSqlQuery(str_query)==false)
    {
        qDebug("selectMaxAppID error_01!");
         return max_app;
    }
    while(m_sqlQuery.next())
    {
        max_app=m_sqlQuery.value(0).toUInt();
        break;
    }
    m_sqlQuery.finish();
//    qDebug()<<"selectAppList="<<app_list;
    return max_app;
}

//获取该账户下的应用数量
u32 AccountSqlite::getAppCountFromAccount(QString account)
{
    u32 app_cnts=0;
    QString str_query = QString::asprintf("select app_id from app_id_list where  creator=\"%s\"", account.toUtf8().data());
//    qDebug()<<str_query;
    if( runSqlQuery(str_query))
    {
        while(m_sqlQuery.next())
        {
          app_cnts++;
        }
    }
    return app_cnts;
}

        以上是应用相关的数据库函数,这里面比较特殊的是selectMaxAppID,用来查询最大的应用ID,这样用户在新建应用时候就知道该分配什么ID给他了,这里面用DESC依据app_id字段降序查询,这样第一个就是最大的应用ID了;还有一个getAppCountFromAccount用来获取某个账户下已经存在的应用数量,目的是为了判断改用户能否再新建应用,我这对这个有数量限制,目前是最多8个应用,这个可以自己更改,或者根据账户的权限等级自己去分配。

二、登录记忆

        在实际使用APP过程中,为了方便用户,一般在一定时间内(比如一星期)可以免登录,直接跳转到主界面进行使用了,那么,我们这边也使用这种模式。首次打开时,跳转到验证码登录页面,引导用户登录,正常操作登录后,用户端会保存当前账户和登录时间,密码不保存;在下次打开APP的时候,内部程序会先读取保存数据,如果有账户并且时间未过期,那么就以这个账户直接获取该账户下的应用列表,完成登录过程,这个过程用户无感,只需等待几秒即可,增强体验感。下面看下具体实现。

        首先需要一个密码保护配置文件,这个密码根据每个手机的MAC地址决定,这样配置文件就不会被复制利用了,具体的密码生成可以使用自己的方法,这里只是个参考。

        接下来就是读取和保存配置的内容了,writeConfig比较简单,就是保存当前登录账户和时间;读取readConfig后,需要检查账户是否符合要求,另外时间上我这里定的是3天以内,如果这两个条件满足要求,就直接免登录 ,同时再保存一次更新时间,如果这里不更新保存也行的,意味着3天后需要强制重新登录,相对安全些,就看自己要如何权衡便捷与安全了。

        另外,drv_com.readConfg和drv_com.writeConfg内部会根据密码自动加解密,如果不需要加密,那就密码传入NULL即可。


void AccountMan::readConfig(void)
{
    QString path=m_rootPath+"/account.txt";
    QJsonObject root_obj=drv_com.readConfg(path, m_keyBuff);
    
    if(root_obj.contains("account"))
    {
        QString account_str=root_obj.value("account").toString(); 
        qDebug()<<"account_str="<<account_str;
        m_accountWork.account=account_str;
    }
    if(root_obj.contains("login_time"))
    {
        qint64 login_time=root_obj.value("login_time").toDouble();
        qDebug()<<"login_time="<<login_time;
        m_accountWork.login_time=login_time;
    }
    qint64 det_time=QDateTime::currentDateTime().toTime_t()-m_accountWork.login_time;
    qDebug()<<"det_time="<<det_time;
    if(checkAccount(m_accountWork.account)==0 && det_time<86400*3)
    {
        emit siqSetLoginState(1);//免登录,发给前端
        writeConfig();//更新登录时间
        emit sigUpdateLoginAccount(m_accountWork.account, 1);//发给控制中心
    }
    else
    {
        emit siqSetLoginState(0);//引导登录
    }
}

void AccountMan::writeConfig(void)
{
    QString path=m_rootPath+"/account.txt";
    QJsonObject root_obj;
    QString account_str=m_accountWork.account;
    root_obj.insert("account", account_str);
    root_obj.insert("login_time", (qint64)QDateTime::currentDateTime().toTime_t());
    
    drv_com.writeConfig(path, root_obj, m_keyBuff);

}

三、新建应用

        账户注册的时候后台系统会自动创建一个应用,如果不够用,当前系统允许再新建7个应用,新建的过程也比较常规,就是请求——创建——回复。在这里,应用管理需要单独区分了,跟以后的分组管理和设备管理统一规划到中心管理去,就是下图新建的文件类;相应的,需要添加订阅的话题和组合新的发布话题。应用相关的订阅话题是yyy125/as/pub/center/AC:5A:FC:C7:15:3D/28454/app。

        在CenterMan类中,请求代码如下,核心参数是new_name,由前端输入,这样服务器后台会直接更新数据库内的应用名称。

void CenterMan::requestNewApp(QString new_name)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;

    root_obj.insert("cmd_type", "new_app");
    root_obj.insert("account", m_loginAccount);
    root_obj.insert("new_name", new_name);
    root_obj.insert("rand_num", m_randNum);
    root_obj.insert("mac", m_macStr); 
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic("app");
    emit sigMqttPushMessage(topic, msg_ba);
}

        以下是后台服务器新建应用部分的代码,基本流程是检查账户合法性、检查应用数量、获取最大应用ID和添加应用。回复的时候同样会返回应用ID和名称。

  if(cmd_type=="new_app")//新建应用
    {
        qDebug()<<"account= "<<account<<" req new app!";
        AccountSqlite::AccountNodeStruct tag_account;
        AccountSqlite::AppNodeStruct tag_app;
        u32 new_app_id=0;
        tag_app.appID=0;
        tag_account.parentAccount="";
        m_accountSqlite->selectAccountByName(account, tag_account);
        qDebug("@@tag_account.auth=0x%08X", tag_account.auth);
        if(tag_account.account.isEmpty()==true)
        {
            qDebug()<<"account"<<account<<" is not found!";
            ackNewAppState(account, mac_str, rand_num, 0, "",1, account+" 未找到账号!");
            return;
        }
        if(tag_account.parentAccount.isEmpty()==false)//检测是否为根账号
        {
           qDebug()<<"account="<<account<<" is not a root count!";
           ackNewAppState(account, mac_str, rand_num, 0, "",1, account+" 该账号不是主账号!");
           return;
        }
        int app_cnts=m_accountSqlite->getAppCountFromAccount(account);//当前的应用数量
        if(app_cnts>=8)//限制每个账号创建应用的数量
        {
            ackNewAppState(account, mac_str, rand_num, 0, "",1, account+" 应用数量已达上限!");
            return;
        }
        u32 max_app=m_accountSqlite->selectMaxAppID();
        if(max_app>APP_ID_MIN)
            new_app_id=max_app+1;
        else  
            new_app_id=APP_ID_MIN+1;
        
        qDebug()<<"new_app_id="<<new_app_id;
       bool ok=m_accountSqlite->addAppIDToList(new_app_id, account);
       if(ok)
       {
           QString new_name="新应用";
           if(root_obj.contains("new_name"))//命令
           {
               QJsonValue value = root_obj.value("new_name");
                new_name=value.toString();
           }
           m_accountSqlite->updateAppName(new_app_id, new_name);   //更新数据库内应用名称
           ackNewAppState(account, mac_str, rand_num, new_app_id, new_name, 0, "创建成功!");
           qDebug()<<"111 addAppIDToList ok, new_app_id="<<new_app_id<<", account="<<account;
       }
       else
       {
           ackNewAppState(account, mac_str, rand_num, 0, "",1, "创建失败!");
       }
    }





void CenterThread::ackNewAppState(QString account, QString mac_str, int rand_num, u32 app_id, QString app_name, int result, QString ack_str)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;
    root_obj.insert("cmd_type", "new_app");
    root_obj.insert("app_id", (qint64)app_id);
    root_obj.insert("app_name", app_name);
    root_obj.insert("create_time", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
    root_obj.insert("result", result);
    root_obj.insert("ack_str", ack_str);
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic(account, mac_str, rand_num, "app");
    emit sigMqttPushMessage(topic, msg_ba);
}

       以下是用户端对返回结果的解析和处理方式。

       if(cmd_type=="new_app")//返回新应用
         {
             u32 app_id=0;
             if(root_obj.contains("app_id"))
             {
                 QJsonValue value = root_obj.value("app_id");
                 if(value.isDouble())
                 {
                     app_id=value.toDouble();
                 }
             }
             if(app_id>0)
             {
                 QString app_name="新应用";
                 if(root_obj.contains("app_name"))
                 {
                     app_name=root_obj.value("app_name").toString();
                 }
                 AppWorkStruct tag_work_app;
                 tag_work_app.appID=app_id;
                 tag_work_app.appName=app_name;
                 m_appWorkList.append(tag_work_app);
             }
         }

四、获取应用列表

        登录成功后的第一件事就是获取当前账户下的应用列表,这样才能进行后续工作,请求代码如下:

void CenterMan::requestAppList(void)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;

    root_obj.insert("account", m_loginAccount);
    root_obj.insert("rand_num", m_randNum);
    root_obj.insert("mac", m_macStr); 
    root_obj.insert("cmd_type", "app_list");
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic("app");
    emit sigMqttPushMessage(topic, msg_ba);
}

服务端代码如下,回复的内容相对繁琐,要一个个组成json数组发送回应。

    else if(cmd_type=="app_list")//获取应用列表
    {
        AccountSqlite::AccountNodeStruct tag_account;
        tag_account.account.clear();
        tag_account.appList.clear();
        m_accountSqlite->selectAccountByName(account, tag_account);
        if(tag_account.account.isEmpty()==false)
        {
            ackReqAppListState(account, mac_str, rand_num, tag_account.appList, 0, "应用获取成功!");
        }
    }



void CenterThread::ackReqAppListState(QString account, QString mac_str, int rand_num, QList<u32> app_list, int result, QString ack_str)
{
    QJsonArray app_array;
    QJsonObject root_obj;
    QJsonDocument json_doc;

    for(int i=0; i<app_list.size(); i++)
    {
        u32 app_id=app_list.at(i);
        if(app_id>0)
        {
            AccountSqlite::AppNodeStruct tag_app_node;
            m_accountSqlite->selectAppInfo(app_id, tag_app_node);
            QJsonObject app_obj;
            app_obj.insert("app_id", (qint64)app_id);
            app_obj.insert("app_name", tag_app_node.appName);
            app_obj.insert("create_time", tag_app_node.createTime);
            app_array.append(app_obj);
        }
    }
    root_obj.insert("cmd_type", "app_list");
    root_obj.insert("account", account);
    root_obj.insert("app_list", app_array);
    root_obj.insert("result", result);
    root_obj.insert("ack_str", ack_str);
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic(account, mac_str, rand_num, "app");
    emit sigMqttPushMessage(topic, msg_ba);
}

        用户端解析如下,有一个细节处理需要处理,就是上次操作的应用ID会保存在配置文件内,需要将此app_id跟当前应用列表逐一对比,如果有匹配则继续将当前app_id作为激活状态,如果没有,那就默认使用列表的第一个app_id,用户切换后会自动保存激活的app_id。

         else if(cmd_type=="app_list")//返回应用列表
         {
             if(root_obj.contains("app_list"))
             {
                 QJsonValue value=root_obj.value("app_list");
                 
                 if(value.isArray())
                 {
                     m_appWorkList.clear();//清除APP列表
                     bool curr_app_flag=false;
                     QJsonArray app_array=value.toArray();
                     qDebug()<<app_array;
                     int nSize=app_array.size();
                     for(int i=0; i<nSize; i++)
                     {
                         QJsonValue value=app_array.at(i);
                         if(value.isObject())
                         {
                             AppWorkStruct tag_app_work;
                             tag_app_work.appID=0;
                             QJsonObject app_obj=value.toObject();

                             if(app_obj.contains("app_name"))
                             {
                                 QJsonValue value=app_obj.value("app_name");
                                 tag_app_work.appName=value.toString();
                             }
                             if(app_obj.contains("app_id"))
                             {
                                 QJsonValue value=app_obj.value("app_id");
                                 if(value.isDouble())
                                 {
                                     u32 app_id=value.toDouble();
                                     if(app_id>0)
                                     {
                                         tag_app_work.appID=app_id;
                                         if(m_currAppWork.appID==app_id)
                                         {
                                             m_currAppWork.appName=tag_app_work.appName;
                                             curr_app_flag=true;
                                         }
                                     }
                                 }
                             }
                             if(app_obj.contains("create_time"))
                             {
                                 QJsonValue value=app_obj.value("create_time");
                                 tag_app_work.createTime=value.toString();
                             }
                             if(tag_app_work.appID>0)
                             {
                                 m_appWorkList.append(tag_app_work);   
                             }
                         }                    
                     }
                     if(curr_app_flag==false && m_appWorkList.size()>0)//没有默认应用
                     {
                         m_currAppWork=m_appWorkList.first();
                     }
                     
                     emit siqUpdateCurrAppName(m_currAppWork.appID, m_currAppWork.appName);
                     writeConfig();
                 }
             }     
         }

五、重命名应用

        用户有时候需要更改应用名称,具体代码如下,只需要传入应用ID和新名称即可。

void CenterMan::requestRenameApp(qint64 app_id, QString new_name)
{
    if(app_id==0)
        return;
    QJsonObject root_obj;
    QJsonDocument json_doc;
    root_obj.insert("account", m_loginAccount);
    root_obj.insert("cmd_type", "rename");
    root_obj.insert("rand_num", m_randNum);
    root_obj.insert("mac", m_macStr);
    root_obj.insert("app_id", (qint64)app_id);
    root_obj.insert("app_name", new_name);
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic("app");
    emit sigMqttPushMessage(topic, msg_ba);
}

        服务器端操作也相对简单,数据库进行更新操作即可。

    else if(cmd_type=="rename")//重命名应用
    {
        u32 app_id=0;
        if(root_obj.contains("app_id"))
        {
            QJsonValue value=root_obj.value("app_id");
            if(value.isDouble())
            {
                app_id=value.toDouble();
            }
        }
        QString app_name="新名称";
        if(root_obj.contains("app_name"))
        {
            QJsonValue value=root_obj.value("app_name");
            if(value.isString())
            {
                app_name=value.toString();
            }
        }
        if(app_id>0)
        {
            m_accountSqlite->updateAppName(app_id, app_name);   
            ackRenameAppState(account, mac_str, rand_num, app_id, app_name, 0, "更新成功!");
        }
        else
        {
            ackRenameAppState(account, mac_str, rand_num, app_id, app_name, 1, "更新失败!");
        }
       
    }





void CenterThread::ackRenameAppState(QString account, QString mac_str, int rand_num, u32 app_id, QString app_name, int result, QString ack_str)
{
    QJsonObject root_obj;
    QJsonDocument json_doc;
    root_obj.insert("cmd_type", "rename");
    root_obj.insert("app_id", (qint64)app_id);
    root_obj.insert("app_name", app_name);
    root_obj.insert("result", result);
    root_obj.insert("ack_str", ack_str);
    json_doc.setObject(root_obj);
    QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);
    QString topic=makePubTopic(account, mac_str, rand_num, "app");
    emit sigMqttPushMessage(topic, msg_ba);
}

        用户端解析后将新名称更新到前端,完成闭环。

        else if(cmd_type=="rename")//返回重命名
         {
             u32 app_id=0;
             if(root_obj.contains("app_id"))
             {
                 QJsonValue value = root_obj.value("app_id");
                 if(value.isDouble())
                 {
                     app_id=value.toDouble();
                 }
             }
             QString app_name="";
             if(root_obj.contains("app_name"))
             {
                 QJsonValue value = root_obj.value("app_name");
                 if(value.isString())
                 {
                     app_name=value.toString();
                 }
             }
             if(app_id>0)
             {
                 int i=0;
                 for(auto iter : m_appWorkList)
                 {
                     if(iter.appID==app_id)
                     {
                         m_appWorkList[i].appName=app_name;
                     }
                     i++;
                 }
                 emit siqUpdateCurrAppName(app_id, app_name);
             }
         }

        目前账户和应用都没设计删除功能,主要是为了避免引起不必要的麻烦,比如我们的app_id是增长型的,每创建一个应用都会先查找最大的app_id,然后再+1,如果删除了,怕会出现混乱,所以就不提供此功能了。

  • 13
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ypp240124016

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值