453 lines
17 KiB
Kotlin
453 lines
17 KiB
Kotlin
package com.img.rabbit.viewmodel
|
||
|
||
import android.app.Activity
|
||
import android.content.Context
|
||
import android.os.Build
|
||
import android.util.Log
|
||
import androidx.compose.runtime.MutableState
|
||
import androidx.compose.runtime.mutableStateOf
|
||
import androidx.compose.runtime.State
|
||
import com.alipay.sdk.app.AuthTask
|
||
import com.img.rabbit.pages.LoginScreenType
|
||
import com.g.gysdk.GYManager
|
||
import com.g.gysdk.GYResponse
|
||
import com.g.gysdk.GyCallBack
|
||
import com.g.gysdk.GyConfig
|
||
import com.github.gzuliyujiang.oaid.DeviceIdentifier
|
||
import com.google.gson.JsonObject
|
||
import com.img.rabbit.bean.local.ErrorBean
|
||
import com.img.rabbit.bean.local.OnekeyPreLogin
|
||
import com.img.rabbit.bean.local.toAlipayResult
|
||
import com.img.rabbit.bean.response.LoginInfoEntity
|
||
import com.img.rabbit.bean.response.UserConfigEntity
|
||
import com.img.rabbit.components.CenterToast
|
||
import com.img.rabbit.provider.api.ApiManager
|
||
import com.img.rabbit.provider.api.ResultVo
|
||
import com.img.rabbit.provider.storage.GlobalStateManager
|
||
import com.img.rabbit.provider.storage.PreferenceUtil
|
||
import com.img.rabbit.provider.utils.HeadParamUtils.applicationContext
|
||
import com.img.rabbit.utils.AppEventBus
|
||
import com.img.rabbit.utils.LoginBindEvent
|
||
import com.img.rabbit.utils.MMKVUtils
|
||
import com.img.rabbit.utils.StringUtils
|
||
import com.tencent.mm.opensdk.modelmsg.SendAuth
|
||
import com.tencent.mm.opensdk.openapi.IWXAPI
|
||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||
import kotlinx.coroutines.Dispatchers
|
||
import kotlinx.coroutines.withContext
|
||
import kotlinx.serialization.json.Json
|
||
import okhttp3.RequestBody.Companion.toRequestBody
|
||
|
||
class LoginViewModel : BaseViewModel() {
|
||
private val TAG = "LoginViewModel"
|
||
|
||
private val ONEKEY_TAG = "OneKeyLoginViewModel"
|
||
//private lateinit var api: IWXAPI
|
||
var isLoginWxAuthor = false
|
||
val authInfoForAlipay: MutableState<String> = mutableStateOf("")
|
||
|
||
val loginScreenType = mutableStateOf(LoginScreenType.LOGIN_NORMAL)
|
||
// 登录用户名
|
||
val userName = mutableStateOf("")
|
||
fun setUserName(loginName: String) {
|
||
userName.value = loginName
|
||
}
|
||
// 登录验证码
|
||
val captcha = mutableStateOf("")
|
||
fun setCaptcha(loginCaptcha: String) {
|
||
captcha.value = loginCaptcha
|
||
}
|
||
// 登录验证码发送时间戳
|
||
val captchaTimestamp = mutableStateOf("")
|
||
|
||
|
||
|
||
|
||
private val isGYUIDValid = mutableStateOf(false)
|
||
var oneKeyPreLogin: OnekeyPreLogin? = null
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// 是否同意政策协议
|
||
private val _policyAgreement = mutableStateOf(true)
|
||
val isPolicyAgreement: State<Boolean> = _policyAgreement
|
||
fun setIsPolicyAgreement(isAgreement: Boolean) {
|
||
_policyAgreement.value = isAgreement
|
||
}
|
||
|
||
// 登录获取结果
|
||
val loginResult = mutableStateOf<ResultVo<LoginInfoEntity>?>(null)
|
||
|
||
// 用户配置获取结果
|
||
val userConfigResult = mutableStateOf<ResultVo<UserConfigEntity>?>(null)
|
||
|
||
// 错误状态
|
||
val errorState = mutableStateOf<ErrorBean?>(null)
|
||
|
||
fun reset(){
|
||
isLoginWxAuthor = false
|
||
authInfoForAlipay.value = ""
|
||
loginScreenType.value = LoginScreenType.LOGIN_NORMAL
|
||
userName.value = ""
|
||
captcha.value = ""
|
||
captchaTimestamp.value = ""
|
||
isGYUIDValid.value = false
|
||
oneKeyPreLogin = null
|
||
setIsPolicyAgreement(true)
|
||
loginResult.value = null
|
||
userConfigResult.value = null
|
||
errorState.value = null
|
||
}
|
||
|
||
fun requestUserConfig(){
|
||
mLaunch {
|
||
//先置空配置
|
||
PreferenceUtil.saveUserConfig(null)
|
||
val oaid = MMKVUtils.getString("oaid") ?: ""
|
||
val response = ApiManager.serviceVo.getUserConfig(oaid, Build.VERSION.SDK_INT, "", DeviceIdentifier.getAndroidID(applicationContext), MMKVUtils.getString("gt_cid") ?: "")
|
||
if (response.status) {
|
||
PreferenceUtil.saveXToken(response.data.token)
|
||
PreferenceUtil.setTimeDiff(response.data.nowtime.toLong() - System.currentTimeMillis() / 1000)
|
||
PreferenceUtil.saveUserConfig(response.data)
|
||
userConfigResult.value = response
|
||
|
||
//以下是刷新状态
|
||
applicationContext?.let { GlobalStateManager(it) }?.storeGlobalUserConfigNotify(true)
|
||
}else{
|
||
Log.w("LoginViewModel", "获取配置失败: code=${response.code}, message=${response.message}")
|
||
}
|
||
isLoading.value = false // 加载完成
|
||
}
|
||
}
|
||
|
||
fun oneKeyLoginForGeTuiSdk(activity: Activity, onShowOneKeyScreen:(Boolean)->Unit) {
|
||
// 初始化 SDK
|
||
GYManager.getInstance().init(GyConfig.with(activity.applicationContext).callBack(object : GyCallBack {
|
||
override fun onSuccess(response: GYResponse?) {
|
||
isGYUIDValid.value = true
|
||
Log.i(ONEKEY_TAG, "--->初始化: onSuccess")
|
||
|
||
//预登录(提高拉起速度,可选但推荐)
|
||
GYManager.getInstance().ePreLogin(/* timeout = */ 8000, /* gyCallBack = */ object : GyCallBack {
|
||
override fun onSuccess(response: GYResponse?) {
|
||
Log.i(ONEKEY_TAG, "--->预登录: onSuccess--->${response}")
|
||
val preLoginData = response?.msg
|
||
if (!preLoginData.isNullOrEmpty()) {
|
||
try {
|
||
val json = Json { ignoreUnknownKeys = true }
|
||
oneKeyPreLogin =
|
||
json.decodeFromString<OnekeyPreLogin>(preLoginData)
|
||
|
||
// 打印解析后的详细数据
|
||
Log.i(ONEKEY_TAG, "=== 预登录数据解析结果 ===")
|
||
Log.i(ONEKEY_TAG, "流程ID: ${oneKeyPreLogin?.process_id}")
|
||
Log.i(ONEKEY_TAG, "运营商类型: ${oneKeyPreLogin?.operatorType}")
|
||
Log.i(ONEKEY_TAG, "客户端类型: ${oneKeyPreLogin?.clienttype}")
|
||
Log.i(ONEKEY_TAG, "访问码: ${oneKeyPreLogin?.accessCode}")
|
||
Log.i(ONEKEY_TAG, "手机号: ${oneKeyPreLogin?.number}")
|
||
Log.i(ONEKEY_TAG, "过期时间: ${oneKeyPreLogin?.expiredTime}")
|
||
Log.i(ONEKEY_TAG, "错误码: ${oneKeyPreLogin?.errorCode}")
|
||
Log.i(ONEKEY_TAG, "错误描述: ${oneKeyPreLogin?.errorDesc}")
|
||
Log.i(ONEKEY_TAG, "耗时: ${oneKeyPreLogin?.costTime}ms")
|
||
Log.i(ONEKEY_TAG, "================================")
|
||
|
||
// 根据解析结果决定是否继续(根据errorCode判断)
|
||
if (oneKeyPreLogin?.errorCode == 0) {
|
||
oneKeyLoginValid(onShowOneKeyScreen = onShowOneKeyScreen)
|
||
} else {
|
||
onShowOneKeyScreen(false)
|
||
Log.e(ONEKEY_TAG, "预登录校验失败: ${oneKeyPreLogin?.errorDesc}")
|
||
}
|
||
|
||
} catch (e: Exception) {
|
||
Log.e(ONEKEY_TAG, "JSON解析失败: ${e.message}")
|
||
Log.e(ONEKEY_TAG, "原始数据: $preLoginData")
|
||
// 解析失败时继续执行原逻辑
|
||
onShowOneKeyScreen(false)
|
||
}
|
||
}
|
||
}
|
||
|
||
override fun onFailed(response: GYResponse?) {
|
||
onShowOneKeyScreen(false)
|
||
Log.i(ONEKEY_TAG, "--->预登录: onFailed--->${response}")
|
||
}
|
||
})
|
||
}
|
||
|
||
override fun onFailed(response: GYResponse?) {
|
||
onShowOneKeyScreen(false)
|
||
Log.i(ONEKEY_TAG, "--->初始化: onFailed--->${response}")
|
||
}
|
||
}).build())
|
||
}
|
||
|
||
private fun oneKeyLoginValid(onShowOneKeyScreen:(Boolean)->Unit) {
|
||
if (GYManager.getInstance().isPreLoginResultValid) {
|
||
//预登录有效,启动登录授权页
|
||
onShowOneKeyScreen(true)
|
||
Log.i(ONEKEY_TAG, "--->预登录校验有效A: onSuccess")
|
||
} else {
|
||
//考虑到是用户在等待,建议超时8s以上,至少设置5s以上
|
||
GYManager.getInstance().ePreLogin(5000, object : GyCallBack {
|
||
override fun onSuccess(response: GYResponse?) {
|
||
//预登录成功,启动登录授权页
|
||
onShowOneKeyScreen(true)
|
||
Log.i(ONEKEY_TAG, "--->预登录校验有效B: onSuccess--->${response}")
|
||
}
|
||
|
||
override fun onFailed(response: GYResponse?) {
|
||
//预登录失败,提示用户稍后重试
|
||
onShowOneKeyScreen(false)
|
||
Log.i(ONEKEY_TAG, "--->预登录校验有效B: onFailed--->${response}")
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
fun requestOneKeyLogin(gyuid: String, token: String) {
|
||
isLoading.value = true // 开始加载
|
||
|
||
// 调用 API 获取数据
|
||
val jsonOneKey = JsonObject()
|
||
|
||
jsonOneKey.addProperty("gyuid", gyuid)
|
||
jsonOneKey.addProperty("token", token)
|
||
val jsonObject = JsonObject()
|
||
jsonObject.addProperty("type", "onekey")
|
||
jsonObject.addProperty("bind", "")
|
||
jsonObject.add("data", jsonOneKey)
|
||
|
||
Log.i(ONEKEY_TAG, "--->开始登录")
|
||
requestLogin(jsonObject, LoginScreenType.LOGIN_ONE_KEY)
|
||
}
|
||
|
||
|
||
/**
|
||
* 请求验证码
|
||
*/
|
||
fun requestCaptcha(phone: String) {
|
||
// 发送请求获取验证码
|
||
isLoading.value = true // 开始加载
|
||
|
||
mLaunch {
|
||
// 调用 API 获取数据
|
||
val jsonObject = JsonObject()
|
||
jsonObject.addProperty("phone", phone)
|
||
val response = ApiManager.serviceVo.sendCode(jsonObject.toString().toRequestBody())
|
||
if (response.status) {
|
||
Log.w("LoginViewModel", "请求验证码成功: ${response.data.timestamp}")
|
||
captchaTimestamp.value = response.data.timestamp
|
||
}else{
|
||
errorState.value = ErrorBean(response.code.toString(), response.message.ifEmpty { "获取验证码失败" })
|
||
}
|
||
isLoading.value = false // 加载完成
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 请求登录(验证码)
|
||
*/
|
||
fun requestLoginForCaptcha(phone: String, code: String) {
|
||
isLoading.value = true // 开始加载
|
||
|
||
// 调用 API 获取数据
|
||
val jsonPhone = JsonObject()
|
||
jsonPhone.addProperty("timestamp", captchaTimestamp.value)
|
||
jsonPhone.addProperty("phone", phone)
|
||
jsonPhone.addProperty("code", code)
|
||
|
||
val jsonObject = JsonObject()
|
||
jsonObject.addProperty("login_type", "phone")
|
||
jsonObject.add("phone", jsonPhone)
|
||
|
||
requestLogin(jsonObject, LoginScreenType.LOGIN_CAPTCHA)
|
||
}
|
||
|
||
fun authorWechat(context: Context, wxApi:IWXAPI) {
|
||
if (isPolicyAgreement.value) {
|
||
if (!wxApi.isWXAppInstalled) {
|
||
CenterToast.show("您没有安装微信客户端,请先下载安装")
|
||
return
|
||
}
|
||
isLoginWxAuthor = true
|
||
val req = SendAuth.Req()
|
||
req.scope = "snsapi_userinfo" // 只能填 snsapi_userinfo
|
||
req.state = context.packageName + Math.random() * 1000 + "_phone"
|
||
wxApi.sendReq(req)
|
||
}else{
|
||
CenterToast.show("请先同意用户协议和隐私政策")
|
||
}
|
||
}
|
||
|
||
fun requestWxLogin(code: String){
|
||
val jsonObject = getWxLoginParam(code) ?: return
|
||
requestLogin(jsonObject, LoginScreenType.LOGIN_WX)
|
||
}
|
||
|
||
/**
|
||
* 拿着微信授权码完成登录(在WXEntryActivity中调用)
|
||
* @param code 微信授权码
|
||
*/
|
||
fun getWxLoginParam(code: String): JsonObject? {
|
||
if(code.isEmpty()){
|
||
return null
|
||
}
|
||
isLoading.value = true // 开始加载
|
||
PreferenceUtil.saveWxCode(code)
|
||
|
||
// 调用 API 获取数据
|
||
val jsonWx = JsonObject()
|
||
jsonWx.addProperty("code", code)
|
||
jsonWx.addProperty("code_type", "")
|
||
|
||
val jsonObject = JsonObject()
|
||
jsonObject.addProperty("login_type", "weixin")
|
||
jsonObject.add("weixin", jsonWx)
|
||
|
||
return jsonObject
|
||
}
|
||
|
||
|
||
/**
|
||
* 请求支付宝登录参数
|
||
*/
|
||
fun requestAliPayAuthParam(context: Context) {
|
||
isLoading.value = true // 开始加载
|
||
|
||
mLaunch {
|
||
val response = ApiManager.serviceVo.getAlipayAuthParam()
|
||
val data = response.data
|
||
val param = data.param
|
||
|
||
authorAlipay(context = context,authParam = param)
|
||
authInfoForAlipay.value = param
|
||
isLoading.value = false // 加载完成
|
||
}
|
||
}
|
||
|
||
private fun authorAlipay(context: Context, authParam: String){
|
||
if(authParam.isEmpty()){
|
||
CenterToast.show("请先获取支付宝登录授权码")
|
||
return
|
||
}
|
||
// 发送请求获取支付宝登录授权码
|
||
mLaunch {
|
||
val authorResult = doAlipayLogin(context as Activity, authParam)
|
||
Log.i("loginWithAliPay", "支付宝登录结果:$authorResult")
|
||
val alipayResult = authorResult.toAlipayResult()
|
||
// 处理支付宝登录结果
|
||
when (alipayResult.resultStatus) {
|
||
"9000" -> {
|
||
// 登录成功,result 字段中包含 auth_code
|
||
val authCode = StringUtils.parseAlipayResult(alipayResult.result ?: "")["auth_code"] ?: ""
|
||
val jsonObject = getAlipayLoginParam(authCode) ?: return@mLaunch
|
||
requestLogin(jsonObject, LoginScreenType.LOGIN_ALIPAY)
|
||
}
|
||
|
||
"6001" -> {
|
||
"用户取消登录"
|
||
}
|
||
|
||
else -> {
|
||
alipayResult.memo ?: "登录失败"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 封装支付宝登录逻辑
|
||
* @param activity 当前 Activity 上下文
|
||
* @param authInfo 后端生成的授权字符串
|
||
* @return 支付宝返回的原始 Map 结果
|
||
*/
|
||
private suspend fun doAlipayLogin(activity: Activity, authInfo: String): Map<String, String> {
|
||
return withContext(Dispatchers.IO) {
|
||
// 初始化 AuthTask
|
||
val authTask = AuthTask(activity)
|
||
|
||
// 调用 authV2。第二个参数为 true 表示如果未安装支付宝则展示 Loading 界面, 该方法会阻塞当前线程直到用户操作结束
|
||
val result = authTask.authV2(authInfo, true)
|
||
Log.i(TAG, "doAlipayLogin: $result")
|
||
|
||
result ?: emptyMap()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 拿着支付宝授权码完成登录
|
||
* @param authCode 支付宝授权码
|
||
*/
|
||
private fun getAlipayLoginParam(authCode: String): JsonObject? {
|
||
if(authCode.isEmpty()){
|
||
return null
|
||
}
|
||
isLoading.value = true // 开始加载
|
||
|
||
// 调用 API 获取数据
|
||
val jsonWx = JsonObject()
|
||
jsonWx.addProperty("auth_code", authCode)//code
|
||
|
||
val jsonObject = JsonObject()
|
||
jsonObject.addProperty("type", "alipay")
|
||
jsonObject.addProperty("bind", "")
|
||
jsonObject.add("data", jsonWx)
|
||
|
||
return jsonObject
|
||
}
|
||
|
||
//请求登录
|
||
private fun requestLogin(jsonObject: JsonObject, loginType: LoginScreenType){
|
||
mLaunch {
|
||
val response = ApiManager.serviceVo.login(jsonObject.toString().toRequestBody())
|
||
if (response.status) {
|
||
PreferenceUtil.saveLoginType(loginType.name)
|
||
PreferenceUtil.saveLoginInfo(response.data.apply { isLogin = true })//记录登录数据
|
||
PreferenceUtil.saveAccessToken(response.data.token)
|
||
loginResult.value = response
|
||
}else{
|
||
errorState.value = ErrorBean(response.code.toString(), response.message.ifEmpty { "登录失败" })
|
||
}
|
||
isLoading.value = false // 加载完成
|
||
}
|
||
}
|
||
|
||
fun requestUserInfo(){
|
||
PreferenceUtil.saveUserInfo(null)
|
||
mLaunch {
|
||
val response = ApiManager.serviceVo.userInfo()
|
||
if(response.status){
|
||
PreferenceUtil.saveUserInfo(response.data)
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 请求退出登录
|
||
*/
|
||
@OptIn(DelicateCoroutinesApi::class)
|
||
fun requestLogout() {
|
||
isLoading.value = true // 开始加载
|
||
mLaunch {
|
||
val response = ApiManager.serviceVo.logout()
|
||
if (response.status) {
|
||
PreferenceUtil.clearLogin()
|
||
AppEventBus.post( LoginBindEvent.Login(userId = null, loginType = PreferenceUtil.getLoginType(), isLogin = false, data = null) )
|
||
} else {
|
||
errorState.value = ErrorBean(response.code.toString(), response.message.ifEmpty { "退出登录失败" })
|
||
}
|
||
isLoading.value = false // 加载完成
|
||
}
|
||
}
|
||
|
||
enum class JumpLoginType(val type: Int,val desc: String){
|
||
NORMAL(0,"正常登录"),
|
||
FROM_ADD(1,"添加账号"),
|
||
FROM_LOGOUT(2,"退出登录")
|
||
}
|
||
} |