当前位置:   article > 正文

物联网实战--平台篇之(三)账户后台数据库

物联网实战--平台篇之(三)账户后台数据库

目录

一、账户后台设计

二、账户数据库

三、数据库操作——增

四、数据库操作——改

五、数据库操作——查


本项目的交流QQ群:701889554

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

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

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

一、账户后台设计

        

        上图是应用服务器里的帐号相关服务,包括账户数据库和软件服务模块。在这里,我们的账户模块设计是比较基础简单的,就是常规的请求手机验证码、注册、登录和重置密码等流程,修改密码是登录后的操作,放在其它文章讲。这些操作是建立在上一篇文章的工程基础之上的,之前已经完成了MQTT网络部分和基础的数据库部分,那么这一章以账户后台为切入点,主要讲解下数据库的具体使用。

二、账户数据库

        工欲善其事必先利其器,在设计注册、登录等流程之前,肯定要预先设计好数据库的,这是根本,对于用户的这些动作才能有对应的操作对象,比如用户登录帐号,把帐号密码发到服务器了,后台服务肯定要对比验证,那比较的对象在哪儿——自然就是数据库里的账户信息了。

        之前说过,为了方便,我们采用的是sqlite文件数据库,上图是当前数据库的信息。根据数据库的基本知识,数据库是由一堆的库表组成的,每张表有各自的作用,表之间通过一些关键字段相互联系。先看账户列表的数据库表,即account_list_tb,包含了id、账户名、密码、权限、父账号、关联手机号、帐号下的所属应用和创建时间,接下来看下这个库表是如何建立的。

  1. #ifndef ACCOUNTSQLITE_H
  2. #define ACCOUNTSQLITE_H
  3. #include <QObject>
  4. #include "BaseSqlite.h"
  5. class AccountSqlite : public BaseSqlite
  6. {
  7. Q_OBJECT
  8. public:
  9. typedef struct
  10. {
  11. QString account; //账户名
  12. QString passWord; //密码
  13. u32 auth; //权限值
  14. QString parentAccount; //父账号
  15. QString phone; //关联手机号
  16. QList<u32>appList; //名下的应用列表
  17. QString createTime; //创建时间
  18. }AccountNodeStruct; //账户节点
  19. typedef struct
  20. {
  21. u32 appID; //应用ID
  22. QString appName; //应用名称
  23. QString creator; //建立者的账户名
  24. QString createTime; //创建时间
  25. }AppNodeStruct; //应用节点
  26. public:
  27. explicit AccountSqlite(QObject *parent = nullptr);
  28. bool createAccountListTable(void);
  29. bool addAccountNode(QString account, QString pass_word, u32 auth, QString parent_account, QString phone);
  30. bool updateAccountPassWord(QString account, QString pass_word);
  31. bool updateAccountPhone(QString account, QString phone);
  32. bool updateAccountAuth(QString account, u32 auth);
  33. bool updateAccountAppList(QString account, QList<u32>app_list);
  34. bool selectAccountByName(QString account, AccountNodeStruct &account_node);
  35. bool selectAccountByPhone(QString phone, AccountNodeStruct &account_node);
  36. bool selectChildAccountList(QString parent_account, QList<AccountNodeStruct>&child_list);
  37. bool searchAccountByName(QString account, AccountNodeStruct &account_node);
  38. bool searchAccountByPhone(QString phone, AccountNodeStruct &account_node);
  39. bool selectAccountAppList(QString account, QList<u32> &app_list);
  40. bool createAppListTable(void);
  41. bool addAppIDToList(u32 app_id, QString creator);
  42. bool updateAppName(u32 app_id, QString app_name);
  43. QString selectAppName(u32 app_id);
  44. bool selectAppInfo(u32 app_id, AppNodeStruct &app_node);
  45. bool selectLastAppInfo(AppNodeStruct &app_node);
  46. bool selectAppList(QList<u32>&app_list);
  47. u32 selectMaxAppID(void);
  48. u32 getAppCountFromAccount(QString account);
  49. private:
  50. u8 m_keyBuff[16];
  51. DrvCommon drv_com;
  52. signals:
  53. };
  54. #endif // ACCOUNTSQLITE_H

        这里是账户数据库相关的头文件,首先可以看到它继承于之前创建的基础数据库驱动类BaseSqlite,接下来就是定义账户的数据结构体,这样便于后期的修改和查询;在功能上,账户相关数据库函数有建表、添加账户、更新密码、更新手机、更新权限、更新应用列表、根据账户名查询、根据手机号查询、子账户查询和应用列表查询等功能;具体每个功能是怎么实现的举个例子,相似度比较高。

  1. //建立账户列表 数据库表
  2. bool AccountSqlite::createAccountListTable(void)
  3. {
  4. QString str_query = "CREATE TABLE If Not Exists account_list_tb ("
  5. "id INTEGER NOT NULL,"
  6. "account char(30) NOT NULL UNIQUE,"
  7. "pass_word varchar(50) NOT NULL,"
  8. "auth bigint DEFAULT 0,"
  9. "parent_account char(30) DEFAULT NULL,"
  10. "phone char(30) DEFAULT NULL UNIQUE,"
  11. "app_list varchar(5000) DEFAULT 0,"
  12. "create_time timestamp DEFAULT (datetime(\'now\',\'localtime\')),"
  13. "PRIMARY KEY (id)"
  14. ") ";
  15. if(runSqlQuery(str_query)==false)
  16. {
  17. qDebug("createAccountsTable error!");
  18. return false;
  19. }
  20. return true;
  21. }

        上面这个是创建账户库表的功能函数,可以发现,主要就是执行数据库语言的语句,runSqlQuery就是执行语句,不同的功能都是需要它去执行的,差异化在执行语句上。建表的语句是

CREATE TABLE If Not Exists account_list_tb

        Not Exists表示不存在account_list_tb表的时候才建立新表,不会重复建表。

        对创建账户数据库表内容进行分析,首先有个自增长的id号,NOT NULL表示不能为空。account行的char(30)表示最大存储30个字符,UNIQUE表示这个字段具有唯一性,所有记录中这个字段的内容不能重复,这是比较重要的,账户名重复了整个系统就乱套了,手机号也是这个道理。app_list字段存放的是这个账户创建的应用,采用base64编码存储,

解码前eyJhcHBfbGlzdCI6WzEyMzAwMl19,

解码后

{
  "app_list": [
    123002
  ]
}

        另外,密码是需要加密存储的,所以在头文件里定义了一个密码缓存区,后续账户数据库里的加解密都用这个密码,可以看到密码在base64解码后是一串乱码,即使数据库被复制了也看不到密码。

三、数据库操作——增

        数据库增加操作的语句是INSERT INTO.....VALUES....,根据内容组合起来,再用runSqlQuery执行即可,下面是增加一个账户的功能函数,要注意的是密码要进一步加密再存储,加密算法采用AES,密码就是之前所说的m_keyBuff;由于account字段是唯一性的,所以如果账户名重复了是会添加失败的。

  1. //添加一个账户信息到数据库
  2. bool AccountSqlite::addAccountNode(QString account, QString pass_word, u32 auth, QString parent_account, QString phone)
  3. {
  4. if(phone.isEmpty())
  5. {
  6. phone=account;
  7. }
  8. if(pass_word.isEmpty())
  9. {
  10. pass_word="12345678";
  11. }
  12. u8 out_passwd[50]={0};
  13. int out_len=drv_com.aes_encrypt_buff((u8*)pass_word.toUtf8().data(), (u16)pass_word.toUtf8().size(), out_passwd, (u16)sizeof (out_passwd), m_keyBuff);//密码加密存储
  14. if(out_len<16)
  15. return false;
  16. QByteArray pwd_ba((char*)out_passwd, out_len);
  17. pass_word=pwd_ba.toBase64();
  18. QString str_query = QString::asprintf("INSERT INTO account_list_tb (account, pass_word, auth, parent_account, phone) VALUES ( \"%s\", \"%s\", %u, \"%s\", \"%s\")",\
  19. account.toUtf8().data(), pass_word.toUtf8().data(), auth, \
  20. parent_account.toUtf8().data(), phone.toUtf8().data());
  21. if( runSqlQuery(str_query))
  22. {
  23. qDebug("addAccountNode ok!");
  24. return true;
  25. }
  26. qDebug("addAccountNode failed!");
  27. return false;
  28. }

四、数据库操作——改

        数据库修改语句是UPDATE....SET....WHERE条件,这里以更新账户密码为例,UPDATE后面是要更新的字段,WHERE后面跟的是条件,这里就是账户名匹配了。

  1. //更新账户密码
  2. bool AccountSqlite::updateAccountPassWord(QString account, QString pass_word)
  3. {
  4. if(pass_word.isEmpty())
  5. {
  6. pass_word="12345678";
  7. }
  8. u8 out_passwd[50]={0};
  9. int out_len=drv_com.aes_encrypt_buff((u8*)pass_word.toUtf8().data(), (u16)pass_word.toUtf8().size(), out_passwd, (u16)sizeof (out_passwd), m_keyBuff);
  10. if(out_len<16)
  11. return false;
  12. QByteArray pwd_ba((char*)out_passwd, out_len);
  13. pass_word=pwd_ba.toBase64();
  14. QString str_query = QString::asprintf("UPDATE account_list_tb SET pass_word=\"%s\" WHERE account=\"%s\"",
  15. pass_word.toUtf8().data(), account.toUtf8().data());
  16. // qDebug()<<str_query;
  17. if( runSqlQuery(str_query))
  18. {
  19. qDebug("updateAccountPassWord ok!");
  20. return true;
  21. }
  22. return false;
  23. }

下面这个更新应用列表也是比较重要的内容,每次创建新应用后都要更新一遍,应用列表是采用json数据格式,并转为base64编码保存的。

  1. //更新账户的应用列表
  2. bool AccountSqlite::updateAccountAppList(QString account, QList<u32> app_list)
  3. {
  4. QJsonDocument json_doc;
  5. QJsonObject root_obj;
  6. QJsonArray app_array;
  7. int nSize=app_list.size();
  8. for(int i=0; i<nSize; i++)
  9. {
  10. u32 app_id=app_list.at(i);
  11. if(app_id>0)
  12. {
  13. app_array.append((qint64)app_id);
  14. }
  15. }
  16. root_obj.insert("app_list", app_array);
  17. json_doc.setObject(root_obj);
  18. QByteArray json_ba=json_doc.toJson(QJsonDocument::Compact);//转为数据保存
  19. qDebug()<<"json_ba="<<json_ba.data();
  20. QString str_query = QString::asprintf("UPDATE account_list_tb SET app_list=\"%s\" WHERE account=\"%s\"",
  21. json_ba.toBase64().data(), account.toUtf8().data());
  22. // qDebug()<<str_query;
  23. if( runSqlQuery(str_query))
  24. {
  25. qDebug("updateAccountAppList ok!");
  26. return true;
  27. }
  28. return false;
  29. }
五、数据库操作——查

        数据库查找操作是使用最频繁的了,语句格式是SELECT......WHERE条件,这里以查询账户信息为例,SELECT后面跟着要查找内容的字段,这里很多人喜欢用*,不建议这样做,*代表选取所有字段,一个是效率肯定有所降低了,另一个是对所选取的字段顺序和数量不明确,在后面的解析转换中会出错,比如过段时间在第一列又增加了个字段内容,那么之前的解析代码就不能用了,顺序不对了。我们这里选择了account, pass_word, auth, parent_account, phone, app_list, create_time这些字段,不管库表增加什么字段,对我们后面解析都没影响。解析的时候也是按照这个顺序和对应的类型一个个转化的,ptr就是索引,每转化一个就自增到下一个字段,保存的时候是什么流程,解析的时候就是一个逆过程,比如这里的密码,先进行base64解码,然后再AES解密,这样才能得到正确的密码。

        另外一个注意点是返回之前要执行下m_sqlQuery.finish()语句,这样才能结束查询,避免出错。

  1. //根据名称 获取帐号信息
  2. bool AccountSqlite::selectAccountByName(QString account, AccountNodeStruct &account_node)
  3. {
  4. QString str_query = QString::asprintf("SELECT account, pass_word, auth, parent_account, phone, app_list, create_time FROM account_list_tb WHERE account=\"%s\"" , account.toUtf8().data());
  5. // qDebug()<<str_query;
  6. if(runSqlQuery(str_query)==false)
  7. {
  8. qDebug("selectAccountNode error_01!");
  9. return false;
  10. }
  11. while(m_sqlQuery.next())
  12. {
  13. int ptr=0;
  14. account_node.account=m_sqlQuery.value(ptr++).toString();
  15. QString pwd_str=m_sqlQuery.value(ptr++).toString();
  16. QByteArray pwd_ba=QByteArray::fromBase64(pwd_str.toUtf8());
  17. u8 out_passwd[50]={0};
  18. int out_len=drv_com.aes_decrypt_buff((u8*)pwd_ba.data(), (u16)pwd_ba.size(), out_passwd, sizeof (out_passwd), m_keyBuff);//存储密码解密
  19. if(out_len<=0)
  20. {
  21. m_sqlQuery.finish();
  22. return false;
  23. }
  24. account_node.passWord=QString((char*)out_passwd);
  25. account_node.auth=m_sqlQuery.value(ptr++).toUInt();
  26. account_node.parentAccount=m_sqlQuery.value(ptr++).toString();
  27. account_node.phone=m_sqlQuery.value(ptr++).toString();
  28. QString json_str=m_sqlQuery.value(ptr++).toString();//应用列表JSON
  29. QByteArray json_ba=QByteArray::fromBase64(json_str.toUtf8());
  30. QJsonParseError json_error;
  31. QJsonDocument json_doc;
  32. json_doc = QJsonDocument(QJsonDocument::fromJson(json_ba, &json_error));//转为JSON格式
  33. if(json_error.error != QJsonParseError::NoError)
  34. {
  35. qDebug()<<"json error= "<<json_error.error;
  36. }
  37. else
  38. {
  39. QJsonObject root_obj = json_doc.object();
  40. if(root_obj.contains("app_list"))//应用列表
  41. {
  42. QJsonValue value=root_obj.value("app_list");
  43. if(value.isArray())
  44. {
  45. QJsonArray app_array=value.toArray();
  46. int nSize=app_array.size();
  47. for(int i=0; i<nSize; i++)
  48. {
  49. QJsonValue value=app_array.at(i);
  50. if(value.isDouble())
  51. {
  52. u32 app_id=(u32)value.toDouble();
  53. if(app_id>0)
  54. {
  55. account_node.appList.append(app_id);//添加app_id
  56. }
  57. }
  58. }
  59. }
  60. }
  61. }
  62. account_node.createTime=m_sqlQuery.value(ptr++).toString();
  63. m_sqlQuery.finish();
  64. return true;
  65. }
  66. qDebug("selectAccountNode error_02!");
  67. return false;
  68. }

对于删除,这里暂时没用到,其实也很简单,就是DELETE....WHERE条件,后面有用到了再做举例。

        

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/558776
推荐阅读
相关标签
  

闽ICP备14008679号