174 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Dart
		
	
	
| import 'dart:io';
 | ||
| import 'dart:ui';
 | ||
| import 'package:dio/dio.dart';
 | ||
| 
 | ||
| /// 全局接口异常
 | ||
| class ApiException implements Exception {
 | ||
|   final String result;
 | ||
|   final String message;
 | ||
|   ApiException(this.result, this.message);
 | ||
| 
 | ||
|   @override
 | ||
|   String toString() => 'ApiException($result): $message';
 | ||
| }
 | ||
| 
 | ||
| /// HTTP 方法枚举
 | ||
| enum Method { get, post, put, delete }
 | ||
| 
 | ||
| /// HTTP 管理器 单例
 | ||
| class HttpManager {
 | ||
|   HttpManager._internal() {
 | ||
|     _dio = Dio(BaseOptions(
 | ||
|       connectTimeout: const Duration(milliseconds: 10000),
 | ||
|       receiveTimeout: const Duration(milliseconds: 10000),
 | ||
|       headers: {
 | ||
|         'Content-Type': Headers.formUrlEncodedContentType,
 | ||
|       },
 | ||
|     ));
 | ||
|     _initInterceptors();
 | ||
|   }
 | ||
| 
 | ||
|   static final HttpManager _instance = HttpManager._internal();
 | ||
|   factory HttpManager() => _instance;
 | ||
|   late final Dio _dio;
 | ||
| 
 | ||
|   // 添加401处理回调
 | ||
|   static VoidCallback? onUnauthorized;
 | ||
| 
 | ||
|   void _initInterceptors() {
 | ||
|     _dio.interceptors
 | ||
|       ..add(LogInterceptor(request: true, responseBody: true, error: true))
 | ||
|       ..add(InterceptorsWrapper(onError: (err, handler) {
 | ||
|         // TODO 暂不处理
 | ||
|         // 捕获401错误
 | ||
|         // if (err.response?.statusCode == 401) {
 | ||
|         //   // 触发全局登出回调
 | ||
|         //   onUnauthorized?.call();
 | ||
|         //   // 创建自定义异常
 | ||
|         //   final apiException = ApiException(
 | ||
|         //       '提示',
 | ||
|         //       '您的账号已在其他设备登录,已自动下线'
 | ||
|         //   );
 | ||
|         //   // 直接抛出业务异常,跳过后续错误处理
 | ||
|         //   return handler.reject(
 | ||
|         //     DioException(
 | ||
|         //       requestOptions: err.requestOptions,
 | ||
|         //       error: apiException,
 | ||
|         //       response: err.response,
 | ||
|         //       type: DioExceptionType.badResponse,
 | ||
|         //     ),
 | ||
|         //   );
 | ||
|         // }
 | ||
|         handler.next(err);
 | ||
|       }));
 | ||
|   }
 | ||
| 
 | ||
|   /// 通用请求方法,返回完整后台 JSON
 | ||
|   Future<Map<String, dynamic>> request(
 | ||
|       String baseUrl,
 | ||
|       String path, {
 | ||
|         Method method = Method.post,
 | ||
|         Map<String, dynamic>? data,
 | ||
|         Map<String, dynamic>? params,
 | ||
|         CancelToken? cancelToken,
 | ||
|       }) async {
 | ||
|     Response resp;
 | ||
|     final url = baseUrl + path;
 | ||
|     final options = Options(
 | ||
|       method: method.name.toUpperCase(),
 | ||
|       contentType: Headers.formUrlEncodedContentType,
 | ||
|     );
 | ||
| 
 | ||
|     try {
 | ||
|       switch (method) {
 | ||
|         case Method.get:
 | ||
|           resp = await _dio.get(
 | ||
|             url,
 | ||
|             queryParameters: params,
 | ||
|             cancelToken: cancelToken,
 | ||
|             options: options,
 | ||
|           );
 | ||
|           break;
 | ||
|         case Method.put:
 | ||
|           resp = await _dio.put(
 | ||
|             url,
 | ||
|             data: data,
 | ||
|             queryParameters: params,
 | ||
|             cancelToken: cancelToken,
 | ||
|             options: options,
 | ||
|           );
 | ||
|           break;
 | ||
|         case Method.delete:
 | ||
|           resp = await _dio.delete(
 | ||
|             url,
 | ||
|             queryParameters: params,
 | ||
|             cancelToken: cancelToken,
 | ||
|             options: options,
 | ||
|           );
 | ||
|           break;
 | ||
|         case Method.post:
 | ||
|           resp = await _dio.post(
 | ||
|             url,
 | ||
|             data: data,
 | ||
|             queryParameters: params,
 | ||
|             cancelToken: cancelToken,
 | ||
|             options: options,
 | ||
|           );
 | ||
|       }
 | ||
|     } on DioException catch (e) {
 | ||
|       // 如果已经是ApiException类型(401转换的)
 | ||
|       if (e.error is ApiException) {
 | ||
|         throw e.error as ApiException;
 | ||
|       }
 | ||
|       // 其他网络错误
 | ||
|       throw ApiException('network_error', e.message ?? e.toString());
 | ||
|     }
 | ||
| 
 | ||
|     // 解析返回 JSON
 | ||
|     final json = resp.data is Map<String, dynamic>
 | ||
|         ? resp.data as Map<String, dynamic>
 | ||
|         : <String, dynamic>{};
 | ||
|     final result = json['result'] as String?;
 | ||
|     final msg = json['msg'] as String? ?? json['message'] as String? ?? '';
 | ||
|     if (result != 'success') {
 | ||
|       // 非 success 都抛异常
 | ||
|       throw ApiException(result ?? 'unknown', msg);
 | ||
|     }
 | ||
|     return json;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| /// 上传图片扩展
 | ||
| extension HttpManagerUpload on HttpManager {
 | ||
|   Future<Map<String, dynamic>> uploadFaceImage({
 | ||
|     required String baseUrl,
 | ||
|     required String path,
 | ||
|     required Map<String, dynamic> fromData,
 | ||
|     CancelToken? cancelToken,
 | ||
|   }) async {
 | ||
|     final form = FormData.fromMap(fromData);
 | ||
|     try {
 | ||
|       final resp = await _dio.post(
 | ||
|         baseUrl + path,
 | ||
|         data: form,
 | ||
|         cancelToken: cancelToken,
 | ||
|         options: Options(
 | ||
|           method: Method.post.name.toUpperCase(),
 | ||
|           contentType: 'multipart/form-data',
 | ||
|         ),
 | ||
|       );
 | ||
| 
 | ||
|       final json = resp.data is Map<String, dynamic>
 | ||
|           ? resp.data as Map<String, dynamic>
 | ||
|           : <String, dynamic>{};
 | ||
| 
 | ||
|       return json;
 | ||
|     } on DioException catch (e) {
 | ||
|       // 如果已经是ApiException类型(401转换的)
 | ||
|       if (e.error is ApiException) {
 | ||
|         throw e.error as ApiException;
 | ||
|       }
 | ||
|       throw ApiException('network_error', e.message ?? e.toString());
 | ||
|     }
 | ||
|   }
 | ||
| } |