Template
P0 代码修复:删除 imSdkAppId、清除内网 IP 与原项目域名、修正 USE_MOCK 默认值 P1 CLAUDE.md 重组:零容忍规则前置,新增 dart-defines 字段对照表 P2 文档优化:先跑再配的 setup 流程,新增 architecture.md 架构图
135 lines
3.5 KiB
Dart
135 lines
3.5 KiB
Dart
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。
|
||
///
|
||
|
||
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 '服务器开小差了,请稍后再试';
|
||
// 业务码 / 4xx:message 已经过 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 => '遇到了点问题,已自动上报';
|
||
}
|