/// 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` 负责,这里只 /// 负责"这个异常有没有人处理过"之外的另一件事——落日志。 /// /// 三类要**主动排除**,否则线上告警会被噪音淹没: /// - [BusinessException]:后端明确告诉我们"这个操作不允许",是预期内的流程 /// 分支(余额不足、单据已关闭),不是缺陷; /// - [UnauthorizedException]:登录过期,SessionNotifier 已经在处理了; /// - [RequestCancelledException]:用户切走了页面,请求被主动取消。 /// --------------------------------------------------------------------------- final class ErrorObserver extends ProviderObserver { /// 构造。 ErrorObserver({required this.logger}); /// 日志出口。 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); } }