import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/network/api_client.dart'; import '../core/network/api_exception.dart'; import '../core/storage/token_store.dart'; import '../models/user_model.dart'; import '../services/auth_service.dart'; import '../services/user_service.dart'; /// 登录态:未登录 / 已登录(含用户信息) /// Token 本体在 TokenStore(安全存储 + 内存缓存),此处只持有用户模型与状态。 class AuthState { const AuthState({this.user, this.restored = false}); final UserModel? user; final bool restored; // 是否已完成启动恢复(避免启动期误跳登录) bool get isLoggedIn => user != null && TokenStore.hasToken; AuthState copyWith({UserModel? user, bool? restored}) => AuthState( user: user ?? this.user, restored: restored ?? this.restored, ); } class AuthNotifier extends StateNotifier { AuthNotifier() : super(const AuthState(restored: false)) { // 会话过期(401 刷新失败)→ 清空登录态 ApiClient.onSessionExpired = _onSessionExpired; } /// 启动恢复:读取安全存储中的 Token,有效则拉取用户信息 Future restore() async { await TokenStore.restore(); if (!TokenStore.hasToken) { state = state.copyWith(restored: true); return; } try { final user = await UserService.fetchMe(); state = AuthState(user: user, restored: true); } on ApiException catch (e) { // 仅 Token 确认失效(1002)才清理回游客态; // 网络异常等情况保留 Token,下次启动/请求重试恢复 if (e.isUnauthorized) await TokenStore.clear(); state = AuthState(restored: true); } catch (_) { // 未知异常:保守保留 Token state = AuthState(restored: true); } } /// 登录/注册成功:Token 已由 AuthService 写入 TokenStore,此处刷新用户。 /// 登录响应里的 user 是简要信息(无 points/bio/gender), /// 需补拉全量资料,否则积分显示为 0、编辑资料保存会把服务端字段清空。 Future onLoginSuccess(UserModel user) async { state = AuthState(user: user, restored: true); try { final full = await UserService.fetchMe(); state = state.copyWith(user: full); } catch (_) { // 补拉失败不影响登录态,保留简要信息 } } /// 修改资料/头像后同步当前用户(不改变登录态与 Token) void updateUser(UserModel user) => state = state.copyWith(user: user); /// 退出登录:先通知服务端注销(失败忽略,本地照常清理),再清空登录态 Future logout() async { try { await AuthService.logout(); } catch (_) { // 服务端已不可达或 Token 已失效:本地清理即可 } await _clearLocal(); } Future _onSessionExpired() async => _clearLocal(); Future _clearLocal() async { await TokenStore.clear(); state = AuthState(restored: true); } } /// 全局登录态 Provider final authProvider = StateNotifierProvider((ref) { return AuthNotifier(); });