runable
This commit is contained in:
351
lib/core/errors/exceptions.dart
Normal file
351
lib/core/errors/exceptions.dart
Normal file
@@ -0,0 +1,351 @@
|
||||
/// Custom exceptions for the Worker app
|
||||
///
|
||||
/// This file defines all custom exception types used throughout the application
|
||||
/// for better error handling and user feedback.
|
||||
library;
|
||||
|
||||
// ============================================================================
|
||||
// Network Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Base exception for all network-related errors
|
||||
class NetworkException implements Exception {
|
||||
const NetworkException(
|
||||
this.message, {
|
||||
this.statusCode,
|
||||
this.data,
|
||||
});
|
||||
|
||||
final String message;
|
||||
final int? statusCode;
|
||||
final dynamic data;
|
||||
|
||||
@override
|
||||
String toString() => 'NetworkException: $message${statusCode != null ? ' (Status: $statusCode)' : ''}';
|
||||
}
|
||||
|
||||
/// Exception thrown when there's no internet connection
|
||||
class NoInternetException extends NetworkException {
|
||||
const NoInternetException()
|
||||
: super(
|
||||
'Không có kết nối internet. Vui lòng kiểm tra kết nối của bạn.',
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when connection times out
|
||||
class TimeoutException extends NetworkException {
|
||||
const TimeoutException()
|
||||
: super(
|
||||
'Kết nối quá lâu. Vui lòng thử lại.',
|
||||
statusCode: 408,
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when server returns 500+ errors
|
||||
class ServerException extends NetworkException {
|
||||
const ServerException([
|
||||
String message = 'Lỗi máy chủ. Vui lòng thử lại sau.',
|
||||
int? statusCode,
|
||||
]) : super(message, statusCode: statusCode);
|
||||
}
|
||||
|
||||
/// Exception thrown when server is unreachable
|
||||
class ServiceUnavailableException extends ServerException {
|
||||
const ServiceUnavailableException()
|
||||
: super(
|
||||
'Dịch vụ tạm thời không khả dụng. Vui lòng thử lại sau.',
|
||||
503,
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Authentication Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Base exception for authentication-related errors
|
||||
class AuthException implements Exception {
|
||||
const AuthException(
|
||||
this.message, {
|
||||
this.statusCode,
|
||||
});
|
||||
|
||||
final String message;
|
||||
final int? statusCode;
|
||||
|
||||
@override
|
||||
String toString() => 'AuthException: $message';
|
||||
}
|
||||
|
||||
/// Exception thrown when authentication credentials are invalid
|
||||
class InvalidCredentialsException extends AuthException {
|
||||
const InvalidCredentialsException()
|
||||
: super(
|
||||
'Thông tin đăng nhập không hợp lệ.',
|
||||
statusCode: 401,
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when user is not authenticated
|
||||
class UnauthorizedException extends AuthException {
|
||||
const UnauthorizedException([
|
||||
super.message = 'Phiên đăng nhập hết hạn. Vui lòng đăng nhập lại.',
|
||||
]) : super(statusCode: 401);
|
||||
}
|
||||
|
||||
/// Exception thrown when user doesn't have permission
|
||||
class ForbiddenException extends AuthException {
|
||||
const ForbiddenException()
|
||||
: super(
|
||||
'Bạn không có quyền truy cập tài nguyên này.',
|
||||
statusCode: 403,
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when auth token is expired
|
||||
class TokenExpiredException extends AuthException {
|
||||
const TokenExpiredException()
|
||||
: super(
|
||||
'Phiên đăng nhập hết hạn. Vui lòng đăng nhập lại.',
|
||||
statusCode: 401,
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when refresh token is invalid
|
||||
class InvalidRefreshTokenException extends AuthException {
|
||||
const InvalidRefreshTokenException()
|
||||
: super(
|
||||
'Không thể làm mới phiên đăng nhập. Vui lòng đăng nhập lại.',
|
||||
statusCode: 401,
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when OTP is invalid
|
||||
class InvalidOTPException extends AuthException {
|
||||
const InvalidOTPException()
|
||||
: super(
|
||||
'Mã OTP không hợp lệ. Vui lòng thử lại.',
|
||||
statusCode: 400,
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when OTP is expired
|
||||
class OTPExpiredException extends AuthException {
|
||||
const OTPExpiredException()
|
||||
: super(
|
||||
'Mã OTP đã hết hạn. Vui lòng yêu cầu mã mới.',
|
||||
statusCode: 400,
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Request Validation Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Exception thrown when request data is invalid
|
||||
class ValidationException implements Exception {
|
||||
const ValidationException(
|
||||
this.message, {
|
||||
this.errors,
|
||||
});
|
||||
|
||||
final String message;
|
||||
final Map<String, List<String>>? errors;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (errors != null && errors!.isNotEmpty) {
|
||||
final errorMessages = errors!.entries
|
||||
.map((e) => '${e.key}: ${e.value.join(", ")}')
|
||||
.join('; ');
|
||||
return 'ValidationException: $message - $errorMessages';
|
||||
}
|
||||
return 'ValidationException: $message';
|
||||
}
|
||||
}
|
||||
|
||||
/// Exception thrown when request parameters are invalid
|
||||
class BadRequestException extends ValidationException {
|
||||
const BadRequestException([
|
||||
String message = 'Yêu cầu không hợp lệ.',
|
||||
Map<String, List<String>>? errors,
|
||||
]) : super(message, errors: errors);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Resource Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Exception thrown when requested resource is not found
|
||||
class NotFoundException implements Exception {
|
||||
const NotFoundException([
|
||||
this.message = 'Không tìm thấy tài nguyên.',
|
||||
this.resourceType,
|
||||
this.resourceId,
|
||||
]);
|
||||
|
||||
final String message;
|
||||
final String? resourceType;
|
||||
final String? resourceId;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (resourceType != null && resourceId != null) {
|
||||
return 'NotFoundException: $resourceType with ID $resourceId not found';
|
||||
}
|
||||
return 'NotFoundException: $message';
|
||||
}
|
||||
}
|
||||
|
||||
/// Exception thrown when trying to create a duplicate resource
|
||||
class ConflictException implements Exception {
|
||||
const ConflictException([
|
||||
this.message = 'Tài nguyên đã tồn tại.',
|
||||
]);
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => 'ConflictException: $message';
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Rate Limiting Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Exception thrown when API rate limit is exceeded
|
||||
class RateLimitException implements Exception {
|
||||
const RateLimitException([
|
||||
this.message = 'Bạn đã gửi quá nhiều yêu cầu. Vui lòng thử lại sau.',
|
||||
this.retryAfter,
|
||||
]);
|
||||
|
||||
final String message;
|
||||
final int? retryAfter; // seconds
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (retryAfter != null) {
|
||||
return 'RateLimitException: $message (Retry after: ${retryAfter}s)';
|
||||
}
|
||||
return 'RateLimitException: $message';
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Payment Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Exception thrown for payment-related errors
|
||||
class PaymentException implements Exception {
|
||||
const PaymentException(
|
||||
this.message, {
|
||||
this.transactionId,
|
||||
});
|
||||
|
||||
final String message;
|
||||
final String? transactionId;
|
||||
|
||||
@override
|
||||
String toString() => 'PaymentException: $message';
|
||||
}
|
||||
|
||||
/// Exception thrown when payment fails
|
||||
class PaymentFailedException extends PaymentException {
|
||||
const PaymentFailedException([
|
||||
String message = 'Thanh toán thất bại. Vui lòng thử lại.',
|
||||
String? transactionId,
|
||||
]) : super(message, transactionId: transactionId);
|
||||
}
|
||||
|
||||
/// Exception thrown when payment is cancelled
|
||||
class PaymentCancelledException extends PaymentException {
|
||||
const PaymentCancelledException()
|
||||
: super('Thanh toán đã bị hủy.');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Cache Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Exception thrown for cache-related errors
|
||||
class CacheException implements Exception {
|
||||
const CacheException([
|
||||
this.message = 'Lỗi khi truy cập bộ nhớ đệm.',
|
||||
]);
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => 'CacheException: $message';
|
||||
}
|
||||
|
||||
/// Exception thrown when cache data is corrupted
|
||||
class CacheCorruptedException extends CacheException {
|
||||
const CacheCorruptedException()
|
||||
: super('Dữ liệu bộ nhớ đệm bị hỏng.');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Storage Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Exception thrown for local storage errors
|
||||
class StorageException implements Exception {
|
||||
const StorageException([
|
||||
this.message = 'Lỗi khi truy cập bộ nhớ cục bộ.',
|
||||
]);
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => 'StorageException: $message';
|
||||
}
|
||||
|
||||
/// Exception thrown when storage is full
|
||||
class StorageFullException extends StorageException {
|
||||
const StorageFullException()
|
||||
: super('Bộ nhớ đã đầy. Vui lòng giải phóng không gian.');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Parse Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Exception thrown when JSON parsing fails
|
||||
class ParseException implements Exception {
|
||||
const ParseException([
|
||||
this.message = 'Lỗi khi phân tích dữ liệu.',
|
||||
this.source,
|
||||
]);
|
||||
|
||||
final String message;
|
||||
final dynamic source;
|
||||
|
||||
@override
|
||||
String toString() => 'ParseException: $message';
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Unknown Exceptions
|
||||
// ============================================================================
|
||||
|
||||
/// Exception thrown for unexpected errors
|
||||
class UnknownException implements Exception {
|
||||
const UnknownException([
|
||||
this.message = 'Đã xảy ra lỗi không xác định.',
|
||||
this.originalError,
|
||||
this.stackTrace,
|
||||
]);
|
||||
|
||||
final String message;
|
||||
final dynamic originalError;
|
||||
final StackTrace? stackTrace;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (originalError != null) {
|
||||
return 'UnknownException: $message (Original: $originalError)';
|
||||
}
|
||||
return 'UnknownException: $message';
|
||||
}
|
||||
}
|
||||
262
lib/core/errors/failures.dart
Normal file
262
lib/core/errors/failures.dart
Normal file
@@ -0,0 +1,262 @@
|
||||
/// Failure classes for error handling in the Worker app
|
||||
///
|
||||
/// Failures represent domain-level errors that can be returned from use cases
|
||||
/// and repositories. They wrap exceptions and provide user-friendly error messages.
|
||||
library;
|
||||
|
||||
/// Base failure class
|
||||
sealed class Failure {
|
||||
const Failure({required this.message});
|
||||
|
||||
/// Network-related failure
|
||||
const factory Failure.network({
|
||||
required String message,
|
||||
int? statusCode,
|
||||
}) = NetworkFailure;
|
||||
|
||||
/// Server error failure (5xx errors)
|
||||
const factory Failure.server({
|
||||
required String message,
|
||||
int? statusCode,
|
||||
}) = ServerFailure;
|
||||
|
||||
/// Authentication failure
|
||||
const factory Failure.authentication({
|
||||
required String message,
|
||||
int? statusCode,
|
||||
}) = AuthenticationFailure;
|
||||
|
||||
/// Validation failure
|
||||
const factory Failure.validation({
|
||||
required String message,
|
||||
Map<String, List<String>>? errors,
|
||||
}) = ValidationFailure;
|
||||
|
||||
/// Not found failure (404)
|
||||
const factory Failure.notFound({
|
||||
required String message,
|
||||
}) = NotFoundFailure;
|
||||
|
||||
/// Conflict failure (409)
|
||||
const factory Failure.conflict({
|
||||
required String message,
|
||||
}) = ConflictFailure;
|
||||
|
||||
/// Rate limit exceeded failure (429)
|
||||
const factory Failure.rateLimit({
|
||||
required String message,
|
||||
int? retryAfter,
|
||||
}) = RateLimitFailure;
|
||||
|
||||
/// Payment failure
|
||||
const factory Failure.payment({
|
||||
required String message,
|
||||
String? transactionId,
|
||||
}) = PaymentFailure;
|
||||
|
||||
/// Cache failure
|
||||
const factory Failure.cache({
|
||||
required String message,
|
||||
}) = CacheFailure;
|
||||
|
||||
/// Storage failure
|
||||
const factory Failure.storage({
|
||||
required String message,
|
||||
}) = StorageFailure;
|
||||
|
||||
/// Parse failure
|
||||
const factory Failure.parse({
|
||||
required String message,
|
||||
}) = ParseFailure;
|
||||
|
||||
/// No internet connection failure
|
||||
const factory Failure.noInternet() = NoInternetFailure;
|
||||
|
||||
/// Timeout failure
|
||||
const factory Failure.timeout() = TimeoutFailure;
|
||||
|
||||
/// Unknown failure
|
||||
const factory Failure.unknown({
|
||||
required String message,
|
||||
}) = UnknownFailure;
|
||||
|
||||
final String message;
|
||||
|
||||
/// Check if this is a critical failure that requires immediate attention
|
||||
bool get isCritical {
|
||||
return switch (this) {
|
||||
ServerFailure() => true,
|
||||
AuthenticationFailure() => true,
|
||||
PaymentFailure() => true,
|
||||
UnknownFailure() => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Check if this failure can be retried
|
||||
bool get canRetry {
|
||||
return switch (this) {
|
||||
NetworkFailure() => true,
|
||||
ServerFailure(:final statusCode) => statusCode == 503,
|
||||
AuthenticationFailure(:final statusCode) => statusCode == 401,
|
||||
RateLimitFailure() => true,
|
||||
CacheFailure() => true,
|
||||
NoInternetFailure() => true,
|
||||
TimeoutFailure() => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Get HTTP status code if available
|
||||
int? get statusCode {
|
||||
return switch (this) {
|
||||
NetworkFailure(:final statusCode) => statusCode,
|
||||
ServerFailure(:final statusCode) => statusCode,
|
||||
AuthenticationFailure(:final statusCode) => statusCode,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Get user-friendly error message
|
||||
String getUserMessage() {
|
||||
return switch (this) {
|
||||
ValidationFailure(:final message, :final errors) => _formatValidationMessage(message, errors),
|
||||
RateLimitFailure(:final message, :final retryAfter) => _formatRateLimitMessage(message, retryAfter),
|
||||
NoInternetFailure() => 'Không có kết nối internet. Vui lòng kiểm tra kết nối của bạn.',
|
||||
TimeoutFailure() => 'Kết nối quá lâu. Vui lòng thử lại.',
|
||||
_ => message,
|
||||
};
|
||||
}
|
||||
|
||||
String _formatValidationMessage(String message, Map<String, List<String>>? errors) {
|
||||
if (errors != null && errors.isNotEmpty) {
|
||||
final firstError = errors.values.first.first;
|
||||
return '$message: $firstError';
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
String _formatRateLimitMessage(String message, int? retryAfter) {
|
||||
if (retryAfter != null) {
|
||||
return '$message Thử lại sau $retryAfter giây.';
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
/// Network-related failure
|
||||
final class NetworkFailure extends Failure {
|
||||
const NetworkFailure({
|
||||
required super.message,
|
||||
this.statusCode,
|
||||
});
|
||||
|
||||
@override
|
||||
final int? statusCode;
|
||||
}
|
||||
|
||||
/// Server error failure (5xx errors)
|
||||
final class ServerFailure extends Failure {
|
||||
const ServerFailure({
|
||||
required super.message,
|
||||
this.statusCode,
|
||||
});
|
||||
|
||||
@override
|
||||
final int? statusCode;
|
||||
}
|
||||
|
||||
/// Authentication failure
|
||||
final class AuthenticationFailure extends Failure {
|
||||
const AuthenticationFailure({
|
||||
required super.message,
|
||||
this.statusCode,
|
||||
});
|
||||
|
||||
@override
|
||||
final int? statusCode;
|
||||
}
|
||||
|
||||
/// Validation failure
|
||||
final class ValidationFailure extends Failure {
|
||||
const ValidationFailure({
|
||||
required super.message,
|
||||
this.errors,
|
||||
});
|
||||
|
||||
final Map<String, List<String>>? errors;
|
||||
}
|
||||
|
||||
/// Not found failure (404)
|
||||
final class NotFoundFailure extends Failure {
|
||||
const NotFoundFailure({
|
||||
required super.message,
|
||||
});
|
||||
}
|
||||
|
||||
/// Conflict failure (409)
|
||||
final class ConflictFailure extends Failure {
|
||||
const ConflictFailure({
|
||||
required super.message,
|
||||
});
|
||||
}
|
||||
|
||||
/// Rate limit exceeded failure (429)
|
||||
final class RateLimitFailure extends Failure {
|
||||
const RateLimitFailure({
|
||||
required super.message,
|
||||
this.retryAfter,
|
||||
});
|
||||
|
||||
final int? retryAfter;
|
||||
}
|
||||
|
||||
/// Payment failure
|
||||
final class PaymentFailure extends Failure {
|
||||
const PaymentFailure({
|
||||
required super.message,
|
||||
this.transactionId,
|
||||
});
|
||||
|
||||
final String? transactionId;
|
||||
}
|
||||
|
||||
/// Cache failure
|
||||
final class CacheFailure extends Failure {
|
||||
const CacheFailure({
|
||||
required super.message,
|
||||
});
|
||||
}
|
||||
|
||||
/// Storage failure
|
||||
final class StorageFailure extends Failure {
|
||||
const StorageFailure({
|
||||
required super.message,
|
||||
});
|
||||
}
|
||||
|
||||
/// Parse failure
|
||||
final class ParseFailure extends Failure {
|
||||
const ParseFailure({
|
||||
required super.message,
|
||||
});
|
||||
}
|
||||
|
||||
/// No internet connection failure
|
||||
final class NoInternetFailure extends Failure {
|
||||
const NoInternetFailure()
|
||||
: super(message: 'Không có kết nối internet');
|
||||
}
|
||||
|
||||
/// Timeout failure
|
||||
final class TimeoutFailure extends Failure {
|
||||
const TimeoutFailure()
|
||||
: super(message: 'Kết nối quá lâu');
|
||||
}
|
||||
|
||||
/// Unknown failure
|
||||
final class UnknownFailure extends Failure {
|
||||
const UnknownFailure({
|
||||
required super.message,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user