登录注册修改密码,网络请求

This commit is contained in:
2026-05-14 14:59:45 +08:00
parent 14d1969903
commit 652e10ec62
12 changed files with 455 additions and 229 deletions

View File

@@ -4,7 +4,7 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-05-13T03:52:47.453596600Z"> <DropdownSelection timestamp="2026-05-14T00:25:02.182359600Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=AMRF025926116461" /> <DeviceId pluginId="PhysicalDevice" identifier="serial=AMRF025926116461" />

View File

@@ -105,13 +105,7 @@
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".home.HomeActivity" android:name=".home.HomeActivity"
android:exported="true"> android:exported="false"/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity <activity
android:name=".login.RegisterSuccessActivity" android:name=".login.RegisterSuccessActivity"
android:exported="false" /> android:exported="false" />
@@ -129,7 +123,13 @@
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".login.LoginActivity" android:name=".login.LoginActivity"
android:exported="false" /> android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"

View File

@@ -15,3 +15,30 @@ data class LoginBean(
val token: String, val token: String,
val userId: Int val userId: Int
) )
data class LoginRequest(
val clientId: String = "428a8310cd442757ae699df5d894f051",
val grantType: String,
val username: String? = null,
val phonenumber: String? = null,
val smsCode: String? = null,
val password: String? = null,
val userType: String = "app_user"
)
data class RegisterRequest(
val clientId: String = "428a8310cd442757ae699df5d894f051",
val grantType: String,
val smsCode: String? = null,
val username: String,
val password: String,
val userType: String = "app_user",
val idCard: String,
val inviteCode: String? = null,
val faceAddr: String
)
data class ForgotRequest(
val phonenumber: String,
val smsCode: String,
val password: String
)

View File

@@ -4,28 +4,27 @@ import android.graphics.Color
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.os.Bundle import android.os.Bundle
import android.os.CountDownTimer import android.os.CountDownTimer
import android.text.InputType
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import bean.ForgotRequest
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.defenseapplication.R import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.ForgetPasswordActivityBinding import com.example.defenseapplication.databinding.ForgetPasswordActivityBinding
import com.example.defenseapplication.utils.HttpUtils
import com.google.firebase.crashlytics.buildtools.reloc.org.apache.http.util.TextUtils import com.google.firebase.crashlytics.buildtools.reloc.org.apache.http.util.TextUtils
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.random.Random
//找回密码
class ForgetPasswordActivity : AppCompatActivity() { class ForgetPasswordActivity : AppCompatActivity() {
private lateinit var binding: ForgetPasswordActivityBinding private lateinit var binding: ForgetPasswordActivityBinding
private var verifyCode: String? = null // 存储模拟验证码
private var countDownTimer: CountDownTimer? = null private var countDownTimer: CountDownTimer? = null
private var isSendingCode = false // 防止重复发送验证码 private var isSendingCode = false
companion object { companion object {
private const val COUNTDOWN_DURATION = 60000L // 60秒倒计时 private const val COUNTDOWN_DURATION = 60000L
private const val COUNTDOWN_INTERVAL = 1000L private const val COUNTDOWN_INTERVAL = 1000L
private const val MIN_PASSWORD_LENGTH = 6 private const val MIN_PASSWORD_LENGTH = 6
private const val MAX_PASSWORD_LENGTH = 20 private const val MAX_PASSWORD_LENGTH = 20
@@ -39,7 +38,6 @@ class ForgetPasswordActivity : AppCompatActivity() {
.statusBarDarkFont(true) .statusBarDarkFont(true)
.titleBar(binding.topContainer) .titleBar(binding.topContainer)
.init() .init()
// 设置渐变背景
val startColor = Color.parseColor("#CBEBFA") val startColor = Color.parseColor("#CBEBFA")
val endColor = Color.parseColor("#FFF2EF") val endColor = Color.parseColor("#FFF2EF")
setGradientBackground(binding.main, startColor, endColor) setGradientBackground(binding.main, startColor, endColor)
@@ -47,12 +45,10 @@ class ForgetPasswordActivity : AppCompatActivity() {
} }
private fun setupClickListeners() { private fun setupClickListeners() {
// 返回按钮
binding.backIcon.setOnClickListener { binding.backIcon.setOnClickListener {
finish() finish()
} }
// 发送验证码按钮
binding.sendCodeBtn.setOnClickListener { binding.sendCodeBtn.setOnClickListener {
if (!isSendingCode && countDownTimer == null) { if (!isSendingCode && countDownTimer == null) {
val phoneNumber = binding.etPhoneNumber.text.toString().trim() val phoneNumber = binding.etPhoneNumber.text.toString().trim()
@@ -66,28 +62,42 @@ class ForgetPasswordActivity : AppCompatActivity() {
} }
} }
// 确定按钮(重置密码)
binding.btnNext.setOnClickListener { binding.btnNext.setOnClickListener {
performResetPassword() performResetPassword()
} }
} }
/**
* 手机号格式校验简单1开头的11位数字
*/
private fun isPhoneNumberValid(phone: String): Boolean { private fun isPhoneNumberValid(phone: String): Boolean {
if (TextUtils.isEmpty(phone)) return false if (TextUtils.isEmpty(phone)) return false
val pattern = Pattern.compile("^1[0-9]{10}$") val pattern = Pattern.compile("^1[0-9]{10}$")
return pattern.matcher(phone).matches() return pattern.matcher(phone).matches()
} }
/**
* 发送验证码
*/
private fun sendVerificationCode(phoneNumber: String) { private fun sendVerificationCode(phoneNumber: String) {
isSendingCode = true isSendingCode = true
binding.sendCodeBtn.isEnabled = false binding.sendCodeBtn.isEnabled = false
CoroutineScope(Dispatchers.IO).launch {
try {
val response = HttpUtils.api.getCode(phoneNumber)
withContext(Dispatchers.Main) {
if (response.code == 200) {
Toast.makeText(this@ForgetPasswordActivity, "验证码已发送", Toast.LENGTH_SHORT).show()
startCountDown()
} else {
Toast.makeText(this@ForgetPasswordActivity, response.msg, Toast.LENGTH_SHORT).show()
binding.sendCodeBtn.isEnabled = true
isSendingCode = false
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(this@ForgetPasswordActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
binding.sendCodeBtn.isEnabled = true
isSendingCode = false
}
}
}
} }
/** /**
@@ -122,31 +132,20 @@ class ForgetPasswordActivity : AppCompatActivity() {
*/ */
private fun performResetPassword() { private fun performResetPassword() {
val phoneNumber = binding.etPhoneNumber.text.toString().trim() val phoneNumber = binding.etPhoneNumber.text.toString().trim()
val code = binding.etSmsCode.text.toString().trim() val smsCode = binding.etSmsCode.text.toString().trim()
val newPassword = binding.etPassword.text.toString().trim() val newPassword = binding.etPassword.text.toString().trim()
val confirmPassword = binding.etInviteCode.text.toString().trim() val confirmPassword = binding.etInviteCode.text.toString().trim()
// 1. 手机号校验
if (!isPhoneNumberValid(phoneNumber)) { if (!isPhoneNumberValid(phoneNumber)) {
Toast.makeText(this, "手机号格式不正确", Toast.LENGTH_SHORT).show() Toast.makeText(this, "手机号格式不正确", Toast.LENGTH_SHORT).show()
return return
} }
// 2. 验证码校验 if (TextUtils.isEmpty(smsCode)) {
if (TextUtils.isEmpty(code)) {
Toast.makeText(this, "请输入验证码", Toast.LENGTH_SHORT).show() Toast.makeText(this, "请输入验证码", Toast.LENGTH_SHORT).show()
return return
} }
if (verifyCode == null) {
Toast.makeText(this, "请先获取验证码", Toast.LENGTH_SHORT).show()
return
}
if (code != verifyCode) {
Toast.makeText(this, "验证码错误", Toast.LENGTH_SHORT).show()
return
}
// 3. 密码校验
if (TextUtils.isEmpty(newPassword) || TextUtils.isEmpty(confirmPassword)) { if (TextUtils.isEmpty(newPassword) || TextUtils.isEmpty(confirmPassword)) {
Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show() Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show()
return return
@@ -164,19 +163,36 @@ class ForgetPasswordActivity : AppCompatActivity() {
return return
} }
// 禁用确定按钮,防重复提交
binding.btnNext.isEnabled = false binding.btnNext.isEnabled = false
binding.btnNext.text = "重置中..." binding.btnNext.text = "重置中..."
val requestJson = """ CoroutineScope(Dispatchers.IO).launch {
{ try {
"phone":"$phoneNumber", val forgotRequest = ForgotRequest(
"code":"$code", phonenumber = phoneNumber,
"newPassword":"$newPassword" smsCode = smsCode,
password = newPassword
)
val response = HttpUtils.api.forgotPassword(forgotRequest)
withContext(Dispatchers.Main) {
binding.btnNext.isEnabled = true
binding.btnNext.text = "确定"
if (response.code == 200) {
Toast.makeText(this@ForgetPasswordActivity, R.string.reset_success, Toast.LENGTH_SHORT).show()
finish()
} else {
Toast.makeText(this@ForgetPasswordActivity, response.msg, Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
binding.btnNext.isEnabled = true
binding.btnNext.text = "确定"
Toast.makeText(this@ForgetPasswordActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
} }
""".trimIndent()
} }
private fun setGradientBackground( private fun setGradientBackground(
view: android.view.View, view: android.view.View,

View File

@@ -1,8 +1,8 @@
package com.example.defenseapplication.login package com.example.defenseapplication.login
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import com.example.defenseapplication.R
import android.os.Bundle import android.os.Bundle
import android.os.CountDownTimer import android.os.CountDownTimer
import android.view.View import android.view.View
@@ -10,9 +10,18 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
import bean.LoginRequest
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.ActivityLoginBinding import com.example.defenseapplication.databinding.ActivityLoginBinding
import com.example.defenseapplication.home.HomeActivity
import com.example.defenseapplication.utils.HttpUtils
import com.example.defenseapplication.utils.UserinfoUtil
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
//登录 import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class LoginActivity : AppCompatActivity() { class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding private lateinit var binding: ActivityLoginBinding
@@ -22,10 +31,18 @@ class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (UserinfoUtil.getUserInfo() != null) {
val intent = Intent(this, HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()
return
}
binding = ActivityLoginBinding.inflate(layoutInflater) binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
// 设置渐变背景
val startColor = Color.parseColor("#CBEBFA") val startColor = Color.parseColor("#CBEBFA")
val endColor = Color.parseColor("#FFF2EF") val endColor = Color.parseColor("#FFF2EF")
setGradientBackground(binding.main, startColor, endColor) setGradientBackground(binding.main, startColor, endColor)
@@ -64,7 +81,7 @@ class LoginActivity : AppCompatActivity() {
binding.registerText.visibility = View.VISIBLE binding.registerText.visibility = View.VISIBLE
binding.forgetText.visibility = View.GONE binding.forgetText.visibility = View.GONE
updateRegisterTextConstraints(center = true) updateRegisterTextConstraints(center = false)
//需要正确的方法名和参数 //需要正确的方法名和参数
updateLoginButtonTopConstraint(R.id.smsRowLayout) updateLoginButtonTopConstraint(R.id.smsRowLayout)
@@ -117,13 +134,17 @@ class LoginActivity : AppCompatActivity() {
Toast.makeText(this, R.string.enter_phone_number, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.enter_phone_number, Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
if (phone.length != 11) {
Toast.makeText(this, R.string.phone_format_invalid, Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
when (currentLoginMode) { when (currentLoginMode) {
LoginMode.SMS -> { LoginMode.SMS -> {
val code = binding.etSmsCode.text.toString() val code = binding.etSmsCode.text.toString()
if (code.isEmpty()) { if (code.isEmpty()) {
Toast.makeText(this, R.string.enter_verification_code_pls, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.enter_verification_code_pls, Toast.LENGTH_SHORT).show()
} else { } else {
Toast.makeText(this, getString(R.string.sms_login_success, phone, code), Toast.LENGTH_SHORT).show() smsLogin(phone, code)
} }
} }
LoginMode.PASSWORD -> { LoginMode.PASSWORD -> {
@@ -131,7 +152,7 @@ class LoginActivity : AppCompatActivity() {
if (pwd.isEmpty()) { if (pwd.isEmpty()) {
Toast.makeText(this, R.string.password_hint, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.password_hint, Toast.LENGTH_SHORT).show()
} else { } else {
Toast.makeText(this, getString(R.string.password_login_success, phone, pwd), Toast.LENGTH_SHORT).show() passwordLogin(phone, pwd)
} }
} }
} }
@@ -143,8 +164,11 @@ class LoginActivity : AppCompatActivity() {
Toast.makeText(this, R.string.phone_hint, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.phone_hint, Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
Toast.makeText(this, getString(R.string.code_sent_to, phone), Toast.LENGTH_SHORT).show() if (phone.length != 11) {
startCountDown() Toast.makeText(this, R.string.phone_format_invalid, Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
sendVerificationCode(phone)
} }
binding.registerText.setOnClickListener { binding.registerText.setOnClickListener {
@@ -173,6 +197,92 @@ class LoginActivity : AppCompatActivity() {
}.start() }.start()
} }
private fun sendVerificationCode(phone: String) {
CoroutineScope(Dispatchers.IO).launch {
try {
val response = HttpUtils.api.getCode(phone)
withContext(Dispatchers.Main) {
if (response.code == 200) {
Toast.makeText(this@LoginActivity, getString(R.string.code_sent_to, phone), Toast.LENGTH_SHORT).show()
startCountDown()
} else {
Toast.makeText(this@LoginActivity, response.msg, Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(this@LoginActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun smsLogin(phone: String, smsCode: String) {
CoroutineScope(Dispatchers.IO).launch {
try {
val loginRequest = LoginRequest(
grantType = "sms",
username = phone,
phonenumber = phone,
smsCode = smsCode
)
android.util.Log.d("LoginActivity", "短信登录请求: $loginRequest")
val response = HttpUtils.api.login(loginRequest)
android.util.Log.d("LoginActivity", "短信登录响应: code=${response.code}, msg=${response.msg}")
withContext(Dispatchers.Main) {
if (response.code == 200 && response.data != null) {
UserinfoUtil.saveUserInfo(response.data)
Toast.makeText(this@LoginActivity, R.string.sms_login_success_title, Toast.LENGTH_SHORT).show()
val intent = Intent(this@LoginActivity, HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()
} else {
Toast.makeText(this@LoginActivity, response.msg, Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
android.util.Log.e("LoginActivity", "短信登录异常", e)
withContext(Dispatchers.Main) {
Toast.makeText(this@LoginActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun passwordLogin(phone: String, password: String) {
CoroutineScope(Dispatchers.IO).launch {
try {
val loginRequest = LoginRequest(
grantType = "password",
username = phone,
phonenumber = phone,
password = password
)
android.util.Log.d("LoginActivity", "密码登录请求: $loginRequest")
val response = HttpUtils.api.login(loginRequest)
android.util.Log.d("LoginActivity", "密码登录响应: code=${response.code}, msg=${response.msg}")
withContext(Dispatchers.Main) {
if (response.code == 200 && response.data != null) {
UserinfoUtil.saveUserInfo(response.data)
Toast.makeText(this@LoginActivity, R.string.password_login_success_title, Toast.LENGTH_SHORT).show()
val intent = Intent(this@LoginActivity, HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()
} else {
Toast.makeText(this@LoginActivity, response.msg, Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
android.util.Log.e("LoginActivity", "密码登录异常", e)
withContext(Dispatchers.Main) {
Toast.makeText(this@LoginActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun setGradientBackground( private fun setGradientBackground(
view: android.view.View, view: android.view.View,
startColor: Int, startColor: Int,

View File

@@ -4,25 +4,34 @@ import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.os.Bundle import android.os.Bundle
import android.os.CountDownTimer
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import bean.RegisterRequest
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.RegisterActivityBinding import com.example.defenseapplication.databinding.RegisterActivityBinding
import com.example.defenseapplication.utils.HttpUtils
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* 注册页面
* 功能:收集用户信息(手机号、身份证号、密码、邀请码),
* 进行前端格式校验,模拟注册请求并反馈结果。
*/
class RegisterActivity : AppCompatActivity() { class RegisterActivity : AppCompatActivity() {
private lateinit var binding: RegisterActivityBinding private lateinit var binding: RegisterActivityBinding
private var isLoading = false // 防止重复点击 private var isLoading = false
private var countDownTimer: CountDownTimer? = null
private var isSendingCode = false
companion object {
private const val COUNTDOWN_DURATION = 60000L
private const val COUNTDOWN_INTERVAL = 1000L
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = RegisterActivityBinding.inflate(layoutInflater) binding = RegisterActivityBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
// 设置渐变背景
val startColor = Color.parseColor("#CBEBFA") val startColor = Color.parseColor("#CBEBFA")
val endColor = Color.parseColor("#FFF2EF") val endColor = Color.parseColor("#FFF2EF")
setGradientBackground(binding.main, startColor, endColor) setGradientBackground(binding.main, startColor, endColor)
@@ -33,172 +42,161 @@ class RegisterActivity : AppCompatActivity() {
.init() .init()
setupListeners() setupListeners()
//setupTextWatchers()
} }
private fun setupListeners() { private fun setupListeners() {
// 返回按钮
binding.backIcon.setOnClickListener { binding.backIcon.setOnClickListener {
finish() finish()
} }
// 下一步按钮 binding.sendCodeBtn.setOnClickListener {
if (!isSendingCode && countDownTimer == null) {
val phone = binding.etPhoneNumber.text.toString().trim()
if (phone.isEmpty()) {
Toast.makeText(this, R.string.enter_phone_number, Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (phone.length != 11) {
Toast.makeText(this, R.string.phone_format_invalid, Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
sendVerificationCode(phone)
}
}
binding.btnNext.setOnClickListener { binding.btnNext.setOnClickListener {
if (!isLoading) { if (!isLoading) {
val intent= Intent(this, FaceLoginActivity::class.java) performRegistration()
}
}
}
private fun sendVerificationCode(userName: String) {
isSendingCode = true
binding.sendCodeBtn.isEnabled = false
CoroutineScope(Dispatchers.IO).launch {
try {
val response = HttpUtils.api.getCode(userName)
withContext(Dispatchers.Main) {
if (response.code == 200) {
Toast.makeText(this@RegisterActivity, "验证码已发送", Toast.LENGTH_SHORT).show()
startCountDown()
} else {
Toast.makeText(this@RegisterActivity, response.msg, Toast.LENGTH_SHORT).show()
binding.sendCodeBtn.isEnabled = true
isSendingCode = false
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(this@RegisterActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
binding.sendCodeBtn.isEnabled = true
isSendingCode = false
}
}
}
}
private fun startCountDown() {
countDownTimer = object : CountDownTimer(COUNTDOWN_DURATION, COUNTDOWN_INTERVAL) {
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished / 1000).toInt()
binding.sendCodeBtn.text = "${secondsRemaining}秒后重试"
binding.sendCodeBtn.isEnabled = false
}
override fun onFinish() {
resetSendButton()
}
}.start()
}
private fun resetSendButton() {
countDownTimer?.cancel()
countDownTimer = null
binding.sendCodeBtn.text = getString(R.string.send_code)
binding.sendCodeBtn.isEnabled = true
isSendingCode = false
}
private fun performRegistration() {
val phone = binding.etPhoneNumber.text.toString().trim()
val smsCode = binding.etSmsCode.text.toString().trim()
val idCard = binding.etIdNumber.text.toString().trim()
val password = binding.etPassword.text.toString().trim()
val inviteCode = binding.etInviteCode.text.toString().trim()
if (phone.isEmpty()) {
Toast.makeText(this, R.string.enter_phone_number, Toast.LENGTH_SHORT).show()
return
}
if (phone.length != 11) {
Toast.makeText(this, R.string.phone_format_invalid, Toast.LENGTH_SHORT).show()
return
}
if (smsCode.isEmpty()) {
Toast.makeText(this, R.string.enter_verification_code_pls, Toast.LENGTH_SHORT).show()
return
}
if (idCard.isEmpty()) {
Toast.makeText(this, R.string.enter_id_number, Toast.LENGTH_SHORT).show()
return
}
if (idCard.length != 18) {
Toast.makeText(this, R.string.hint_input_correct_id_card, Toast.LENGTH_SHORT).show()
return
}
if (password.isEmpty()) {
Toast.makeText(this, R.string.password_hint, Toast.LENGTH_SHORT).show()
return
}
if (password.length < 6 || password.length > 20) {
Toast.makeText(this, R.string.password_requirement, Toast.LENGTH_SHORT).show()
return
}
isLoading = true
binding.btnNext.isEnabled = false
binding.btnNext.text = getString(R.string.registering)
CoroutineScope(Dispatchers.IO).launch {
try {
val registerRequest = RegisterRequest(
grantType = "password",
smsCode = smsCode,
username = phone,
password = password,
idCard = idCard,
inviteCode = if (inviteCode.isEmpty()) null else inviteCode,
faceAddr = "https://tiebapic.baidu.com/forum/pic/item/2924ab18972bd407021da3e33d899e510eb309d1.jpg?tbpicau=2026-04-22-05_44105254d24a81214177d11b72513421"
)
val response = HttpUtils.api.register(registerRequest)
withContext(Dispatchers.Main) {
isLoading = false
binding.btnNext.isEnabled = true
binding.btnNext.text = getString(R.string.next_step)
if (response.code == 200) {
Toast.makeText(this@RegisterActivity, R.string.register_success, Toast.LENGTH_SHORT).show()
val intent = Intent(this@RegisterActivity, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent) startActivity(intent)
Toast.makeText(this, "跳转到人脸页面", Toast.LENGTH_SHORT).show() finish()
//performRegistration() } else {
Toast.makeText(this@RegisterActivity, response.msg, Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
isLoading = false
binding.btnNext.isEnabled = true
binding.btnNext.text = getString(R.string.next_step)
Toast.makeText(this@RegisterActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
} }
} }
} }
//
// /**
// * 为输入框添加文字监听,实时清除错误提示
// */
// private fun setupTextWatchers() {
// val errorClearWatcher = object : TextWatcher {
// override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
// override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
// override fun afterTextChanged(s: Editable?) {
// // 当用户开始输入时,清除该字段的错误
// when (binding.etPhoneNumber.hasFocus()) {
// true -> binding.etPhoneNumber.error = null
// else -> Unit
// }
// when (binding.etIdNumber.hasFocus()) {
// true -> binding.etIdNumber.error = null
// else -> Unit
// }
// when (binding.etPassword.hasFocus()) {
// true -> binding.etPassword.error = null
// else -> Unit
// }
// }
// }
//
// binding.etPhoneNumber.addTextChangedListener(errorClearWatcher)
// binding.etIdNumber.addTextChangedListener(errorClearWatcher)
// binding.etPassword.addTextChangedListener(errorClearWatcher)
// }
//
// /**
// * 执行注册流程:校验输入 -> 模拟网络请求
// */
// private fun performRegistration() {
// val phone = binding.etPhoneNumber.text.toString().trim()
// val idNumber = binding.etIdNumber.text.toString().trim()
// val password = binding.etPassword.text.toString().trim()
// val inviteCode = binding.etInviteCode.text.toString().trim()
//
// // 1. 校验
// val phoneValid = validatePhone(phone)
// val idValid = validateIdNumber(idNumber)
// val passwordValid = validatePassword(password)
//
// if (!phoneValid) {
// binding.etPhoneNumber.error = "请输入有效的11位手机号"
// binding.etPhoneNumber.requestFocus()
// return
// }
// if (!idValid) {
// binding.etIdNumber.error = "请输入正确的身份证号"
// binding.etIdNumber.requestFocus()
// return
// }
// if (!passwordValid) {
// binding.etPassword.error = "密码长度需为6~20位"
// binding.etPassword.requestFocus()
// return
// }
//
// // 2. 模拟注册请求(展示加载状态)
// isLoading = true
// binding.btnNext.isEnabled = false
// binding.btnNext.text = "注册中..."
//
// lifecycleScope.launch {
// // 模拟网络延迟 1.5 秒
// val registerResult = simulateRegister(phone, idNumber, password, inviteCode)
// delay(1500)
//
// isLoading = false
// binding.btnNext.isEnabled = true
// binding.btnNext.text = "下一步"
//
// if (registerResult) {
// Toast.makeText(this@RegisterActivity, "注册成功!", Toast.LENGTH_SHORT).show()
// // 实际项目中通常跳转到登录页或主页,这里简单关闭当前页面
// finish()
// } else {
// Toast.makeText(this@RegisterActivity, "注册失败,请稍后重试", Toast.LENGTH_SHORT).show()
// }
// }
// }
//
// /**
// * 模拟注册请求(实际应替换为 Retrofit/OkHttp 调用)
// */
// private suspend fun simulateRegister(
// phone: String,
// idNumber: String,
// password: String,
// inviteCode: String
// ): Boolean {
// // 模拟网络请求,此处始终返回成功(实际根据后端响应决定)
// // 您可以在这里添加真实网络请求逻辑
// return true
// }
// /**
// * 校验手机号中国大陆手机号简单校验1开头的11位数字第二位3-9
// */
// private fun validatePhone(phone: String): Boolean {
// val phoneRegex = Pattern.compile("^1[3-9]\\d{9}$")
// return phone.isNotEmpty() && phoneRegex.matcher(phone).matches()
// }
//
// /**
// * 校验身份证号支持15位和18位18位时校验最后一位校验码
// */
// private fun validateIdNumber(idNumber: String): Boolean {
// if (idNumber.isEmpty()) return false
// // 长度校验
// if (idNumber.length != 15 && idNumber.length != 18) return false
//
// // 15位全数字
// if (idNumber.length == 15) {
// return idNumber.all { it.isDigit() }
// }
//
// // 18位前17位为数字最后一位为数字或大写X
// val regex = Regex("^\\d{17}[\\dXx]$")
// if (!regex.matches(idNumber)) return false
//
// // 校验最后一位校验码(可选,提升准确性)
// return verifyIdCardChecksum(idNumber)
// }
//
// /**
// * 身份证最后一位校验码校验仅18位
// */
// private fun verifyIdCardChecksum(id18: String): Boolean {
// val weights = intArrayOf(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
// val checkCodes = charArrayOf('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')
// var sum = 0
// for (i in 0 until 17) {
// sum += (id18[i] - '0') * weights[i]
// }
// val mod = sum % 11
// val expectedCheckCode = checkCodes[mod]
// return id18[17].uppercaseChar() == expectedCheckCode
// }
//
// /**
// * 校验密码长度6~20位
// */
// private fun validatePassword(password: String): Boolean {
// return password.length in 6..20
// }
private fun setGradientBackground( private fun setGradientBackground(
view: android.view.View, view: android.view.View,
startColor: Int, startColor: Int,

View File

@@ -11,7 +11,7 @@ import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.RegisterSuccessActivityBinding import com.example.defenseapplication.databinding.RegisterSuccessActivityBinding
import com.example.defenseapplication.home.HomeActivity import com.example.defenseapplication.home.HomeActivity
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
//注册 //注册成功界面
class RegisterSuccessActivity : AppCompatActivity() { class RegisterSuccessActivity : AppCompatActivity() {
private lateinit var binding: RegisterSuccessActivityBinding private lateinit var binding: RegisterSuccessActivityBinding

View File

@@ -1,14 +1,27 @@
package com.example.defenseapplication.utils package com.example.defenseapplication.utils
import com.google.gson.Gson import bean.ForgotRequest
import okhttp3.OkHttpClient import com.example.defenseapplication.bean.GetCodeBean
import bean.LoginBean
import bean.LoginRequest
import bean.RegisterRequest
import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Query
interface ApiService { interface ApiService {
// 获取登录注册验证码
@GET("/resource/sms/code") @GET("/resource/sms/code")
suspend fun getCode(): String suspend fun getCode(@Query("userName") userName: String): GetCodeBean<String>
@POST("/app/login")
suspend fun login(@Body loginRequest: LoginRequest): GetCodeBean<LoginBean>
@POST("/app/register")
suspend fun register(@Body registerRequest: RegisterRequest): GetCodeBean<Any>
@PUT("/app/forgot")
suspend fun forgotPassword(@Body forgotRequest: ForgotRequest): GetCodeBean<Any>
} }

View File

@@ -10,7 +10,7 @@ import java.util.concurrent.TimeUnit
//网络工具类 //网络工具类
object HttpUtils { object HttpUtils {
val baseURL = "https://stem-boozy-margarine.ngrok-free.dev"
val okHttpClient = OkHttpClient.Builder() val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.writeTimeout(3, TimeUnit.MINUTES) .writeTimeout(3, TimeUnit.MINUTES)
@@ -30,7 +30,7 @@ object HttpUtils {
val retrofit = Retrofit.Builder() val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient) .client(okHttpClient)
.baseUrl("https://stem-boozy-margarine.ngrok-free.dev") .baseUrl(baseURL)
.build() .build()

View File

@@ -97,6 +97,64 @@
app:layout_constraintStart_toEndOf="@+id/ivPhoneIcon" app:layout_constraintStart_toEndOf="@+id/ivPhoneIcon"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- 验证码输入框 -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/smsRowLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/textinput_white_bg"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:layout_constraintTop_toBottomOf="@+id/phoneRowLayout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="visible">
<!-- 左侧图标 -->
<ImageView
android:id="@+id/ivSmsIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/vc"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<!-- 验证码输入框(无背景) -->
<EditText
android:id="@+id/etSmsCode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:hint="@string/sms_code_hint"
android:textColorHint="@color/gray"
android:textColor="@android:color/black"
android:background="@null"
android:inputType="number"
android:paddingVertical="16dp"
app:layout_constraintStart_toEndOf="@+id/ivSmsIcon"
app:layout_constraintEnd_toStartOf="@+id/sendCodeBtn"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<!-- 发送验证码按钮 -->
<android.widget.Button
android:id="@+id/sendCodeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_sms_code"
android:textSize="16sp"
android:textColor="@color/white"
android:background="@drawable/rounded_blue_bg"
android:paddingHorizontal="12dp"
android:maxWidth="120dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="12dp" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<!-- 身份证号输入框 --> <!-- 身份证号输入框 -->
@@ -110,7 +168,7 @@
android:paddingEnd="16dp" android:paddingEnd="16dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/phoneRowLayout"> app:layout_constraintTop_toBottomOf="@+id/smsRowLayout">
<ImageView <ImageView
android:id="@+id/ivIdIcon" android:id="@+id/ivIdIcon"

View File

@@ -39,6 +39,7 @@
<string name="and"></string> <string name="and"></string>
<string name="privacy_policy">隐私政策</string> <string name="privacy_policy">隐私政策</string>
<string name="register_success">注册成功</string> <string name="register_success">注册成功</string>
<string name="registering">注册中...</string>
<string name="register_success_message">您的账号已成功创建</string> <string name="register_success_message">您的账号已成功创建</string>
<string name="go_to_login">去登录</string> <string name="go_to_login">去登录</string>
@@ -320,6 +321,7 @@
<string name="determine">确定</string> <string name="determine">确定</string>
<string name="id_number">身份证号</string> <string name="id_number">身份证号</string>
<string name="enter_id_number">请输入身份证号</string> <string name="enter_id_number">请输入身份证号</string>
<string name="hint_input_correct_id_card">请输入正确的身份证号</string>
<string name="invite_code_optional">邀请码(非必填)</string> <string name="invite_code_optional">邀请码(非必填)</string>
<string name="enter_invite_code">请输入邀请码</string> <string name="enter_invite_code">请输入邀请码</string>
<string name="scan_add">扫描添加</string> <string name="scan_add">扫描添加</string>

View File

@@ -35,6 +35,7 @@
<string name="and">and</string> <string name="and">and</string>
<string name="privacy_policy">Privacy Policy</string> <string name="privacy_policy">Privacy Policy</string>
<string name="register_success">Registration Successful</string> <string name="register_success">Registration Successful</string>
<string name="registering">Registering...</string>
<string name="register_success_message">Your account has been created successfully</string> <string name="register_success_message">Your account has been created successfully</string>
<string name="go_to_login">Go to Login</string> <string name="go_to_login">Go to Login</string>
@@ -304,6 +305,7 @@
<string name="determine">Confirm</string> <string name="determine">Confirm</string>
<string name="id_number">ID Number</string> <string name="id_number">ID Number</string>
<string name="enter_id_number">Enter ID number</string> <string name="enter_id_number">Enter ID number</string>
<string name="hint_input_correct_id_card">Please enter a valid ID card number</string>
<string name="invite_code_optional">Invite Code (Optional)</string> <string name="invite_code_optional">Invite Code (Optional)</string>
<string name="enter_invite_code">Enter invite code</string> <string name="enter_invite_code">Enter invite code</string>
<string name="scan_add">Scan to Add</string> <string name="scan_add">Scan to Add</string>