Files
flutterApp/lib/core/network/dio_client.dart
Songzex 786d11c936 完成封装打印和推送功能日志方法和使用
完成添加绑定设备接口的日志便于追溯
2026-03-04 16:33:56 +08:00

128 lines
4.3 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:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:get_it/get_it.dart';
import 'package:maibu_satabot_v2/core/consts/http_api_consts.dart';
import '../app/app_user_cubit.dart';
import '../di/injection.dart';
import '../infrastructure/logging/sentry_logger_impl.dart';
import '../logging/i_logger_service.dart';
class DioClient {
static Dio create() {
final dio = Dio(
BaseOptions(
baseUrl: HttpApiConsts.baseUrl,
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 5),
headers: {'Content-Type': 'application/json'},
),
);
if (kDebugMode) {
dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
}
// dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
// dio.interceptors.add(
// InterceptorsWrapper(
// onRequest: (options, handler) {
// // 1. 从 GetIt 中获取全局的 AppUserCubit
// final userCubit = sl<AppUserCubit>();
//
// // 2. 从 Cubit 的状态中提取 Token
// final token = userCubit.state.user?.token;
//
// // 3. 如果 Token 存在,则添加到请求头
// if (token != null && token.isNotEmpty) {
// options.headers['Authorization'] = 'Bearer $token';
// }
//
// // 继续发送请求
// return handler.next(options);
// },
// ),
// );
// return dio;
// }
dio.interceptors.add(InterceptorsWrapper(
onError: (DioException error, ErrorInterceptorHandler handler) async {
// 捕获所有网络异常 (超时、无网络、DNS 错误等)
await _reportToSentry(error);
return handler.next(error);
},
onResponse: (Response response, ResponseInterceptorHandler handler) async {
// 捕获业务错误 (如 HTTP 4xx, 5xx)
if (response.statusCode != null && response.statusCode! >= 400) {
await _reportHttpError(response);
}
return handler.next(response);
},
onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
// 原有 Token 逻辑保持不变
try {
final userCubit = sl<AppUserCubit>();
final token = userCubit.state.user?.token;
if (token != null && token.isNotEmpty) {
options.headers['Authorization'] = 'Bearer $token';
}
} catch (e) {
// 防止获取 Token 时出错阻断请求
debugPrint('获取 Token 失败: $e');
}
return handler.next(options);
},
));
return dio;
}
// 上报 HTTP 状态码错误 (404, 500 等)
static Future<void> _reportHttpError(Response response) async {
try {
final logger = GetIt.I<ILoggerService>() as SentryLoggerImpl;
// 记录详细的错误上下文
logger.logWithLevel(
'HTTP 请求失败: ${response.statusCode}',
level: 'ERROR',
data: {
'url': response.requestOptions.uri.toString(),
'method': response.requestOptions.method,
'statusCode': response.statusCode,
'responseData': response.data, // 注意:如果响应包含敏感信息,请脱敏
},
);
// 可选:如果希望每个 4xx/5xx 都生成一个独立 Issue,取消下面注释
// await Sentry.captureMessage(
// 'HTTP Error ${response.statusCode}: ${response.requestOptions.path}',
// level: SentryLevel.error,
// extra: {'data': response.data},
// );
} catch (e) {
debugPrint('Sentry 上报 HTTP 错误失败: $e');
}
}
// 上报网络异常 (超时、断开等)
static Future<void> _reportToSentry(DioException error) async {
try {
final logger = GetIt.I<ILoggerService>() as SentryLoggerImpl;
logger.logWithLevel(
'网络异常: ${error.type}',
level: 'ERROR',
data: {
'url': error.requestOptions.uri.toString(),
'method': error.requestOptions.method,
'message': error.message,
'type': error.type.name,
},
);
// 捕获完整堆栈
logger.captureException(error, stackTrace: error.stackTrace);
} catch (e) {
debugPrint('Sentry 上报网络异常失败: $e');
}
}
}