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

目录

一、账户后台设计

二、账户数据库

三、数据库操作——增

四、数据库操作——改

五、数据库操作——查


本项目的交流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、账户名、密码、权限、父账号、关联手机号、帐号下的所属应用和创建时间,接下来看下这个库表是如何建立的。

#ifndef ACCOUNTSQLITE_H
#define ACCOUNTSQLITE_H

#include <QObject>
#include "BaseSqlite.h"

class AccountSqlite : public BaseSqlite
{
    Q_OBJECT
public:
    typedef struct
    {
        QString account;  //账户名
        QString passWord;  //密码
        u32 auth;  //权限值
        QString parentAccount; //父账号
        QString phone; //关联手机号
        QList<u32>appList;  //名下的应用列表
        QString createTime; //创建时间
    }AccountNodeStruct;  //账户节点

    typedef struct
    {
        u32 appID;   //应用ID
        QString appName; //应用名称
        QString creator;  //建立者的账户名
        QString createTime; //创建时间
    }AppNodeStruct;  //应用节点
    
public:
    explicit AccountSqlite(QObject *parent = nullptr);
    
    bool createAccountListTable(void);
    bool addAccountNode(QString account, QString pass_word, u32 auth, QString parent_account, QString phone);
    bool updateAccountPassWord(QString account, QString pass_word);
    bool updateAccountPhone(QString account, QString phone);
    bool updateAccountAuth(QString account, u32 auth);
    bool updateAccountAppList(QString account, QList<u32>app_list);
    bool selectAccountByName(QString account, AccountNodeStruct &account_node);
    bool selectAccountByPhone(QString phone, AccountNodeStruct &account_node);
    bool selectChildAccountList(QString parent_account, QList<AccountNodeStruct>&child_list);
    bool searchAccountByName(QString account, AccountNodeStruct &account_node);
    bool searchAccountByPhone(QString phone, AccountNodeStruct &account_node);
    bool selectAccountAppList(QString account, QList<u32> &app_list);
    
    bool createAppListTable(void);
    bool addAppIDToList(u32 app_id, QString creator);
    bool updateAppName(u32 app_id, QString app_name);
    QString selectAppName(u32 app_id);
    bool selectAppInfo(u32 app_id, AppNodeStruct &app_node);
    bool selectLastAppInfo(AppNodeStruct &app_node);
    bool selectAppList(QList<u32>&app_list);
    u32 selectMaxAppID(void);
    u32 getAppCountFromAccount(QString account);

private:
    u8 m_keyBuff[16];
    DrvCommon drv_com;
signals:
    
};

#endif // ACCOUNTSQLITE_H

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

//建立账户列表  数据库表
bool AccountSqlite::createAccountListTable(void)
{
    QString str_query = "CREATE TABLE If Not Exists account_list_tb ("
            "id INTEGER NOT NULL,"
            "account char(30) NOT NULL UNIQUE,"
            "pass_word varchar(50) NOT NULL,"
            "auth bigint DEFAULT 0,"
            "parent_account char(30) DEFAULT NULL,"
            "phone char(30) DEFAULT NULL UNIQUE,"
            "app_list varchar(5000) DEFAULT 0,"
            "create_time timestamp DEFAULT (datetime(\'now\',\'localtime\')),"
            "PRIMARY KEY (id)"
          ") ";


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

        上面这个是创建账户库表的功能函数,可以发现,主要就是执行数据库语言的语句,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字段是唯一性的,所以如果账户名重复了是会添加失败的。


//添加一个账户信息到数据库
bool AccountSqlite::addAccountNode(QString account, QString pass_word, u32 auth, QString parent_account, QString phone)
{
    if(phone.isEmpty())
    {
        phone=account;
    }
    if(pass_word.isEmpty())
    {
        pass_word="12345678";
    }
    u8 out_passwd[50]={0};
    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);//密码加密存储
    if(out_len<16)
        return  false;
    QByteArray pwd_ba((char*)out_passwd, out_len);
    pass_word=pwd_ba.toBase64();
    QString str_query = QString::asprintf("INSERT INTO account_list_tb (account, pass_word, auth, parent_account, phone) VALUES (  \"%s\", \"%s\", %u, \"%s\", \"%s\")",\
                                          account.toUtf8().data(), pass_word.toUtf8().data(), auth, \
                                          parent_account.toUtf8().data(), phone.toUtf8().data());
    if( runSqlQuery(str_query))
    {
        qDebug("addAccountNode ok!");
        return true;
    }

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

四、数据库操作——改

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

//更新账户密码
bool AccountSqlite::updateAccountPassWord(QString account, QString pass_word)
{
    if(pass_word.isEmpty())
    {
        pass_word="12345678";
    }
    u8 out_passwd[50]={0};
    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);
    if(out_len<16)
        return  false;
    QByteArray pwd_ba((char*)out_passwd, out_len);
    pass_word=pwd_ba.toBase64();

    QString str_query = QString::asprintf("UPDATE account_list_tb SET pass_word=\"%s\"   WHERE account=\"%s\"",
                                          pass_word.toUtf8().data(), account.toUtf8().data());
//    qDebug()<<str_query;
    if( runSqlQuery(str_query))
    {
        qDebug("updateAccountPassWord ok!");
        return true;
    }
    return false;
}

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

//更新账户的应用列表
bool AccountSqlite::updateAccountAppList(QString account, QList<u32> app_list)
{
    QJsonDocument json_doc;
    QJsonObject root_obj;
    QJsonArray app_array;
    int nSize=app_list.size();
    for(int i=0; i<nSize; i++)
    {
        u32 app_id=app_list.at(i);
        if(app_id>0)
        {
            app_array.append((qint64)app_id);
        }
    }
    root_obj.insert("app_list", app_array);
    json_doc.setObject(root_obj);
    QByteArray json_ba=json_doc.toJson(QJsonDocument::Compact);//转为数据保存
    qDebug()<<"json_ba="<<json_ba.data();
    QString str_query = QString::asprintf("UPDATE account_list_tb SET  app_list=\"%s\"   WHERE account=\"%s\"",
                                          json_ba.toBase64().data(), account.toUtf8().data());

//    qDebug()<<str_query;
    if( runSqlQuery(str_query))
    {
        qDebug("updateAccountAppList ok!");
        return true;
    }
    return false;
}
五、数据库操作——查

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

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


//根据名称 获取帐号信息
bool AccountSqlite::selectAccountByName(QString account, AccountNodeStruct &account_node)
{
    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());

//    qDebug()<<str_query;
    if(runSqlQuery(str_query)==false)
    {
        qDebug("selectAccountNode error_01!");
         return false;
    }
    while(m_sqlQuery.next())
    {
        int ptr=0;
        account_node.account=m_sqlQuery.value(ptr++).toString();
        QString pwd_str=m_sqlQuery.value(ptr++).toString();
        QByteArray pwd_ba=QByteArray::fromBase64(pwd_str.toUtf8());
        u8 out_passwd[50]={0};
        int out_len=drv_com.aes_decrypt_buff((u8*)pwd_ba.data(), (u16)pwd_ba.size(), out_passwd, sizeof (out_passwd), m_keyBuff);//存储密码解密
        if(out_len<=0)
        {
            m_sqlQuery.finish();
            return  false;
        }
        account_node.passWord=QString((char*)out_passwd);
        account_node.auth=m_sqlQuery.value(ptr++).toUInt();
        account_node.parentAccount=m_sqlQuery.value(ptr++).toString();
        account_node.phone=m_sqlQuery.value(ptr++).toString();

        QString json_str=m_sqlQuery.value(ptr++).toString();//应用列表JSON
        QByteArray json_ba=QByteArray::fromBase64(json_str.toUtf8());
        QJsonParseError json_error;
        QJsonDocument json_doc;
        json_doc = QJsonDocument(QJsonDocument::fromJson(json_ba, &json_error));//转为JSON格式
        if(json_error.error != QJsonParseError::NoError)
        {
            qDebug()<<"json error= "<<json_error.error;
        }
        else
        {
            QJsonObject root_obj = json_doc.object();
            if(root_obj.contains("app_list"))//应用列表
            {
                QJsonValue value=root_obj.value("app_list");
                if(value.isArray())
                {
                    QJsonArray app_array=value.toArray();
                    int nSize=app_array.size();
                    for(int i=0; i<nSize; i++)
                    {
                        QJsonValue value=app_array.at(i);
                        if(value.isDouble())
                        {
                            u32 app_id=(u32)value.toDouble();
                            if(app_id>0)
                            {
                                account_node.appList.append(app_id);//添加app_id
                            }
                        }
                    }
                }
            }
        }
        account_node.createTime=m_sqlQuery.value(ptr++).toString();
        m_sqlQuery.finish();
        return true;
    }
    qDebug("selectAccountNode error_02!");
    return false;
}

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

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/603804.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Netty的第一个简单Demo实现

说明 Netty 的一个练习&#xff0c;使用 Netty 连通 服务端 和 客户端&#xff0c;进行基本的通信。 需求 Client 连接服务端后&#xff0c;给服务端发送消息HelloServer Server 客户端连接成功后&#xff0c;打印连接成功读取到客户端的消息后&#xff0c;打印到控制台&…

企业是保留传统的MES还是换新的MES?

在选择上MES系统的时候&#xff0c;企业可以根据自身所处行业不同、当前阶段不同&#xff0c;以及业务需求的差异&#xff0c;对症下药&#xff0c;选择适合自己的解决方案。对于有些企业本来就有MES系统&#xff0c;但是已经过时过旧&#xff0c;就要考虑换新的MES系统了. 保留…

高精度数学计算的瑞士军刀,mpmath库详解与应用示例

写在前言 hello&#xff0c;大家好&#xff0c;我是一点&#xff0c;专注于Python编程&#xff0c;如果你也对感Python感兴趣&#xff0c;欢迎关注交流。 做为一个一只脚已经踏进35岁大关的程序员&#xff0c;对于职场&#xff0c;几乎向上无望&#xff0c;已经没有太多的期待…

DJANGO_PART 1

DJANGO_PART 1 文章目录 DJANGO_PART 11. 安装DJANGO2. 创建项目3. APP概念4. 快速上手5. templates6. 引入其它静态文件7. 模板语法8. 请求与响应 1. 安装DJANGO 安装语句&#xff1a;pip install django 2. 创建项目 django中项目会有一些默认的文件和默认的文件夹 终端创建…

渗透之sql注入实战2(二次注入)

目录 平台地址&#xff1a; 开始&#xff1a; 方法1&#xff1a; 方法二 找提示 这里存在一个文件包含&#xff08;file&#xff09;。 爆源码 index.php源码 confirm.php源码&#xff1a; search.php源码&#xff1a; change.php源码&#xff1a; delete.php源码&…

Linux---HTTP协议

HTTP HTTP协议&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一种应用层协议&#xff0c;主要用于在Web浏览器和Web服务器之间传输数据。 一、认识URL 平时我们俗称的 " 网址 " 其实就是说的 URL http请求样例 看起来是一行一…

英语学习笔记7——Are you a teacher?

Are you a teacher? 你是教师吗&#xff1f; 词汇 Vocabulary name /neɪm/ n. 名字&#xff0c;名声 英文名字构成&#xff1a; 名 字 姓      given name family name  也叫做&#xff1a;first name last name      例&#xff1a;Yanyan Gao 例句&#xff1…

Baidu Comate智能编码助手-程序员的神助攻

&#x1f389;Baidu Comate:程序员编程最牛的辅助利器&#xff0c;没有之一 &#x1f388;AI编程助力开发者提质增效 智能代码补全&#xff1a;AI编程助手可以根据上下文和用户的编码习惯&#xff0c;智能地提供代码补全和建议&#xff0c;帮助程序员更快速地编写代码&#xf…

Service 和 Ingress

文章目录 Service 和 IngressServiceEndpointservice 的定义代理集群外部服务反向代理外部域名Service 常用类型 IngressIngress-nginx安装使用 Service 和 Ingress service 和 ingress 是kubernetes 中用来转发网络请求的两个服务&#xff0c;两个服务用处不同&#xff0c;se…

OpenGL 入门(四)—— 贴纸与美颜滤镜

本篇我们来介绍贴纸效果与美颜滤镜的实现。 1、贴纸效果 贴纸实际上是一个图片&#xff0c;用 Bitmap 加载图片后用 OpenGL 渲染到指定的位置上。我们举例添加一个耳朵贴纸&#xff1a; 1.1 获取人脸位置 上一篇我们在讲大眼滤镜时&#xff0c;在 Native 层除了获取到人脸 5…

JAVA学习笔记(第三周)

文章目录 继承概述使用场景继承的特点子类继承的内容成员变量访问特点成员方法访问特点方法的重写构造方法this super 多态多态的表现形式多态的前提成员变量和方法调用instanceof优势弊端 包包名的规则全类名final常量 权限修饰符代码块 继承 概述 继承就是子类继承父类的特征…

PHP ASCII码的字符串用mb_convert_encoding 转utf-8之后不生效

检测数据类型是ascii,转码之后再检测还是utf-8没生效 private function toUTF8($str){$encode mb_detect_encoding($str, array("ASCII",UTF-8,"GB2312","GBK",BIG5,LATIN1));if ($encode ! UTF-8) {$str1 mb_convert_encoding($str, UTF-8, …

(2024,LSTM,Transformer,指数门控,归一化器状态,多头内存混合)xLSTM:扩展的 LSTM

xLSTM: Extended Long Short-Term Memory 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 简介 2. 扩展的 LSTM 2.1 LSTM 回顾 2.2 sLSTM 2.3 mLSTM 2.4 xLSTM 架构 2…

python:画饼图

我现在因工作需要在写一篇中文文章&#xff0c;领导要我用python处理数据和画图&#xff0c;那我也刚好学习一下python画图。 import matplotlib.pyplot as plt # 饼图数据 labels [A, B, C, D] sizes [15, 30, 45, 10] # 每个部分的大小 # 绘制饼图 plt.figure(figsize(6,…

读书笔记——《高质量C++/C编程指南》(2)

目录 前言 命名规则 共性规则 简单的Windows应用程序命名规则 表达式和基本语句 运算符优先级 复合表达式 if语句 布尔变量与零值比较 整型变量与零值比较 浮点变量与零值比较 指针变量与零值比较 对if 语句的补充说明 循环语句的效率 for 语句的循环控制变量 s…

数据库大作业——基于qt开发的图书管理系统(四)项目目录的整理与绘制登录页面

项目目录的管理 前言 在上几篇的文章里面我们完成了基本环境的搭建,整理了项目数据库表结构并且成功的手动的加载了Qt的mysql数据库驱动&#xff0c;现在就要开始完成项目准备工作的最后一步:构建项目目录,一个好的项目离不开一个好的代码组织结构,所以在开始动手写我们这个项…

Java | Leetcode Java题解之第70题爬楼梯

题目&#xff1a; 题解&#xff1a; public class Solution {public int climbStairs(int n) {double sqrt5 Math.sqrt(5);double fibn Math.pow((1 sqrt5) / 2, n 1) - Math.pow((1 - sqrt5) / 2, n 1);return (int) Math.round(fibn / sqrt5);} }

无人机+通信中继:短波电台技术详解

随着无线通信技术的不断发展&#xff0c;无人机作为一种新型的信息传输平台&#xff0c;已经在多个领域得到了广泛应用。其中&#xff0c;无人机与短波电台的结合&#xff0c;为通信中继领域带来了全新的可能性。本文将详细解析无人机在通信中继中的应用&#xff0c;以及短波电…

产品专访|“产品”远程运维系统与“设备”远程运维系统的区别?

在日益复杂的工业制造环境下&#xff0c;远程运维已经成为生产制造企业不可或缺的一部分。在这个大背景下&#xff0c;产品远程运维系统和设备远程运维系统的需求越来越多&#xff0c;各自发挥着独特的作用。然而&#xff0c;尽管它们都涉及到远程运维的概念&#xff0c;但在实…

Nest.js中使用任务调度

java中的xxl在nestJs中是有内置的任务调度nestjs/schedule npm install --save nestjs/schedule 在model中引入使用 在service中直接使用就行 具体间隔多久看官方配置 Task Scheduling | NestJS 中文文档 | NestJS 中文网