From aa20ce3039ac82d23890c7b12e117d392f225c18 Mon Sep 17 00:00:00 2001 From: penghanyuan Date: Sat, 14 Feb 2026 10:39:00 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=E4=B8=8E=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 MainActivity.kt 中调整登录状态管理逻辑,确保实时同步 TokenManager 的状态,并根据登录状态自动导航至主界面或登录页。 - 更新 NicknameSetupScreen.kt 中的图标描述文本,以提升用户体验。 - 在 AuthViewModel.kt 中重构 token 刷新逻辑,采用静默刷新策略,确保用户状态的稳定性而不影响当前登录状态。 --- .../java/com/huaga/life_echo/MainActivity.kt | 21 +++-- .../ui/screens/ConversationListScreen.kt | 4 +- .../ui/screens/CreateMemoryScreen.kt | 3 +- .../ui/screens/NicknameSetupScreen.kt | 2 +- .../life_echo/ui/viewmodel/AuthViewModel.kt | 82 ++++++++++--------- 5 files changed, 60 insertions(+), 52 deletions(-) diff --git a/app-android/app/src/main/java/com/huaga/life_echo/MainActivity.kt b/app-android/app/src/main/java/com/huaga/life_echo/MainActivity.kt index ff3e028..83799a6 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/MainActivity.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/MainActivity.kt @@ -143,7 +143,9 @@ fun SystemUiController( @PreviewScreenSizes @Composable fun LifeechoApp(initialLoggedIn: Boolean = false) { - var isLoggedIn by rememberSaveable { mutableStateOf(initialLoggedIn) } + // 使用 remember(非 rememberSaveable),避免进程恢复时使用过时的登录状态 + // 登录状态始终以 TokenManager 为准 + var isLoggedIn by remember { mutableStateOf(initialLoggedIn) } val navController = rememberNavController() var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.CHAT) } @@ -151,20 +153,25 @@ fun LifeechoApp(initialLoggedIn: Boolean = false) { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route - // 同步登录状态变化,并在登录状态恢复时自动导航到主界面 + // 实时同步 TokenManager 的登录状态,并处理导航 androidx.compose.runtime.LaunchedEffect(TokenManager.isLoggedIn) { val newLoggedIn = TokenManager.isLoggedIn - val wasLoggedOut = !isLoggedIn + val previousLoggedIn = isLoggedIn isLoggedIn = newLoggedIn - // 如果从未登录变为已登录,且当前在登录页,自动跳转到主界面 - if (newLoggedIn && wasLoggedOut) { - val currentRoute = navController.currentDestination?.route - if (currentRoute == Screen.Login.route || currentRoute == null) { + if (newLoggedIn && !previousLoggedIn) { + // 从未登录变为已登录:跳转到主界面 + val route = navController.currentDestination?.route + if (route == Screen.Login.route || route == Screen.NicknameSetup.route || route == null) { navController.navigate(Screen.ConversationList.route) { popUpTo(0) { inclusive = true } } } + } else if (!newLoggedIn && previousLoggedIn) { + // 从已登录变为未登录(如后台token刷新失败):跳转到登录页 + navController.navigate(Screen.Login.route) { + popUpTo(0) { inclusive = true } + } } } diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt index ab9eebc..483c88b 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt @@ -222,8 +222,8 @@ fun ConversationListScreen( // 对话列表 items(conversations, key = { it.id }) { conversation -> - // 兼容旧数据:将旧名称"回忆录助手"也识别为默认助手 - val isAssistant = conversation.title == null || conversation.title == "回忆录助手" + // 兼容新旧数据:将"岁月知己"和旧名称"回忆录助手"都识别为默认助手 + val isAssistant = conversation.title == null || conversation.title == "岁月知己" || conversation.title == "回忆录助手" val displayTitle = if (isAssistant) "岁月知己" else conversation.title!! val dto = ConversationListItemDto( id = conversation.id, diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt index 19537e6..9f83124 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt @@ -160,8 +160,7 @@ fun CreateMemoryScreen( ChatHeader( title = "岁月知己", isOnline = connectionStatus == "已连接", - onBackClick = { navController?.popBackStack() }, - onNewConversationClick = { viewModel.startConversation() } + onBackClick = { navController?.popBackStack() } ) // WebSocket调试面板(仅在开发模式下显示) diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/NicknameSetupScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/NicknameSetupScreen.kt index 397713d..58862ba 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/NicknameSetupScreen.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/NicknameSetupScreen.kt @@ -75,7 +75,7 @@ fun NicknameSetupScreen( // 欢迎图标 Icon( imageVector = AppIcons.PersonAdd, - contentDescription = "设置昵称", + contentDescription = "怎么称呼你呢?", modifier = Modifier.size(80.dp), tint = LightPurple ) diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/AuthViewModel.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/AuthViewModel.kt index f272ce9..64a28b1 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/AuthViewModel.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/AuthViewModel.kt @@ -54,62 +54,64 @@ class AuthViewModel(private val context: Context) : ViewModel() { /** * 检查认证状态 - * 启动时验证token有效性,如果access_token过期则尝试用refresh_token刷新 + * + * 策略:只要本地有缓存的token,就认为已登录,直接进入主界面。 + * 后台尝试验证和刷新token,但不会因为网络错误而清除token。 + * token的失效由AuthInterceptor在实际API调用时处理。 */ fun checkAuthStatus() { viewModelScope.launch { val accessToken = TokenManager.getAccessToken() val refreshToken = TokenManager.getRefreshToken() + val hasToken = !accessToken.isNullOrBlank() || !refreshToken.isNullOrBlank() + // 只要有缓存的token就认为已登录 + _isLoggedIn.value = hasToken + + if (!hasToken) return@launch + + // 后台尝试获取用户信息(best effort,不影响登录状态) if (!accessToken.isNullOrBlank()) { - // 有access_token,尝试获取用户信息验证token有效性 val success = loadUserInfo(accessToken) - if (success) { - _isLoggedIn.value = true - } else { - // access_token可能过期,尝试用refresh_token刷新 - if (!refreshToken.isNullOrBlank()) { - tryRefreshAndRestore(refreshToken) - } else { - // 没有refresh_token,需要重新登录 - TokenManager.clearTokens() - _isLoggedIn.value = false - } + if (!success && !refreshToken.isNullOrBlank()) { + // access_token可能过期,尝试用refresh_token静默刷新 + tryRefreshTokenSilently(refreshToken) } } else if (!refreshToken.isNullOrBlank()) { - // 没有access_token但有refresh_token,尝试刷新 - tryRefreshAndRestore(refreshToken) - } else { - // 完全没有token - _isLoggedIn.value = false + // 只有refresh_token,尝试静默刷新 + tryRefreshTokenSilently(refreshToken) } } } /** - * 尝试使用refresh_token刷新access_token并恢复登录 + * 静默刷新token(后台执行,不影响登录状态) + * 只在刷新成功时更新token,失败时不清除现有token + * token失效的处理交给AuthInterceptor在实际API调用时处理 */ - private suspend fun tryRefreshAndRestore(refreshToken: String) { - val refreshResult = authService.refreshToken(refreshToken) - refreshResult.fold( - onSuccess = { tokenResponse -> - // 刷新成功,保存新token - val userId = TokenManager.getUserId() ?: "" - TokenManager.saveTokens( - tokenResponse.access_token, - tokenResponse.refresh_token, - userId - ) - // 用新token获取用户信息 - val success = loadUserInfo(tokenResponse.access_token) - _isLoggedIn.value = true - }, - onFailure = { - // 刷新也失败了,需要重新登录 - TokenManager.clearTokens() - _isLoggedIn.value = false - } - ) + private suspend fun tryRefreshTokenSilently(refreshToken: String) { + try { + val refreshResult = authService.refreshToken(refreshToken) + refreshResult.fold( + onSuccess = { tokenResponse -> + // 刷新成功,保存新token + val userId = TokenManager.getUserId() ?: "" + TokenManager.saveTokens( + tokenResponse.access_token, + tokenResponse.refresh_token, + userId + ) + // 用新token获取用户信息 + loadUserInfo(tokenResponse.access_token) + }, + onFailure = { + // 静默刷新失败,不清除token + // 后续API调用时AuthInterceptor会再次尝试刷新 + } + ) + } catch (_: Exception) { + // 网络异常等,忽略,保持现有登录状态 + } } /**