173 lines
4.8 KiB
Dart
173 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) {
|
||
// 捕获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());
|
||
}
|
||
}
|
||
} |