基于kotlin开发的验证码发送注册的app

简介: ”麻雀虽小五脏俱全”就是它了,设计后端即springboot的开发,app处理网络请求的开发,appUI界面的设计(虽然只有一步,但也还是吧),数据库查询相关,app搭建相关架构的实现等等,值得学习一波。

一、前言

最近其实有一点“不务正业”,快两个月了都在学网络相关的后端开发,安卓方面很久没去研究了,这次带来的demo是大项目中的一个小小的一块,由于编程语言渐渐转向kotlin,所以原本的项目需要进行重构,不过还不是非常熟练,所以这次写了这个注册功能的demo,百分百kotlin就是它了,验证码是自己搭建的服务器那边处理的,所以还是一个非常值得自己做的一个功能,起初是想采用短信实现的,后面看到腾讯短信业务要企业级用户才能使用,就放弃了,自己造轮子显然不是一个明智的选择,不过鉴于这个功能在思路上非常的简单,所以简单实现了一下,不过不要小看这个demo,”麻雀虽小五脏俱全”就是它了,设计后端即springboot的开发,app处理网络请求的开发,appUI界面的设计(虽然只有一步,但也还是吧),数据库查询相关,app搭建相关架构的实现等等,值得学习一波。下面是制作过程的思维导图

在这里插入图片描述

最终的成品也展示一下

在这里插入图片描述

二、spring boot端相关接口开发

在设计之前还需要设计两个表,建议在本地开发完成之后再上线服务器,所以最好本地也建个表,访问更快,表的设计比较简单,这里就展示一下结构图

注册表

在这里插入图片描述

验证表

在这里插入图片描述

还有这里面用到的比较多的就是MyStatus这个数据类,因为注册最终的展现形式都差不多,所以采用统一的状态方式进行返回内容,下面展示一下类的结构

data class MyStatus(var account:String, var status: String, var message:String, var time:String)

用到的json转化工具是hutool,参考一下之前写的博客hutool的使用

1.开发发送验证码接口

首先确定一下,接口的形式

http://域名:端口号/verify/{邮箱}

只需要一个参数就可以了,确认完参数,我们开始进行下一步,设计一下发送验证码的流程

//1.首先进行查询最近的验证码的发送时间,与目前的做比较
//可能会有三种情况:查询为空,间隔时间大于5分钟,间隔时间小于5分钟
//小于5分钟直接返回提示,验证频繁
//查询为空和大于5分钟继续
//下面展示核心代码,具体实现源码文末领取

//查找是否存在上一次验证码记录,并进行比较,5分钟以内就不重复发送
        val isExist = "select  * from verify WHERE mail = ? ORDER BY sendTime desc LIMIT 1"
        val psExist = con.prepareStatement(isExist)
        psExist.setString(1, mail)
        val resultSet:ResultSet = psExist.executeQuery()
        
        //这里的if语句就相当于在做判空操作
        if (resultSet.next()){
            //计算时间差
            val t = System.currentTimeMillis() - MyDate.timeToLong(resultSet.getString("sendTime"))
            //判断超过时间就不发送
            if(t < 5*60*1000){
                val status = MyStatus(mail,"404","请勿重复发送验证码",timeVerify)
                return JSONUtil.toJsonStr(status)
            }
        }
//2.生成验证码
//这里采用随机数的方法随机生成了100000~999999的数字
    val random = Random()
    val code = random.nextInt(999999 - 100000 + 1) + 100000
    val message = "【Dream】您的验证码$code,该验证码5分钟内有效,请勿泄漏于他人,时间${timeVerify}"
//3.发送邮件
//首先对邮箱进行一个检查
//无效返回提示,有效则继续

    try{
        SendMail(mail).sendTextEmail(message, "【Dream】注册验证码")
    }catch (e:Exception){
        val status = MyStatus(mail,"404","邮箱无效",timeVerify)
        return JSONUtil.toJsonStr(status)
    }
//4.插入验证码发送的记录
//这里是为后面的校验做准备
    val insertCode = "insert into verify(mail,myCode,sendTime) values(?,?,?)"
    val psCode = con.prepareStatement(insertCode)
    return try {
        psCode.setString(1, mail)
        psCode.setString(2, code.toString())
        psCode.setString(3, timeVerify)
        psCode.executeUpdate()
        val status = MyStatus(mail,"200","发送验证码成功",timeVerify)
        JSONUtil.toJsonStr(status)
    }catch (e:Exception){
        //插入时异常
        val status = MyStatus(mail,"404","系统故障",timeVerify)
        JSONUtil.toJsonStr(status)
    }

在这里插入图片描述

2.开发注册接口

接口格式确定一下,这里本来应该可以采用post进行开发的,由于参数也不是太多,所以采用仍然采用get进行开发

http://域名:端口号/register/{邮箱}/{密码}/{验证码}

三个参数,不算太多,可能就是浏览器手动请求有点累

//1.判断用户是否已经存在
//这里采用了主键约束,所以插入的时候根据数据库的返回结果即可判断是否已经存在
//存在,返回已经存在的提示,反之则继续
//2.判断验证码是否过期
//查询最近一次的验证码发送时间
//若查询为空,则说明用户还没发送验证码,返回提示,不为空继续
//若时间与当前的时间间隔大于5分钟就返回验证码已经过期的提示,反之继续

    //判断是否发送过验证码
    if(!resultSet.next()){
        val status = MyStatus(mail,"404","暂未发送验证码",timeRegister)
        return JSONUtil.toJsonStr(status)
    }

    //判断是否验证码是否过期
    val t = System.currentTimeMillis() - MyDate.timeToLong(resultSet.getString("sendTime"))
    if (t > 5*60*1000){
        val status = MyStatus(mail,"404","验证码过期",timeRegister)
        return JSONUtil.toJsonStr(status)
    }
//3.判断验证码是否正确
//正确则进行下一步操作,错误返回提示

    //判断验证码是否正确
    if (code != resultSet.getString("myCode")){
        val status = MyStatus(mail,"404","验证码错误",timeRegister)
        return JSONUtil.toJsonStr(status)
    }
//4.检查sql语句是否出错,即判断用户是否已经存在
//错误,返回用户已存在的提示,否则继续
//插入数据库成功,发送邮箱给用户提示已经发送成功

    //检查数据库查询是否有错误发生
    return try{
        val register = "insert into register(mail,myPassword)values(?,?)"
        val psRegister = con.prepareStatement(register)
        psRegister.setString(1,mail)
        psRegister.setString(2,password)
        psRegister.executeUpdate()
        val status = MyStatus(mail,"200","注册成功",timeRegister)

        //发送注册成功通知
        val message = "【Dream】尊敬的用户:恭喜你已成功注册Dream,后续软件使用问题关注公众号:android 踩坑小天才 进行咨询,感谢您的支持"
        SendMail(mail).sendTextEmail(message, "【Dream】注册成功通知")
        JSONUtil.toJsonStr(status)
    }catch (e:Exception){
        val status = MyStatus(mail,"404","用户已存在",timeRegister)
        JSONUtil.toJsonStr(status)
    }

在这里插入图片描述

三、app客户端界面UI相关开发

这方面说实话,审美不是很好,甚至这个颜色还是从某某平台扣的,过程比较简单,如果真的要开发一款比较好的软件,UI设计非常重要

//1.确定布局
//这个最简单的就是使用线性布局

在这里插入图片描述

//2.确定主要控件
//这里邮箱,密码,验证码就是主要的控件
//都是EditText,需要设置一下输入的数据类型
//这里以邮箱为例
//主要用到了hint即还未输入前界面显示的提示性文字
//inputType,控制输入的数据格式

<LinearLayout
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="horizontal">

    <TextView
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="2"
              android:text="注册邮箱:"
              android:gravity="center"
              android:textColor="#000000" />

    <EditText
              android:id="@+id/re_mail"
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="6"
              android:hint="请输入合法的邮箱"
              android:inputType="textEmailAddress" />
</LinearLayout>
//3.界面优化
//这部分主要是利用了Google提供的cardView进行圆角化处理
//还有就是控件直接的间隔调控即layout_margin
//这里利用线性布局中的权重配置,以适配不同分辨率的手机
//这里简单展示一下

<com.google.android.material.card.MaterialCardView
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1"
     android:layout_margin="10dp"
     app:cardCornerRadius="5dp"
     app:elevation="15dp">
    <LinearLayout
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:layout_weight="1"
                  android:orientation="horizontal">

        <TextView
                  android:layout_width="0dp"
                  android:layout_height="match_parent"
                  android:layout_weight="2"
                  android:gravity="center"
                  android:text="hello"
                  android:textColor="#000000" />

    </LinearLayout>
</com.google.android.material.card.MaterialCardView>
//4.图标
//众所周知,图标是相当重要的,能不能给一个良好的第一印象就靠它了
//这里是上阿里云矢量图库里面找的
//是不是相当的帅气

在这里插入图片描述

四、app网络请求处理相关开发

基于retrofit开发的,下面展示一下基本流程

//1.创建接收json数据的数据模型

data class RegisterData(val account:String,val password:String,val verifyCode:String)
data class StatusResponse(val account:String, val status: String, val message:String, val time:String )
//2.创建服务接口

interface LoginService {
    //发送验证码接口
    @GET("verify/{mail}")
    fun getVerifyStatus(@Path("mail")mail:String): Call<StatusResponse>

    //注册接口
    @GET("register/{mail}/{password}/{code}")
    fun getRegisterStatus(@Path("mail")mail:String,@Path("password")password:String,@Path("code")code:String)                                                                                               :Call<StatusResponse>
}
//3.服务创建的类
//这里面进一步完成retrofit的封装

object ServiceCreator {

    //如果是本地测试的话,用自己电脑的ip地址即可
    //cmd ipconfig即可获取
    private const val BASE_URL = "http://IP地址:8080/"
    private val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    fun <T> create (serviceClass:Class<T>):T {
        return retrofit.create(serviceClass)
    }
    //泛型实化,用到reified关键字
    inline fun <reified T>create():T = create(T::class.java)
}
//4.统一处理网络请求
//这里进行网络请求的处理,用到协程使得网络请求可以异步执行

object LoginDemoNetwork {
    private val loginService = ServiceCreator.create<LoginService>()
    //suspend kotlin中协程的关键字
    suspend fun getVerifyStatus(query: String): StatusResponse = loginService.getVerifyStatus(query).await()
    suspend fun getRegisterStatus(query1: String,query2: String,query3: String) =                          loginService.getRegisterStatus(query1,query2,query3).await()
    
    //网络回调的处理
    private suspend fun <T> Call<T>.await():T{
        return suspendCoroutine { continuation ->
            enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {
                    val body = response.body()
                    if (body != null)continuation.resume(body)
                    else continuation.resumeWithException(RuntimeException("response body is null"))
                }
                override fun onFailure(call: Call<T>, t: Throwable) {
                    continuation.resumeWithException(t)
                }
            })
        }
    }
}
//5.respository里面进行网络请求的最终处理并且返回回调结果

object Repository {

    fun getVerifyStatus(query: String) = fire(Dispatchers.IO){
        val verifyResponse = LoginDemoNetwork.getVerifyStatus(query)
        Result.success(verifyResponse)
    }
    fun getRegisterStatus(query1: String,query2: String,query3: String) = fire(Dispatchers.IO){
        val registerResponse = LoginDemoNetwork.getRegisterStatus(query1,query2, query3)
        Result.success(registerResponse)
    }
    /**
     * 对于结果处理进行一个高阶函数的封装
     */
    private fun <T> fire(context: CoroutineContext,block:suspend () -> Result<T>) = liveData<Result<T>>(context) {
        val result = try {
            block()
        }catch (e:Exception){
            Result.failure<T>(e)
        }
        emit(result)
    }
}

五、基于MVVM架构的模块组装

这一块主要是对fragment和viewModel进行设计,由于我们的需求比较简单,所以这一块也实现的比较简单

//1.viewModel层设计
class LoginViewModel : ViewModel() {
    //创建了两个网络请求的liveData
    private val verifyLiveData = MutableLiveData<String>()
    private val registerLiveData = MutableLiveData<RegisterData>()

    //对于liveData进行转换
    val verifyStatusLiveData = Transformations.switchMap(verifyLiveData) { query ->
        Repository.getVerifyStatus(query)
    }
    val registerStatusLiveData = Transformations.switchMap(registerLiveData){register->
        Repository.getRegisterStatus(register.account,register.password,register.verifyCode)
    }
    
    //赋值
    fun getVerifyStatus(query:String){
        verifyLiveData.value = query
    }
    fun getRegisterStatus(query1: String,query2: String,query3: String){
        registerLiveData.value = RegisterData(query1,query2,query3)
    }

}
//2.fragment设计
//这里主要就是创建了两个liveData的监听和两个listener
//还有一些简单的网络判断
class LoginFragment : Fragment() {

    private val viewModel by lazy { ViewModelProvider(this).get(LoginViewModel::class.java) }
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.login_fragment, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        sent.setOnClickListener {
            viewModel.getVerifyStatus(re_mail.text.toString())
        }
        register.setOnClickListener {
            if(re_password.text.toString() == check_password.text.toString()){
                viewModel.getRegisterStatus(re_mail.text.toString(),re_password.text.toString(),check_code.text.toString())
            }else{
                "两次密码输入不一致".showToast()
            }
        }
        viewModel.verifyStatusLiveData.observe(viewLifecycleOwner) { result ->
            val status = result.getOrNull()
            if (status != null){
                if(status.status == "200"){
                    status.message.showToast()
                }else{
                    status.message.showToast()
                }
            }else{
                "网络错误".showToast()
            }
        }
        viewModel.registerStatusLiveData.observe(viewLifecycleOwner) { result ->
            val status = result.getOrNull()
            if (status != null){
                if(status.status == "200"){
                    status.message.showToast()
                }else{
                    status.message.showToast()
                }
            }else{
                "网络错误".showToast()
            }
        }
    }
}

最后,如果有感兴趣的,可以发送关键字:验证码 到公众号:android 踩坑小天才 获取源码和签名好的app

目录
相关文章
|
11天前
|
Web App开发 Java 视频直播
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
对于软件、计算机等专业的毕业生,毕业设计需实现实用软件或APP。新颖的设计应结合最新技术,如5G时代的音视频技术。示例包括: 1. **短视频分享APP**: 集成FFmpeg实现视频剪辑功能,如添加字幕、转场特效等。 2. **电商购物APP**: 具备直播带货功能,使用RTMP/SRT协议支持流畅直播体验。 3. **同城生活APP**: 引入WebRTC技术实现可信的视频通话功能。这些应用不仅实用,还能展示开发者紧跟技术潮流的能力。
32 4
FFmpeg开发笔记(四十九)助您在毕业设计中脱颖而出的几个流行APP
|
1天前
|
安全 Java Android开发
Kotlin入门实用开发技巧与注意事项
本文源自公众号“AntDream”。Kotlin是由JetBrains开发的现代编程语言,自2017年成为Android官方开发语言后迅速流行。本文作者分享了Kotlin的实用技巧,包括变量声明、空安全、扩展函数等,帮助初学者避免常见问题。
28 15
|
2天前
|
安全 Android开发 开发者
探索安卓开发的未来:Kotlin的崛起与Flutter的挑战
在移动开发的广阔天地中,安卓平台始终占据着举足轻重的地位。随着技术的不断进步和开发者需求的多样化,Kotlin和Flutter成为了改变游戏规则的新玩家。本文将深入探讨Kotlin如何以其现代化的特性赢得开发者的青睐,以及Flutter凭借跨平台的能力如何挑战传统的安卓开发模式。通过实际案例分析,我们将揭示这两种技术如何塑造未来的安卓应用开发。
17 6
|
5天前
|
移动开发 小程序 JavaScript
uni-app开发微信小程序
本文详细介绍如何使用 uni-app 开发微信小程序,涵盖需求分析、架构思路及实施方案。主要功能包括用户登录、商品列表展示、商品详情、购物车及订单管理。技术栈采用 uni-app、uView UI 和 RESTful API。文章通过具体示例代码展示了从初始化项目、配置全局样式到实现各页面组件及 API 接口的全过程,并提供了完整的文件结构和配置文件示例。此外,还介绍了微信授权登录及后端接口模拟方法,确保项目的稳定性和安全性。通过本教程,读者可快速掌握使用 uni-app 开发微信小程序的方法。
19 3
|
26天前
|
Web App开发 Android开发
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
实时数据传输在互联网中至关重要,不仅支持即时通讯如QQ、微信的文字与图片传输,还包括音视频通信。一对一通信常采用WebRTC技术,如《Android Studio开发实战》中的App集成示例;而一对多的在线直播则需部署独立的流媒体服务器,使用如SRT等协议。SRT因其优越的直播质量正逐渐成为主流。本文档概述了SRT协议的使用,包括通过OBS Studio和SRT Streamer进行SRT直播推流的方法,并展示了推流与拉流的成功实例。更多细节参见《FFmpeg开发实战》一书。
36 1
FFmpeg开发笔记(四十六)利用SRT协议构建手机APP的直播Demo
|
18天前
|
IDE Java 开发工具
探索安卓开发之旅:打造你的第一款App
【8月更文挑战第24天】在这篇文章中,我们将一起踏上激动人心的安卓开发之旅。不论你是编程新手还是希望扩展技能的老手,本文将为你提供一份详尽指南,帮助你理解安卓开发的基础知识并实现你的第一个应用程序。从搭建开发环境到编写“Hello World”,每一步都将用浅显易懂的语言进行解释。那么,让我们开始吧!
|
1天前
|
调度 Android开发 开发者
探索安卓开发中的新技术:Kotlin协程
【9月更文挑战第9天】本文将深入探讨Kotlin协程在安卓开发中的应用,揭示其如何优雅地处理异步任务。我们将从基础概念入手,逐步深入到实际开发场景,最后通过代码示例直观展示协程的魔力。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往更高效、更简洁代码的大门。
|
11天前
|
Android开发 iOS开发 C#
Xamarin:用C#打造跨平台移动应用的终极利器——从零开始构建你的第一个iOS与Android通用App,体验前所未有的高效与便捷开发之旅
【8月更文挑战第31天】Xamarin 是一个强大的框架,允许开发者使用单一的 C# 代码库构建高性能的原生移动应用,支持 iOS、Android 和 Windows 平台。作为微软的一部分,Xamarin 充分利用了 .NET 框架的强大功能,提供了丰富的 API 和工具集,简化了跨平台移动应用开发。本文通过一个简单的示例应用介绍了如何使用 Xamarin.Forms 快速创建跨平台应用,包括设置开发环境、定义用户界面和实现按钮点击事件处理逻辑。这个示例展示了 Xamarin.Forms 的基本功能,帮助开发者提高开发效率并实现一致的用户体验。
23 0
|
16天前
|
Java 程序员 Android开发
探索安卓开发:构建你的第一个App
【8月更文挑战第27天】在数字化时代的浪潮中,移动应用成为人们生活不可或缺的一部分。对于渴望进入软件开发领域的新手而言,掌握如何构建一款简单的安卓App是开启技术之旅的关键一步。本文旨在通过浅显易懂的语言和步骤分解,引导初学者了解安卓开发的基础知识,并跟随示例代码,一步步实现自己的第一个安卓App。从环境搭建到界面设计,再到功能实现,我们将一同揭开编程的神秘面纱,让每个人都能体会到创造软件的乐趣。
|
安全 搜索推荐 生物认证
APP的注册和登录功能设计
APP的注册和登录功能设计
APP的注册和登录功能设计