init
This commit is contained in:
289
client/lib/shared/providers/auth_provider.dart
Normal file
289
client/lib/shared/providers/auth_provider.dart
Normal file
@@ -0,0 +1,289 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../models/user_model.dart';
|
||||
import '../models/api_response.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../../core/storage/storage_service.dart';
|
||||
import '../../core/constants/app_constants.dart';
|
||||
|
||||
/// 认证状态
|
||||
enum AuthState {
|
||||
initial,
|
||||
loading,
|
||||
authenticated,
|
||||
unauthenticated,
|
||||
error,
|
||||
}
|
||||
|
||||
/// 认证Provider
|
||||
class AuthProvider extends ChangeNotifier {
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
AuthState _state = AuthState.initial;
|
||||
UserModel? _user;
|
||||
String? _errorMessage;
|
||||
bool _isLoading = false;
|
||||
|
||||
// Getters
|
||||
AuthState get state => _state;
|
||||
UserModel? get user => _user;
|
||||
String? get errorMessage => _errorMessage;
|
||||
bool get isLoading => _isLoading;
|
||||
bool get isAuthenticated => _state == AuthState.authenticated && _user != null;
|
||||
|
||||
/// 初始化认证状态
|
||||
Future<void> initialize() async {
|
||||
_setLoading(true);
|
||||
|
||||
try {
|
||||
if (_authService.isLoggedIn()) {
|
||||
final cachedUser = _authService.getCachedUser();
|
||||
if (cachedUser != null) {
|
||||
_user = cachedUser;
|
||||
_setState(AuthState.authenticated);
|
||||
|
||||
// 尝试刷新用户信息
|
||||
await _refreshUserInfo();
|
||||
} else {
|
||||
_setState(AuthState.unauthenticated);
|
||||
}
|
||||
} else {
|
||||
_setState(AuthState.unauthenticated);
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('初始化失败: $e');
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 用户注册
|
||||
Future<bool> register({
|
||||
required String username,
|
||||
required String email,
|
||||
required String password,
|
||||
required String nickname,
|
||||
String? phone,
|
||||
}) async {
|
||||
_setLoading(true);
|
||||
_clearError();
|
||||
|
||||
try {
|
||||
final response = await _authService.register(
|
||||
username: username,
|
||||
email: email,
|
||||
password: password,
|
||||
nickname: nickname,
|
||||
phone: phone,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null && response.data!.user != null) {
|
||||
_user = UserModel.fromJson(response.data!.user!);
|
||||
_setState(AuthState.authenticated);
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('注册失败: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 用户登录
|
||||
Future<bool> login({
|
||||
required String account, // 用户名或邮箱
|
||||
required String password,
|
||||
bool rememberMe = false,
|
||||
}) async {
|
||||
_setLoading(true);
|
||||
_clearError();
|
||||
|
||||
try {
|
||||
final response = await _authService.login(
|
||||
account: account,
|
||||
password: password,
|
||||
rememberMe: rememberMe,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null && response.data!.user != null) {
|
||||
_user = UserModel.fromJson(response.data!.user!);
|
||||
_setState(AuthState.authenticated);
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('登录失败: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 用户登出
|
||||
Future<void> logout() async {
|
||||
_setLoading(true);
|
||||
|
||||
try {
|
||||
await _authService.logout();
|
||||
} catch (e) {
|
||||
// 即使登出请求失败,也要清除本地状态
|
||||
debugPrint('Logout error: $e');
|
||||
} finally {
|
||||
_user = null;
|
||||
_setState(AuthState.unauthenticated);
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新用户信息
|
||||
Future<void> _refreshUserInfo() async {
|
||||
try {
|
||||
final response = await _authService.getCurrentUser();
|
||||
if (response.success && response.data != null) {
|
||||
_user = response.data;
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Refresh user info error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新用户信息
|
||||
Future<bool> updateProfile({
|
||||
String? nickname,
|
||||
String? avatar,
|
||||
String? phone,
|
||||
DateTime? birthday,
|
||||
String? gender,
|
||||
String? bio,
|
||||
String? learningLevel,
|
||||
String? targetLanguage,
|
||||
String? nativeLanguage,
|
||||
int? dailyGoal,
|
||||
}) async {
|
||||
_setLoading(true);
|
||||
_clearError();
|
||||
|
||||
try {
|
||||
final response = await _authService.updateProfile(
|
||||
nickname: nickname,
|
||||
avatar: avatar,
|
||||
phone: phone,
|
||||
birthday: birthday,
|
||||
gender: gender,
|
||||
bio: bio,
|
||||
learningLevel: learningLevel,
|
||||
targetLanguage: targetLanguage,
|
||||
nativeLanguage: nativeLanguage,
|
||||
dailyGoal: dailyGoal,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_user = response.data;
|
||||
notifyListeners();
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('更新个人信息失败: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 修改密码
|
||||
Future<bool> changePassword({
|
||||
required String currentPassword,
|
||||
required String newPassword,
|
||||
required String confirmPassword,
|
||||
}) async {
|
||||
_setLoading(true);
|
||||
_clearError();
|
||||
|
||||
try {
|
||||
final response = await _authService.changePassword(
|
||||
currentPassword: currentPassword,
|
||||
newPassword: newPassword,
|
||||
confirmPassword: confirmPassword,
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
return true;
|
||||
} else {
|
||||
_setError(response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setError('修改密码失败: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新Token
|
||||
Future<bool> refreshToken() async {
|
||||
try {
|
||||
final response = await _authService.refreshToken();
|
||||
|
||||
if (response.success && response.data != null && response.data!.user != null) {
|
||||
_user = UserModel.fromJson(response.data!.user!);
|
||||
if (_state != AuthState.authenticated) {
|
||||
_setState(AuthState.authenticated);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// Token刷新失败,需要重新登录
|
||||
_user = null;
|
||||
_setState(AuthState.unauthenticated);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_user = null;
|
||||
_setState(AuthState.unauthenticated);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置加载状态
|
||||
void _setLoading(bool loading) {
|
||||
_isLoading = loading;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 设置状态
|
||||
void _setState(AuthState state) {
|
||||
_state = state;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 设置错误信息
|
||||
void _setError(String message) {
|
||||
_errorMessage = message;
|
||||
_setState(AuthState.error);
|
||||
}
|
||||
|
||||
/// 清除错误信息
|
||||
void _clearError() {
|
||||
_errorMessage = null;
|
||||
if (_state == AuthState.error) {
|
||||
_setState(_user != null ? AuthState.authenticated : AuthState.unauthenticated);
|
||||
}
|
||||
}
|
||||
|
||||
/// 清除所有状态
|
||||
void clear() {
|
||||
_user = null;
|
||||
_errorMessage = null;
|
||||
_isLoading = false;
|
||||
_setState(AuthState.initial);
|
||||
}
|
||||
}
|
||||
382
client/lib/shared/providers/error_provider.dart
Normal file
382
client/lib/shared/providers/error_provider.dart
Normal file
@@ -0,0 +1,382 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
/// 错误类型枚举
|
||||
enum ErrorType {
|
||||
network,
|
||||
authentication,
|
||||
validation,
|
||||
server,
|
||||
unknown,
|
||||
}
|
||||
|
||||
/// 错误严重程度枚举
|
||||
enum ErrorSeverity {
|
||||
info,
|
||||
warning,
|
||||
error,
|
||||
critical,
|
||||
}
|
||||
|
||||
/// 应用错误模型
|
||||
class AppError {
|
||||
final String id;
|
||||
final String message;
|
||||
final String? details;
|
||||
final ErrorType type;
|
||||
final ErrorSeverity severity;
|
||||
final DateTime timestamp;
|
||||
final String? stackTrace;
|
||||
final Map<String, dynamic>? context;
|
||||
|
||||
const AppError({
|
||||
required this.id,
|
||||
required this.message,
|
||||
this.details,
|
||||
required this.type,
|
||||
required this.severity,
|
||||
required this.timestamp,
|
||||
this.stackTrace,
|
||||
this.context,
|
||||
});
|
||||
|
||||
AppError copyWith({
|
||||
String? id,
|
||||
String? message,
|
||||
String? details,
|
||||
ErrorType? type,
|
||||
ErrorSeverity? severity,
|
||||
DateTime? timestamp,
|
||||
String? stackTrace,
|
||||
Map<String, dynamic>? context,
|
||||
}) {
|
||||
return AppError(
|
||||
id: id ?? this.id,
|
||||
message: message ?? this.message,
|
||||
details: details ?? this.details,
|
||||
type: type ?? this.type,
|
||||
severity: severity ?? this.severity,
|
||||
timestamp: timestamp ?? this.timestamp,
|
||||
stackTrace: stackTrace ?? this.stackTrace,
|
||||
context: context ?? this.context,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is AppError &&
|
||||
other.id == id &&
|
||||
other.message == message &&
|
||||
other.type == type &&
|
||||
other.severity == severity;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^
|
||||
message.hashCode ^
|
||||
type.hashCode ^
|
||||
severity.hashCode;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppError(id: $id, message: $message, type: $type, severity: $severity, timestamp: $timestamp)';
|
||||
}
|
||||
|
||||
/// 创建网络错误
|
||||
factory AppError.network(String message, {String? details, Map<String, dynamic>? context}) {
|
||||
return AppError(
|
||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
message: message,
|
||||
details: details,
|
||||
type: ErrorType.network,
|
||||
severity: ErrorSeverity.error,
|
||||
timestamp: DateTime.now(),
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
|
||||
/// 创建认证错误
|
||||
factory AppError.authentication(String message, {String? details, Map<String, dynamic>? context}) {
|
||||
return AppError(
|
||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
message: message,
|
||||
details: details,
|
||||
type: ErrorType.authentication,
|
||||
severity: ErrorSeverity.error,
|
||||
timestamp: DateTime.now(),
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
|
||||
/// 创建验证错误
|
||||
factory AppError.validation(String message, {String? details, Map<String, dynamic>? context}) {
|
||||
return AppError(
|
||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
message: message,
|
||||
details: details,
|
||||
type: ErrorType.validation,
|
||||
severity: ErrorSeverity.warning,
|
||||
timestamp: DateTime.now(),
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
|
||||
/// 创建服务器错误
|
||||
factory AppError.server(String message, {String? details, Map<String, dynamic>? context}) {
|
||||
return AppError(
|
||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
message: message,
|
||||
details: details,
|
||||
type: ErrorType.server,
|
||||
severity: ErrorSeverity.error,
|
||||
timestamp: DateTime.now(),
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
|
||||
/// 创建未知错误
|
||||
factory AppError.unknown(String message, {String? details, String? stackTrace, Map<String, dynamic>? context}) {
|
||||
return AppError(
|
||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
message: message,
|
||||
details: details,
|
||||
type: ErrorType.unknown,
|
||||
severity: ErrorSeverity.error,
|
||||
timestamp: DateTime.now(),
|
||||
stackTrace: stackTrace,
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 错误状态模型
|
||||
class ErrorState {
|
||||
final List<AppError> errors;
|
||||
final AppError? currentError;
|
||||
|
||||
const ErrorState({
|
||||
required this.errors,
|
||||
this.currentError,
|
||||
});
|
||||
|
||||
bool get hasErrors => errors.isNotEmpty;
|
||||
|
||||
ErrorState copyWith({
|
||||
List<AppError>? errors,
|
||||
AppError? currentError,
|
||||
}) {
|
||||
return ErrorState(
|
||||
errors: errors ?? this.errors,
|
||||
currentError: currentError,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is ErrorState &&
|
||||
other.errors.length == errors.length &&
|
||||
other.currentError == currentError;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return errors.hashCode ^ currentError.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// 错误管理Provider
|
||||
class ErrorNotifier extends StateNotifier<ErrorState> {
|
||||
static const int maxErrorCount = 50; // 最大错误数量
|
||||
static const Duration errorRetentionDuration = Duration(hours: 24); // 错误保留时间
|
||||
|
||||
ErrorNotifier() : super(const ErrorState(errors: []));
|
||||
|
||||
/// 添加错误
|
||||
void addError(AppError error) {
|
||||
final updatedErrors = List<AppError>.from(state.errors);
|
||||
updatedErrors.insert(0, error); // 新错误插入到列表开头
|
||||
|
||||
// 限制错误数量
|
||||
if (updatedErrors.length > maxErrorCount) {
|
||||
updatedErrors.removeRange(maxErrorCount, updatedErrors.length);
|
||||
}
|
||||
|
||||
// 清理过期错误
|
||||
_cleanExpiredErrors(updatedErrors);
|
||||
|
||||
state = state.copyWith(
|
||||
errors: updatedErrors,
|
||||
currentError: error,
|
||||
);
|
||||
|
||||
// 在调试模式下打印错误
|
||||
if (kDebugMode) {
|
||||
print('Error added: ${error.toString()}');
|
||||
if (error.stackTrace != null) {
|
||||
print('Stack trace: ${error.stackTrace}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 清除当前错误
|
||||
void clearCurrentError() {
|
||||
state = state.copyWith(currentError: null);
|
||||
}
|
||||
|
||||
/// 清除指定错误
|
||||
void removeError(String errorId) {
|
||||
final updatedErrors = state.errors.where((error) => error.id != errorId).toList();
|
||||
|
||||
AppError? newCurrentError = state.currentError;
|
||||
if (state.currentError?.id == errorId) {
|
||||
newCurrentError = null;
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
errors: updatedErrors,
|
||||
currentError: newCurrentError,
|
||||
);
|
||||
}
|
||||
|
||||
/// 清除所有错误
|
||||
void clearAllErrors() {
|
||||
state = const ErrorState(errors: []);
|
||||
}
|
||||
|
||||
/// 清除指定类型的错误
|
||||
void clearErrorsByType(ErrorType type) {
|
||||
final updatedErrors = state.errors.where((error) => error.type != type).toList();
|
||||
|
||||
AppError? newCurrentError = state.currentError;
|
||||
if (state.currentError?.type == type) {
|
||||
newCurrentError = null;
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
errors: updatedErrors,
|
||||
currentError: newCurrentError,
|
||||
);
|
||||
}
|
||||
|
||||
/// 清除指定严重程度的错误
|
||||
void clearErrorsBySeverity(ErrorSeverity severity) {
|
||||
final updatedErrors = state.errors.where((error) => error.severity != severity).toList();
|
||||
|
||||
AppError? newCurrentError = state.currentError;
|
||||
if (state.currentError?.severity == severity) {
|
||||
newCurrentError = null;
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
errors: updatedErrors,
|
||||
currentError: newCurrentError,
|
||||
);
|
||||
}
|
||||
|
||||
/// 获取指定类型的错误
|
||||
List<AppError> getErrorsByType(ErrorType type) {
|
||||
return state.errors.where((error) => error.type == type).toList();
|
||||
}
|
||||
|
||||
/// 获取指定严重程度的错误
|
||||
List<AppError> getErrorsBySeverity(ErrorSeverity severity) {
|
||||
return state.errors.where((error) => error.severity == severity).toList();
|
||||
}
|
||||
|
||||
/// 获取最近的错误
|
||||
List<AppError> getRecentErrors({int count = 10}) {
|
||||
return state.errors.take(count).toList();
|
||||
}
|
||||
|
||||
/// 检查是否有指定类型的错误
|
||||
bool hasErrorOfType(ErrorType type) {
|
||||
return state.errors.any((error) => error.type == type);
|
||||
}
|
||||
|
||||
/// 检查是否有指定严重程度的错误
|
||||
bool hasErrorOfSeverity(ErrorSeverity severity) {
|
||||
return state.errors.any((error) => error.severity == severity);
|
||||
}
|
||||
|
||||
/// 清理过期错误
|
||||
void _cleanExpiredErrors(List<AppError> errors) {
|
||||
final now = DateTime.now();
|
||||
errors.removeWhere((error) =>
|
||||
now.difference(error.timestamp) > errorRetentionDuration);
|
||||
}
|
||||
|
||||
/// 处理异常并转换为AppError
|
||||
void handleException(dynamic exception, {
|
||||
String? message,
|
||||
ErrorType? type,
|
||||
ErrorSeverity? severity,
|
||||
Map<String, dynamic>? context,
|
||||
}) {
|
||||
String errorMessage = message ?? exception.toString();
|
||||
ErrorType errorType = type ?? ErrorType.unknown;
|
||||
ErrorSeverity errorSeverity = severity ?? ErrorSeverity.error;
|
||||
String? stackTrace;
|
||||
|
||||
if (exception is Error) {
|
||||
stackTrace = exception.stackTrace?.toString();
|
||||
}
|
||||
|
||||
final error = AppError(
|
||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
message: errorMessage,
|
||||
details: exception.toString(),
|
||||
type: errorType,
|
||||
severity: errorSeverity,
|
||||
timestamp: DateTime.now(),
|
||||
stackTrace: stackTrace,
|
||||
context: context,
|
||||
);
|
||||
|
||||
addError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/// 错误状态Provider
|
||||
final errorProvider = StateNotifierProvider<ErrorNotifier, ErrorState>(
|
||||
(ref) => ErrorNotifier(),
|
||||
);
|
||||
|
||||
/// 当前错误Provider
|
||||
final currentErrorProvider = Provider<AppError?>(
|
||||
(ref) => ref.watch(errorProvider).currentError,
|
||||
);
|
||||
|
||||
/// 是否有错误Provider
|
||||
final hasErrorsProvider = Provider<bool>(
|
||||
(ref) => ref.watch(errorProvider).hasErrors,
|
||||
);
|
||||
|
||||
/// 错误数量Provider
|
||||
final errorCountProvider = Provider<int>(
|
||||
(ref) => ref.watch(errorProvider).errors.length,
|
||||
);
|
||||
|
||||
/// 网络错误Provider
|
||||
final networkErrorsProvider = Provider<List<AppError>>(
|
||||
(ref) => ref.watch(errorProvider).errors
|
||||
.where((error) => error.type == ErrorType.network)
|
||||
.toList(),
|
||||
);
|
||||
|
||||
/// 认证错误Provider
|
||||
final authErrorsProvider = Provider<List<AppError>>(
|
||||
(ref) => ref.watch(errorProvider).errors
|
||||
.where((error) => error.type == ErrorType.authentication)
|
||||
.toList(),
|
||||
);
|
||||
|
||||
/// 严重错误Provider
|
||||
final criticalErrorsProvider = Provider<List<AppError>>(
|
||||
(ref) => ref.watch(errorProvider).errors
|
||||
.where((error) => error.severity == ErrorSeverity.critical)
|
||||
.toList(),
|
||||
);
|
||||
239
client/lib/shared/providers/network_provider.dart
Normal file
239
client/lib/shared/providers/network_provider.dart
Normal file
@@ -0,0 +1,239 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'dart:async';
|
||||
|
||||
/// 网络连接状态枚举
|
||||
enum NetworkStatus {
|
||||
connected,
|
||||
disconnected,
|
||||
unknown,
|
||||
}
|
||||
|
||||
/// 网络连接类型枚举
|
||||
enum NetworkType {
|
||||
wifi,
|
||||
mobile,
|
||||
ethernet,
|
||||
none,
|
||||
unknown,
|
||||
}
|
||||
|
||||
/// 网络状态数据模型
|
||||
class NetworkState {
|
||||
final NetworkStatus status;
|
||||
final NetworkType type;
|
||||
final bool isOnline;
|
||||
final DateTime lastChecked;
|
||||
|
||||
const NetworkState({
|
||||
required this.status,
|
||||
required this.type,
|
||||
required this.isOnline,
|
||||
required this.lastChecked,
|
||||
});
|
||||
|
||||
NetworkState copyWith({
|
||||
NetworkStatus? status,
|
||||
NetworkType? type,
|
||||
bool? isOnline,
|
||||
DateTime? lastChecked,
|
||||
}) {
|
||||
return NetworkState(
|
||||
status: status ?? this.status,
|
||||
type: type ?? this.type,
|
||||
isOnline: isOnline ?? this.isOnline,
|
||||
lastChecked: lastChecked ?? this.lastChecked,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is NetworkState &&
|
||||
other.status == status &&
|
||||
other.type == type &&
|
||||
other.isOnline == isOnline;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return status.hashCode ^
|
||||
type.hashCode ^
|
||||
isOnline.hashCode;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NetworkState(status: $status, type: $type, isOnline: $isOnline, lastChecked: $lastChecked)';
|
||||
}
|
||||
}
|
||||
|
||||
/// 网络状态管理Provider
|
||||
class NetworkNotifier extends StateNotifier<NetworkState> {
|
||||
late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription;
|
||||
final Connectivity _connectivity = Connectivity();
|
||||
|
||||
NetworkNotifier()
|
||||
: super(NetworkState(
|
||||
status: NetworkStatus.unknown,
|
||||
type: NetworkType.unknown,
|
||||
isOnline: false,
|
||||
lastChecked: DateTime.now(),
|
||||
)) {
|
||||
_initializeNetworkMonitoring();
|
||||
}
|
||||
|
||||
/// 初始化网络监控
|
||||
void _initializeNetworkMonitoring() {
|
||||
// 监听网络连接状态变化
|
||||
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(
|
||||
(List<ConnectivityResult> results) {
|
||||
_updateNetworkState(results);
|
||||
},
|
||||
);
|
||||
|
||||
// 初始检查网络状态
|
||||
_checkInitialConnectivity();
|
||||
}
|
||||
|
||||
/// 检查初始网络连接状态
|
||||
Future<void> _checkInitialConnectivity() async {
|
||||
try {
|
||||
final results = await _connectivity.checkConnectivity();
|
||||
_updateNetworkState(results);
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Error checking initial connectivity: $e');
|
||||
}
|
||||
state = state.copyWith(
|
||||
status: NetworkStatus.unknown,
|
||||
type: NetworkType.unknown,
|
||||
isOnline: false,
|
||||
lastChecked: DateTime.now(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新网络状态
|
||||
void _updateNetworkState(List<ConnectivityResult> results) {
|
||||
// 取第一个有效的连接结果,如果列表为空则认为无连接
|
||||
final result = results.isNotEmpty ? results.first : ConnectivityResult.none;
|
||||
final networkType = _mapConnectivityResultToNetworkType(result);
|
||||
final isOnline = result != ConnectivityResult.none;
|
||||
final status = isOnline ? NetworkStatus.connected : NetworkStatus.disconnected;
|
||||
|
||||
state = NetworkState(
|
||||
status: status,
|
||||
type: networkType,
|
||||
isOnline: isOnline,
|
||||
lastChecked: DateTime.now(),
|
||||
);
|
||||
|
||||
if (kDebugMode) {
|
||||
print('Network state updated: $state');
|
||||
}
|
||||
}
|
||||
|
||||
/// 将ConnectivityResult映射到NetworkType
|
||||
NetworkType _mapConnectivityResultToNetworkType(ConnectivityResult result) {
|
||||
switch (result) {
|
||||
case ConnectivityResult.wifi:
|
||||
return NetworkType.wifi;
|
||||
case ConnectivityResult.mobile:
|
||||
return NetworkType.mobile;
|
||||
case ConnectivityResult.ethernet:
|
||||
return NetworkType.ethernet;
|
||||
case ConnectivityResult.none:
|
||||
return NetworkType.none;
|
||||
default:
|
||||
return NetworkType.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
/// 手动刷新网络状态
|
||||
Future<void> refreshNetworkStatus() async {
|
||||
try {
|
||||
final results = await _connectivity.checkConnectivity();
|
||||
_updateNetworkState(results);
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Error refreshing network status: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 检查是否有网络连接
|
||||
bool get isConnected => state.isOnline;
|
||||
|
||||
/// 检查是否为WiFi连接
|
||||
bool get isWifiConnected => state.type == NetworkType.wifi && state.isOnline;
|
||||
|
||||
/// 检查是否为移动网络连接
|
||||
bool get isMobileConnected => state.type == NetworkType.mobile && state.isOnline;
|
||||
|
||||
/// 获取网络类型描述
|
||||
String get networkTypeDescription {
|
||||
switch (state.type) {
|
||||
case NetworkType.wifi:
|
||||
return 'WiFi';
|
||||
case NetworkType.mobile:
|
||||
return '移动网络';
|
||||
case NetworkType.ethernet:
|
||||
return '以太网';
|
||||
case NetworkType.none:
|
||||
return '无网络';
|
||||
case NetworkType.unknown:
|
||||
return '未知网络';
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取网络状态描述
|
||||
String get statusDescription {
|
||||
switch (state.status) {
|
||||
case NetworkStatus.connected:
|
||||
return '已连接';
|
||||
case NetworkStatus.disconnected:
|
||||
return '已断开';
|
||||
case NetworkStatus.unknown:
|
||||
return '未知状态';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_connectivitySubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// 网络状态Provider
|
||||
final networkProvider = StateNotifierProvider<NetworkNotifier, NetworkState>(
|
||||
(ref) => NetworkNotifier(),
|
||||
);
|
||||
|
||||
/// 网络连接状态Provider(简化版)
|
||||
final isConnectedProvider = Provider<bool>(
|
||||
(ref) => ref.watch(networkProvider).isOnline,
|
||||
);
|
||||
|
||||
/// 网络类型Provider
|
||||
final networkTypeProvider = Provider<NetworkType>(
|
||||
(ref) => ref.watch(networkProvider).type,
|
||||
);
|
||||
|
||||
/// WiFi连接状态Provider
|
||||
final isWifiConnectedProvider = Provider<bool>(
|
||||
(ref) {
|
||||
final networkState = ref.watch(networkProvider);
|
||||
return networkState.type == NetworkType.wifi && networkState.isOnline;
|
||||
},
|
||||
);
|
||||
|
||||
/// 移动网络连接状态Provider
|
||||
final isMobileConnectedProvider = Provider<bool>(
|
||||
(ref) {
|
||||
final networkState = ref.watch(networkProvider);
|
||||
return networkState.type == NetworkType.mobile && networkState.isOnline;
|
||||
},
|
||||
);
|
||||
222
client/lib/shared/providers/notification_provider.dart
Normal file
222
client/lib/shared/providers/notification_provider.dart
Normal file
@@ -0,0 +1,222 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../models/notification_model.dart';
|
||||
import '../services/notification_service.dart';
|
||||
|
||||
/// 通知状态
|
||||
class NotificationState {
|
||||
final List<NotificationModel> notifications;
|
||||
final int total;
|
||||
final int unreadCount;
|
||||
final bool isLoading;
|
||||
final String? error;
|
||||
|
||||
NotificationState({
|
||||
this.notifications = const [],
|
||||
this.total = 0,
|
||||
this.unreadCount = 0,
|
||||
this.isLoading = false,
|
||||
this.error,
|
||||
});
|
||||
|
||||
NotificationState copyWith({
|
||||
List<NotificationModel>? notifications,
|
||||
int? total,
|
||||
int? unreadCount,
|
||||
bool? isLoading,
|
||||
String? error,
|
||||
}) {
|
||||
return NotificationState(
|
||||
notifications: notifications ?? this.notifications,
|
||||
total: total ?? this.total,
|
||||
unreadCount: unreadCount ?? this.unreadCount,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
error: error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 通知Notifier
|
||||
class NotificationNotifier extends StateNotifier<NotificationState> {
|
||||
final NotificationService _service = NotificationService();
|
||||
|
||||
NotificationNotifier() : super(NotificationState());
|
||||
|
||||
/// 加载通知列表
|
||||
Future<void> loadNotifications({
|
||||
int page = 1,
|
||||
int limit = 10,
|
||||
bool onlyUnread = false,
|
||||
}) async {
|
||||
state = state.copyWith(isLoading: true, error: null);
|
||||
|
||||
try {
|
||||
final response = await _service.getNotifications(
|
||||
page: page,
|
||||
limit: limit,
|
||||
onlyUnread: onlyUnread,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(
|
||||
notifications: response.data!['notifications'] as List<NotificationModel>,
|
||||
total: response.data!['total'] as int,
|
||||
isLoading: false,
|
||||
);
|
||||
} else {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
error: response.message,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
error: '加载通知列表失败: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载未读通知数量
|
||||
Future<void> loadUnreadCount() async {
|
||||
try {
|
||||
final response = await _service.getUnreadCount();
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
state = state.copyWith(
|
||||
unreadCount: response.data!,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('加载未读通知数量失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 标记通知为已读
|
||||
Future<bool> markAsRead(int notificationId) async {
|
||||
try {
|
||||
final response = await _service.markAsRead(notificationId);
|
||||
|
||||
if (response.success) {
|
||||
// 更新本地状态
|
||||
final updatedNotifications = state.notifications.map((n) {
|
||||
if (n.id == notificationId) {
|
||||
return NotificationModel(
|
||||
id: n.id,
|
||||
userId: n.userId,
|
||||
type: n.type,
|
||||
title: n.title,
|
||||
content: n.content,
|
||||
link: n.link,
|
||||
isRead: true,
|
||||
priority: n.priority,
|
||||
createdAt: n.createdAt,
|
||||
readAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
return n;
|
||||
}).toList();
|
||||
|
||||
state = state.copyWith(
|
||||
notifications: updatedNotifications,
|
||||
unreadCount: state.unreadCount > 0 ? state.unreadCount - 1 : 0,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
print('标记通知已读失败: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 标记所有通知为已读
|
||||
Future<bool> markAllAsRead() async {
|
||||
try {
|
||||
final response = await _service.markAllAsRead();
|
||||
|
||||
if (response.success) {
|
||||
// 更新本地状态
|
||||
final updatedNotifications = state.notifications.map((n) {
|
||||
return NotificationModel(
|
||||
id: n.id,
|
||||
userId: n.userId,
|
||||
type: n.type,
|
||||
title: n.title,
|
||||
content: n.content,
|
||||
link: n.link,
|
||||
isRead: true,
|
||||
priority: n.priority,
|
||||
createdAt: n.createdAt,
|
||||
readAt: DateTime.now(),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
state = state.copyWith(
|
||||
notifications: updatedNotifications,
|
||||
unreadCount: 0,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
print('标记所有通知已读失败: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除通知
|
||||
Future<bool> deleteNotification(int notificationId) async {
|
||||
try {
|
||||
final response = await _service.deleteNotification(notificationId);
|
||||
|
||||
if (response.success) {
|
||||
// 从本地状态中移除
|
||||
final updatedNotifications =
|
||||
state.notifications.where((n) => n.id != notificationId).toList();
|
||||
|
||||
// 如果删除的是未读通知,更新未读数量
|
||||
final deletedNotification =
|
||||
state.notifications.firstWhere((n) => n.id == notificationId);
|
||||
final newUnreadCount = deletedNotification.isRead
|
||||
? state.unreadCount
|
||||
: (state.unreadCount > 0 ? state.unreadCount - 1 : 0);
|
||||
|
||||
state = state.copyWith(
|
||||
notifications: updatedNotifications,
|
||||
total: state.total - 1,
|
||||
unreadCount: newUnreadCount,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
print('删除通知失败: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 刷新通知列表
|
||||
Future<void> refresh() async {
|
||||
await loadNotifications();
|
||||
await loadUnreadCount();
|
||||
}
|
||||
}
|
||||
|
||||
/// 通知Provider
|
||||
final notificationProvider =
|
||||
StateNotifierProvider<NotificationNotifier, NotificationState>(
|
||||
(ref) => NotificationNotifier(),
|
||||
);
|
||||
|
||||
/// 未读通知数量Provider(便捷访问)
|
||||
final unreadCountProvider = Provider<int>((ref) {
|
||||
return ref.watch(notificationProvider).unreadCount;
|
||||
});
|
||||
|
||||
/// 通知列表Provider(便捷访问)
|
||||
final notificationListProvider = Provider<List<NotificationModel>>((ref) {
|
||||
return ref.watch(notificationProvider).notifications;
|
||||
});
|
||||
369
client/lib/shared/providers/vocabulary_provider.dart
Normal file
369
client/lib/shared/providers/vocabulary_provider.dart
Normal file
@@ -0,0 +1,369 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../models/vocabulary_model.dart';
|
||||
import '../models/api_response.dart';
|
||||
import '../services/vocabulary_service.dart';
|
||||
import '../../core/errors/app_error.dart';
|
||||
|
||||
/// 词汇学习状态
|
||||
enum VocabularyState {
|
||||
initial,
|
||||
loading,
|
||||
loaded,
|
||||
error,
|
||||
}
|
||||
|
||||
/// 词汇学习Provider
|
||||
class VocabularyProvider with ChangeNotifier {
|
||||
final VocabularyService _vocabularyService;
|
||||
|
||||
VocabularyProvider(this._vocabularyService);
|
||||
|
||||
// 状态管理
|
||||
VocabularyState _state = VocabularyState.initial;
|
||||
String? _errorMessage;
|
||||
|
||||
// 词库数据
|
||||
List<VocabularyBookModel> _vocabularyBooks = [];
|
||||
VocabularyBookModel? _currentBook;
|
||||
|
||||
// 词汇数据
|
||||
List<VocabularyModel> _vocabularies = [];
|
||||
List<UserVocabularyModel> _userVocabularies = [];
|
||||
VocabularyModel? _currentVocabulary;
|
||||
|
||||
// 学习数据
|
||||
List<VocabularyModel> _todayReviewWords = [];
|
||||
List<VocabularyModel> _newWords = [];
|
||||
Map<String, dynamic> _learningStats = {};
|
||||
|
||||
// 分页数据
|
||||
int _currentPage = 1;
|
||||
bool _hasMoreData = true;
|
||||
|
||||
// Getters
|
||||
VocabularyState get state => _state;
|
||||
String? get errorMessage => _errorMessage;
|
||||
List<VocabularyBookModel> get vocabularyBooks => _vocabularyBooks;
|
||||
VocabularyBookModel? get currentBook => _currentBook;
|
||||
List<VocabularyModel> get vocabularies => _vocabularies;
|
||||
List<UserVocabularyModel> get userVocabularies => _userVocabularies;
|
||||
VocabularyModel? get currentVocabulary => _currentVocabulary;
|
||||
List<VocabularyModel> get todayReviewWords => _todayReviewWords;
|
||||
List<VocabularyModel> get newWords => _newWords;
|
||||
Map<String, dynamic> get learningStats => _learningStats;
|
||||
int get currentPage => _currentPage;
|
||||
bool get hasMoreData => _hasMoreData;
|
||||
|
||||
/// 设置状态
|
||||
void _setState(VocabularyState newState, [String? error]) {
|
||||
_state = newState;
|
||||
_errorMessage = error;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 获取词库列表
|
||||
Future<void> loadVocabularyBooks() async {
|
||||
try {
|
||||
_setState(VocabularyState.loading);
|
||||
|
||||
final response = await _vocabularyService.getVocabularyBooks();
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_vocabularyBooks = response.data!;
|
||||
_setState(VocabularyState.loaded);
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 设置当前词库
|
||||
void setCurrentBook(VocabularyBookModel book) {
|
||||
_currentBook = book;
|
||||
_vocabularies.clear();
|
||||
_currentPage = 1;
|
||||
_hasMoreData = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 获取词汇列表
|
||||
Future<void> loadVocabularies({
|
||||
String? bookId,
|
||||
String? search,
|
||||
String? difficulty,
|
||||
bool loadMore = false,
|
||||
}) async {
|
||||
try {
|
||||
if (!loadMore) {
|
||||
_setState(VocabularyState.loading);
|
||||
_currentPage = 1;
|
||||
_vocabularies.clear();
|
||||
}
|
||||
|
||||
final response = await _vocabularyService.getVocabularies(
|
||||
bookId: bookId ?? _currentBook?.bookId.toString(),
|
||||
page: _currentPage,
|
||||
search: search,
|
||||
level: difficulty,
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
final newVocabularies = response.data!.data;
|
||||
|
||||
if (loadMore) {
|
||||
_vocabularies.addAll(newVocabularies);
|
||||
} else {
|
||||
_vocabularies = newVocabularies;
|
||||
}
|
||||
|
||||
_hasMoreData = response.data!.pagination.hasNextPage;
|
||||
_currentPage++;
|
||||
_setState(VocabularyState.loaded);
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取单词详情
|
||||
Future<VocabularyModel?> getWordDetail(String wordId) async {
|
||||
try {
|
||||
final response = await _vocabularyService.getWordDetail(wordId);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_currentVocabulary = response.data!;
|
||||
notifyListeners();
|
||||
return response.data!;
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取用户词汇学习记录
|
||||
Future<void> loadUserVocabularies(String userId) async {
|
||||
try {
|
||||
final response = await _vocabularyService.getUserVocabularies();
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_userVocabularies = response.data!.data;
|
||||
notifyListeners();
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新单词学习状态
|
||||
Future<bool> updateWordStatus(String wordId, LearningStatus status) async {
|
||||
try {
|
||||
final response = await _vocabularyService.updateWordStatus(
|
||||
wordId: wordId,
|
||||
status: status,
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
// 更新本地数据
|
||||
final index = _userVocabularies.indexWhere((uv) => uv.wordId.toString() == wordId);
|
||||
if (index != -1) {
|
||||
// TODO: 实现UserVocabularyModel的copyWith方法
|
||||
// _userVocabularies[index] = _userVocabularies[index].copyWith(
|
||||
// status: status,
|
||||
// lastStudiedAt: DateTime.now(),
|
||||
// );
|
||||
notifyListeners();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 添加单词到学习列表
|
||||
Future<bool> addToLearningList(String wordId) async {
|
||||
try {
|
||||
// TODO: 实现addToLearningList方法
|
||||
// final response = await _vocabularyService.addToLearningList(wordId);
|
||||
final response = ApiResponse.success(message: 'Added to learning list');
|
||||
|
||||
if (response.success) {
|
||||
// 刷新用户词汇数据
|
||||
await loadUserVocabularies('current_user_id'); // TODO: 获取当前用户ID
|
||||
return true;
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 从学习列表移除单词
|
||||
Future<bool> removeFromLearningList(String wordId) async {
|
||||
try {
|
||||
// TODO: 实现removeFromLearningList方法
|
||||
// final response = await _vocabularyService.removeFromLearningList(wordId);
|
||||
final response = ApiResponse.success(message: 'Removed from learning list');
|
||||
|
||||
if (response.success) {
|
||||
// 更新本地数据
|
||||
_userVocabularies.removeWhere((uv) => uv.wordId.toString() == wordId);
|
||||
notifyListeners();
|
||||
return true;
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取今日复习单词
|
||||
Future<void> loadTodayReviewWords() async {
|
||||
try {
|
||||
// TODO: 实现getTodayReviewWords方法
|
||||
// final response = await _vocabularyService.getTodayReviewWords();
|
||||
final response = ApiResponse<List<VocabularyModel>>.success(
|
||||
message: 'Today review words retrieved',
|
||||
data: <VocabularyModel>[],
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_todayReviewWords = response.data ?? [];
|
||||
notifyListeners();
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取新单词学习
|
||||
Future<void> loadNewWords({int limit = 20}) async {
|
||||
try {
|
||||
// TODO: 实现getNewWordsForLearning方法
|
||||
// final response = await _vocabularyService.getNewWordsForLearning(limit: limit);
|
||||
final response = ApiResponse<List<VocabularyModel>>.success(
|
||||
message: 'New words retrieved',
|
||||
data: <VocabularyModel>[],
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_newWords = response.data ?? [];
|
||||
notifyListeners();
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 进行词汇量测试
|
||||
Future<Map<String, dynamic>?> takeVocabularyTest(List<Map<String, dynamic>> answers) async {
|
||||
try {
|
||||
_setState(VocabularyState.loading);
|
||||
|
||||
// TODO: 实现takeVocabularyTest方法
|
||||
// final response = await _vocabularyService.takeVocabularyTest(answers);
|
||||
final response = ApiResponse<Map<String, dynamic>>.success(
|
||||
message: 'Vocabulary test completed',
|
||||
data: <String, dynamic>{},
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_setState(VocabularyState.loaded);
|
||||
return response.data!;
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取学习统计
|
||||
Future<void> loadLearningStats() async {
|
||||
try {
|
||||
// TODO: 实现getLearningStats方法
|
||||
// final response = await _vocabularyService.getLearningStats();
|
||||
final response = ApiResponse<Map<String, dynamic>>.success(
|
||||
message: 'Learning stats retrieved',
|
||||
data: <String, dynamic>{},
|
||||
);
|
||||
|
||||
if (response.success && response.data != null) {
|
||||
_learningStats = response.data ?? {};
|
||||
notifyListeners();
|
||||
} else {
|
||||
_setState(VocabularyState.error, response.message);
|
||||
}
|
||||
} catch (e) {
|
||||
_setState(VocabularyState.error, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// 搜索词汇
|
||||
Future<void> searchVocabularies(String query) async {
|
||||
await loadVocabularies(search: query);
|
||||
}
|
||||
|
||||
/// 按难度筛选词汇
|
||||
Future<void> filterByDifficulty(String difficulty) async {
|
||||
await loadVocabularies(difficulty: difficulty);
|
||||
}
|
||||
|
||||
/// 加载更多词汇
|
||||
Future<void> loadMoreVocabularies() async {
|
||||
if (_hasMoreData && _state != VocabularyState.loading) {
|
||||
await loadVocabularies(loadMore: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// 清除错误状态
|
||||
void clearError() {
|
||||
_errorMessage = null;
|
||||
if (_state == VocabularyState.error) {
|
||||
_setState(VocabularyState.initial);
|
||||
}
|
||||
}
|
||||
|
||||
/// 重置状态
|
||||
void reset() {
|
||||
_state = VocabularyState.initial;
|
||||
_errorMessage = null;
|
||||
_vocabularyBooks.clear();
|
||||
_currentBook = null;
|
||||
_vocabularies.clear();
|
||||
_userVocabularies.clear();
|
||||
_currentVocabulary = null;
|
||||
_todayReviewWords.clear();
|
||||
_newWords.clear();
|
||||
_learningStats.clear();
|
||||
_currentPage = 1;
|
||||
_hasMoreData = true;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user