需求:
客户需要做一个纯手机接单的app,电脑接单也可以实现只是政策要求手机接单。因为晚上时常会遗忘订单,导致客户绩效考核。
解决思路:
既然是手机接单就有两种方法。root法和免root的无障碍法,root法有工具task app,免root有工具 一触即发app和自动精灵等等,我个人认为一触即发简单上手快推荐。
一触即发的条件判断有,截图法和控件查找法,开始我用控件法,但是app是uniapp开发的混合app,不能存在安卓控件的说法。因此只能使用截图法。
难点:
如果只是判断后点击那还简单,但是除了正常接单流程外还有一些异常要处理,下面一一列举
接单app和客户app需要持久化保活,在我之前的文章有处理办法。
网络不稳定导致的白色屏幕,这算是一种异常。可以通过铃声提醒的方式解决。
token会6小时后过期,过期后要么重新登录,要么自动续期。如果自动续期,需要重新编写app太复杂。这里采用重新登陆的方法,但是重新登陆需要短信验证码,因此引入新的问题,一触即发如何读取短信验证码。我的处理思路是,在检测到登陆超时,自动点击发送验证码,在接收验证码的手机上安装监听指定短信的app并转发给中间服务器,中间服务器临时保存验证码并提供获取验证码接口, 一触即发app发送请求获取验证码并填入app登陆。
细节:
我们先说一触即发脚本,在使用前需要在app里开启权限,开启无障碍、忽略电池优化,悬浮窗,所有的权限管理,在设置里显示 识图显示矩形,上键停止脚本
//在一触即发导入脚本即可使用,这里在一个脚本处理了两个app,思路就是截图判断是否在某一页或者是订单,是的话点击即可,包含异常处理 //打开 铁塔aiot app并处理接单 //定义两个变量 变量 永真变量 = 真; 变量 验证码 = ""; //开始循环接单 判断循环首 永真变量 = true 打开应用("com.chinatower.powerservice"); 随机延时(5000,5000); 弹出提示("开始查找aiot订单"); //这里判断是否是接单的页面,依据是特征图片 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_ADBA2F4708FDE61B5248F2B5A19A239E.dddbbb,精确度:80 ,null) 则 弹出提示("找到工单模块,管理工单"); //点击接单模块 点击(222,855,1); 点击(222,855,1); 随机延时(10000,10000); //检查有没有订单; 如果 屏幕中是否包含文字("暂无内容" ,'node' ,null) 则 弹出提示("暂无内容"); 返回键(); //登录异常判断; 如果 屏幕中是否包含文字("登录超时" ,'node' ,null) 或 屏幕中是否包含文字("账号验证" ,'node' ,null) 则 弹出提示("登录异常处理"); 判断循环首 永真变量 = true 震动(长)(); 铃声提示(); 弹出提示("请重新登录"); 关闭应用("com.chinatower.powerservice"); //因为关闭方法不起作用只能三个返回表示关闭app 返回键(); 返回键(); 返回键(); // 任务键(); 打开应用("com.chinatower.powerservice"); 弹出提示("正在输入密码"); //删除登录; 点击(960,726,1); 输入文字(280,754,"guokai1"); //删除密码; 点击(889,873,1); 输入文字(304,879,"82212991@Lbb520"); //同意协议; 点击(89,2307,1); //点击登录; 点击(549,1393,1); 随机延时(3000,3000); //发送验证码; 点击(882,916,1); 随机延时(5000,5000); 验证码 = 网页访问("http://125.75.111.54:9002/api/sms/latest" ,'GET' ,header:{} ,data:{}); 验证码 = 取文本中间("{验证码}","验证码",","); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 输入文字(361,926,"{验证码}"); //登录; 点击(515,1413,1); 如果 屏幕中是否包含文字("设备汇总" ,'node' ,null) 或 屏幕中是否包含文字("常用功能" ,'node' ,null) 或 屏幕中是否包含文字("修改密码" ,'node' ,null) 或 屏幕中是否包含文字("待办工单" ,'node' ,null) 则 弹出提示("登录成功进入接单"); 退出循环; 否则 // //; 弹出提示("登录失败重新登录"); 结束 如果 判断循环尾 否则 弹出提示("未检测到登录异常,尝试接单"); 结束 如果 否则 弹出提示("发现工单"); //登录异常判断; 如果 屏幕中是否包含文字("登录超时" ,'node' ,null) 或 屏幕中是否包含文字("账号验证" ,'node' ,null) 则 弹出提示("登录异常处理"); 判断循环首 永真变量 = true 震动(长)(); 铃声提示(); 弹出提示("请重新登录"); 关闭应用("com.chinatower.powerservice"); 返回键(); 返回键(); 返回键(); // 任务键(); 打开应用("com.chinatower.powerservice"); 弹出提示("正在输入密码"); //删除登录; 点击(960,726,1); 输入文字(280,754,"guokai1"); //删除密码; 点击(889,873,1); 输入文字(304,879,"82212991@Lbb520"); //同意协议; 点击(89,2307,1); //点击登录; 点击(549,1393,1); 随机延时(3000,3000); //发送验证码; 点击(882,916,1); 随机延时(5000,5000); 验证码 = 网页访问("http://125.75.111.54:9002/api/sms/latest" ,'GET' ,header:{} ,data:{}); 验证码 = 取文本中间("{验证码}","验证码",","); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 输入文字(361,926,"{验证码}"); //登录; 点击(515,1413,1); 如果 屏幕中是否包含文字("设备汇总" ,'node' ,null) 或 屏幕中是否包含文字("常用功能" ,'node' ,null) 或 屏幕中是否包含文字("修改密码" ,'node' ,null) 或 屏幕中是否包含文字("待办工单" ,'node' ,null) 则 弹出提示("登录成功进入接单"); 退出循环; 否则 // //; 弹出提示("登录失败重新登录"); 结束 如果 判断循环尾 否则 弹出提示("未检测到登录异常,尝试接单"); 结束 如果 随机延时(5000,5000); //进入订单; 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_5561A0D3E52614D9F9D6371B196D921B_copy_copy_copy.dddbbb",精确度: 80,null,false); 点击(508,530,1); 点击(467,512,1); // 点击(375,256,1); 随机延时(3000,3000); //接单; 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_D354F18F6107B973B80B39984C35A4E0.dddbbb",精确度: 55,null,false); 点击(547,2307,1); //接单; // 点击(368,1244,1); //确定; 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_1B6BF0D4DD8304905281333C863850C3.dddbbb",精确度: 80,null,false); 点击(684,1424,1); //确定; // 点击(455,754,1); 返回键(); // 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_E22206D6CB4ECE772D26A7CE5E21A5FF.dddbbb",精确度: 80,null,false); 点击(255,840,1); 点击(502,560,1); 结束 如果 否则 弹出提示("未找到"); 点击(138,2268,1); 返回键(); //登录异常判断; 如果 屏幕中是否包含文字("登录超时" ,'node' ,null) 或 屏幕中是否包含文字("账号验证" ,'node' ,null) 则 弹出提示("登录异常处理"); 判断循环首 永真变量 = true 震动(长)(); 铃声提示(); 弹出提示("请重新登录"); 关闭应用("com.chinatower.powerservice"); 返回键(); 返回键(); 返回键(); // 任务键(); 打开应用("com.chinatower.powerservice"); 弹出提示("正在输入密码"); //删除登录; 点击(960,726,1); 输入文字(280,754,"guokai1"); //删除密码; 点击(889,873,1); 输入文字(304,879,"82212991@Lbb520"); //同意协议; 点击(89,2307,1); //点击登录; 点击(549,1393,1); 随机延时(3000,3000); //发送验证码; 点击(882,916,1); 随机延时(5000,5000); 验证码 = 网页访问("http://125.75.111.54:9002/api/sms/latest" ,'GET' ,header:{} ,data:{}); 验证码 = 取文本中间("{验证码}","验证码",","); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 输入文字(361,926,"{验证码}"); //登录; 点击(515,1413,1); 如果 屏幕中是否包含文字("设备汇总" ,'node' ,null) 或 屏幕中是否包含文字("常用功能" ,'node' ,null) 或 屏幕中是否包含文字("修改密码" ,'node' ,null) 或 屏幕中是否包含文字("待办工单" ,'node' ,null) 则 弹出提示("登录成功进入接单"); 退出循环; 否则 // //; 弹出提示("登录失败重新登录"); 结束 如果 判断循环尾 否则 弹出提示("未检测到登录异常,尝试接单"); 结束 如果 结束 如果 随机延时(5000,5000); //处理铁塔新网管app 打开应用("com.chinatower.energymonitor"); 随机延时(5000,5000); 弹出提示("开始查找网管订单"); 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_2C97C45A842E917408B9666466501638.dddbbb,精确度:91 ,null) 则 弹出提示("找到工单模块,管理工单"); 弹出提示("找到工单模块,管理工单"); 随机延时(6000,6000); // 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_86BA010D78C5584204EDB769C5C22F38.dddbbb",精确度: 80,null,false); 点击(279,827,1); // 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_9D13BC164F494F7A9CF33E51C67877B7.dddbbb",精确度: 50,null,false); //检查有没有订单; 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_E597066365DD74373F2834FCF8BD242D.dddbbb,精确度:100 ,null) 或 屏幕中是否包含文字("暂无内容" ,'node' ,null) 则 弹出提示("暂无内容"); //暂无; 返回键(); //登录异常判断; 如果 屏幕中是否包含文字("登录超时" ,'node' ,null) 或 屏幕中是否包含文字("记住密码" ,'node' ,null) 或 屏幕中是否包含文字("您的登录状态已过期" ,'node' ,null) 或 屏幕中是否包含文字("获取验证码" ,'node' ,null) 则 弹出提示("登录异常处理"); 判断循环首 永真变量 = true 震动(长)(); 铃声提示(); 弹出提示("请重新登录"); 关闭应用("com.chinatower.energymonitor"); 返回键(); 返回键(); 返回键(); // 任务键(); 打开应用("com.chinatower.energymonitor"); 弹出提示("正在输入密码"); //进入账号; 点击(230,714,1); //删除账号; 点击(937,703,1); 输入文字(237,727,"guokai1"); //进入密码; 点击(318,929,1); //删除密码; 点击(877,898,1); 输入文字(328,928,"82212991@Lbb520"); //同意协议; 点击(86,2307,1); //点击登录; 点击(556,1379,1); 点击(792,903,1); 点击文字("获取验证码",'node',null,false); 随机延时(5000,5000); 验证码 = 网页访问("http://125.75.111.54:9002/api/sms/latest" ,'GET' ,header:{} ,data:{}); 验证码 = 取文本中间("{验证码}","验证码:",","); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 输入文字(303,921,"{验证码}"); //正式登录; 点击(507,1248,1); 如果 屏幕中是否包含文字("设备汇总" ,'node' ,null) 或 屏幕中是否包含文字("常用功能" ,'node' ,null) 或 屏幕中是否包含文字("修改密码" ,'node' ,null) 或 屏幕中是否包含文字("待办工单" ,'node' ,null) 则 弹出提示("登录成功进入接单"); 退出循环; 否则 // //; 弹出提示("登录失败重新登录"); 结束 如果 判断循环尾 否则 弹出提示("未检测到登录异常,尝试接单"); 结束 如果 否则 随机延时(5000,5000); //判断是否有单; 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_695B9FA0C04A43180E293D54E4C6B9F7.dddbbb,精确度:80 ,null) 则 //如果没有代办,则进行语音提示,用户重新登录; 随机延时(5000,5000); // 如果 屏幕中是否包含文字("登录超时" ,'node' ,null) 或 屏幕中是否包含文字("您的登录状态已过期" ,'node' ,null) 或 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_E59F283A11FDE68C8F860D293DA1464B.dddbbb,精确度:80 ,null) 则 弹出提示("异常处理"); 判断循环首 永真变量 = true 震动(长)(); 铃声提示(); 弹出提示("请重新登录"); 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_7D02EA11322C5FFF2151EB4269873D33.dddbbb,精确度:80 ,null) 且 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_92139E94017B0A1680B46CDAA6B52671.dddbbb,精确度:80 ,null) 且 屏幕中是否包含文字("设备汇总" ,'node' ,null) 则 退出循环; 否则 结束 如果 判断循环尾 否则 结束 如果 //登录异常判断; 如果 屏幕中是否包含文字("登录超时" ,'node' ,null) 或 屏幕中是否包含文字("记住密码" ,'node' ,null) 或 屏幕中是否包含文字("您的登录状态已过期" ,'node' ,null) 或 屏幕中是否包含文字("获取验证码" ,'node' ,null) 则 弹出提示("登录异常处理"); 判断循环首 永真变量 = true 震动(长)(); 铃声提示(); 弹出提示("请重新登录"); 关闭应用("com.chinatower.energymonitor"); 返回键(); 返回键(); 返回键(); // 任务键(); 打开应用("com.chinatower.energymonitor"); 弹出提示("正在输入密码"); //进入账号; 点击(230,714,1); //删除账号; 点击(937,703,1); 输入文字(237,727,"guokai1"); //进入密码; 点击(318,929,1); //删除密码; 点击(877,898,1); 输入文字(328,928,"82212991@Lbb520"); //同意协议; 点击(86,2307,1); //点击登录; 点击(556,1379,1); 点击(792,903,1); 点击文字("获取验证码",'node',null,false); 随机延时(5000,5000); 验证码 = 网页访问("http://125.75.111.54:9002/api/sms/latest" ,'GET' ,header:{} ,data:{}); 验证码 = 取文本中间("{验证码}","验证码:",","); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 输入文字(303,921,"{验证码}"); //正式登录; 点击(507,1248,1); 如果 屏幕中是否包含文字("设备汇总" ,'node' ,null) 或 屏幕中是否包含文字("常用功能" ,'node' ,null) 或 屏幕中是否包含文字("修改密码" ,'node' ,null) 或 屏幕中是否包含文字("待办工单" ,'node' ,null) 则 弹出提示("登录成功进入接单"); 退出循环; 否则 // //; 弹出提示("登录失败重新登录"); 结束 如果 判断循环尾 否则 弹出提示("未检测到登录异常,尝试接单"); 结束 如果 弹出提示("发现工单"); //进入订单; 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_5561A0D3E52614D9F9D6371B196D921B_copy_copy_copy.dddbbb",精确度: 84,null,false); 点击(484,483,1); 滑动([{手势: &手势1,time: 789}],false); 滑动([{手势: &手势1,time: 1326}],false); //接单; 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_EDF06D95AE95876317FB3C557B35399C_copy_copy_copy_copy.dddbbb",精确度: 80,null,false); 点击(518,2281,1); //确定; 点击图片("/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_9920D481D731BDCF0293B1FFE2BD528F_copy_copy.dddbbb",精确度: 80,null,false); 返回键(); 否则 //登录异常处理; // 打开应用("com.chinatower.energymonitor"); // 判断循环首 永真变量 = true 铃声提示(); 震动(长)(); 弹出提示("请重新登录"); 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_BEFCAB3735BC5271347D245290F22261.dddbbb,精确度:80 ,null) 则 退出循环; 结束 如果 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_BC254EAC6CA641960C1E3D004394ED30.dddbbb,精确度:80 ,null) 则 退出循环; 否则 结束 如果 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_F403337EC41FD824B66F859F342663D7.dddbbb,精确度:80 ,null) 则 退出循环; 结束 如果 如果 屏幕中是否包含图片(/storage/emulated/0/YiChuJiFaProject/workSpaceImage/img_E0332EC3573B2E895156835F0A6AB702.dddbbb,精确度:80 ,null) 则 退出循环; 结束 如果 判断循环尾 //白屏检测; 判断循环首 永真变量 = true 震动(长)(); 铃声提示(); 弹出提示("请重新登录"); 关闭应用("com.chinatower.energymonitor"); 返回键(); 返回键(); 返回键(); // 任务键(); 打开应用("com.chinatower.energymonitor"); 弹出提示("正在输入密码"); //进入账号; 点击(230,714,1); //删除账号; 点击(937,703,1); 输入文字(237,727,"guokai1"); //进入密码; 点击(318,929,1); //删除密码; 点击(877,898,1); 输入文字(328,928,"82212991@Lbb520"); //同意协议; 点击(86,2307,1); //点击登录; 点击(556,1379,1); 点击(792,903,1); 点击文字("获取验证码",'node',null,false); 随机延时(5000,5000); 验证码 = 网页访问("http://125.75.111.54:9002/api/sms/latest" ,'GET' ,header:{} ,data:{}); 验证码 = 取文本中间("{验证码}","验证码:",","); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 输入文字(303,921,"{验证码}"); //正式登录; 点击(507,1248,1); 如果 屏幕中是否包含文字("设备汇总" ,'node' ,null) 或 屏幕中是否包含文字("常用功能" ,'node' ,null) 或 屏幕中是否包含文字("修改密码" ,'node' ,null) 或 屏幕中是否包含文字("待办工单" ,'node' ,null) 则 弹出提示("登录成功进入接单"); 退出循环; 否则 // //; 弹出提示("登录失败重新登录,下一次循环"); 结束 如果 判断循环尾 结束 如果 结束 如果 否则 弹出提示("未找到模块"); 铃声提示(); 点击(159,2249,1); 点击(138,2268,1); //防止接单界面出现白屏,后卡住; 返回键(); //登录异常判断; 如果 屏幕中是否包含文字("登录超时" ,'node' ,null) 或 屏幕中是否包含文字("记住密码" ,'node' ,null) 或 屏幕中是否包含文字("您的登录状态已过期" ,'node' ,null) 或 屏幕中是否包含文字("获取验证码" ,'node' ,null) 则 弹出提示("登录异常处理"); 判断循环首 永真变量 = true 震动(长)(); 铃声提示(); 弹出提示("请重新登录"); 关闭应用("com.chinatower.energymonitor"); 返回键(); 返回键(); 返回键(); // 任务键(); 打开应用("com.chinatower.energymonitor"); 弹出提示("正在输入密码"); //进入账号; 点击(230,714,1); //删除账号; 点击(937,703,1); 输入文字(237,727,"guokai1"); //进入密码; 点击(318,929,1); //删除密码; 点击(877,898,1); 输入文字(328,928,"82212991@Lbb520"); //同意协议; 点击(86,2307,1); //点击登录; 点击(556,1379,1); 点击(792,903,1); 点击文字("获取验证码",'node',null,false); 随机延时(5000,5000); 验证码 = 网页访问("http://125.75.111.54:9002/api/sms/latest" ,'GET' ,header:{} ,data:{}); 验证码 = 取文本中间("{验证码}","验证码:",","); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 弹出提示("成功获取验证码{验证码}"); 输入文字(303,921,"{验证码}"); //正式登录; 点击(507,1248,1); 如果 屏幕中是否包含文字("设备汇总" ,'node' ,null) 或 屏幕中是否包含文字("常用功能" ,'node' ,null) 或 屏幕中是否包含文字("修改密码" ,'node' ,null) 或 屏幕中是否包含文字("待办工单" ,'node' ,null) 则 弹出提示("登录成功进入接单"); 退出循环; 否则 // //; 弹出提示("登录失败重新登录"); 结束 如果 判断循环尾 否则 弹出提示("未检测到登录异常,尝试接单"); 结束 如果 结束 如果 随机延时(5000,5000); 判断循环尾
服务器端使用springboot,代码如下、
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>demo</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Spring Boot Starter Web 包含了 Spring MVC 和内嵌的 Tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Starter Data JPA 提供了对 Spring Data 和 JPA 的支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/javax.persistence/persistence-api --> <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> </dependency> <!-- H2 Database,一个内存数据库,用于快速开发和测试 --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties
# H2 ????? spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect # ????? H2 ??? spring.h2.console.enabled=true spring.h2.console.path=/h2-console server.port=9002
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; @SpringBootApplication @EntityScan(basePackages = "com.example.demo") // 指定实体类的扫描路径 public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/sms") public class SmsController { @Autowired private SmsRepository smsRepository; @PostMapping public void receiveSms( SmsRequest smsRequest) { SmsEntity smsEntity = new SmsEntity(); smsEntity.setPhoneNumber(smsRequest.getPhoneNumber()); smsEntity.setMessage(smsRequest.getMessage()); smsRepository.save(smsEntity); } @GetMapping("/latest") public SmsEntity getLatestSms() { // 从数据库中获取最新的一条短信 return smsRepository.findFirstByOrderByIdDesc().orElse(null); } @GetMapping("/all") public List<SmsEntity> getAllSms() { // 从数据库中获取所有短信 return smsRepository.findAll(); } }
package com.example.demo; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class SmsEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } private String phoneNumber; private String message; // getters and setters }
package com.example.demo; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; public interface SmsRepository extends JpaRepository<SmsEntity, Long> { Optional<SmsEntity> findFirstByOrderByIdDesc(); // 更新这里 }
package com.example.demo; public class SmsRequest { public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public String getMessage() { } public void setMessage(String message) { this.message = message; } private String phoneNumber; private String message; // getters and setters }
安卓转发短信的实现
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- 宣告应用使用的硬件特性,这里声明了对电话功能的需求,但设置为非必需 --> <uses-feature android:name="android.hardware.telephony" android:required="false" /> <!-- 宣告应用所需的权限 --> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- 注意:上面的 INTERNET 权限只需要声明一次 --> <application <!-- 定义应用的网络安全配置 --> android:networkSecurityConfig="@xml/network_security_config" <!-- 允许应用进行备份 --> android:allowBackup="true" <!-- 定义数据提取规则 --> android:dataExtractionRules="@xml/data_extraction_rules" <!-- 定义完整备份内容规则 --> android:fullBackupContent="@xml/backup_rules" <!-- 设置应用图标 --> android:icon="@mipmap/ic_launcher" <!-- 设置应用标签 --> android:label="@string/app_name" <!-- 设置圆形应用图标 --> android:roundIcon="@mipmap/ic_launcher_round" <!-- 设置应用支持从右到左的布局 --> android:supportsRtl="true" <!-- 设置应用主题 --> android:theme="@style/Theme.MyApplication" <!-- 定义目标 API 版本,这里设置为 31 --> tools:targetApi="31"> <!-- 定义应用的主 Activity,会在启动时被调用 --> <activity android:name=".MainActivity" <!-- 定义 Activity 是否可以被其他应用组件访问 --> android:exported="true"> <intent-filter> <!-- 定义 Activity 接收的主要动作 --> <action android:name="android.intent.action.MAIN" /> <!-- 定义 Activity 接收的主要类别 --> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 定义短信接收器,用于接收短信 --> <receiver android:name=".SmsReceiver" <!-- 定义接收器是否可以被其他应用组件访问 --> android:exported="true"> <intent-filter> <!-- 定义接收器接收的主要动作 --> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> <!-- 定义后台服务,用于处理短信 --> <service android:name=".SmsService" /> </application> </manifest>
// 包名,这里表示该文件属于com.example.myapplication包 package com.example.myapplication; // 导入必要的 Android 类 import android.content.Intent; import android.os.Bundle; import android.widget.CompoundButton; import android.widget.Switch; import androidx.appcompat.app.AppCompatActivity; // MainActivity 类,继承自 AppCompatActivity 类 public class MainActivity extends AppCompatActivity { // 声明 Switch 类型的成员变量 smsSwitch private Switch smsSwitch; // 在 Activity 创建时调用的方法 @Override protected void onCreate(Bundle savedInstanceState) { // 调用父类的 onCreate 方法 super.onCreate(savedInstanceState); // 设置当前 Activity 使用的布局文件 setContentView(R.layout.activity_main); // 通过布局文件中定义的 ID 获取 Switch 控件的实例 smsSwitch = findViewById(R.id.smsSwitch); // 为 Switch 控件设置状态变更监听器 smsSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { // 在 Switch 状态发生变化时调用的方法 @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // 判断 Switch 的状态是否为选中 if (isChecked) { // 如果选中,启动 SmsService 服务 startService(new Intent(MainActivity.this, SmsService.class)); } else { // 如果未选中,停止 SmsService 服务 stopService(new Intent(MainActivity.this, SmsService.class)); } } }); } }
package com.example.myapplication; // SmsService.java import android.app.Service; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.util.Log; // SmsService.java import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.telephony.SmsMessage; // SmsService.java import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.telephony.SmsMessage; // SmsService.java import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.telephony.SmsMessage; public class SmsService extends Service { private SmsReceiver smsReceiver; @Override public void onCreate() { super.onCreate(); smsReceiver = new SmsReceiver(); // 创建一个 IntentFilter 对象并添加接收的 action IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED"); // 注册广播接收器 registerReceiver(smsReceiver, intentFilter); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // 处理服务启动命令 return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); // 在服务销毁时取消注册广播接收器 unregisterReceiver(smsReceiver); } @Override public IBinder onBind(Intent intent) { return null; } }
// 包名,这里表示该文件属于com.example.myapplication包 package com.example.myapplication; // 导入必要的 Android 类 import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.telephony.SmsMessage; import android.widget.Toast; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import java.util.Map; import android.os.AsyncTask; // SmsReceiver 类,继承自 BroadcastReceiver 类 public class SmsReceiver extends BroadcastReceiver { // 重写 BroadcastReceiver 的 onReceive 方法,用于接收短信广播 @Override public void onReceive(Context context, Intent intent) { // 获取广播中的附加数据 Bundle bundle = intent.getExtras(); // 检查附加数据是否为空 if (bundle != null) { // 获取短信数据数组 Object[] pdus = (Object[]) bundle.get("pdus"); // 检查短信数据数组是否为空 if (pdus != null) { // 遍历短信数据数组 for (Object pdu : pdus) { // 将每个数据转换为 SmsMessage 对象 SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu); // 获取短信发送者的号码 String sender = smsMessage.getDisplayOriginatingAddress(); // 获取短信内容 String message = smsMessage.getDisplayMessageBody(); // 检查短信内容是否包含 "铁塔" 字符串 if (message.contains("铁塔")) { // 如果包含,则将短信信息传递给后台服务处理 sendSmsToServer(context, sender, message); } } } } } // 将短信信息传递给后台服务处理的方法 private void sendSmsToServer(Context context, String sender, String message) { // 创建异步任务对象,并执行异步任务 new SendSmsTask(context).execute(sender, message); } // 异步任务类,用于在后台处理网络请求 private static class SendSmsTask extends AsyncTask<String, Void, Void> { // 上下文对象 private Context context; // 构造方法,用于初始化上下文对象 SendSmsTask(Context context) { this.context = context; } // 在后台执行网络请求的方法 @Override protected Void doInBackground(String... params) { try { // 构建要发送的数据 Map<String, String> postData = new HashMap<>(); postData.put("phoneNumber", params[0]); postData.put("message", params[1]); // 构建服务器地址 URL url = new URL("http://125.75.111.54:9002/api/sms"); // 替换为你的服务器地址 // 打开连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // 将数据写入请求体 OutputStream os = connection.getOutputStream(); os.write(getPostDataString(postData).getBytes("UTF-8")); os.flush(); os.close(); // 获取服务器响应 int responseCode = connection.getResponseCode(); // 检查服务器响应状态 if (responseCode == HttpURLConnection.HTTP_OK) { // 服务器成功接收数据 publishProgress(); // 更新 UI } else { // 服务器返回错误 publishProgress(); // 更新 UI } } catch (Exception e) { e.printStackTrace(); publishProgress(); // 更新 UI } return null; } // 在 UI 线程中更新 UI,例如显示 Toast 提示用户 @Override protected void onProgressUpdate(Void... values) { Toast.makeText(context, "短信内容发送成功", Toast.LENGTH_SHORT).show(); } // 将 Map 中的数据转换为 POST 请求的数据格式 private static String getPostDataString(Map<String, String> params) { StringBuilder result = new StringBuilder(); boolean first = true; for (Map.Entry<String, String> entry : params.entrySet()) { if (first) { first = false; } else { result.append("&"); } result.append(entry.getKey()).append("=").append(entry.getValue()); } return result.toString(); } } }
标签:node,登录,提示,安卓,验证码,---,点击,接单,null From: https://www.cnblogs.com/GKLBB/p/17857595.html