Files
flutter-template/lib/core/error/failures.dart
T
SkyJourney 61017f1c39 chore: 初始化脚手架 — sunny_mochi 企业级 Flutter 模板
Core 基础设施:Flavor 三包体系、Drift+SQLCipher 加密数据库
7 拦截器 Dio 网络栈、CrashReporter 崩溃日志、Sealed Failure 错误体系
5 色板主题系统、Auth 认证骨架(Clean Architecture)、Dev Panel、Mock Adapter

通过验证:flutter analyze(0 errors)、flutter test(全绿)、staging APK 74.8MB
2026-05-14 12:51:05 +08:00

135 lines
3.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:sunny_mochi/core/error/exception_mapper.dart'
show ExceptionMapper;
/// 应用层统一失败模型。
///
/// 所有数据/网络/缓存层的异常通过 [ExceptionMapper] 转换为 [Failure] 子类,
/// presentation 层只面对 Failure,避免散落 try-catch DioException / DriftException。
///
/// **实现 [Exception]**:让 datasource/repository 可以直接 `throw failure;`
/// 不触发 `only_throw_errors` lint。
///
/// 详见 docs/flutter-architecture-design.md §四.2。
sealed class Failure implements Exception {
const Failure({required this.message, this.cause, this.stackTrace});
final String message;
final Object? cause;
final StackTrace? stackTrace;
/// 子类自我描述前缀(避免直接用 [Object.runtimeType]Release 混淆友好)。
String get _typeName;
/// 适合展示给用户的文案(区别于 [message],后者含技术细节,供 Sentry/Talker 日志使用)。
String get userMessage;
@override
String toString() => '$_typeName: $message';
}
/// 网络层失败:超时 / 无网 / DNS 解析失败 / 证书绑定失败 / 连接断开。
class NetworkFailure extends Failure {
const NetworkFailure({
required super.message,
this.kind = NetworkFailureKind.unknown,
super.cause,
super.stackTrace,
});
final NetworkFailureKind kind;
@override
String get _typeName => 'NetworkFailure';
@override
String get userMessage => message;
}
enum NetworkFailureKind {
timeout,
noNetwork,
badCertificate,
cancelled,
unknown,
}
/// 鉴权失败:未登录 / Token 过期 / refresh 失败 → 应跳登录。
class AuthFailure extends Failure {
const AuthFailure({
required super.message,
this.kind = AuthFailureKind.unauthorized,
super.cause,
super.stackTrace,
});
final AuthFailureKind kind;
@override
String get _typeName => 'AuthFailure';
@override
String get userMessage => message;
}
enum AuthFailureKind {
unauthorized, // A0401
forbidden, // 403
refreshFailed,
cryptoFailed, // 客户端加密失败(如登录密码 RSA 加密前置错)
}
/// 服务端业务失败:500/501 / 业务校验失败 / 参数错误。
class ServerFailure extends Failure {
const ServerFailure({
required super.message,
this.code,
this.statusCode,
super.cause,
super.stackTrace,
});
final String? code; // 业务错误码(如 A0500 / A0400 / A0404
final int? statusCode; // HTTP 状态码
@override
String get _typeName => 'ServerFailure';
@override
String get userMessage {
// HTTP 5xx:服务端内部错误,后端 message 可能含技术细节(SQL/堆栈),统一遮蔽
if (statusCode != null && statusCode! >= 500) return '服务器开小差了,请稍后再试';
// 业务码 / 4xxmessage 已经过 ResponseCode.friendlyMessage 映射,安全展示
return message;
}
}
/// 本地缓存/数据库失败:Drift 异常 / SQLCipher 解密失败 / 文件 IO 错误。
class CacheFailure extends Failure {
const CacheFailure({
required super.message,
super.cause,
super.stackTrace,
});
@override
String get _typeName => 'CacheFailure';
@override
String get userMessage => '本地数据异常,请重启应用试试';
}
/// 兜底未知错误。
class UnknownFailure extends Failure {
const UnknownFailure({
required super.message,
super.cause,
super.stackTrace,
});
@override
String get _typeName => 'UnknownFailure';
@override
String get userMessage => '遇到了点问题,已自动上报';
}