This commit is contained in:
sjk
2025-11-17 13:39:05 +08:00
commit d4cfe2b9de
479 changed files with 109324 additions and 0 deletions

View 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);
}
}

View 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(),
);

View 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;
},
);

View 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;
});

View 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();
}
}