import 'dart:io'; import 'package:dio/dio.dart' hide LogInterceptor; import 'package:sino_med_cloud/core/constants/api_constants.dart'; import 'package:sino_med_cloud/core/network/interceptors/auth_interceptor.dart'; import 'package:sino_med_cloud/core/network/interceptors/error_interceptor.dart'; import 'package:sino_med_cloud/core/network/interceptors/log_interceptor.dart'; import 'package:sino_med_cloud/core/network/interceptors/network_interceptor.dart'; import 'package:sino_med_cloud/core/utils/logger.dart'; import 'package:sino_med_cloud/base/common_response.dart'; import '../../base/list_response.dart'; /// Dio 客户端 - 统一网络请求管理 class DioClient { // 使用一个内部方法来创建和配置 Dio 实例 static final Dio _dio = _createDio(); static Dio _createDio() { AppLogger.d('DioClient - 开始创建 Dio 实例并配置拦截器'); final dio = Dio( BaseOptions( baseUrl: ApiConstants.baseUrl, connectTimeout: Duration(seconds: ApiConstants.connectTimeout), receiveTimeout: Duration(seconds: ApiConstants.receiveTimeout), sendTimeout: Duration(seconds: ApiConstants.sendTimeout), headers: { ApiConstants.contentType: ApiConstants.applicationJson, }, ), ); // 立即添加拦截器 dio.interceptors.addAll([ AuthInterceptor(), // 认证拦截器 LogInterceptor(), // 日志拦截器 ErrorInterceptor(), // 错误拦截器 NetworkInterceptor(), // 网络状态拦截器(在最后检查,确保异常能被 ErrorInterceptor 捕获) ]); AppLogger.d('DioClient - Dio 实例初始化完成,拦截器数量: ${dio.interceptors.length}'); return dio; } // 暴露 dio 实例 static Dio get dio => _dio; /// 重置 Dio 实例(用于重新配置) static void reset() { _dio.interceptors.clear(); _dio.interceptors.addAll([ AuthInterceptor(), LogInterceptor(), ErrorInterceptor(), NetworkInterceptor(), ]); } // ==================== GET 请求 ==================== /// GET 请求 /// /// [path] 请求路径(相对于 baseUrl) /// [fromJsonT] JSON 转换为 T 类型的函数 /// [queryParameters] 查询参数 /// [options] 请求选项 /// /// 返回 [BaseCommonResponse] 对象 static Future> get( String path, { required T Function(Object? json) fromJsonT, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onReceiveProgress, }) async { try { final response = await dio.get( path, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onReceiveProgress: onReceiveProgress, ); return BaseCommonResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } static Future> getList( String path, { required T Function(Object? json) fromJsonT, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onReceiveProgress, }) async { try { final response = await dio.get( path, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onReceiveProgress: onReceiveProgress, ); return BaseListResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } // ==================== POST 请求 ==================== /// POST 请求 /// /// [path] 请求路径(相对于 baseUrl) /// [fromJsonT] JSON 转换为 T 类型的函数 /// [data] 请求体数据 /// [queryParameters] 查询参数 /// [options] 请求选项 /// /// 返回 [BaseCommonResponse] 对象 static Future> post( String path, { required T Function(Object? json) fromJsonT, dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { try { final response = await dio.post( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); return BaseCommonResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } // ==================== DELETE 请求 ==================== /// DELETE 请求 /// /// [path] 请求路径(相对于 baseUrl) /// [fromJsonT] JSON 转换为 T 类型的函数 /// [data] 请求体数据(可选) /// [queryParameters] 查询参数 /// [options] 请求选项 /// /// 返回 [BaseCommonResponse] 对象 static Future> delete( String path, { required T Function(Object? json) fromJsonT, dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, }) async { try { final response = await dio.delete( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, ); return BaseCommonResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } // ==================== PUT 请求 ==================== /// PUT 请求 /// /// [path] 请求路径(相对于 baseUrl) /// [fromJsonT] JSON 转换为 T 类型的函数 /// [data] 请求体数据 /// [queryParameters] 查询参数 /// [options] 请求选项 /// /// 返回 [BaseCommonResponse] 对象 static Future> put( String path, { required T Function(Object? json) fromJsonT, dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { try { final response = await dio.put( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); return BaseCommonResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } // ==================== PATCH 请求 ==================== /// PATCH 请求 /// /// [path] 请求路径(相对于 baseUrl) /// [fromJsonT] JSON 转换为 T 类型的函数 /// [data] 请求体数据 /// [queryParameters] 查询参数 /// [options] 请求选项 /// /// 返回 [BaseCommonResponse] 对象 static Future> patch( String path, { required T Function(Object? json) fromJsonT, dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { try { final response = await dio.patch( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); return BaseCommonResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } // ==================== 文件上传 ==================== /// 单文件上传 /// /// [path] 请求路径(相对于 baseUrl) /// [fromJsonT] JSON 转换为 T 类型的函数 /// [file] 要上传的文件 /// [fileKey] 文件字段名,默认为 'file' /// [fileName] 文件名,如果不提供则使用文件的原始名称 /// [fields] 其他表单字段(可选) /// [queryParameters] 查询参数 /// [options] 请求选项 /// [onSendProgress] 上传进度回调 /// /// 返回 [BaseCommonResponse] 对象 /// /// 示例: /// ```dart /// final file = File('/path/to/image.jpg'); /// final response = await DioClient.uploadFile( /// '/api/upload', /// file: file, /// fileKey: 'image', /// fileName: 'avatar.jpg', /// fields: {'userId': '123'}, /// fromJsonT: (json) => UploadResult.fromJson(json as Map), /// ); /// ``` static Future> uploadFile( String path, { required File file, required T Function(Object? json) fromJsonT, String fileKey = 'file', String? fileName, Map? fields, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, }) async { try { // 检查文件是否存在 if (!await file.exists()) { throw DioException( requestOptions: RequestOptions(path: path), error: '文件不存在: ${file.path}', type: DioExceptionType.unknown, ); } // 构建文件名 final name = fileName ?? file.path.split('/').last; // 创建 FormData final formData = FormData.fromMap({ fileKey: await MultipartFile.fromFile( file.path, filename: name, ), if (fields != null) ...fields, }); // 设置请求选项,使用 multipart/form-data final requestOptions = options ?? Options(); requestOptions.headers ??= {}; requestOptions.headers!['Content-Type'] = 'multipart/form-data'; final response = await dio.post( path, data: formData, queryParameters: queryParameters, options: requestOptions, cancelToken: cancelToken, onSendProgress: onSendProgress, ); return BaseCommonResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } /// 多文件上传 /// /// [path] 请求路径(相对于 baseUrl) /// [fromJsonT] JSON 转换为 T 类型的函数 /// [files] 要上传的文件列表 /// [fileKey] 文件字段名,默认为 'files' /// [fields] 其他表单字段(可选) /// [queryParameters] 查询参数 /// [options] 请求选项 /// [onSendProgress] 上传进度回调 /// /// 返回 [BaseCommonResponse] 对象 /// /// 示例: /// ```dart /// final files = [ /// File('/path/to/image1.jpg'), /// File('/path/to/image2.jpg'), /// ]; /// final response = await DioClient.uploadFiles( /// '/api/upload/multiple', /// files: files, /// fileKey: 'images', /// fields: {'albumId': '123'}, /// fromJsonT: (json) => UploadResult.fromJson(json as Map), /// ); /// ``` static Future> uploadFiles( String path, { required List files, required T Function(Object? json) fromJsonT, String fileKey = 'files', Map? fields, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, }) async { try { if (files.isEmpty) { throw DioException( requestOptions: RequestOptions(path: path), error: '文件列表不能为空', type: DioExceptionType.unknown, ); } // 检查所有文件是否存在 for (final file in files) { if (!await file.exists()) { throw DioException( requestOptions: RequestOptions(path: path), error: '文件不存在: ${file.path}', type: DioExceptionType.unknown, ); } } // 创建 MultipartFile 列表 final multipartFiles = await Future.wait( files.map((file) async { final fileName = file.path.split('/').last; return MultipartFile.fromFile( file.path, filename: fileName, ); }), ); // 创建 FormData final formData = FormData.fromMap({ fileKey: multipartFiles, if (fields != null) ...fields, }); // 设置请求选项,使用 multipart/form-data final requestOptions = options ?? Options(); requestOptions.headers ??= {}; requestOptions.headers!['Content-Type'] = 'multipart/form-data'; final response = await dio.post( path, data: formData, queryParameters: queryParameters, options: requestOptions, cancelToken: cancelToken, onSendProgress: onSendProgress, ); return BaseCommonResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } /// 自定义 FormData 上传 /// /// 提供更灵活的文件上传方式,可以自定义 FormData 的内容 /// /// [path] 请求路径(相对于 baseUrl) /// [fromJsonT] JSON 转换为 T 类型的函数 /// [formData] 自定义的 FormData 对象 /// [queryParameters] 查询参数 /// [options] 请求选项 /// [onSendProgress] 上传进度回调 /// /// 返回 [BaseCommonResponse] 对象 /// /// 示例: /// ```dart /// final formData = FormData.fromMap({ /// 'file1': await MultipartFile.fromFile('/path/to/file1.jpg'), /// 'file2': await MultipartFile.fromFile('/path/to/file2.jpg'), /// 'userId': '123', /// 'description': '上传描述', /// }); /// final response = await DioClient.uploadFormData( /// '/api/upload/custom', /// formData: formData, /// fromJsonT: (json) => UploadResult.fromJson(json as Map), /// ); /// ``` static Future> uploadFormData( String path, { required FormData formData, required T Function(Object? json) fromJsonT, Map? queryParameters, Options? options, CancelToken? cancelToken, ProgressCallback? onSendProgress, }) async { try { // 设置请求选项,使用 multipart/form-data final requestOptions = options ?? Options(); requestOptions.headers ??= {}; requestOptions.headers!['Content-Type'] = 'multipart/form-data'; final response = await dio.post( path, data: formData, queryParameters: queryParameters, options: requestOptions, cancelToken: cancelToken, onSendProgress: onSendProgress, ); return BaseCommonResponse.fromJson( response.data as Map, fromJsonT, ); } on DioException { // 错误已由 ErrorInterceptor 处理,直接抛出 rethrow; } } }