312 lines
7.9 KiB
Dart
312 lines
7.9 KiB
Dart
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
|
|
import '../../../core/models/user_model.dart';
|
|||
|
|
import '../../../core/services/auth_service.dart';
|
|||
|
|
import '../../../core/services/storage_service.dart';
|
|||
|
|
import '../../../core/network/api_client.dart';
|
|||
|
|
import '../../../core/errors/app_exception.dart';
|
|||
|
|
|
|||
|
|
/// 认证状态
|
|||
|
|
class AuthState {
|
|||
|
|
final User? user;
|
|||
|
|
final bool isAuthenticated;
|
|||
|
|
final bool isLoading;
|
|||
|
|
final String? error;
|
|||
|
|
|
|||
|
|
const AuthState({
|
|||
|
|
this.user,
|
|||
|
|
this.isAuthenticated = false,
|
|||
|
|
this.isLoading = false,
|
|||
|
|
this.error,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
AuthState copyWith({
|
|||
|
|
User? user,
|
|||
|
|
bool? isAuthenticated,
|
|||
|
|
bool? isLoading,
|
|||
|
|
String? error,
|
|||
|
|
}) {
|
|||
|
|
return AuthState(
|
|||
|
|
user: user ?? this.user,
|
|||
|
|
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
|
|||
|
|
isLoading: isLoading ?? this.isLoading,
|
|||
|
|
error: error,
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 认证状态管理器
|
|||
|
|
class AuthNotifier extends StateNotifier<AuthState> {
|
|||
|
|
final AuthService _authService;
|
|||
|
|
final StorageService _storageService;
|
|||
|
|
|
|||
|
|
AuthNotifier(this._authService, this._storageService) : super(const AuthState()) {
|
|||
|
|
_checkAuthStatus();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 检查认证状态
|
|||
|
|
Future<void> _checkAuthStatus() async {
|
|||
|
|
try {
|
|||
|
|
final token = await _storageService.getToken();
|
|||
|
|
if (token != null && token.isNotEmpty) {
|
|||
|
|
// 获取真实用户信息
|
|||
|
|
try {
|
|||
|
|
final user = await _authService.getUserInfo();
|
|||
|
|
state = state.copyWith(
|
|||
|
|
user: user,
|
|||
|
|
isAuthenticated: true,
|
|||
|
|
);
|
|||
|
|
} catch (e) {
|
|||
|
|
// Token可能已过期,清除token
|
|||
|
|
await _storageService.clearTokens();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
// 忽略错误,保持未认证状态
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 登录
|
|||
|
|
Future<void> login({
|
|||
|
|
required String account, // 用户名或邮箱
|
|||
|
|
required String password,
|
|||
|
|
bool rememberMe = false,
|
|||
|
|
}) async {
|
|||
|
|
state = state.copyWith(isLoading: true, error: null);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 调用真实API
|
|||
|
|
final authResponse = await _authService.login(
|
|||
|
|
account: account,
|
|||
|
|
password: password,
|
|||
|
|
rememberMe: rememberMe,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 保存token
|
|||
|
|
await _storageService.saveToken(authResponse.token);
|
|||
|
|
if (rememberMe && authResponse.refreshToken != null) {
|
|||
|
|
await _storageService.saveRefreshToken(authResponse.refreshToken!);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
state = state.copyWith(
|
|||
|
|
user: authResponse.user,
|
|||
|
|
isAuthenticated: true,
|
|||
|
|
isLoading: false,
|
|||
|
|
);
|
|||
|
|
} catch (e) {
|
|||
|
|
state = state.copyWith(
|
|||
|
|
isLoading: false,
|
|||
|
|
error: e.toString(),
|
|||
|
|
);
|
|||
|
|
rethrow;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 注册
|
|||
|
|
Future<void> register({
|
|||
|
|
required String email,
|
|||
|
|
required String password,
|
|||
|
|
required String username,
|
|||
|
|
required String nickname,
|
|||
|
|
}) async {
|
|||
|
|
state = state.copyWith(isLoading: true, error: null);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 调用真实API
|
|||
|
|
final authResponse = await _authService.register(
|
|||
|
|
email: email,
|
|||
|
|
password: password,
|
|||
|
|
username: username,
|
|||
|
|
nickname: nickname,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 保存token
|
|||
|
|
await _storageService.saveToken(authResponse.token);
|
|||
|
|
if (authResponse.refreshToken != null) {
|
|||
|
|
await _storageService.saveRefreshToken(authResponse.refreshToken!);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新状态
|
|||
|
|
state = state.copyWith(
|
|||
|
|
user: authResponse.user,
|
|||
|
|
isAuthenticated: true,
|
|||
|
|
isLoading: false,
|
|||
|
|
);
|
|||
|
|
} catch (e) {
|
|||
|
|
state = state.copyWith(
|
|||
|
|
isLoading: false,
|
|||
|
|
error: e.toString(),
|
|||
|
|
);
|
|||
|
|
rethrow;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 登出
|
|||
|
|
Future<void> logout() async {
|
|||
|
|
state = state.copyWith(isLoading: true, error: null);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 模拟网络延迟
|
|||
|
|
await Future.delayed(const Duration(milliseconds: 500));
|
|||
|
|
|
|||
|
|
await _storageService.clearTokens();
|
|||
|
|
|
|||
|
|
state = const AuthState();
|
|||
|
|
} catch (e) {
|
|||
|
|
state = state.copyWith(
|
|||
|
|
isLoading: false,
|
|||
|
|
error: e.toString(),
|
|||
|
|
);
|
|||
|
|
rethrow;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 忘记密码
|
|||
|
|
Future<void> forgotPassword(String email) async {
|
|||
|
|
state = state.copyWith(isLoading: true, error: null);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 模拟网络延迟
|
|||
|
|
await Future.delayed(const Duration(milliseconds: 1000));
|
|||
|
|
|
|||
|
|
if (email.isEmpty) {
|
|||
|
|
throw Exception('邮箱地址不能为空');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 模拟发送重置邮件成功
|
|||
|
|
state = state.copyWith(isLoading: false);
|
|||
|
|
} catch (e) {
|
|||
|
|
state = state.copyWith(
|
|||
|
|
isLoading: false,
|
|||
|
|
error: e.toString(),
|
|||
|
|
);
|
|||
|
|
rethrow;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 重置密码
|
|||
|
|
Future<void> resetPassword({
|
|||
|
|
required String token,
|
|||
|
|
required String newPassword,
|
|||
|
|
}) async {
|
|||
|
|
state = state.copyWith(isLoading: true, error: null);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 模拟网络延迟
|
|||
|
|
await Future.delayed(const Duration(milliseconds: 1000));
|
|||
|
|
|
|||
|
|
if (newPassword.length < 6) {
|
|||
|
|
throw Exception('密码长度不能少于6位');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
state = state.copyWith(isLoading: false);
|
|||
|
|
} catch (e) {
|
|||
|
|
state = state.copyWith(
|
|||
|
|
isLoading: false,
|
|||
|
|
error: e.toString(),
|
|||
|
|
);
|
|||
|
|
rethrow;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 更新用户信息
|
|||
|
|
Future<void> updateProfile({
|
|||
|
|
String? username,
|
|||
|
|
String? email,
|
|||
|
|
String? phone,
|
|||
|
|
String? avatar,
|
|||
|
|
}) async {
|
|||
|
|
if (state.user == null) return;
|
|||
|
|
|
|||
|
|
state = state.copyWith(isLoading: true, error: null);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 模拟网络延迟
|
|||
|
|
await Future.delayed(const Duration(milliseconds: 1000));
|
|||
|
|
|
|||
|
|
final updatedUser = state.user!.copyWith(
|
|||
|
|
username: username ?? state.user!.username,
|
|||
|
|
email: email ?? state.user!.email,
|
|||
|
|
phone: phone ?? state.user!.phone,
|
|||
|
|
avatar: avatar ?? state.user!.avatar,
|
|||
|
|
updatedAt: DateTime.now(),
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
state = state.copyWith(
|
|||
|
|
user: updatedUser,
|
|||
|
|
isLoading: false,
|
|||
|
|
);
|
|||
|
|
} catch (e) {
|
|||
|
|
state = state.copyWith(
|
|||
|
|
isLoading: false,
|
|||
|
|
error: e.toString(),
|
|||
|
|
);
|
|||
|
|
rethrow;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 修改密码
|
|||
|
|
Future<void> changePassword({
|
|||
|
|
required String currentPassword,
|
|||
|
|
required String newPassword,
|
|||
|
|
}) async {
|
|||
|
|
state = state.copyWith(isLoading: true, error: null);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 模拟网络延迟
|
|||
|
|
await Future.delayed(const Duration(milliseconds: 1000));
|
|||
|
|
|
|||
|
|
if (currentPassword.isEmpty || newPassword.isEmpty) {
|
|||
|
|
throw Exception('当前密码和新密码不能为空');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (newPassword.length < 6) {
|
|||
|
|
throw Exception('新密码长度不能少于6位');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
state = state.copyWith(isLoading: false);
|
|||
|
|
} catch (e) {
|
|||
|
|
state = state.copyWith(
|
|||
|
|
isLoading: false,
|
|||
|
|
error: e.toString(),
|
|||
|
|
);
|
|||
|
|
rethrow;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 清除错误
|
|||
|
|
void clearError() {
|
|||
|
|
state = state.copyWith(error: null);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 认证服务提供者
|
|||
|
|
final authServiceProvider = Provider<AuthService>((ref) {
|
|||
|
|
return AuthService(ApiClient.instance);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/// 存储服务提供者 - 改为同步Provider,因为StorageService已在main中初始化
|
|||
|
|
final storageServiceProvider = Provider<StorageService>((ref) {
|
|||
|
|
// StorageService已经在main.dart中初始化,直接获取实例
|
|||
|
|
// 这里使用一个技巧:通过Future.value包装已初始化的实例
|
|||
|
|
throw UnimplementedError('Use storageServiceInstanceProvider instead');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/// 认证状态提供者 - 改为StateNotifierProvider以保持状态
|
|||
|
|
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
|
|||
|
|
final authService = ref.watch(authServiceProvider);
|
|||
|
|
// 直接使用已初始化的StorageService实例
|
|||
|
|
final storageService = StorageService.instance;
|
|||
|
|
|
|||
|
|
return AuthNotifier(authService, storageService);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/// 是否已认证的提供者
|
|||
|
|
final isAuthenticatedProvider = Provider<bool>((ref) {
|
|||
|
|
return ref.watch(authProvider).isAuthenticated;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/// 当前用户提供者
|
|||
|
|
final currentUserProvider = Provider<User?>((ref) {
|
|||
|
|
return ref.watch(authProvider).user;
|
|||
|
|
});
|