264 lines
7.6 KiB
Dart
264 lines
7.6 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import '../models/user_credentials.dart';
|
|
import '../providers/auth_provider.dart';
|
|
|
|
/// Session manager for handling app startup and session restoration
|
|
///
|
|
/// Provides utilities for:
|
|
/// - Checking if saved credentials exist
|
|
/// - Attempting to restore previous session
|
|
/// - Handling session expiration
|
|
///
|
|
/// Usage example:
|
|
/// ```dart
|
|
/// final sessionManager = SessionManager(authProvider: authProvider);
|
|
///
|
|
/// // Check if session can be restored
|
|
/// final canRestore = await sessionManager.canRestoreSession();
|
|
///
|
|
/// // Attempt to restore session
|
|
/// final restored = await sessionManager.restoreSession();
|
|
///
|
|
/// // Handle session expiration
|
|
/// await sessionManager.handleSessionExpired();
|
|
/// ```
|
|
class SessionManager {
|
|
final AuthProvider _authProvider;
|
|
|
|
SessionManager({required AuthProvider authProvider})
|
|
: _authProvider = authProvider;
|
|
|
|
/// Check if saved credentials exist
|
|
///
|
|
/// Returns true if credentials are stored, false otherwise
|
|
/// Does not validate if the session is still active
|
|
Future<bool> hasSavedCredentials() async {
|
|
try {
|
|
final credentials = await UserCredentials.loadSecurely();
|
|
return credentials != null;
|
|
} catch (e) {
|
|
debugPrint('Error checking saved credentials: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Check if session can be restored
|
|
///
|
|
/// Checks if saved credentials exist and are valid
|
|
/// Returns true if session restoration should be attempted
|
|
Future<bool> canRestoreSession() async {
|
|
return await hasSavedCredentials();
|
|
}
|
|
|
|
/// Attempt to restore session from saved credentials
|
|
///
|
|
/// Loads credentials from secure storage and attempts login
|
|
/// Returns SessionRestoreResult with status and details
|
|
Future<SessionRestoreResult> restoreSession() async {
|
|
try {
|
|
// Check if credentials exist
|
|
final hasCredentials = await hasSavedCredentials();
|
|
if (!hasCredentials) {
|
|
return SessionRestoreResult(
|
|
success: false,
|
|
reason: SessionRestoreFailureReason.noCredentials,
|
|
message: '未找到保存的登录凭证',
|
|
);
|
|
}
|
|
|
|
// Attempt to restore session using AuthProvider
|
|
final restored = await _authProvider.restoreSession();
|
|
|
|
if (restored) {
|
|
return SessionRestoreResult(success: true, message: '会话恢复成功');
|
|
} else {
|
|
// Check the error message from auth provider
|
|
final errorMessage = _authProvider.errorMessage;
|
|
final reason = _determineFailureReason(errorMessage);
|
|
|
|
return SessionRestoreResult(
|
|
success: false,
|
|
reason: reason,
|
|
message: errorMessage ?? '会话恢复失败',
|
|
);
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Error restoring session: $e');
|
|
return SessionRestoreResult(
|
|
success: false,
|
|
reason: SessionRestoreFailureReason.unknown,
|
|
message: '会话恢复出错: $e',
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Handle session expiration
|
|
///
|
|
/// Clears current session and credentials
|
|
/// Should be called when session is detected as expired
|
|
Future<void> handleSessionExpired() async {
|
|
try {
|
|
await _authProvider.logout();
|
|
debugPrint('Session expired and cleared');
|
|
} catch (e) {
|
|
debugPrint('Error handling session expiration: $e');
|
|
}
|
|
}
|
|
|
|
/// Clear saved session data
|
|
///
|
|
/// Removes all saved credentials and session information
|
|
/// Useful for logout or when user wants to clear data
|
|
Future<void> clearSession() async {
|
|
try {
|
|
await _authProvider.logout();
|
|
debugPrint('Session cleared successfully');
|
|
} catch (e) {
|
|
debugPrint('Error clearing session: $e');
|
|
}
|
|
}
|
|
|
|
/// Validate current session
|
|
///
|
|
/// Checks if the current session is still valid
|
|
/// Returns true if session is active and healthy
|
|
Future<bool> validateSession() async {
|
|
try {
|
|
if (!_authProvider.isAuthenticated) {
|
|
return false;
|
|
}
|
|
|
|
return await _authProvider.checkSession();
|
|
} catch (e) {
|
|
debugPrint('Error validating session: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Get session status
|
|
///
|
|
/// Returns current session status information
|
|
SessionStatus getSessionStatus() {
|
|
return SessionStatus(
|
|
isAuthenticated: _authProvider.isAuthenticated,
|
|
authState: _authProvider.state,
|
|
hasConnection: _authProvider.connection != null,
|
|
errorMessage: _authProvider.errorMessage,
|
|
);
|
|
}
|
|
|
|
/// Determine failure reason from error message
|
|
SessionRestoreFailureReason _determineFailureReason(String? errorMessage) {
|
|
if (errorMessage == null) {
|
|
return SessionRestoreFailureReason.unknown;
|
|
}
|
|
|
|
if (errorMessage.contains('密码错误') || errorMessage.contains('凭证')) {
|
|
return SessionRestoreFailureReason.invalidCredentials;
|
|
} else if (errorMessage.contains('网络') || errorMessage.contains('连接')) {
|
|
return SessionRestoreFailureReason.networkError;
|
|
} else if (errorMessage.contains('过期')) {
|
|
return SessionRestoreFailureReason.sessionExpired;
|
|
} else {
|
|
return SessionRestoreFailureReason.unknown;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Result of session restoration attempt
|
|
class SessionRestoreResult {
|
|
final bool success;
|
|
final SessionRestoreFailureReason? reason;
|
|
final String message;
|
|
|
|
SessionRestoreResult({
|
|
required this.success,
|
|
this.reason,
|
|
required this.message,
|
|
});
|
|
|
|
@override
|
|
String toString() {
|
|
return 'SessionRestoreResult(success: $success, reason: $reason, message: $message)';
|
|
}
|
|
}
|
|
|
|
/// Reasons why session restoration might fail
|
|
enum SessionRestoreFailureReason {
|
|
/// No saved credentials found
|
|
noCredentials,
|
|
|
|
/// Saved credentials are invalid
|
|
invalidCredentials,
|
|
|
|
/// Session has expired
|
|
sessionExpired,
|
|
|
|
/// Network connection error
|
|
networkError,
|
|
|
|
/// Unknown error
|
|
unknown,
|
|
}
|
|
|
|
/// Current session status information
|
|
class SessionStatus {
|
|
final bool isAuthenticated;
|
|
final AuthState authState;
|
|
final bool hasConnection;
|
|
final String? errorMessage;
|
|
|
|
SessionStatus({
|
|
required this.isAuthenticated,
|
|
required this.authState,
|
|
required this.hasConnection,
|
|
this.errorMessage,
|
|
});
|
|
|
|
/// Check if session is healthy
|
|
bool get isHealthy =>
|
|
isAuthenticated && hasConnection && errorMessage == null;
|
|
|
|
@override
|
|
String toString() {
|
|
return 'SessionStatus('
|
|
'isAuthenticated: $isAuthenticated, '
|
|
'authState: $authState, '
|
|
'hasConnection: $hasConnection, '
|
|
'errorMessage: $errorMessage'
|
|
')';
|
|
}
|
|
}
|
|
|
|
/// Extension methods for SessionRestoreFailureReason
|
|
extension SessionRestoreFailureReasonExtension on SessionRestoreFailureReason {
|
|
/// Get user-friendly message for the failure reason
|
|
String get userMessage {
|
|
switch (this) {
|
|
case SessionRestoreFailureReason.noCredentials:
|
|
return '未找到保存的登录信息,请重新登录';
|
|
case SessionRestoreFailureReason.invalidCredentials:
|
|
return '登录凭证无效,请重新登录';
|
|
case SessionRestoreFailureReason.sessionExpired:
|
|
return '会话已过期,请重新登录';
|
|
case SessionRestoreFailureReason.networkError:
|
|
return '网络连接失败,请检查网络后重试';
|
|
case SessionRestoreFailureReason.unknown:
|
|
return '会话恢复失败,请重新登录';
|
|
}
|
|
}
|
|
|
|
/// Check if user should be prompted to login again
|
|
bool get shouldPromptLogin {
|
|
switch (this) {
|
|
case SessionRestoreFailureReason.noCredentials:
|
|
case SessionRestoreFailureReason.invalidCredentials:
|
|
case SessionRestoreFailureReason.sessionExpired:
|
|
return true;
|
|
case SessionRestoreFailureReason.networkError:
|
|
case SessionRestoreFailureReason.unknown:
|
|
return false; // User might want to retry
|
|
}
|
|
}
|
|
}
|