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:
hailin 2025-12-27 09:55:17 -08:00
parent e742a360ec
commit 4de96dac9d
4 changed files with 58 additions and 17 deletions

View File

@ -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

View File

@ -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

View File

@ -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 个存储键 + 遥测数据');
} }
/// ///

View File

@ -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);