// 认证 BLoC — 管理用户登录状态和认证流程 // // 状态机: AuthInitial → AuthLoading → Unauthenticated/Authenticated // ↕ // Authenticating → Authenticated/AuthError import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:logger/logger.dart'; import '../../../data/models/auth_token.dart'; import '../../../data/models/user.dart'; import '../../../data/remote/api_client.dart'; import '../../../data/repositories/auth_repository.dart'; part 'auth_event.dart'; part 'auth_state.dart'; /// 认证 BLoC — 处理所有认证相关的状态转换 class AuthBloc extends Bloc { final AuthRepository _authRepository; final Logger _logger = Logger(printer: PrettyPrinter(methodCount: 0)); AuthBloc({required AuthRepository authRepository}) : _authRepository = authRepository, super(const AuthInitial()) { // 注册事件处理器 on(_onAppStarted); on(_onLoginRequested); on(_onRegisterRequested); on(_onRoleSelected); on(_onClassCodeSubmitted); on(_onLogoutRequested); on(_onTokenRefreshed); on(_onAuthExpired); } /// App 启动 — 从本地存储恢复认证状态 Future _onAppStarted( AppStarted event, Emitter emit, ) async { emit(const AuthLoading()); try { final user = await _authRepository.restoreAuth(); if (user != null) { emit(Authenticated( user: user, needsRoleSelection: !user.hasRole, )); } else { emit(const Unauthenticated()); } } catch (e) { _logger.e('恢复认证状态失败: $e'); emit(const Unauthenticated()); } } /// 用户登录 Future _onLoginRequested( LoginRequested event, Emitter emit, ) async { emit(const Authenticating()); try { final user = await _authRepository.login( username: event.username, password: event.password, ); emit(Authenticated( user: user, needsRoleSelection: !user.hasRole, )); } on AuthException catch (e) { _logger.w('登录失败: ${e.message}'); emit(AuthError(e.message)); } on OfflineException { emit(const AuthError('网络不可用,请检查网络连接', retryable: true)); } catch (e) { _logger.e('登录异常: $e'); emit(const AuthError('登录失败,请稍后重试')); } } /// 用户注册 Future _onRegisterRequested( RegisterRequested event, Emitter emit, ) async { emit(const Authenticating(isRegister: true)); try { final user = await _authRepository.register( username: event.username, password: event.password, displayName: event.displayName, ); // 注册成功后需要选择角色 emit(Authenticated( user: user, needsRoleSelection: true, )); } on AuthException catch (e) { _logger.w('注册失败: ${e.message}'); emit(AuthError(e.message)); } on OfflineException { emit(const AuthError('网络不可用,请检查网络连接', retryable: true)); } catch (e) { _logger.e('注册异常: $e'); emit(const AuthError('注册失败,请稍后重试')); } } /// 用户选择角色 Future _onRoleSelected( RoleSelected event, Emitter emit, ) async { final currentState = state; if (currentState is! Authenticated) return; // 根据角色决定下一步 final needsClassCode = event.role == UserRoleType.student || event.role == UserRoleType.parent; emit(currentState.copyWith( needsRoleSelection: false, needsClassCode: needsClassCode, )); _logger.i('角色选择: ${event.role.name}, 需要班级码: $needsClassCode'); } /// 班级码加入 Future _onClassCodeSubmitted( ClassCodeSubmitted event, Emitter emit, ) async { final currentState = state; if (currentState is! Authenticated) return; // TODO: 调用后端 API 验证班级码并加入班级 // 当前先标记为已完成,班级码验证在 F8 阶段完善 emit(currentState.copyWith( needsClassCode: false, )); _logger.i('班级码加入: ${event.classCode}'); } /// 用户登出 Future _onLogoutRequested( LogoutRequested event, Emitter emit, ) async { try { await _authRepository.logout(); } catch (e) { _logger.w('登出失败(忽略): $e'); } emit(const Unauthenticated()); } /// 令牌刷新成功 Future _onTokenRefreshed( TokenRefreshed event, Emitter emit, ) async { _logger.d('令牌已刷新'); // 不改变当前状态,仅更新令牌 } /// 认证过期(401 拦截器触发) Future _onAuthExpired( AuthExpired event, Emitter emit, ) async { _logger.w('认证过期,需要重新登录'); emit(const Unauthenticated()); } }