import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../data/datasources/remote/auth_remote_datasource.dart'; import '../../core/di/injection.dart'; class UserState { final String? accountSequence; final String? nickname; final String? phone; final String? kycStatus; final String? source; final String? status; final String? realName; final DateTime? createdAt; final DateTime? lastLoginAt; final String? accessToken; final String? refreshToken; final int avatarIndex; final bool isLoggedIn; final bool isLoading; final String? error; UserState({ this.accountSequence, this.nickname, this.phone, this.kycStatus, this.source, this.status, this.realName, this.createdAt, this.lastLoginAt, this.accessToken, this.refreshToken, this.avatarIndex = 0, this.isLoggedIn = false, this.isLoading = false, this.error, }); bool get isKycVerified => kycStatus == 'VERIFIED'; UserState copyWith({ String? accountSequence, String? nickname, String? phone, String? kycStatus, String? source, String? status, String? realName, DateTime? createdAt, DateTime? lastLoginAt, String? accessToken, String? refreshToken, int? avatarIndex, bool? isLoggedIn, bool? isLoading, String? error, }) { return UserState( accountSequence: accountSequence ?? this.accountSequence, nickname: nickname ?? this.nickname, phone: phone ?? this.phone, kycStatus: kycStatus ?? this.kycStatus, source: source ?? this.source, status: status ?? this.status, realName: realName ?? this.realName, createdAt: createdAt ?? this.createdAt, lastLoginAt: lastLoginAt ?? this.lastLoginAt, accessToken: accessToken ?? this.accessToken, refreshToken: refreshToken ?? this.refreshToken, avatarIndex: avatarIndex ?? this.avatarIndex, isLoggedIn: isLoggedIn ?? this.isLoggedIn, isLoading: isLoading ?? this.isLoading, error: error, ); } } class UserNotifier extends StateNotifier { final AuthRemoteDataSource _authDataSource; UserNotifier(this._authDataSource) : super(UserState()) { _loadFromStorage(); } Future _loadFromStorage() async { final prefs = await SharedPreferences.getInstance(); final accessToken = prefs.getString('access_token'); final refreshToken = prefs.getString('refresh_token'); final accountSequence = prefs.getString('account_sequence'); final phone = prefs.getString('phone'); final avatarIndex = prefs.getInt('avatar_index') ?? 0; final nickname = prefs.getString('nickname'); if (accessToken != null && refreshToken != null && accountSequence != null) { state = state.copyWith( accessToken: accessToken, refreshToken: refreshToken, accountSequence: accountSequence, phone: phone, avatarIndex: avatarIndex, nickname: nickname, isLoggedIn: true, ); // 登录后自动获取用户详情 await fetchProfile(); } } Future _saveToStorage(AuthResult result) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('access_token', result.accessToken); await prefs.setString('refresh_token', result.refreshToken); await prefs.setString('account_sequence', result.user.accountSequence); await prefs.setString('phone', result.user.phone); } Future _clearStorage() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove('access_token'); await prefs.remove('refresh_token'); await prefs.remove('account_sequence'); await prefs.remove('phone'); await prefs.remove('avatar_index'); await prefs.remove('nickname'); } Future sendSmsCode(String phone, String type) async { state = state.copyWith(isLoading: true, error: null); try { await _authDataSource.sendSmsCode(phone, type); state = state.copyWith(isLoading: false); } catch (e) { state = state.copyWith(isLoading: false, error: e.toString()); rethrow; } } Future register(String phone, String password, String smsCode) async { state = state.copyWith(isLoading: true, error: null); try { final result = await _authDataSource.register(phone, password, smsCode); await _saveToStorage(result); state = state.copyWith( accessToken: result.accessToken, refreshToken: result.refreshToken, accountSequence: result.user.accountSequence, phone: result.user.phone, kycStatus: result.user.kycStatus, source: result.user.source, isLoggedIn: true, isLoading: false, ); } catch (e) { state = state.copyWith(isLoading: false, error: e.toString()); rethrow; } } Future loginWithPassword(String phone, String password) async { state = state.copyWith(isLoading: true, error: null); try { final result = await _authDataSource.loginWithPassword(phone, password); await _saveToStorage(result); state = state.copyWith( accessToken: result.accessToken, refreshToken: result.refreshToken, accountSequence: result.user.accountSequence, phone: result.user.phone, kycStatus: result.user.kycStatus, source: result.user.source, isLoggedIn: true, isLoading: false, ); // 登录后获取详细信息 await fetchProfile(); } catch (e) { state = state.copyWith(isLoading: false, error: e.toString()); rethrow; } } Future loginWithSms(String phone, String smsCode) async { state = state.copyWith(isLoading: true, error: null); try { final result = await _authDataSource.loginWithSms(phone, smsCode); await _saveToStorage(result); state = state.copyWith( accessToken: result.accessToken, refreshToken: result.refreshToken, accountSequence: result.user.accountSequence, phone: result.user.phone, kycStatus: result.user.kycStatus, source: result.user.source, isLoggedIn: true, isLoading: false, ); // 登录后获取详细信息 await fetchProfile(); } catch (e) { state = state.copyWith(isLoading: false, error: e.toString()); rethrow; } } Future logout() async { if (state.refreshToken != null) { try { await _authDataSource.logout(state.refreshToken!); } catch (_) {} } await _clearStorage(); state = UserState(); } Future refreshTokenIfNeeded() async { if (state.refreshToken == null) { throw Exception('No refresh token available'); } try { final newAccessToken = await _authDataSource.refreshToken(state.refreshToken!); final prefs = await SharedPreferences.getInstance(); await prefs.setString('access_token', newAccessToken); state = state.copyWith(accessToken: newAccessToken); } catch (e) { // 清除本地存储,不调用远程logout API(可能网络不可用) await _clearStorage(); state = UserState(); rethrow; } } Future resetPassword(String phone, String smsCode, String newPassword) async { state = state.copyWith(isLoading: true, error: null); try { await _authDataSource.resetPassword(phone, smsCode, newPassword); state = state.copyWith(isLoading: false); } catch (e) { state = state.copyWith(isLoading: false, error: e.toString()); rethrow; } } Future changePassword(String oldPassword, String newPassword) async { state = state.copyWith(isLoading: true, error: null); try { await _authDataSource.changePassword(oldPassword, newPassword); state = state.copyWith(isLoading: false); } catch (e) { state = state.copyWith(isLoading: false, error: e.toString()); rethrow; } } /// 获取用户详细信息 Future fetchProfile() async { if (!state.isLoggedIn) return; try { final profile = await _authDataSource.getProfile(); state = state.copyWith( phone: profile.phone, kycStatus: profile.kycStatus, source: profile.source, status: profile.status, realName: profile.realName, createdAt: profile.createdAt, lastLoginAt: profile.lastLoginAt, ); } catch (e) { // 静默失败,不影响用户体验 } } /// 更新头像索引(本地存储) Future updateAvatar(int avatarIndex) async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt('avatar_index', avatarIndex); state = state.copyWith(avatarIndex: avatarIndex); } /// 更新昵称(本地存储) Future updateNickname(String nickname) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('nickname', nickname); state = state.copyWith(nickname: nickname); } } final userNotifierProvider = StateNotifierProvider( (ref) => UserNotifier(getIt()), ); final currentAccountSequenceProvider = Provider((ref) { return ref.watch(userNotifierProvider).accountSequence; }); final isLoggedInProvider = Provider((ref) { return ref.watch(userNotifierProvider).isLoggedIn; }); final accessTokenProvider = Provider((ref) { return ref.watch(userNotifierProvider).accessToken; });