circle_app/circle_app/lib/net/dio_manager.dart
2025-03-12 16:43:43 +08:00

525 lines
16 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 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:circle_app/router/routers.dart';
import 'package:circle_app/utils/util.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter_bugly/flutter_bugly.dart';
import '../utils/SharedPreferencesHelper.dart';
import '../utils/device.dart';
import 'api.dart';
import 'package:connectivity/connectivity.dart';
// const String baseUrl = Api.baseUrl;
/// Dio 请求方法
enum DioMethod {
get,
post,
put,
delete,
postBody,
}
class DioManager {
factory DioManager() => getInstance();
static DioManager get instance => getInstance();
static DioManager? _instance;
static DioManager getInstance() {
if (_instance == null) {
_instance = DioManager._init();
}
return _instance!;
}
static CancelToken _globalCancelToken = CancelToken(); // 添加全局CancelToken
// 存储每个请求的 CancelToken
Map<String, CancelToken> _cancelTokens = {};
Dio? _dio;
DioManager._init() {
_dio ??= Dio(BaseOptions(
baseUrl:
// Api.testBaseUrl,
Api.baseUrl1,
// 连接服务器超时时间,单位是毫秒
connectTimeout: const Duration(seconds: 15),
// 接收数据的最长时限
receiveTimeout: const Duration(seconds: 15),
));
_dio!.interceptors.add(LogInterceptor(
responseBody: true,
));
_dio!.interceptors.add(TimingInterceptor()); // 添加拦截器
// _dio?.httpClientAdapter = IOHttpClientAdapter()
// ..createHttpClient = () {
// // 设置最大连接数
// HttpClient client = HttpClient();
// client.idleTimeout = Duration(seconds: 15); // 连接池的空闲超时
// client.maxConnectionsPerHost = 15; // 设置同一主机的最大连接数为 15
// return client;
// };
}
setDioTimeOut(int time) {
_dio?.options.connectTimeout = Duration(milliseconds: time);
}
setDioBaseUrl(String url) {
if (url != _dio!.options.baseUrl && _dio!.options.baseUrl == Api.testBaseUrl)
_dio?.options = BaseOptions(
// 请求基地址
baseUrl: url,
// 连接服务器超时时间,单位是毫秒
connectTimeout: const Duration(seconds: 30),
// 接收数据的最长时限
receiveTimeout: _dio!.options!.receiveTimeout,
);
}
cancelAllRequest() {
// _globalCancelToken.cancel("用户登出,取消所有请求");
// _globalCancelToken = CancelToken(); // 重新初始化 CancelToken 以便后续请求
}
// 生成请求 Key根据 URL 和参数)
String _generateRequestKey(String url, Map<String, dynamic>? params) {
return "$url-${params?.toString() ?? ''}";
}
setReceiveTimeout (int time) {
_dio!.options = BaseOptions(
// 请求基地址
baseUrl: _dio!.options!.baseUrl,
// 连接服务器超时时间,单位是毫秒
connectTimeout: const Duration(seconds: 15),
// 接收数据的最长时限
receiveTimeout: Duration(seconds: time),
);
}
Future download(String urlPath, String savePath,
ProgressCallback progressCallback) async {
return await _dio!
.download(urlPath, savePath, onReceiveProgress: progressCallback);
}
/// get请求
/// [isShowErrorToast] 出现错误的情况是否自动弹窗提示错误默认true
/// [isAddTokenInHeader] 请求头是否添加token
/// [fromJson]是将json转为model的方法
Future get<T>({
required String url,
bool isShowErrorToast = true,
bool isAddTokenInHeader = true,
Map<String, dynamic>? params,
}) async {
return await requestHttp(
url,
method: DioMethod.get,
isShowErrorToast: isShowErrorToast,
params: params,
);
}
/// post 请求
Future post<T>(
{required String url,
Map<String, dynamic>? params,
bool isAddTokenInHeader = true,
bool isShowErrorToast = true,
FormData? formData,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress}) async {
return await requestHttp<T>(url,
method: DioMethod.post,
isShowErrorToast: isShowErrorToast,
params: params,
formData: formData,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress);
}
/// post 请求
Future postBody<T>({required String url,
Map<String, dynamic>? params,
bool isAddTokenInHeader = true,
bool isShowErrorToast = true,
FormData? formData,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress}) async {
return await requestHttp<T>(url,
method: DioMethod.postBody,
isShowErrorToast: isShowErrorToast,
params: params,
formData: formData,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress);
}
/// put 请求
Future put<T>({
required String url,
Map<String, dynamic>? params,
bool isAddTokenInHeader = true,
bool isShowErrorToast = true,
FormData? formData,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) async {
return await requestHttp<T>(
url,
method: DioMethod.put,
// 修改请求方法为 put
isShowErrorToast: isShowErrorToast,
params: params,
formData: formData,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
}
Future delete<T>({
required String url,
Map<String, dynamic>? params,
bool isAddTokenInHeader = true,
bool isShowErrorToast = true,
FormData? formData,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) async {
return await requestHttp<T>(
url,
method: DioMethod.delete,
// 修改请求方法为 delete
isShowErrorToast: isShowErrorToast,
params: params,
formData: formData,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
}
/// Dio request 方法
Future requestHttp<T>(String url,
{DioMethod method = DioMethod.get,
Map<String, dynamic>? params,
bool isShowErrorToast = true,
bool isAddTokenInHeader = true,
FormData? formData,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress}) async {
const methodValues = {
DioMethod.get: 'get',
DioMethod.post: 'post',
DioMethod.delete: 'delete',
DioMethod.put: 'put'
};
var connectivityResult = await Connectivity().checkConnectivity();
if (connectivityResult == ConnectivityResult.none) {
// showOKToast("网络不流畅哦,请检查网络情况");
return {'code': 404, 'msg': '网络不流畅哦,请检查网络情况'};
}
try {
Response response;
var options = Options();
bool isAgreemement = await getAgreemement();
if (isAgreemement) {
options = Options(method: methodValues[method], headers: {
"Authorization": await getAuthorization(),
'VersionName': await getVersionName(),
'VersionCode': await getVersionCode(),
'Platform': Platform.isIOS ? '1' : '0',
'OsVersion': await getDeviceId(),
'Imei': await getImei(),
'Brand': await getBrand(),
});
} else {
options = Options(method: methodValues[method], headers: {
"Authorization": await getAuthorization(),
'VersionName': await getVersionName(),
'VersionCode': await getVersionCode(),
'Platform': Platform.isIOS ? '1' : '0',
});
}
print(">>>>>$params");
// 如果没有提供 CancelToken则使用全局的
cancelToken ??= _globalCancelToken;
String key = _generateRequestKey(url, params ?? {});
// 如果该请求已经存在,则取消上一个
if (_cancelTokens.containsKey(key)) {
_cancelTokens[key]!.cancel("重复请求,取消上一个");
}
// 创建新的 CancelToken
cancelToken = CancelToken();
_cancelTokens[key] = cancelToken;
/// 不同请求方法,不同的请求参数,按实际项目需求分.
/// 这里 get 是 queryParameters其它用 data. FormData 也是 data
/// 注意: 只有 post 方法支持发送 FormData.
switch (method) {
case DioMethod.get:
response = await _dio!
.request(url, queryParameters: params, options: options,cancelToken: cancelToken);
break;
case DioMethod.post:
response = await _dio!.post(url,
data: params, cancelToken: cancelToken, options: options);
break;
case DioMethod.postBody:
response = await _dio!.post(
url, data: params, cancelToken: cancelToken, options: options);
break;
case DioMethod.put:
response =
await _dio!.put(url, data: json.encode(params), options: options,cancelToken: cancelToken);
break;
case DioMethod.delete:
response = await _dio!
.delete(url, queryParameters: params, options: options,cancelToken: cancelToken);
break;
default:
// 如果有formData参数说明是传文件忽略params的参数
if (formData != null) {
response = await _dio!.post(url,
data: formData,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress);
} else {
response = await _dio!.request(url,
data: params, cancelToken: cancelToken, options: options);
}
}
// 请求完成后移除 token
_cancelTokens.remove(key);
// json转model
String jsonStr = json.encode(response.data);
Map<String, dynamic> responseMap = json.decode(jsonStr);
if (responseMap["code"] == 5003 || responseMap["code"] == 30003) {
showOKToast(responseMap['msg']);
pushLoginPage();
} else {
switch (responseMap["code"]) {
case 4001:
case 4002:
case 4003:
case 4004:
case 4100:
case 4101:
case 4102:
case 5002:
case 5001:
case 5000:
pushLoginPage();
break;
}
if (responseMap["code"] != 200 && responseMap["code"] != 10000 && !url.contains(Api.checkWxNumState) && responseMap["code"] != 21201 && responseMap['code'] != 32104 && responseMap['code'] != 4005) {
showOKToast(responseMap['msg']);
}
}
return responseMap;
} on DioException catch (e) {
if (e.type == DioExceptionType.cancel) return {'code': 500, 'msg': ''};
print("请求失败: ${e.message}");
print("请求URL: ${e.requestOptions.uri}");
print("请求Headers: ${e.requestOptions.headers}");
print("请求数据: ${e.requestOptions.data}");
print("错误类型: ${e.type}"); // 检查 DioErrorType
// 获取连接池信息(仅适用于 Dart 原生 HttpClient
// final adapter = _dio!.httpClientAdapter as IOHttpClientAdapter;
// HttpClient client = adapter.createHttpClient!();
// print("最大连接数: ${client.maxConnectionsPerHost}");
SharedPreferencesHelper sp = await SharedPreferencesHelper.getInstance();
if (sp.getString(SharedPreferencesHelper.LOGINPHONE) == '18800000100') {
return {'code': 500, 'msg': ''};
}
// 其他类型的错误(如协议错误、解析错误等)
var info = await FlutterBugly.uploadException(
message: url + ':' + e.error.toString(),
detail: e.message ?? '',
);
print(info);
return {'code': 500, 'msg': '当前使用用户过多,请稍后再试'};
} catch (e) {
SharedPreferencesHelper sp = await SharedPreferencesHelper.getInstance();
if (sp.getString(SharedPreferencesHelper.LOGINPHONE) == '18800000100') {
return {'code': 500, 'msg': ''};
}
// 其他类型的错误(如协议错误、解析错误等)
var info = await FlutterBugly.uploadException(
message: url + e.toString(),
detail: '',
);
// 其他一些意外的报错
return {'code': 500, 'msg': '当前使用用户过多,请稍后再试'};
}
}
//
// // 错误判断
// void onErrorInterceptor(DioError err) {
// // 异常分类
// switch (err.type) {
// // 4xx 5xx response
// case DioErrorType.response:
// err.requestOptions.extra["errorMsg"] = err.response?.data ?? "连接异常";
// break;
// case DioErrorType.connectTimeout:
// err.requestOptions.extra["errorMsg"] = "连接超时";
// break;
// case DioErrorType.sendTimeout:
// err.requestOptions.extra["errorMsg"] = "发送超时";
// break;
// case DioErrorType.receiveTimeout:
// err.requestOptions.extra["errorMsg"] = "接收超时";
// break;
// case DioErrorType.cancel:
// err.requestOptions.extra["errorMsg"] =
// err.message.isNotEmpty ? err.message : "取消连接";
// break;
// case DioErrorType.other:
// default:
// err.requestOptions.extra["errorMsg"] = "连接异常";
// break;
// }
// }
}
class BaseResponse<T> {
int code;
String msg;
dynamic? data;
BaseResponse({required this.code, required this.msg, required this.data});
isSuccess() {
return code == 200;
}
factory BaseResponse.fromJson(
Map<String, dynamic> json, T Function(dynamic) fromJsonData) {
dynamic dataJson = json['data'];
T? data;
if (dataJson != null) {
if (dataJson is String) {
// 处理 dataJson 是 String 类型的情况
// 例如,可以直接将其赋值给 data 变量
data = null; // 根据你的需求修改赋值语句
} else if (dataJson is Map<String, dynamic>) {
// 处理 dataJson 是 Map<String, dynamic> 类型的情况
if (fromJsonData != null) {
data = fromJsonData(dataJson);
} else {
throw Exception('未提供 fromJsonData 函数来解析数据。');
}
} else if (dataJson is List) {
data = fromJsonData(dataJson);
} else {
// throw Exception('无效的数据格式。期望是 String 或 Map<String, dynamic> 类型。');
}
}
return BaseResponse(
code: json['code'],
msg: json['msg'],
data: data,
);
}
}
class ConnectivityInterceptor extends Interceptor {
@override
Future<void> onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
if (!await isInternetAvailable()) {
showOKToast("网络不流畅哦,请检查网络情况");
}
return handler.next(options);
}
Future<bool> isInternetAvailable() async {
var connectivityResult = await Connectivity().checkConnectivity();
return connectivityResult != ConnectivityResult.none;
}
}
class QnTokenData {
final String token;
final String cdnPrefix;
QnTokenData({required this.token, required this.cdnPrefix});
factory QnTokenData.fromJson(Map<String, dynamic> json) {
return QnTokenData(
token: json['token'],
cdnPrefix: json['cdn_prefix'],
);
}
}
class TimingInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
options.extra["startTime"] = DateTime.now(); // 记录开始时间
super.onRequest(options, handler);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
DateTime startTime = response.requestOptions.extra["startTime"];
DateTime endTime = DateTime.now();
Duration duration = endTime.difference(startTime);
print("请求 URL: ${response.requestOptions.uri}");
print("请求耗时: ${duration.inMilliseconds} ms");
super.onResponse(response, handler);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
DateTime startTime = err.requestOptions.extra["startTime"];
DateTime endTime = DateTime.now();
Duration duration = endTime.difference(startTime);
print("请求失败 URL: ${err.requestOptions.uri}");
print("请求失败耗时: ${duration.inMilliseconds} ms");
super.onError(err, handler);
}
}