物联网实战--平台篇之(四)账户后台交互

目录

一、交互逻辑

二、请求验证码

三、帐号注册

四、帐号/验证码登录

五、重置密码


本项目的交流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

一、交互逻辑

        对于账户的注册、登录等流程的交互逻辑基本上是这样的:

        1、用户端APP提交相关信息;

        2、服务器接收解析信息,同时对信息的合法性进行认证;

        3、根据命令类型执行对应的操作;

        4、返回操作结果;

        5、用户端APP显示结果并进入下一步。

        两个工程都有跟账户相关的类文件,核心就是对这个流程中的不同指令进行处理,接下来根据指令详细看下处理流程。

        

二、请求验证码

        先看下图箭头所指的三个关键信息,这是手机APP的代码,刚开始的时候会获取设备的mac地址和一个随机数,然后把他们作为订阅话题的组成部分,这样服务器就可以根据上报携带的身份信息进行针对性地回应了,这个订阅话题示例:yyy125/as/pub/account/E0:23:A3:62:63:E2/1629/#

这样基本上具备唯一性了。第三个箭头是账户相关的前后端交互接口,QML文件中可以直接用theAccountMan调用AccountMan类中的后端函数。

        接下来进入用户端APP的AccountMan类中,核心是下图框框内的几个函数,分别是请求验证码、请求注册、请求账户登录、请求验证码登录和请求重置密码,可以看出,所有流程都是以APP端请求开始的。其它是对字符串进行有效性验证的函数,比如手机号、账户名和密码的格式进行校验,便于前端检测输入的合法性。

        

void AccountMan::requestVerCode(QString account, QString phone)
{QJsonObject root_obj;QJsonDocument json_doc;root_obj.insert("cmd_type", "req_vercode");root_obj.insert("account", account);root_obj.insert("phone", phone);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("code");emit sigMqttPushMessage(topic, msg_ba);
}QString AccountMan::makePubTopic(QString key_str)
{QString topic=QString(TOPIC_HEAD)+"as/sub/account/"+key_str;return  topic;
}

        以上是请求验证码的代码,参数账户名和手机号由前端输入,其中账户名可以为空,保留功能,核心是要手机号,然后用json的形式组合参数,命令类型是req_vercode,随机数和mac地址是主程序中传进来的,服务器就是根据这两个信息组合返回的发布话题的,这样才能收到返回结果。在这里还有个组合发布话题的函数,现在传入的是code关键字,组合后就是yyy125/as/sub/account/code,字面上就能理解了这个话题的数据是发送给应用服务器的,内容是账户相关的,子功能为验证码,至于具体要干嘛那就交给数据包内的cmd_type字段去处理了,对于验证码功能主要就是 “请求验证码” 命令了。

        数据发送到服务器后,主程序先根据话题筛选出账户类话题,然后把该类型话题的数据转发到账户线程中去进一步处理,代码如下:


void MainInterface::slotMqttReceived(const QMQTT::Message &message)
{QJsonParseError json_error;QJsonDocument json_doc;QString recv_topic=message.topic();qDebug()<<"msg topic= "<<message.topic();qDebug()<<message.payload().data();json_doc = QJsonDocument::fromJson(message.payload(), &json_error);//转为JSON格式if(json_error.error != QJsonParseError::NoError){
//        qDebug()<<"json error= "<<json_error.error;return;}QJsonObject rootObj = json_doc.object();if(recv_topic.contains("account/"))//账户类相关的话题{emit sigAccountThreadMessage(recv_topic, rootObj);}}

        进入账户线程后代码如下,根据话题的子功能进行分类处理,对于验证码类型,我们刚才组合话题时候添加的关键字是code,那么话题内有/code的就进行具体的验证码数据解析,具体函数是parseVerCodeTopic(),在函数内,我们获取了命令类型、手机号、mac地址和随机数等数据,命令类型目前只有请求验证码,对于此命令的处理步骤是:

        1、检查手机号;

        2、获取随机验证码;

        3、缓存验证码;

        4、发送验证码;

        5、回复用户端APP

        其中,步骤3在缓存时会检查该手机号之前是否有验证码存在,如果有的话就缓存失败,同时也会回复用户端失败的信息;验证码的添加成功后会在一个列表内暂存30秒,超时后自动删除;步骤4中的发送验证码,理论上需要SMS服务器的,这个是需要企业才能办理的业务,我们当前先打印出来就好,实际的发送后面再专门出一篇。


void AccountThread::slotAccountThreadMessage(QString topic, QJsonObject root_obj)
{if(topic.contains("/reg"))//注册相关{parseRegTopic(root_obj);}else if(topic.contains("/login"))//登录相关{parseLoginTopic(root_obj);}else if(topic.contains("/code"))//验证码相关{parseVerCodeTopic(root_obj);}else if(topic.contains("/passwd"))//密码相关{parsePasswdTopic(root_obj);}else if(topic.contains("/child"))//子账户相关{}else if(topic.contains("/app"))//应用相关{}else if(topic.contains("/group"))//分组相关{}else if(topic.contains("/device"))//设备相关{}
}//解析验证码话题
void AccountThread::parseVerCodeTopic(QJsonObject root_obj)
{QString cmd_type="";if(root_obj.contains("cmd_type"))//命令类型{QJsonValue value = root_obj.value("cmd_type");if(value.isString()){cmd_type=value.toString();}}QString account="";if(root_obj.contains("account"))//账户{QJsonValue value = root_obj.value("account");if(value.isString()){account=value.toString();}}int rand_num=0;if(root_obj.contains("rand_num"))//随机数{QJsonValue value = root_obj.value("rand_num");if(value.isDouble()){rand_num=(int)value.toDouble();}}QString mac_str="";if(root_obj.contains("mac"))//mac{QJsonValue value = root_obj.value("mac");if(value.isString()){mac_str=value.toString();}}QString phone="";if(root_obj.contains("phone"))//手机号码{QJsonValue value = root_obj.value("phone");if(value.isString()){phone=value.toString();}}if(cmd_type=="req_vercode")//请求验证码{if(phone.isEmpty()){qDebug()<<"phone.isEmpty()";return;}QString ver_code_str=takeVerCode();bool ok=addReqVerCodeNode(account, phone, ver_code_str);//添加进列表,进行超时检测if(ok){//向SMS服务器发送验证码
//            sendSmsCheckCode(phone, ver_code_str);ackReqVerCodeState(account, mac_str, rand_num, phone, 0, "验证码已发送!");//返回验证码已发送状态qDebug()<<"req_vercode ok";}else{ackReqVerCodeState(account, mac_str, rand_num, phone, 1, "重复发送!");//}}
}

        回复函数根据不同的命令类型参数略有区别,下面是请求验证码的函数,注意点就是回复的话题,需要根据上传的mac、随机数和关键字段组合,与开头就形成闭环了。

void AccountThread::ackReqVerCodeState(QString account, QString mac_str, int rand_num, QString phone, int result, QString ack_str)
{QJsonObject root_obj;QJsonDocument json_doc;root_obj.insert("cmd_type", "req_vercode");root_obj.insert("result", result);root_obj.insert("phone", phone);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, "code");emit sigMqttPushMessage(topic, msg_ba);
}//生成发布话题
QString AccountThread::makePubTopic(QString account, QString mac_str, int rand_num, QString key_str)
{if(account.isEmpty()){}QString topic=QString(TOPIC_HEAD) + SERVER_PUB_TOPIC+QString("/account/")+mac_str+QString::asprintf("/%d/", rand_num)+key_str;return topic;
}

        回到用户端这边,主程序也是根据话题类型归类处理的,目前只进行简单的信息提示处理,就是将服务器的回复信息发送到QML前端进行显示交互。

        

        总的来讲,整个流程就是这样了,其他注册、登录等功能也是类似的。

三、帐号注册

        1、用户端发送注册信息,包含账户、密码、手机、验证码等:

void AccountMan::requestReg(QString account, QString passwd, QString phone, QString ver_code)
{QJsonObject root_obj;QJsonDocument json_doc;root_obj.insert("cmd_type", "req_reg");root_obj.insert("account", account);root_obj.insert("pass_word", passwd);root_obj.insert("phone", phone);root_obj.insert("rand_num", m_randNum);root_obj.insert("mac", m_macStr);root_obj.insert("ver_code", ver_code);json_doc.setObject(root_obj);QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);QString topic=makePubTopic("reg");emit sigMqttPushMessage(topic, msg_ba);
}

        2、服务器接收解析,在这里要对账户和手机号的重复性进行检查,注册成功后会默认新建一个app_id。


//解析注册话题
void AccountThread::parseRegTopic(QJsonObject root_obj)
{QString cmd_type="";if(root_obj.contains("cmd_type"))//命令类型{QJsonValue value = root_obj.value("cmd_type");if(value.isString()){cmd_type=value.toString();}}QString account="";if(root_obj.contains("account"))//账户{QJsonValue value = root_obj.value("account");if(value.isString()){account=value.toString();}}int rand_num=0;if(root_obj.contains("rand_num"))//随机数{QJsonValue value = root_obj.value("rand_num");if(value.isDouble()){rand_num=(int)value.toDouble();}}QString mac_str="";if(root_obj.contains("mac"))//mac{QJsonValue value = root_obj.value("mac");if(value.isString()){mac_str=value.toString();}}QString pass_word="";if(root_obj.contains("pass_word"))//密码{QJsonValue value = root_obj.value("pass_word");if(value.isString()){pass_word=value.toString();}}QString phone="";if(root_obj.contains("phone"))//手机号码{QJsonValue value = root_obj.value("phone");if(value.isString()){phone=value.toString();}}QString ver_code="";if(root_obj.contains("ver_code"))//验证码{QJsonValue value = root_obj.value("ver_code");if(value.isString()){ver_code=value.toString();}}if(cmd_type=="req_reg")//请求注册{bool ok;account.toDouble(&ok);if(ok){ackReqRegState(account, mac_str, rand_num, phone, 6, "账户不能为纯数字!");return;}AccountSqlite::AccountNodeStruct tag_account;bool ok1, ok2;ok1=m_accountSqlite->searchAccountByName(account, tag_account);ok2=m_accountSqlite->searchAccountByPhone(phone, tag_account);if(ok1==false && ok2==false)//未找到重复的,可以注册{for(auto iter : m_reqVerCodeList){if(iter.phone == phone)//根据手机号检索{if(iter.verCode == ver_code)//验证码相等{bool ok=m_accountSqlite->addAccountNode(account, pass_word, 0x00, "", phone);if(ok){ackReqRegState(account, mac_str, rand_num, phone, 0, "注册成功!");qDebug()<<"reg ok!";//自动创建一个应用u32 max_app=m_accountSqlite->selectMaxAppID();u32 new_app_id=0;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;m_accountSqlite->addAppIDToList(new_app_id, account);}else{ackReqRegState(account, mac_str, rand_num, phone, 1, "数据库存储出错!");qDebug()<<"reg sql error!!";}}else{ackReqRegState(account, mac_str, rand_num, phone, 2, "验证码错误!");qDebug()<<"reg ver_code error!";}return;}}ackReqRegState(account, mac_str, rand_num, phone, 3, "验证码超时!");qDebug()<<"no found code!";}else{if(ok1==true){ackReqRegState(account, mac_str, rand_num, phone, 4, "账户名已存在!");qDebug()<<"have same account="<<account;}else if(ok2==true){ackReqRegState(account, mac_str, rand_num, phone, 5, "手机号已存在!");qDebug()<<"have same phone="<<phone;}}}
}

四、帐号/验证码登录

        1、用户发送帐号登录信息,主要包括账户和密码:

void AccountMan::requestLogin(QString account, QString pass_word, int remember)
{if(remember){}QJsonObject root_obj;QJsonDocument json_doc;root_obj.insert("cmd_type", "login_pwd");root_obj.insert("account", account);root_obj.insert("pass_word", pass_word);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("login");emit sigMqttPushMessage(topic, msg_ba);
}

        如果是验证码登录,那就是手机号和验证码

void AccountMan::requestLoginByVerCode(QString phone, QString ver_code)
{QJsonObject root_obj;QJsonDocument json_doc;root_obj.insert("cmd_type", "login_code");root_obj.insert("phone", phone);root_obj.insert("ver_code", ver_code);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("login");emit sigMqttPushMessage(topic, msg_ba);
}

        2、服务端接收解析,登录操作要仔细校验账户名和密码,账户也可以填手机号,服务端会自己判断;验证码登录的时候,如果该手机号未注册则会自动注册,用户名默认是Y+手机号,密码随机,需要自己去重置。


//解析登录话题
void AccountThread::parseLoginTopic(QJsonObject root_obj)
{QString cmd_type="";if(root_obj.contains("cmd_type"))//命令类型{QJsonValue value = root_obj.value("cmd_type");if(value.isString()){cmd_type=value.toString();}}QString account="";if(root_obj.contains("account"))//账户{QJsonValue value = root_obj.value("account");if(value.isString()){account=value.toString();}}int rand_num=0;if(root_obj.contains("rand_num"))//随机数{QJsonValue value = root_obj.value("rand_num");if(value.isDouble()){rand_num=(int)value.toDouble();}}QString mac_str="";if(root_obj.contains("mac"))//mac{QJsonValue value = root_obj.value("mac");if(value.isString()){mac_str=value.toString();}}QString pass_word="";if(root_obj.contains("pass_word"))//密码{QJsonValue value = root_obj.value("pass_word");if(value.isString()){pass_word=value.toString();}}QString phone="";if(root_obj.contains("phone"))//手机号码{QJsonValue value = root_obj.value("phone");if(value.isString()){phone=value.toString();}}QString ver_code="";if(root_obj.contains("ver_code"))//验证码{QJsonValue value = root_obj.value("ver_code");if(value.isString()){ver_code=value.toString();}}AccountSqlite::AccountNodeStruct tag_account;if(cmd_type=="login_pwd")//密码登录{if(!account.isEmpty()){                        bool ok=account.toDouble();if(ok)//手机号登录{QString phone=account;if( m_accountSqlite->selectAccountByPhone(phone, tag_account)==false){ackLoginState("", phone, "", mac_str, rand_num, 0, "", 1, "手机号未注册!");return;}}else{if( m_accountSqlite->selectAccountByName(account, tag_account)==false){ackLoginState(account, "", "", mac_str, rand_num, 0, "", 2, "帐号未注册!");return;}}if((account==tag_account.account || account==tag_account.phone) && pass_word==tag_account.passWord)//再次校验用户名跟密码{ackLoginState(tag_account.account,tag_account.phone, tag_account.parentAccount, mac_str, rand_num, tag_account.auth, tag_account.createTime, 0, "登录成功!");}else{ackLoginState(tag_account.account,tag_account.phone, tag_account.parentAccount, mac_str, rand_num, tag_account.auth, tag_account.createTime, 3, "密码错误!");}}else{ackLoginState(account, "", "", mac_str, rand_num, 0, "", 4, "账户不存在!");}}else if(cmd_type=="login_code")//验证码登录{bool ok; phone.toDouble(&ok);if(phone.size()!=11 || !ok){qDebug()<<"login phone="<<phone<<" error!";ackReqRegState(phone, mac_str, rand_num, phone, 1, "手机号有误!");return;}bool flag=false;for(auto iter : m_reqVerCodeList){if(iter.phone == phone){if(iter.verCode == ver_code){flag=true;}break;}}if(flag==false){ackReqRegState(phone, mac_str, rand_num, phone, 1, "验证码错误!");return;}tag_account.phone.clear();m_accountSqlite->searchAccountByPhone(phone, tag_account);if(phone==tag_account.phone){ackReqRegState(phone, mac_str, rand_num, phone, 0, "登录成功!");}else//新手机号,直接注册{account="Y"+phone;pass_word.clear();for(int i=0; i<8; i++){pass_word+=QString::asprintf("%d", drv_com.takeRandNumber()%10);//随机密码}qDebug()<<account<<" pass_word="<<pass_word;bool ok=m_accountSqlite->addAccountNode(account, pass_word, 0x00, "", phone);if(ok){ackReqRegState(phone, mac_str, rand_num, phone, 0, "登录(注册)成功!");qDebug()<<"reg ok!";//自动创建一个应用u32 max_app=m_accountSqlite->selectMaxAppID();u32 new_app_id=0;if(max_app>APP_ID_MIN)new_app_id=max_app+1;else  new_app_id=APP_ID_MIN+1;qDebug()<<"phone new_app_id="<<new_app_id;m_accountSqlite->addAppIDToList(new_app_id, account);}else{ackReqRegState(phone, mac_str, rand_num, phone, 1, "数据库存储错误!");qDebug()<<"reg sql error!!";}}}}

五、重置密码

        1、用户发送重置信息主要包括用户名、新密码、手机号和校验码:

void AccountMan::requestResetPasswd(QString account, QString pass_word, QString phone, QString ver_code)
{QJsonObject root_obj;QJsonDocument json_doc;root_obj.insert("cmd_type", "reset_pwd");root_obj.insert("account", account);root_obj.insert("pass_word", pass_word);root_obj.insert("phone", phone);root_obj.insert("rand_num", m_randNum);root_obj.insert("mac", m_macStr);root_obj.insert("ver_code", ver_code);json_doc.setObject(root_obj);QByteArray msg_ba = json_doc.toJson(QJsonDocument::Indented);QString topic=makePubTopic("passwd");emit sigMqttPushMessage(topic, msg_ba);
}

        2、服务端接收解析,首先要检查与数据库里的账户和手机是否匹配,然后校验验证码,通过后就可以更新密码了,为了确保写入成功,需要再读取出来进行对比,新密码校验成功后才算真正完成了密码重置。


void AccountThread::parsePasswdTopic(QJsonObject root_obj)
{QString cmd_type="";if(root_obj.contains("cmd_type"))//命令类型{QJsonValue value = root_obj.value("cmd_type");if(value.isString()){cmd_type=value.toString();}}QString account="";if(root_obj.contains("account"))//账户{QJsonValue value = root_obj.value("account");if(value.isString()){account=value.toString();}}QString mac_str="";if(root_obj.contains("mac"))//mac{QJsonValue value = root_obj.value("mac");if(value.isString()){mac_str=value.toString();}}int rand_num=0;if(root_obj.contains("rand_num"))//rand_num{QJsonValue value = root_obj.value("rand_num");if(value.isDouble()){rand_num=(int)value.toDouble();}}QString pass_word="";if(root_obj.contains("pass_word"))//密码{QJsonValue value = root_obj.value("pass_word");if(value.isString()){pass_word=value.toString();}}QString phone="";if(root_obj.contains("phone"))//手机号码{QJsonValue value = root_obj.value("phone");if(value.isString()){phone=value.toString();}}QString ver_code="";if(root_obj.contains("ver_code"))//验证码{QJsonValue value = root_obj.value("ver_code");if(value.isString()){ver_code=value.toString();}}if(cmd_type=="reset_pwd"){if(account.isEmpty() || phone.isEmpty()){qDebug()<<"account.isEmpty() || phone.isEmpty()";return;}AccountSqlite::AccountNodeStruct tag_account;m_accountSqlite->selectAccountByPhone(phone, tag_account);if(tag_account.account!=account){ackResetPasswdState(account, mac_str, rand_num, 4, "账户与手机不匹配!");return;}for(auto iter : m_reqVerCodeList){if(iter.phone == phone){if(iter.verCode == ver_code){tag_account.passWord.clear();m_accountSqlite->updateAccountPassWord(account, pass_word);//更新密码m_accountSqlite->selectAccountByName(account, tag_account);//重新获取密码if(tag_account.passWord==pass_word)//新密码校验{ackResetPasswdState(account, mac_str, rand_num, 0, "密码修改成功!");qDebug("set new pwd ok!");}else{ackResetPasswdState(account, mac_str, rand_num, 1, "密码修改(校验)失败!");qDebug("set new pwd failed!");}}else{ackResetPasswdState(account, mac_str, rand_num, 2, "验证码错误!");qDebug()<<"reg ver_code error!";}return;}}ackResetPasswdState(account, mac_str, rand_num, 3, "验证码已过期!");}}

        服务端经常有数据库操作步骤,这里再看下数据库的创建和打开,因为账户管理任务是独立的线程,所以我这里数据库定义为指针类型,然后在槽函数里new一个 AccountSqlite(),这样数据库操作才属于线程内部,这点很重要,这是QT的特性;然后就是打开数据库,参数就是文件名(包括路径)+连接名称,如果文件不存在就会自动创建,连接名称随意,这里一般不会重复;最后就是创建账户表和应用表了,之前数据库语句介绍过了,这样操作并不会重复创建库表。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

从离线到实时:无锡锡商银行基于 Apache Doris 的数据仓库演进实践

作者&#xff1a;武基鹏&#xff0c;无锡锡商银行 大数据技术经理 编辑整理&#xff1a;SelectDB 技术团队 导读&#xff1a;为实现数据资产的价值转化以及全面数字化、智能化的风险管理&#xff0c;无锡锡商银行大数据平台经历从 Hive 离线数据仓库到 Apache Doris 实时数据仓…

深入解析智能指针:从实践到原理

&#x1f466;个人主页&#xff1a;晚风相伴 &#x1f440;如果觉得内容对你有所帮助的话&#xff0c;还请一键三连&#xff08;点赞、关注、收藏&#xff09;哦 如果内容有错或者不足的话&#xff0c;还望你能指出。 目录 智能指针的引入 内存泄漏 RAII 智能指针的使用及原…

[附源码]秦时明月6.2魔改版_搭建架设教程_附GM工具_安卓苹果

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了 一. 演示视频 秦时明…

射频无源器件之耦合器

一. 耦合器的作用 在射频电路中,射频耦合器将一路微波功率按比例分成几路,用于检测或监测信号,如功率测量和波检测,还可改变信号的幅度、相位等特性,以满足不同的通信需求。根据输入与耦合端的功率差,常被分为5dB、6dB、10dB等耦合器。射频耦合器的类型主要包括定向耦合…

为软件教学文档增加实践能力

为了更方便软件教学&#xff0c;我们在凌鲨(OpenLinkSaas)上增加了公共资源引用的功能。 目前可以被引用的公共资源: 微应用常用软件公共知识库Docker模板 引用公共资源 引用微应用 目前微应用包含了主流数据库&#xff0c;终端等工具&#xff0c;可以方便的进行各种相关实…

OpenCV|简单绘制一个矩形

OpenCV中的rectangle() 为绘制矩形命令&#xff0c;形式如下&#xff1a; # (img: cv2.typing.MatLike, pt1: cv2.typing.Point, pt2: cv2.typing.Point, color: cv2.typing.Scalar, thickness: int ..., lineType: int ..., shift: int ...)cv2.rectangle(img, pt1, pt2, …

2024软件测试自动化面试题(含答案)

1.如何把自动化测试在公司中实施并推广起来的&#xff1f; 选择长期的有稳定模块的项目 项目组调研选择自动化工具并开会演示demo案例&#xff0c;我们主要是演示selenium和robot framework两种。 搭建自动化测试框架&#xff0c;在项目中逐步开展自动化。 把该项目的自动化…

数据结构-线性表-链表-2.3-6

有一个带头结点的单链表L&#xff0c;设计一个算法使其元素递增有序。 void sort(Linklist &L){LNode *pL->next,*pre;LNode *rp->next;p->nextNULL;pr;while(p){rp->next;preL;while(pre->next!NULL&&pre->next->data<p->data){prepre…

县供电公司员工向媒体投稿发文章用亲身经历告诉你并不难

在县供电公司的日子里,我肩负着一项至关重要的使命——信息宣传工作。这不仅仅是一份职责,更是连接公司与外界的桥梁,通过新闻稿件传递我们的声音,展示我们的成果。然而,回忆起刚刚踏入这个领域的时光,那段经历至今让我感慨万千。 初涉投稿,步履维艰 刚接手这项工作时,我的投稿…

暴打前任互动玩法开播教程

暴打前任互动玩法开播教程 暴打前任互动玩法开播教程【相关直播互动插件咩播需要额外官方购买】 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码&#xff1a;0b8x

安装docker20.10.18版本步骤

安装docker20.10.18版本步骤 准备低版本安装包 #安装20.10.18版本的dockercd /opt #切换目录#上传需要的docker20.10.18.zip安装包unzip docker20.10.18.zip #解压cd docker20.10.18/ #切换目录yum install -y *.rpm #安装systemctl enable --now docker.service #开机自启并…

NineData亮相2024中国移动算力网络大会

4月28日至29日&#xff0c;2024中国移动算力网络大会在苏州召开。大会以“算力网络点亮AI新时代”为主题&#xff0c;全面展示了中国移动最新算力网络成果与能力。江苏省委常委、苏州市委书记刘小涛&#xff0c;副省长赵岩出席开幕式并致辞。内蒙古自治区副主席白清元出席。中国…

redis集群-主从机连接过程

首先从机需要发送自身携带的replid和offset向主机请求连接 replid&#xff1a;replid是所有主机在启动时会生成的一个固定标识&#xff0c;它表示当前复制流的id&#xff0c;当从机第一次请求连接时&#xff0c;主机会将自己的replid发送给从机&#xff0c;从机在接下来的请求…

5月8日学习记录

_[FBCTF2019]RCEService&#xff08;preg_match函数的绕过&#xff09; 涉及知识点&#xff1a;preg_match函数绕过&#xff0c;json的格式&#xff0c;正则回溯 打开环境&#xff0c;要求用json的格式输入 搜索学习一下json的语法规则 数组&#xff08;Array&#xff09;用方括…

无意的一次学习,竟让我摆脱了Android控制?

由于鸿蒙的爆火&#xff0c;为了赶上时代先锋。到目前为止也研究过很长一段时间。作为一名Android的研发人员&#xff0c;免不了对其评头论足&#xff0c;指导文档如何写才算专业&#xff1f;页面如何绘制&#xff1f;页面如何跳转&#xff1f;有没有四大组件等等。 而Harmony…

PDF转word转ppt软件

下载地址&#xff1a;PDF转word转ppt软件.zip 平时工作生活经常要用到PDF转word转ppt软件&#xff0c;电脑自带的又要开会员啥的很麻烦&#xff0c;现在分享这款软件直接激活就可以免费使用了&#xff0c;超级好用&#xff0c;喜欢的可以下载

党建教育vr虚拟现实展厅真正实现了绿色、低碳的展示方式

在数字化浪潮席卷的今天&#xff0c;传统企业门户官网已难以满足企业日益增长的展示需求。面对这一挑战&#xff0c;北京华锐凭借深厚的行业经验和领先的技术实力&#xff0c;为您提供全新的元宇宙虚拟展厅制作服务&#xff0c;助您轻松打破现实与虚拟的界限&#xff0c;开启企…

第八届大数据与物联网国际会议(BDIOT 2024)即将召开!

第八届大数据与物联网国际会议(BDIOT 2024)将于2024年9月14-16日在澳门圣若瑟大学举行。数聚未来&#xff0c;物联世界&#xff01;BDIOT 2024旨在搭建为各位与会代表展示自己研究成果、分享经验、建立联系和开展合作的平台&#xff0c;共同探讨大数据与物联网领域的未来发展方…

人工智能-2024期中考试

前言 人工智能期中考试&#xff0c;认真准备了但是没考好&#xff0c;结果中游偏下水平。 第4题没拿分 &#xff08;遗传算法&#xff1a;知识点在课堂上一笔带过没有细讲&#xff0c;轮盘赌算法在书本上没有提到&#xff0c;考试的时候也没讲清楚&#xff0c;只能靠猜&…

鸿蒙OpenHarmony开发板:【子系统配置规则】

子系统 子系统配置规则 通过build仓下的subsystem_config.json可以查看所有子系统的配置规则。 {"arkui": {"path": "foundation/arkui", # 路径"name": "arkui" # 子系统名},"ai": {&q…