package com.img.rabbit.viewmodel import android.app.Activity import android.content.Context import android.os.Build import android.util.Log import android.widget.Toast import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.State import androidx.compose.runtime.mutableIntStateOf 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.response.LoginInfoEntity import com.img.rabbit.config.Constants 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.utils.MMKVUtils import com.tencent.mm.opensdk.modelmsg.SendAuth import com.tencent.mm.opensdk.openapi.IWXAPI import com.tencent.mm.opensdk.openapi.WXAPIFactory import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.internal.platform.PlatformRegistry.applicationContext class LoginViewModel : BaseViewModel() { private val TAG = "LoginViewModel" private val ONEKEY_TAG = "OneKeyLoginViewModel" //private lateinit var api: IWXAPI val authInfoForAlipay: MutableState = mutableStateOf("") val loginScreenType = mutableStateOf(LoginScreenType.LOGIN_NORMAL) // 登录用户名 val userName = mutableStateOf("") // 登录验证码 val captcha = mutableStateOf("") // 登录验证码发送时间戳 val captchaTimestamp = mutableStateOf("") fun setCaptcha(loginCaptcha: String) { captcha.value = loginCaptcha } fun setUserName(loginName: String) { userName.value = loginName } private val isGYUIDValid = mutableStateOf(false) var oneKeyPreLogin: OnekeyPreLogin? = null // 是否同意政策协议 private val _policyAgreement = mutableStateOf(true) val isPolicyAgreement: State = _policyAgreement fun setIsPolicyAgreement(isAgreement: Boolean) { _policyAgreement.value = isAgreement } private val _isLogin = mutableStateOf(false) val isLogin: State = _isLogin fun setLogin(isLogin: Boolean) { _isLogin.value = isLogin } // 登录状态 val loginState = mutableStateOf?>(null) // 错误状态 val errorState = mutableStateOf(null) fun requestUserConfig(){ mLaunch { 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) }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(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) requestLogin(jsonObject) } /** * 请求验证码 */ 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) } fun loginWithWechat(context: Context, wxApi:IWXAPI) { if (isPolicyAgreement.value) { doWxAuth(context, wxApi) }else{ Toast.makeText(context, "请先同意用户协议和隐私政策", Toast.LENGTH_SHORT).show() } } //获取微信授权 private fun doWxAuth(context: Context, wxApi:IWXAPI) { if (!wxApi.isWXAppInstalled) { Toast.makeText(context, "您没有安装微信客户端,请先下载安装", Toast.LENGTH_SHORT).show() return } val req = SendAuth.Req() req.scope = "snsapi_userinfo" // 只能填 snsapi_userinfo req.state = context.packageName + Math.random() * 1000 + "_phone" wxApi.sendReq(req) } /** * 拿着微信授权码完成登录(在WXEntryActivity中调用) * @param wechatCode 微信授权码 */ fun requestWxLogin(wechatCode: String) { if(wechatCode.isEmpty()){ return } isLoading.value = true // 开始加载 PreferenceUtil.saveWxCode(wechatCode) // 调用 API 获取数据 val jsonWx = JsonObject() jsonWx.addProperty("code", wechatCode) jsonWx.addProperty("code_type", "") val jsonObject = JsonObject() jsonObject.addProperty("login_type", "weixin") jsonObject.add("weixin", jsonWx) requestLogin(jsonObject) } /** * 支付宝登录 * authInfo: 该参数需由后端生成并加签,包含 app_id、pid、target_id 等信息 */ fun loginWithAliPay(context: Context,onAuthResult: (Map) -> Unit) { if(authInfoForAlipay.value.isEmpty()){ Toast.makeText(context, "请先获取支付宝登录授权码", Toast.LENGTH_SHORT).show() return } // 发送请求获取支付宝登录授权码 mLaunch { onAuthResult(doAlipayLogin(context as Activity, authInfoForAlipay.value)) } } /** * 请求支付宝登录参数 */ fun requestAliPayAuthParam() { isLoading.value = true // 开始加载 mLaunch { val response = ApiManager.serviceVo.getAlipayAuthParam() val data = response.data val param = data.param authInfoForAlipay.value = param isLoading.value = false // 加载完成 } } /** * 封装支付宝登录逻辑 * @param activity 当前 Activity 上下文 * @param authInfo 后端生成的授权字符串 * @return 支付宝返回的原始 Map 结果 */ private suspend fun doAlipayLogin(activity: Activity, authInfo: String): Map { 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() } } /** * 拿着微信授权码完成登录(在WXEntryActivity中调用) * @param authCode 微信授权码 */ fun requestAlipayLogin(authCode: String) { if(authCode.isEmpty()){ return } 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) requestLogin(jsonObject) } //请求登录 private fun requestLogin(jsonObject: JsonObject){ mLaunch { val response = ApiManager.serviceVo.login(jsonObject.toString().toRequestBody()) if (response.status) { loginState.value = response val loginInfoEntity = response.data loginInfoEntity.isLogin = true //记录登录数据 PreferenceUtil.saveLoginInfo(loginInfoEntity) }else{ errorState.value = ErrorBean(response.code.toString(), response.message.ifEmpty { "登录失败" }) } isLoading.value = false // 加载完成 } } fun requestUserInfo(){ mLaunch { val response = ApiManager.serviceVo.userInfo() if(response.status){ PreferenceUtil.saveUserInfo(response.data) } } } /** * 请求退出登录 */ @OptIn(DelicateCoroutinesApi::class) fun requestLogout(context: Context) { isLoading.value = true // 开始加载 mLaunch { val response = ApiManager.serviceVo.logout() if (response.status) { //logoutState.value = response PreferenceUtil.clearLogin() setLogin(false) // 跳转登录页面 //restViewModel.intValue = 1 GlobalScope.launch { GlobalStateManager(context).storeGlobalLogout(true) } } 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,"退出登录") } }