import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart' hide Response; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import 'package:problem_check_system/app/routes/app_routes.dart'; import 'package:problem_check_system/data/repositories/auth_repository.dart'; // DioProvider 是一个 GetxService,确保它在应用生命周期内是单例的。 // 它负责初始化和配置 Dio 实例,并添加所有拦截器。 class HttpProvider extends GetxService { static const String _baseUrl = 'https://xhdev.anxincloud.cn'; late final Dio _dio; @override Future onInit() async { super.onInit(); _initDio(); } // 初始化 Dio 并配置基础选项。 void _initDio() { _dio = Dio( BaseOptions( baseUrl: _baseUrl, connectTimeout: const Duration(seconds: 30), receiveTimeout: const Duration(seconds: 30), sendTimeout: const Duration(seconds: 30), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ), ); // 添加拦截器。拦截器的顺序非常关键: // 1. 认证拦截器 (AuthInterceptor): 负责添加 token 和处理 401 错误,优先级最高。 // 2. 错误拦截器 (ErrorInterceptor): 处理通用错误,作为所有其他拦截器之后的最终捕获。 // 3. 日志拦截器 (LoggerInterceptor): 在调试模式下打印详细日志,方便开发。 _dio.interceptors.addAll(_getInterceptors()); } List _getInterceptors() { return [_getErrorInterceptor(), if (kDebugMode) _getLoggerInterceptor()]; } /// 日志拦截器:在调试模式下打印详细的请求和响应日志。 Interceptor _getLoggerInterceptor() { return PrettyDioLogger( requestHeader: true, requestBody: true, responseHeader: true, responseBody: true, error: true, compact: false, maxWidth: 90, ); } /// 错误拦截器:处理通用的网络和服务器端错误,并显示 Snackbar 提示。 Interceptor _getErrorInterceptor() { return InterceptorsWrapper( // 在请求发送前执行 onRequest: (options, handler) async { try { // 尝试获取 AuthRepository 并添加 token 到请求头。 final authRepository = Get.find(); final token = authRepository.getToken(); if (token != null && token.isNotEmpty) { options.headers['Authorization'] = 'Bearer $token'; } } catch (e) { // 如果 AuthRepository 尚未初始化(例如在登录或注册时),则跳过添加认证头。 Get.snackbar( '认证过期', '请重新手动登录', colorText: Colors.white, backgroundColor: Colors.red, snackPosition: SnackPosition.TOP, ); } return handler.next(options); }, onError: (error, handler) { // 处理网络连接超时或未知网络错误。 if (error.type == DioExceptionType.connectionTimeout || error.type == DioExceptionType.receiveTimeout || error.type == DioExceptionType.sendTimeout) { Get.snackbar( '网络超时', '请检查网络连接后重试', colorText: Colors.white, backgroundColor: Colors.red, snackPosition: SnackPosition.TOP, ); } else if (error.type == DioExceptionType.unknown) { Get.snackbar( '网络异常', '请检查网络连接后重试', colorText: Colors.white, backgroundColor: Colors.red, snackPosition: SnackPosition.TOP, ); } // 这部分代码只会在 AuthInterceptor 没有处理的错误(即非 401)时执行。 if (error.response != null) { final message = _handleStatusCode(error.response!); Get.snackbar( '请求错误', message, colorText: Colors.white, backgroundColor: Colors.red, snackPosition: SnackPosition.TOP, ); } return handler.next(error); }, ); } /// 辅助方法:根据 HTTP 状态码返回用户友好的错误信息。 String _handleStatusCode(Response response) { switch (response.statusCode) { case 400: return response.data?['detail'] ?? '请求参数错误'; case 401: final authRepository = Get.find(); authRepository.clearAuthData(); Get.offAllNamed(AppRoutes.login); return '未授权,请重新登录'; case 403: return response.data?['detail'] ?? '访问被拒绝'; case 404: return response.data?['detail'] ?? '请求资源不存在'; case 422: final errors = response.data?['errors']; if (errors != null && errors is Map && errors.isNotEmpty) { return errors.values.first?.first?.toString() ?? '数据验证失败'; } return response.data?['detail'] ?? '数据验证失败'; case 500: return response.data?['detail'] ?? '服务器内部错误'; case 502: return response.data?['detail'] ?? '网关错误'; case 503: return response.data?['detail'] ?? '服务不可用'; default: return response.data?['detail'] ?? '网络异常(${response.statusCode})'; } } void clear() { _dio.interceptors.clear(); } /// 新增的请求方法 /// 发送 GET 请求 Future get( String path, { Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onReceiveProgress, }) async { return await _dio.get( path, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onReceiveProgress: onReceiveProgress, ); } /// 发送 POST 请求 Future post( String path, { dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { return await _dio.post( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); } /// 发送 PUT 请求 Future put( String path, { dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { return await _dio.put( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); } /// 发送 DELETE 请求 Future delete( String path, { dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, }) async { return await _dio.delete( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, ); } }