Files
conti-retail-app/app/lib/src/error_observer.dart
T

44 lines
1.8 KiB
Dart
Raw Normal View History

2026-08-17 15:29:55 +08:00
/// Provider 层的全局错误出口。来源:conti-docs/12-error-and-api-contract.md §五。
library;
import 'package:core_foundation/core_foundation.dart';
import 'package:core_logging/core_logging.dart';
import 'package:core_ui/core_ui.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
/// 所有 provider 抛出的异常都会经过这里。
///
/// ---------------------------------------------------------------------------
/// 它是**兜底**,不是主路径:UI 该显示的错误由 `AsyncValueView` 负责,这里只
/// 负责"这个异常有没有人处理过"之外的另一件事——落日志。
2026-08-17 15:29:55 +08:00
///
/// 三类要**主动排除**,否则线上告警会被噪音淹没:
/// - [BusinessException]:后端明确告诉我们"这个操作不允许",是预期内的流程
/// 分支(余额不足、单据已关闭),不是缺陷;
/// - [UnauthorizedException]:登录过期,SessionNotifier 已经在处理了;
/// - [RequestCancelledException]:用户切走了页面,请求被主动取消。
/// ---------------------------------------------------------------------------
final class ErrorObserver extends ProviderObserver {
/// 构造。
ErrorObserver({required this.logger});
2026-08-17 15:29:55 +08:00
/// 日志出口。
final AppLogger logger;
@override
void providerDidFail(ProviderObserverContext context, Object error, StackTrace stackTrace) {
final String name = context.provider.name ?? context.provider.runtimeType.toString();
if (ErrorPresenter.isSilent(error)) {
return;
}
if (error is BusinessException) {
// 记一条 info 就够:需要它来复盘"用户为什么走不下去",但它不是缺陷。
logger.i('业务拒绝 $name: ${error.code} ${error.message}');
return;
}
logger.e('provider 失败 $name', error: error, stackTrace: stackTrace);
}
}