init
This commit is contained in:
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(),
|
||||
);
|
||||
Reference in New Issue
Block a user