auth to server-V90

This commit is contained in:
Eric85710 2026-05-12 15:48:13 +08:00
parent b843518d8b
commit 321be95ff0
4 changed files with 52 additions and 13 deletions

View file

@ -77,7 +77,7 @@ fun LoginScreen(
//background
Column(
modifier = Modifier
.fillMaxSize()

View file

@ -8,8 +8,10 @@ import com.example.login_v3.data.repository.basic.AuthRepository
import com.example.login_v3.data.repository.basic.TokenManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -23,12 +25,20 @@ sealed class LoginUiState {
@HiltViewModel
class AuthViewModel @Inject constructor(
private val repository: AuthRepository,
private val tokenManager: TokenManager // 注入 TokenManager
private val tokenManager: TokenManager
) : ViewModel() {
private val _loginState = MutableStateFlow<LoginUiState>(LoginUiState.Idle)
val loginState: StateFlow<LoginUiState> = _loginState.asStateFlow()
// --- 新增:供 UI 顯示所有已登入帳號與當前帳號 ---
val allUserIds: StateFlow<Set<String>> = tokenManager.allUserIds
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptySet())
val currentUserId: StateFlow<String?> = tokenManager.currentUserId
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
// ------------------------------------------
fun login(username: String, password: String) {
viewModelScope.launch {
_loginState.value = LoginUiState.Loading
@ -39,10 +49,15 @@ class AuthViewModel @Inject constructor(
if (response.isSuccessful && response.body() != null) {
val loginResponse = response.body()!!
// 1. 先把 Token 存入 DataStore
tokenManager.saveAuthData(loginResponse.access_token)
// 修改:存入 token 時同時存入 userId (假設你的 user 物件裡有 id)
// 如果 LoginResponse 沒給 id,也可以用 username 作為唯一識別碼
val userId = loginResponse.user.id.toString()
tokenManager.saveAuthData(
userId = userId,
token = loginResponse.access_token
)
// 2. 更新 UI 狀態
_loginState.value = LoginUiState.Success(loginResponse.user)
} else {
_loginState.value = LoginUiState.Error("登入失敗:${response.code()}")
@ -52,4 +67,19 @@ class AuthViewModel @Inject constructor(
}
}
}
// --- 新增:切換帳號功能 ---
fun switchAccount(userId: String) {
viewModelScope.launch {
tokenManager.switchAccount(userId)
// 切換後,通常會需要重新初始化某些頁面資料,或發送一個事件通知 UI
}
}
// --- 新增:登出功能 ---
fun logout(userId: String) {
viewModelScope.launch {
tokenManager.logout(userId)
}
}
}

View file

@ -46,20 +46,30 @@ object NetworkModule {
@Singleton
fun provideOkHttpClient(tokenManager: TokenManager): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS) // 連線逾時
.readTimeout(15, TimeUnit.SECONDS) // 讀取逾時
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.addInterceptor { chain ->
val token = runBlocking { tokenManager.accessToken.first() }
val requestBuilder = chain.request().newBuilder()
// 1. 動態獲取「目前選中帳號」的 Token
// 使用 first() 確保只取目前最新的那一個值
val token = runBlocking { tokenManager.currentAccessToken.first() }
val requestBuilder = chain.request().newBuilder()
token?.let {
requestBuilder.addHeader("Authorization", "Bearer $it")
}
val response = chain.proceed(requestBuilder.build())
// 2. 處理 Token 失效 (401)
if (response.code == 401) {
runBlocking { tokenManager.clearAuthData() }
runBlocking {
// 獲取目前是哪個 ID 失效了
val currentId = tokenManager.currentUserId.first()
currentId?.let {
// 只登出當前失效的帳號,不影響其他帳號
tokenManager.logout(it)
}
}
}
response

View file

@ -20,11 +20,10 @@ class TokenManager @Inject constructor(
preferences[USER_ID_LIST] ?: emptySet()
}
// TokenManager 內部建議改動
companion object {
// 1. 記錄目前正在使用的 UserId
private val USER_ID_LIST = stringSetPreferencesKey("user_id_list") // 移到這裡
private val CURRENT_USER_ID = stringPreferencesKey("current_user_id")
// 2. 輔助函式:根據 userId 動態產生 Token 的 Key
private fun accessTokenKey(userId: String) = stringPreferencesKey("access_token_$userId")
}