382 lines
9.8 KiB
Dart
382 lines
9.8 KiB
Dart
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(),
|
|
); |