import 'dart:async'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:sunny_mochi/core/error/exception_mapper.dart'; import 'package:sunny_mochi/features/auth/auth_providers.dart'; import 'package:sunny_mochi/features/auth/domain/entities/user_entity.dart'; import 'package:sunny_mochi/features/auth/presentation/notifiers/auth_status_provider.dart'; part 'auth_notifier.freezed.dart'; part 'auth_notifier.g.dart'; @freezed sealed class AuthState with _$AuthState { const factory AuthState.initial() = AuthInitial; const factory AuthState.loading() = AuthLoading; const factory AuthState.authenticated(UserEntity user) = AuthAuthenticated; const factory AuthState.unauthenticated() = AuthUnauthenticated; const factory AuthState.error(String message) = AuthError; } @riverpod class AuthNotifier extends _$AuthNotifier { static const _mapper = ExceptionMapper(); // generation 计数:防止旧 async 回调覆盖最新登录态 int _generation = 0; @override AuthState build() { final gen = ++_generation; unawaited(_checkAuth(gen)); return const AuthState.initial(); } Future _checkAuth(int gen) async { final user = await ref.read(authRepositoryProvider).getCurrentUser(); if (_generation != gen) return; if (user != null) { state = AuthState.authenticated(user); ref.read(authStatusProvider).markLoggedIn(); unawaited(refreshUserInfo()); } else { state = const AuthState.unauthenticated(); ref.read(authStatusProvider).markLoggedOut(); } } Future loginWithPhone(String phone, String code) async { _generation++; state = const AuthState.loading(); try { final user = await ref .read(authRepositoryProvider) .loginWithPhone(phone: phone, code: code); state = AuthState.authenticated(user); ref.read(authStatusProvider).markLoggedIn(); unawaited(refreshUserInfo()); } on Object catch (e, st) { final failure = _mapper.fromUnknown(e, st); state = AuthState.error(failure.message); } } Future loginWithPassword(String phone, String password) async { _generation++; state = const AuthState.loading(); try { final user = await ref .read(authRepositoryProvider) .loginWithPassword(phone: phone, password: password); state = AuthState.authenticated(user); ref.read(authStatusProvider).markLoggedIn(); unawaited(refreshUserInfo()); } on Object catch (e, st) { final failure = _mapper.fromUnknown(e, st); state = AuthState.error(failure.message); } } Future logout() async { _generation++; state = const AuthState.unauthenticated(); ref.read(authStatusProvider).markLoggedOut(); unawaited(ref.read(authRepositoryProvider).logout()); } /// 发送短信验证码。返回 dev/test 环境后端回显的验证码(release 为 null),供 UI 自动填入。 Future sendSmsCode(String phone) async { try { return await ref.read(authRepositoryProvider).sendSmsCode(phone); } on Object catch (e, st) { throw _mapper.fromUnknown(e, st); } } Future refreshUserInfo() async { final current = state; if (current is! AuthAuthenticated) return; final gen = _generation; try { final fresh = await ref.read(authRepositoryProvider).fetchUserProfile(); if (_generation != gen) return; state = AuthState.authenticated( fresh.copyWith( token: current.user.token, userId: fresh.userId ?? current.user.userId, ), ); } on Object { // profile 拉取失败不应改变登录状态 } } }