登录相关,首次提交

This commit is contained in:
fengjignqi
2026-09-18 09:59:43 +08:00
parent 6e9b97e42e
commit f7cf112dc9
45 changed files with 2362 additions and 177 deletions

View File

@@ -1,11 +1,21 @@
package com.juhabi.birds
object AppConfig {
const val BASE_URL = "http://47.97.217.123:8081/"
const val APP_CLIENT_ID = "428a8310cd442757ae699df5d894f051"
// const val BASE_URL = "http://47.97.217.123:8081"
const val BASE_URL = "http://192.168.1.217:8081/"
const val APP_CLIENT_ID = "app"
const val APP_USER_TYPE = "app_user"
const val CONTENT_LANGUAGE = "zh_CN"
//验证码场景 --注册
const val SEND_CODE_SCENE_REGISTER = "register"
//验证码场景 --登录
const val SEND_CODE_SCENE_LOGIN = "login"
//验证码场景 --重置密码
const val SEND_CODE_SCENE_RESET = "resetPwd"
@Volatile
var userToken: String = ""
//是否登录
var USER_LOGIN: Boolean = false;
}

View File

@@ -6,6 +6,8 @@ import android.app.Application
import android.content.Context
import android.os.Build
import android.os.Bundle
import com.juhabi.birds.network.TokenManager
import com.juhabi.birds.util.SharedPreferencesUtil
import com.juhabi.birds.util.manager.UserInfoManager
import me.jessyan.autosize.AutoSize
import me.jessyan.autosize.AutoSizeConfig
@@ -36,9 +38,21 @@ class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
/** Add application-wide, privacy-safe SDK initialization here. */
private fun initialize() {
UserInfoManager.initialize(this)
restoreLoginSession()
// NetworkManager is created lazily by ApiClient, so it needs no eager initialization.
}
/**
* 启动时从本地恢复 token,写入 AppConfig.userToken,
* 业务接口通过 NetworkManager.getToken() 自动带上 Authorization。
*/
private fun restoreLoginSession() {
val token = TokenManager.get(this).trim()
AppConfig.userToken = token
AppConfig.USER_LOGIN = token.isNotBlank() &&
SharedPreferencesUtil.getBoolean(this, KEY_USER_LOGIN, defaultValue = token.isNotBlank())
}
override fun onActivityResumed(activity: Activity) {
currentActivity = activity
}
@@ -80,6 +94,8 @@ class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
}
companion object {
private const val KEY_USER_LOGIN = "user_login"
lateinit var instance: MyApplication
private set

View File

@@ -0,0 +1,217 @@
package com.juhabi.birds.login
import android.os.Bundle
import android.os.CountDownTimer
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ForgetPsdLayoutBinding
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginImpl
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.util.ToastUtil
/**
* 忘记密码 / 修改密码
*/
class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>(),
LoginContract.LoginView, View.OnClickListener {
private var codeCountDownTimer: CountDownTimer? = null
private var isPasswordVisible = false
private var isConfirmPasswordVisible = false
override fun createBinding(inflater: LayoutInflater): ForgetPsdLayoutBinding {
return ForgetPsdLayoutBinding.inflate(inflater)
}
override fun createPresenter(): LoginPresenter? {
return LoginPresenter(LoginImpl())
}
override fun initWindows() {
ImmersionBar.with(this)
.statusBarView(binding.statusBarView)
.statusBarDarkFont(true)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun initView(savedInstanceState: Bundle?) {
binding.includedTitle.tvTitle.setText(R.string.forget_password_title)
listOf(
binding.includedTitle.imgBack,
binding.getCodeButton,
binding.passwordVisibilityButton,
binding.confirmPasswordVisibilityButton,
binding.confirmButton,
).forEach { view ->
view.setOnClickListener(this)
}
}
override fun onClick(view: View) {
when (view.id) {
R.id.img_back -> {
finish()
}
R.id.getCodeButton -> {//获取验证码
val phone = binding.phoneEditText.text.toString().trim()
if (phone.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
return
}
presenter?.getPhoneSmsCode(phone, AppConfig.SEND_CODE_SCENE_RESET)
}
R.id.passwordVisibilityButton -> {
isPasswordVisible = !isPasswordVisible
togglePasswordVisibility(binding.passwordEditText, isPasswordVisible)
}
R.id.confirmPasswordVisibilityButton -> {
isConfirmPasswordVisible = !isConfirmPasswordVisible
togglePasswordVisibility(binding.confirmPasswordEditText, isConfirmPasswordVisible)
}
R.id.confirmButton -> {//修改密码
val phone = binding.phoneEditText.text.toString().trim()
val code = binding.codeEditText.text.toString().trim()
val password = binding.passwordEditText.text.toString()
val confirmPassword = binding.confirmPasswordEditText.text.toString()
if (phone.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
return
}
if (code.isBlank()) {
ToastUtil.showShort(mContext, "验证码不能为空")
return
}
if (password.isBlank()) {
ToastUtil.showShort(mContext, "密码不能为空")
return
}
if (confirmPassword.isBlank()) {
ToastUtil.showShort(mContext, "请再次输入密码")
return
}
if (password != confirmPassword) {
ToastUtil.showShort(mContext, "两次输入的密码不一致")
return
}
if (!isValidPassword(password)) {
ToastUtil.showShort(
mContext,
"密码需为8-20位,且包含大小写字母和数字"
)
return
}
presenter?.forgetPsdByPhone(phone, code, password)
}
}
}
private fun togglePasswordVisibility(editText: EditText, visible: Boolean) {
val selection = editText.selectionEnd
editText.inputType = if (visible) {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
} else {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
editText.setSelection(selection.coerceAtLeast(0))
editText.typeface = binding.phoneEditText.typeface
}
override fun showError(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
}
override fun sendCodeSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
codeCountDownTimer?.cancel()
binding.getCodeButton.apply {
isEnabled = false
isClickable = false
setBackgroundResource(R.drawable.shape_register_code_disabled_bg)
text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS)
}
codeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) {
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished + 999L) / 1_000L
binding.getCodeButton.text = getString(
R.string.register_code_countdown,
secondsRemaining
)
}
override fun onFinish() {
codeCountDownTimer = null
binding.getCodeButton.apply {
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
setText(R.string.register_get_code)
}
}
}.start()
}
override fun onRegisterSuccess(message: String) {
}
override fun onForgetPsdSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message.ifBlank { "密码修改成功" })
finish()
}
override fun showUsernameError(message: String) {
ToastUtil.showShort(mContext, message)
}
override fun showPasswordError(message: String) {
ToastUtil.showShort(mContext, message)
}
override fun clearInputErrors() {
}
override fun onLoginSuccess(user: UserEntity) {
}
override fun onDestroy() {
codeCountDownTimer?.cancel()
codeCountDownTimer = null
super.onDestroy()
}
/** 8-20 位,且同时包含大写、小写字母和数字。 */
private fun isValidPassword(password: String): Boolean {
if (password.length !in PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH) {
return false
}
val hasUpper = password.any { it.isUpperCase() }
val hasLower = password.any { it.isLowerCase() }
val hasDigit = password.any { it.isDigit() }
return hasUpper && hasLower && hasDigit
}
private companion object {
const val CODE_COUNTDOWN_SECONDS = 60L
const val CODE_COUNTDOWN_MILLIS = CODE_COUNTDOWN_SECONDS * 1_000L
const val PASSWORD_MIN_LENGTH = 8
const val PASSWORD_MAX_LENGTH = 20
}
}

View File

@@ -1,20 +1,35 @@
package com.juhabi.birds.login
import android.os.Bundle
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ActivityLoginBinding
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginImpl
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.network.TokenManager
import com.juhabi.birds.mine.MineCenterActivity
import com.juhabi.birds.network.TokenManager
import com.juhabi.birds.util.SharedPreferencesUtil
import com.juhabi.birds.util.ToastUtil
/**
* 登录
*/
class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
LoginContract.LoginView {
LoginContract.LoginView, View.OnClickListener {
/** 是否使用密码登录;false 为手机号 + 验证码登录 */
private var loginByPassword = true
private var isPasswordVisible = false
private var codeCountDownTimer: CountDownTimer? = null
override fun createBinding(inflater: LayoutInflater): ActivityLoginBinding {
return ActivityLoginBinding.inflate(inflater)
@@ -24,48 +39,176 @@ class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
return LoginPresenter(LoginImpl())
}
override fun initWindows() {
ImmersionBar.with(this)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun initView(savedInstanceState: Bundle?) {
// if (TokenManager.get(this).isNotBlank()) {
// startActivity(Intent(this, MineCenterActivity::class.java))
// finish()
// return
// }
if(binding.usernameEditText.text.toString().isNotBlank()){
ToastUtil.showShort(mContext,"");
return
listOf(
binding.tvChangeCode,
binding.forgotPasswordButton,
binding.passwordVisibilityButton,
binding.getCodeButton,
binding.loginButton,
binding.registerButton,
).forEach { button ->
button.setOnClickListener(this)
}
binding.loginButton.setOnClickListener {
presenter?.login(
binding.usernameEditText.text?.toString().orEmpty(),
binding.passwordEditText.text?.toString().orEmpty()
)
}
override fun onClick(view: View) {
when (view.id) {
R.id.tvChangeCode -> {//更换登录方式
if (loginByPassword) {
binding.linearCode.visibility = View.VISIBLE
binding.linearPsd.visibility = View.GONE
loginByPassword = false
binding.tvChangeCode.setText(R.string.register_change_psd_login)
} else {
binding.linearCode.visibility = View.GONE
binding.linearPsd.visibility = View.VISIBLE
loginByPassword = true
binding.tvChangeCode.setText(R.string.register_change_code_login)
}
}
R.id.getCodeButton -> {//获取验证码
val phone = binding.accountEditText.text.toString().trim()
if (phone.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
return
}
presenter?.getPhoneSmsCode(phone, AppConfig.SEND_CODE_SCENE_LOGIN)
}
R.id.registerButton -> {//注册
goTo(RegisterActivity::class.java, null)
}
R.id.forgotPasswordButton -> {//忘记密码
goTo(ForgetPsdActivity::class.java, null)
}
R.id.passwordVisibilityButton -> {
togglePasswordVisibility()
}
R.id.loginButton -> {//登录
val account = binding.accountEditText.text.toString().trim()
if (account.isBlank()) {
ToastUtil.showShort(mContext, "账号不能为空")
return
}
if (loginByPassword) {
val password = binding.passwordEditText.text.toString()
if (password.isBlank()) {
ToastUtil.showShort(mContext, "密码不能为空")
return
}
presenter?.loginByPhonePsd(account, password)
} else {
val code = binding.codeEditText.text.toString().trim()
if (code.isBlank()) {
ToastUtil.showShort(mContext, "验证码不能为空")
return
}
presenter?.loginByPhoneSms(account, code)
}
}
}
}
private fun togglePasswordVisibility() {
isPasswordVisible = !isPasswordVisible
val selection = binding.passwordEditText.selectionEnd
binding.passwordEditText.inputType = if (isPasswordVisible) {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
} else {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
// 保持光标位置,并触发 typeface 刷新
binding.passwordEditText.setSelection(selection.coerceAtLeast(0))
binding.passwordEditText.typeface = binding.accountEditText.typeface
}
override fun sendCodeSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
codeCountDownTimer?.cancel()
binding.getCodeButton.apply {
isEnabled = false
isClickable = false
setBackgroundResource(R.drawable.shape_register_code_disabled_bg)
text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS)
}
codeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) {
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished + 999L) / 1_000L
binding.getCodeButton.text = getString(
R.string.register_code_countdown,
secondsRemaining
)
}
override fun onFinish() {
codeCountDownTimer = null
binding.getCodeButton.apply {
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
setText(R.string.register_get_code)
}
}
}.start()
}
override fun onRegisterSuccess(message: String) {
}
override fun onForgetPsdSuccess(message: String) {
}
override fun showUsernameError(message: String) {
binding.usernameLayout.error = message
binding.usernameEditText.requestFocus()
ToastUtil.showShort(mContext, message)
}
override fun showPasswordError(message: String) {
binding.passwordLayout.error = message
binding.passwordEditText.requestFocus()
ToastUtil.showShort(mContext, message)
}
override fun clearInputErrors() {
binding.usernameLayout.error = null
binding.passwordLayout.error = null
}
override fun onLoginSuccess(user: LoginUser) {
TokenManager.save(this, user.accessToken)
binding.resultTextView.text = "登录成功,Token 已保存"
showMessage("登录成功")
override fun onLoginSuccess(user: UserEntity) {
val token = user.token.trim()
// SharedPreferences 持久化 token,并同步到内存 AppConfig.userToken
TokenManager.save(this, token)
AppConfig.USER_LOGIN = token.isNotBlank()
SharedPreferencesUtil.putBoolean(this, KEY_USER_LOGIN, AppConfig.USER_LOGIN)
ToastUtil.showShort(mContext, "登录成功")
startActivity(Intent(this, MineCenterActivity::class.java))
finish()
}
override fun showError(message: String) {
ToastUtil.showShort(mContext,message)
hideLoading()
ToastUtil.showShort(mContext, message)
}
override fun onDestroy() {
codeCountDownTimer?.cancel()
codeCountDownTimer = null
super.onDestroy()
}
private companion object {
const val CODE_COUNTDOWN_SECONDS = 60L
const val CODE_COUNTDOWN_MILLIS = CODE_COUNTDOWN_SECONDS * 1_000L
const val KEY_USER_LOGIN = "user_login"
}
}

View File

@@ -0,0 +1,192 @@
package com.juhabi.birds.login
import android.os.Bundle
import android.os.CountDownTimer
import android.view.LayoutInflater
import android.view.View
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.RegisterLayoutBinding
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginImpl
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.util.ToastUtil
/**
* 注册
*/
class RegisterActivity : BaseActivity<RegisterLayoutBinding, LoginPresenter>(),
LoginContract.LoginView, View.OnClickListener {
private var codeCountDownTimer: CountDownTimer? = null
override fun createBinding(inflater: LayoutInflater): RegisterLayoutBinding {
return RegisterLayoutBinding.inflate(inflater)
}
override fun createPresenter(): LoginPresenter? {
return LoginPresenter(LoginImpl())
}
override fun initWindows() {
ImmersionBar.with(this)
.statusBarView(binding.statusBarView)
.statusBarDarkFont(true)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun initView(savedInstanceState: Bundle?) {
binding.includedTitle.tvTitle.setText(R.string.login_register)
listOf(
binding.passwordVisibilityButton,
binding.getCodeButton,
binding.confirmButton,
binding.includedTitle.imgBack,
).forEach { button ->
button.setOnClickListener(this)
}
}
override fun onClick(view: View) {
when (view.id) {
R.id.img_back ->{
finish()
}
R.id.getCodeButton -> {//获取验证码
if (binding.phoneEditText.text.toString().isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
return
}
presenter?.getPhoneSmsCode(
binding.phoneEditText.text.toString(),
AppConfig.SEND_CODE_SCENE_REGISTER
)
}
R.id.confirmButton -> {//注册
val phone = binding.phoneEditText.text.toString().trim()
val code = binding.codeEditText.text.toString().trim()
val password = binding.passwordEditText.text.toString()
val confirmPassword = binding.confirmPasswordEditText.text.toString()
if (phone.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
return
}
if (code.isBlank()) {
ToastUtil.showShort(mContext, "验证码不能为空")
return
}
if (password.isBlank()) {
ToastUtil.showShort(mContext, "密码不能为空")
return
}
if (confirmPassword.isBlank()) {
ToastUtil.showShort(mContext, "请再次输入密码")
return
}
if (password != confirmPassword) {
ToastUtil.showShort(mContext, "两次输入的密码不一致")
return
}
if (!isValidPassword(password)) {
ToastUtil.showShort(
mContext,
"密码需为8-20位,且包含大小写字母和数字"
)
return
}
presenter?.registerByPhone(phone, code, password)
}
}
}
override fun showError(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
}
override fun sendCodeSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
codeCountDownTimer?.cancel()
binding.getCodeButton.apply {
isEnabled = false
isClickable = false
setBackgroundResource(R.drawable.shape_register_code_disabled_bg)
text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS)
}
codeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) {
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished + 999L) / 1_000L
binding.getCodeButton.text = getString(
R.string.register_code_countdown,
secondsRemaining
)
}
override fun onFinish() {
codeCountDownTimer = null
binding.getCodeButton.apply {
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
setText(R.string.register_get_code)
}
}
}.start()
}
override fun onRegisterSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message.ifBlank { "注册成功" })
finish()
}
override fun onForgetPsdSuccess(message: String) {
}
override fun showUsernameError(message: String) {
}
override fun showPasswordError(message: String) {
}
override fun clearInputErrors() {
}
override fun onLoginSuccess(user: UserEntity) {
}
override fun onDestroy() {
codeCountDownTimer?.cancel()
codeCountDownTimer = null
super.onDestroy()
}
/** 8-20 位,且同时包含大写、小写字母和数字。 */
private fun isValidPassword(password: String): Boolean {
if (password.length !in PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH) {
return false
}
val hasUpper = password.any { it.isUpperCase() }
val hasLower = password.any { it.isLowerCase() }
val hasDigit = password.any { it.isDigit() }
return hasUpper && hasLower && hasDigit
}
private companion object {
const val CODE_COUNTDOWN_SECONDS = 60L
const val CODE_COUNTDOWN_MILLIS = CODE_COUNTDOWN_SECONDS * 1_000L
const val PASSWORD_MIN_LENGTH = 8
const val PASSWORD_MAX_LENGTH = 20
}
}

View File

@@ -0,0 +1,86 @@
package com.juhabi.birds.login
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ActiveWelcomeBinding
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.main.MainActivity
/**
* 启动页面
*/
class WelcomeActivity: BaseActivity<ActiveWelcomeBinding,LoginPresenter>(),LoginContract.LoginView {
private val handler = Handler(Looper.getMainLooper())
private val navigateRunnable = Runnable {
val targetActivity = if (AppConfig.USER_LOGIN) {
MainActivity::class.java
} else {
LoginActivity::class.java
}
startActivity(Intent(this, targetActivity))
finish()
}
override fun createBinding(inflater: LayoutInflater): ActiveWelcomeBinding {
return ActiveWelcomeBinding.inflate(inflater)
}
override fun initWindows() {
ImmersionBar.with(this)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun createPresenter(): LoginPresenter? {
return null
}
override fun initView(savedInstanceState: Bundle?) {
handler.postDelayed(navigateRunnable, SPLASH_DELAY_MILLIS)
}
override fun showError(message: String) {
}
override fun sendCodeSuccess(message: String) {
}
override fun onRegisterSuccess(message: String) {
}
override fun onForgetPsdSuccess(message: String) {
}
override fun showUsernameError(message: String) {
}
override fun showPasswordError(message: String) {
}
override fun clearInputErrors() {
}
override fun onLoginSuccess(user: UserEntity) {
}
override fun onDestroy() {
handler.removeCallbacks(navigateRunnable)
super.onDestroy()
}
private companion object {
const val SPLASH_DELAY_MILLIS = 2_000L
}
}

View File

@@ -1,18 +0,0 @@
package com.juhabi.birds.login.model
import com.google.gson.annotations.SerializedName
data class LoginUser(
val scope: Any? = null,
val openid: Any? = null,
@SerializedName("access_token")
val accessToken: String = "",
@SerializedName("refresh_token")
val refreshToken: Any? = null,
@SerializedName("expire_in")
val expireIn: Long = 0,
@SerializedName("refresh_expire_in")
val refreshExpireIn: Any? = null,
@SerializedName("client_id")
val clientId: String = ""
)

View File

@@ -0,0 +1,7 @@
package com.juhabi.birds.login.model
data class SendCodeEntity(
val code: Int,
val `data`: Any,
val msg: String
)

View File

@@ -0,0 +1,11 @@
package com.juhabi.birds.login.model
import com.google.gson.annotations.SerializedName
data class UserEntity(
val scope: Any? = null,
val openid: Any? = null,
@SerializedName("access_token")
val token: String = "",
)

View File

@@ -1,7 +1,8 @@
package com.juhabi.birds.login.mvp
import com.juhabi.birds.base.IView
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
@@ -12,15 +13,38 @@ interface LoginContract {
}
interface DataModel {
fun login(json: JSONObject, callback: Callback<BaseBean<LoginUser>>)
//手机验证码
fun sendPhoneCode(json: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
//手机号+验证码注册
fun registerByPhone(json: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
//手机+密码登录
fun loginByPhonePsd(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//手机+验证码登录
fun loginByPhoneSms(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//手机号+验证码修改密码
fun forgetPsdByPhone(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//邮箱验证码
fun sendEmailCode(json: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
//邮箱+验证码注册
fun registerByEmail(json: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
//邮箱+密码登录
fun loginByEmailPsd(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//邮箱+验证码登录
fun loginByEmailSms(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//邮箱+验证码修改密码
fun forgetPsdByEmail(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
fun cancel()
}
interface LoginView : IView {
fun sendCodeSuccess(message: String)
fun onRegisterSuccess(message: String)
fun onForgetPsdSuccess(message: String)
fun showUsernameError(message: String)
fun showPasswordError(message: String)
fun clearInputErrors()
fun onLoginSuccess(user: LoginUser)
fun onLoginSuccess(user: UserEntity)
}
}

View File

@@ -2,7 +2,8 @@ package com.juhabi.birds.login.mvp
import android.os.Handler
import android.os.Looper
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.ApiClient
import com.juhabi.birds.network.BaseBean
import com.juhabi.birds.network.NetworkManager
@@ -13,45 +14,330 @@ import retrofit2.Response
class LoginImpl : LoginContract.DataModel {
private val mainHandler = Handler(Looper.getMainLooper())
private var loginCall: Call<BaseBean<LoginUser>>? = null
private var loginCall: Call<BaseBean<UserEntity>>? = null
override fun login(
private val calls = mutableSetOf<Call<*>>()
/**
* 发送手机验证码
*/
override fun sendPhoneCode(json: JSONObject, callback: LoginContract.Callback<BaseBean<SendCodeEntity>>) {
enqueueSendCodeCall(
ApiClient.apiService.sendPhoneCode(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
/**
* 手机号注册
*/
override fun registerByPhone(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<LoginUser>>
callback: LoginContract.Callback<BaseBean<SendCodeEntity>>
) {
enqueueSendCodeCall(
ApiClient.apiService.registerByPhone(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
private fun enqueueSendCodeCall(
call: Call<BaseBean<SendCodeEntity>>,
callback: LoginContract.Callback<BaseBean<SendCodeEntity>>
) {
calls += call
call.enqueue(object : Callback<BaseBean<SendCodeEntity>> {
override fun onResponse(
call: Call<BaseBean<SendCodeEntity>>,
response: Response<BaseBean<SendCodeEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(
call: Call<BaseBean<SendCodeEntity>>,
error: Throwable
) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun loginByPhonePsd(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
enqueueLoginCall(
ApiClient.apiService.loginByPhonePsd(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
override fun loginByPhoneSms(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
enqueueLoginCall(
ApiClient.apiService.loginByPhoneSms(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
override fun forgetPsdByPhone(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
enqueueLoginCall(
ApiClient.apiService.forgetPsdByPhone(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
override fun sendEmailCode(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<SendCodeEntity>>
) {
val call = ApiClient.apiService.sendEmailCode(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<SendCodeEntity>> {
override fun onResponse(
call: Call<BaseBean<SendCodeEntity>>,
response: Response<BaseBean<SendCodeEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<SendCodeEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun registerByEmail(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<SendCodeEntity>>
) {
val call = ApiClient.apiService.registerByE(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<SendCodeEntity>> {
override fun onResponse(
call: Call<BaseBean<SendCodeEntity>>,
response: Response<BaseBean<SendCodeEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<SendCodeEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun loginByEmailPsd(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
val call = ApiClient.apiService.loginByEmailPsd(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<UserEntity>> {
override fun onResponse(
call: Call<BaseBean<UserEntity>>,
response: Response<BaseBean<UserEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<UserEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun loginByEmailSms(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
val call = ApiClient.apiService.loginByEmailSms(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<UserEntity>> {
override fun onResponse(
call: Call<BaseBean<UserEntity>>,
response: Response<BaseBean<UserEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<UserEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun forgetPsdByEmail(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
val call = ApiClient.apiService.forgetPsdByEmail(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<UserEntity>> {
override fun onResponse(
call: Call<BaseBean<UserEntity>>,
response: Response<BaseBean<UserEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<UserEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
private fun enqueueLoginCall(
call: Call<BaseBean<UserEntity>>,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
loginCall?.cancel()
loginCall = ApiClient.apiService
.login(NetworkManager.getToken(), NetworkManager.toRequestBody(json))
.also { call ->
call.enqueue(object : Callback<BaseBean<LoginUser>> {
override fun onResponse(
call: Call<BaseBean<LoginUser>>,
response: Response<BaseBean<LoginUser>>
) {
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
loginCall = call
call.enqueue(object : Callback<BaseBean<UserEntity>> {
override fun onResponse(
call: Call<BaseBean<UserEntity>>,
response: Response<BaseBean<UserEntity>>
) {
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<LoginUser>>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
override fun onFailure(call: Call<BaseBean<UserEntity>>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
})
}
}
})
}
override fun cancel() {
loginCall?.cancel()
loginCall = null
calls.forEach { it.cancel() }
calls.clear()
mainHandler.removeCallbacksAndMessages(null)
}
}

View File

@@ -1,44 +1,31 @@
package com.juhabi.birds.login.mvp
import com.juhabi.birds.base.BasePresenter
import com.juhabi.birds.AppConfig
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginContract.LoginView>() {
fun login(username: String, password: String) {
val currentView = view ?: return
currentView.clearInputErrors()
if (username.isBlank()) {
currentView.showUsernameError("请输入账号")
return
}
if (password.isBlank()) {
currentView.showPasswordError("请输入密码")
return
}
currentView.showLoading()
fun getPhoneSmsCode(phoneNumber: String, sendCodeSceneRegister: String) {
val json = JSONObject();
json.put("grantType", "password")
json.put("username", username.trim())
json.put("password", password)
json.put("clientId", AppConfig.APP_CLIENT_ID)
json.put("userType", AppConfig.APP_USER_TYPE)
loginImpl.login(
json.put("phoneNumber", phoneNumber)
json.put("scene", sendCodeSceneRegister)
loginImpl.sendPhoneCode(
json,
object : LoginContract.Callback<BaseBean<LoginUser>> {
override fun success(data: BaseBean<LoginUser>) {
object : LoginContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
val attachedView = view ?: return
attachedView.hideLoading()
val user = data.data
if ((data.code == 0 || data.code == 200) && user != null) {
attachedView.onLoginSuccess(user)
// 发码接口通常只返回 code/msg,data 可能为 null,不能依赖 data 非空
if (data.code == 0 || data.code == 200) {
attachedView.sendCodeSuccess(
data.message.ifBlank { "验证码已发送" }
)
} else {
attachedView.showError(data.message.ifBlank { "登录失败" })
attachedView.showError(data.message.ifBlank { "服务异常" })
}
}
@@ -52,8 +39,310 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
)
}
/**
* 手机 + 密码登录
* 入参:phoneNumber、password
*/
fun loginByPhonePsd(phoneNumber: String, password: String) {
val currentView = view ?: return
currentView.showLoading()
val json = JSONObject()
json.put("phoneNumber", phoneNumber.trim())
json.put("password", password)
loginImpl.loginByPhonePsd(
json,
createLoginCallback()
)
}
/**
* 手机 + 验证码登录
* 入参:phoneNumber、code
*/
fun loginByPhoneSms(phoneNumber: String, code: String) {
val currentView = view ?: return
currentView.showLoading()
val json = JSONObject()
json.put("phoneNumber", phoneNumber.trim())
json.put("code", code.trim())
loginImpl.loginByPhoneSms(
json,
createLoginCallback()
)
}
private fun createLoginCallback(): LoginContract.Callback<BaseBean<UserEntity>> {
return object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val attachedView = view ?: return
attachedView.hideLoading()
val user = data.data
if ((data.code == 0 || data.code == 200) && user != null) {
attachedView.onLoginSuccess(user)
} else {
attachedView.showError(data.message.ifBlank { "登录失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
}
override fun onDestroy() {
loginImpl.cancel()
super.onDestroy()
}
/**
* 手机号注册
*/
fun registerByPhone(phone: String, code: String, password: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("phoneNumber", phone.trim())
json.put("code", code.trim())
json.put("password", password)
loginImpl.registerByPhone(
json,
object : LoginContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
val currentView = view ?: return
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onRegisterSuccess(
data.message.ifBlank { "注册成功" }
)
} else {
currentView.showError(data.message.ifBlank { "注册失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 手机号 + 验证码修改密码
* 入参:phoneNumber、code、newPassword
*/
fun forgetPsdByPhone(phoneNumber: String, code: String, newPassword: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("phoneNumber", phoneNumber.trim())
json.put("code", code.trim())
json.put("newPassword", newPassword)
loginImpl.forgetPsdByPhone(
json,
object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val currentView = view ?: return
currentView.hideLoading()
// 重置密码接口 data 可能为空,以 code 判断成功
if (data.code == 0 || data.code == 200) {
currentView.onForgetPsdSuccess(
data.message.ifBlank { "密码修改成功" }
)
} else {
currentView.showError(data.message.ifBlank { "密码修改失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 获取邮箱验证码
* 入参:email、scene
*/
fun getEmailSmsCode(email: String, scene: String) {
val json = JSONObject()
json.put("email", email.trim())
json.put("scene", scene)
loginImpl.sendEmailCode(
json,
object : LoginContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
val attachedView = view ?: return
attachedView.hideLoading()
if (data.code == 0 || data.code == 200) {
attachedView.sendCodeSuccess(
data.message.ifBlank { "验证码已发送" }
)
} else {
attachedView.showError(data.message.ifBlank { "服务异常" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 邮箱注册
* 入参:email、code、password、nickName
*/
fun registerByEmail(email: String, code: String, password: String, nickName: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("email", email.trim())
json.put("code", code.trim())
json.put("password", password)
json.put("nickName", nickName.trim())
loginImpl.registerByEmail(
json,
object : LoginContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
val currentView = view ?: return
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onRegisterSuccess(
data.message.ifBlank { "注册成功" }
)
} else {
currentView.showError(data.message.ifBlank { "注册失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 邮箱 + 密码登录
* 入参:email、password
*/
fun loginByEmailPsd(email: String, password: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("email", email.trim())
json.put("password", password)
loginImpl.loginByEmailPsd(
json,
object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val currentView = view ?: return
currentView.hideLoading()
val user = data.data
if ((data.code == 0 || data.code == 200) && user != null) {
currentView.onLoginSuccess(user)
} else {
currentView.showError(data.message.ifBlank { "登录失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 邮箱 + 验证码登录
* 入参:email、code
*/
fun loginByEmailSms(email: String, code: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("email", email.trim())
json.put("code", code.trim())
loginImpl.loginByEmailSms(
json,
object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val currentView = view ?: return
currentView.hideLoading()
val user = data.data
if ((data.code == 0 || data.code == 200) && user != null) {
currentView.onLoginSuccess(user)
} else {
currentView.showError(data.message.ifBlank { "登录失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 邮箱 + 验证码修改密码
* 入参:email、code、newPassword
*/
fun forgetPsdByEmail(email: String, code: String, newPassword: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("email", email.trim())
json.put("code", code.trim())
json.put("newPassword", newPassword)
loginImpl.forgetPsdByEmail(
json,
object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val currentView = view ?: return
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onForgetPsdSuccess(
data.message.ifBlank { "密码修改成功" }
)
} else {
currentView.showError(data.message.ifBlank { "密码修改失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
}

View File

@@ -1,6 +1,7 @@
package com.juhabi.birds.network
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.main.model.AppVersionInfo
import com.juhabi.birds.mine.model.MineUserInfo
import okhttp3.RequestBody
@@ -15,8 +16,105 @@ import retrofit2.http.POST
* 项目统一接口定义。所有业务接口地址集中维护在这里。
*/
interface ApiService {
@POST("auth/login")
fun login(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<LoginUser>>
//发送手机验证码
@POST("app/auth/sms/code")
fun sendPhoneCode(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<SendCodeEntity>>
//手机号注册
@POST("app/auth/register")
fun registerByPhone(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<SendCodeEntity>>
//手机+密码登录
/** 入参
* {
* "phoneNumber": "18764689178",
* "password": "Zz123456"
* }
*/
@POST("app/auth/login")
fun loginByPhonePsd(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//手机+验证码
/**入参
* {
* "phoneNumber": "string",
* "code": "string"
* }
*/
@POST("app/auth/sms-login")
fun loginByPhoneSms(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//手机号+验证码 修改密码
/**
* {
* "phoneNumber": "string",
* "code": "string",
* "newPassword": "string"
* }
*/
@POST("app/auth/reset-pwd")
fun forgetPsdByPhone(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//发送邮箱验证码
/**
* {
* "email": "string",
* "scene": "string"
* }
*/
@POST("app/auth/email/code")
fun sendEmailCode(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<SendCodeEntity>>
//邮箱注册
/**
* {
* "email": "string",
* "code": "string",
* "password": "string",
* "nickName": "string"
* }
*/
@POST("app/auth/email/register")
fun registerByE(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<SendCodeEntity>>
//邮箱+密码登录
/** 入参
* {
* "email": "string",
* "password": "string"
* }
*/
@POST("app/auth/email/login")
fun loginByEmailPsd(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//邮箱+验证码登录
/**入参
{
"email": "string",
"code": "string"
}
*/
@POST("app/auth/email/code-login")
fun loginByEmailSms(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//邮箱+验证码 修改密码
/**
* {
* "email": "string",
* "code": "string",
* "newPassword": "string"
* }
*/
@POST("app/auth/email/reset-pwd")
fun forgetPsdByEmail(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
@GET("app/v1/getUserInfo")
fun getMineUserInfo(@HeaderMap headers: Map<String, String>, @QueryMap params: Map<String, String>): Call<BaseBean<MineUserInfo>>