fix(mobile-app): 完善退出登录时的数据清理逻辑
遵循大厂最佳实践,确保退出登录后下次登录是干净的环境: SecureStorage 新增清除项: - inviterReferralCode (临时邀请码) - biometricEnabled (生物识别设置) LocalStorage 新增清除项: - lastSyncTime (最后同步时间) - cachedRankingData (排行榜缓存) - cachedMiningStatus (矿机状态缓存) 遥测数据: - 清除用户相关的事件队列 重构: - MultiAccountService 增加 LocalStorage 和 TelemetryStorage 依赖 - 更新依赖注入容器 - TelemetryStorage 新增 clearUserData 方法 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e742a360ec
commit
4de96dac9d
|
|
@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart';
|
||||||
import 'core/di/injection_container.dart';
|
import 'core/di/injection_container.dart';
|
||||||
import 'core/theme/app_theme.dart';
|
import 'core/theme/app_theme.dart';
|
||||||
import 'core/services/auth_event_service.dart';
|
import 'core/services/auth_event_service.dart';
|
||||||
import 'core/services/multi_account_service.dart';
|
|
||||||
import 'routes/app_router.dart';
|
import 'routes/app_router.dart';
|
||||||
import 'routes/route_paths.dart';
|
import 'routes/route_paths.dart';
|
||||||
|
|
||||||
|
|
@ -48,8 +47,7 @@ class _AppState extends ConsumerState<App> {
|
||||||
debugPrint('[App] Token expired, navigating to login page');
|
debugPrint('[App] Token expired, navigating to login page');
|
||||||
|
|
||||||
// 清除当前账号状态(但保留账号列表和向导页标识)
|
// 清除当前账号状态(但保留账号列表和向导页标识)
|
||||||
final secureStorage = ref.read(secureStorageProvider);
|
final multiAccountService = ref.read(multiAccountServiceProvider);
|
||||||
final multiAccountService = MultiAccountService(secureStorage);
|
|
||||||
await multiAccountService.logoutCurrentAccount();
|
await multiAccountService.logoutCurrentAccount();
|
||||||
|
|
||||||
// 使用全局 Navigator Key 跳转到登录页面
|
// 使用全局 Navigator Key 跳转到登录页面
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import '../services/notification_service.dart';
|
||||||
import '../services/system_config_service.dart';
|
import '../services/system_config_service.dart';
|
||||||
import '../services/contract_signing_service.dart';
|
import '../services/contract_signing_service.dart';
|
||||||
import '../services/contract_check_service.dart';
|
import '../services/contract_check_service.dart';
|
||||||
|
import '../telemetry/storage/telemetry_storage.dart';
|
||||||
import '../../features/kyc/data/kyc_service.dart';
|
import '../../features/kyc/data/kyc_service.dart';
|
||||||
|
|
||||||
// Storage Providers
|
// Storage Providers
|
||||||
|
|
@ -41,10 +42,17 @@ final accountServiceProvider = Provider<AccountService>((ref) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Telemetry Storage Provider
|
||||||
|
final telemetryStorageProvider = Provider<TelemetryStorage>((ref) {
|
||||||
|
return TelemetryStorage();
|
||||||
|
});
|
||||||
|
|
||||||
// Multi Account Service Provider
|
// Multi Account Service Provider
|
||||||
final multiAccountServiceProvider = Provider<MultiAccountService>((ref) {
|
final multiAccountServiceProvider = Provider<MultiAccountService>((ref) {
|
||||||
final secureStorage = ref.watch(secureStorageProvider);
|
final secureStorage = ref.watch(secureStorageProvider);
|
||||||
return MultiAccountService(secureStorage);
|
final localStorage = ref.watch(localStorageProvider);
|
||||||
|
final telemetryStorage = ref.watch(telemetryStorageProvider);
|
||||||
|
return MultiAccountService(secureStorage, localStorage, telemetryStorage);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Referral Service Provider
|
// Referral Service Provider
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import '../storage/secure_storage.dart';
|
import '../storage/secure_storage.dart';
|
||||||
|
import '../storage/local_storage.dart';
|
||||||
import '../storage/storage_keys.dart';
|
import '../storage/storage_keys.dart';
|
||||||
import '../telemetry/telemetry_service.dart';
|
import '../telemetry/telemetry_service.dart';
|
||||||
|
import '../telemetry/storage/telemetry_storage.dart';
|
||||||
import '../sentry/sentry_service.dart';
|
import '../sentry/sentry_service.dart';
|
||||||
|
|
||||||
/// 账号信息摘要(用于账号列表显示)
|
/// 账号信息摘要(用于账号列表显示)
|
||||||
|
|
@ -49,8 +51,14 @@ class MultiAccountService {
|
||||||
static const String _tag = '[MultiAccountService]';
|
static const String _tag = '[MultiAccountService]';
|
||||||
|
|
||||||
final SecureStorage _secureStorage;
|
final SecureStorage _secureStorage;
|
||||||
|
final LocalStorage _localStorage;
|
||||||
|
final TelemetryStorage _telemetryStorage;
|
||||||
|
|
||||||
MultiAccountService(this._secureStorage);
|
MultiAccountService(
|
||||||
|
this._secureStorage,
|
||||||
|
this._localStorage,
|
||||||
|
this._telemetryStorage,
|
||||||
|
);
|
||||||
|
|
||||||
/// 获取账号列表
|
/// 获取账号列表
|
||||||
Future<List<AccountSummary>> getAccountList() async {
|
Future<List<AccountSummary>> getAccountList() async {
|
||||||
|
|
@ -241,6 +249,7 @@ class MultiAccountService {
|
||||||
|
|
||||||
/// 退出当前账号(不删除账号数据)
|
/// 退出当前账号(不删除账号数据)
|
||||||
/// 清除当前会话状态,但保留账号在列表中
|
/// 清除当前会话状态,但保留账号在列表中
|
||||||
|
/// 遵循大厂最佳实践,确保下次登录是干净的环境
|
||||||
Future<void> logoutCurrentAccount() async {
|
Future<void> logoutCurrentAccount() async {
|
||||||
debugPrint('$_tag logoutCurrentAccount() - 退出当前账号');
|
debugPrint('$_tag logoutCurrentAccount() - 退出当前账号');
|
||||||
|
|
||||||
|
|
@ -250,48 +259,67 @@ class MultiAccountService {
|
||||||
// 清除当前账号标记
|
// 清除当前账号标记
|
||||||
await setCurrentAccountId(null);
|
await setCurrentAccountId(null);
|
||||||
|
|
||||||
// 清除所有当前账号相关的状态(但不删除账号列表)
|
// ===== 1. 清除 SecureStorage 中的敏感数据 =====
|
||||||
// 这样新账号创建/导入时不会受到旧状态影响
|
final secureKeysToClear = [
|
||||||
final keysToClear = [
|
// Token(必须清除)
|
||||||
// Token
|
|
||||||
StorageKeys.accessToken,
|
StorageKeys.accessToken,
|
||||||
StorageKeys.refreshToken,
|
StorageKeys.refreshToken,
|
||||||
// 账号信息
|
// 账号信息(必须清除)
|
||||||
StorageKeys.userSerialNum,
|
StorageKeys.userSerialNum,
|
||||||
StorageKeys.username,
|
StorageKeys.username,
|
||||||
StorageKeys.avatarSvg,
|
StorageKeys.avatarSvg,
|
||||||
StorageKeys.avatarUrl,
|
StorageKeys.avatarUrl,
|
||||||
StorageKeys.referralCode,
|
StorageKeys.referralCode,
|
||||||
StorageKeys.inviterSequence,
|
StorageKeys.inviterSequence,
|
||||||
|
StorageKeys.inviterReferralCode, // 临时邀请码
|
||||||
StorageKeys.isAccountCreated,
|
StorageKeys.isAccountCreated,
|
||||||
StorageKeys.phoneNumber,
|
StorageKeys.phoneNumber,
|
||||||
StorageKeys.isPasswordSet,
|
StorageKeys.isPasswordSet,
|
||||||
// 钱包信息
|
// 钱包信息(必须清除)
|
||||||
StorageKeys.walletAddressBsc,
|
StorageKeys.walletAddressBsc,
|
||||||
StorageKeys.walletAddressKava,
|
StorageKeys.walletAddressKava,
|
||||||
StorageKeys.walletAddressDst,
|
StorageKeys.walletAddressDst,
|
||||||
StorageKeys.mnemonic,
|
StorageKeys.mnemonic,
|
||||||
StorageKeys.isWalletReady,
|
StorageKeys.isWalletReady,
|
||||||
StorageKeys.isMnemonicBackedUp,
|
StorageKeys.isMnemonicBackedUp,
|
||||||
|
// 安全设置(与账号绑定,需清除)
|
||||||
|
StorageKeys.biometricEnabled,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (final key in keysToClear) {
|
for (final key in secureKeysToClear) {
|
||||||
await _secureStorage.delete(key: key);
|
await _secureStorage.delete(key: key);
|
||||||
}
|
}
|
||||||
|
debugPrint('$_tag logoutCurrentAccount() - 已清除 ${secureKeysToClear.length} 个 SecureStorage 键');
|
||||||
|
|
||||||
// 清除遥测服务的用户ID
|
// ===== 2. 清除 LocalStorage 中的缓存数据 =====
|
||||||
|
final localKeysToClear = [
|
||||||
|
StorageKeys.lastSyncTime,
|
||||||
|
StorageKeys.cachedRankingData,
|
||||||
|
StorageKeys.cachedMiningStatus,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (final key in localKeysToClear) {
|
||||||
|
await _localStorage.remove(key);
|
||||||
|
}
|
||||||
|
debugPrint('$_tag logoutCurrentAccount() - 已清除 ${localKeysToClear.length} 个 LocalStorage 缓存');
|
||||||
|
|
||||||
|
// ===== 3. 清除遥测事件队列(用户相关数据) =====
|
||||||
|
await _telemetryStorage.clearUserData();
|
||||||
|
|
||||||
|
// ===== 4. 清除遥测服务的用户ID =====
|
||||||
if (TelemetryService().isInitialized) {
|
if (TelemetryService().isInitialized) {
|
||||||
TelemetryService().clearUserId();
|
TelemetryService().clearUserId();
|
||||||
debugPrint('$_tag logoutCurrentAccount() - 清除TelemetryService userId');
|
debugPrint('$_tag logoutCurrentAccount() - 清除 TelemetryService userId');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除 Sentry 用户信息
|
// ===== 5. 清除 Sentry 用户信息 =====
|
||||||
if (SentryService().isInitialized) {
|
if (SentryService().isInitialized) {
|
||||||
SentryService().clearUser();
|
SentryService().clearUser();
|
||||||
debugPrint('$_tag logoutCurrentAccount() - 清除SentryService userId');
|
debugPrint('$_tag logoutCurrentAccount() - 清除 SentryService userId');
|
||||||
}
|
}
|
||||||
|
|
||||||
debugPrint('$_tag logoutCurrentAccount() - 退出完成,已清除 ${keysToClear.length} 个状态');
|
final totalCleared = secureKeysToClear.length + localKeysToClear.length;
|
||||||
|
debugPrint('$_tag logoutCurrentAccount() - 退出完成,共清除 $totalCleared 个存储键 + 遥测数据');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 完全删除账号(包括所有数据)
|
/// 完全删除账号(包括所有数据)
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,13 @@ class TelemetryStorage {
|
||||||
await _prefs.remove(_keyEventQueue);
|
await _prefs.remove(_keyEventQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 清空所有遥测数据(退出登录时调用)
|
||||||
|
/// 保留 installId 和 deviceContext(设备级别数据)
|
||||||
|
Future<void> clearUserData() async {
|
||||||
|
await _prefs.remove(_keyEventQueue);
|
||||||
|
debugPrint('📊 TelemetryStorage: 已清除用户相关遥测数据');
|
||||||
|
}
|
||||||
|
|
||||||
// 私有方法
|
// 私有方法
|
||||||
List<Map<String, dynamic>> _getEventQueue() {
|
List<Map<String, dynamic>> _getEventQueue() {
|
||||||
final str = _prefs.getString(_keyEventQueue);
|
final str = _prefs.getString(_keyEventQueue);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue