239 lines
7.3 KiB
Dart
239 lines
7.3 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'package:dio/dio.dart';
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
import 'package:aslan/chatvibe_core/utilities/at_loading_manager.dart';
|
|
import 'package:aslan/chatvibe_data/sources/local/user_manager.dart';
|
|
import 'package:aslan/chatvibe_core/constants/at_global_config.dart';
|
|
import 'package:aslan/chatvibe_core/utilities/at_deviceId_utils.dart';
|
|
import 'package:aslan/chatvibe_data/sources/remote/net/api.dart';
|
|
import 'package:aslan/chatvibe_data/sources/remote/net/at_logger.dart';
|
|
|
|
NetworkClient get http => _httpInstance;
|
|
bool inLoginPage = false;
|
|
|
|
late final NetworkClient _httpInstance = NetworkClient();
|
|
|
|
class NetworkClient extends BaseNetworkClient {
|
|
@override
|
|
void init() {
|
|
// 设置基础URL
|
|
dio.options.baseUrl = ATGlobalConfig.apiHost;
|
|
|
|
// 添加拦截器
|
|
dio.interceptors
|
|
..add(ApiInterceptor())
|
|
..add(ATDioLogger())
|
|
..add(TimeOutInterceptor());
|
|
|
|
setupCacheInterceptor(dio); // 传入 dio 实例
|
|
}
|
|
|
|
Future<void> setupCacheInterceptor(Dio dio) async {
|
|
// 缓存配置保持不变...
|
|
}
|
|
}
|
|
|
|
class ApiInterceptor extends Interceptor {
|
|
@override
|
|
void onRequest(
|
|
RequestOptions options,
|
|
RequestInterceptorHandler handler,
|
|
) async {
|
|
// 确保使用最新的 baseUrl
|
|
options.baseUrl = ATGlobalConfig.apiHost;
|
|
String token = AccountStorage().getToken();
|
|
var imei = await ATDeviceIdUtils.collectDeviceId();
|
|
Map<String, dynamic> headMap = {};
|
|
headMap["req-lang"] = ATGlobalConfig.lang;
|
|
headMap["X-Forwarded-Proto"] = "http";
|
|
headMap["User-Agent"] = "Dart/3.7.2(dart:io)";
|
|
headMap["req-imei"] = imei;
|
|
|
|
headMap["Host"] = options.uri.host;
|
|
headMap["req-app-intel"] =
|
|
"version=${ATGlobalConfig.version};build=${ATGlobalConfig.build};model=${ATGlobalConfig.model};sysVersion=${ATGlobalConfig.sysVersion};channel=${ATGlobalConfig.channel}";
|
|
headMap["req-client"] = Platform.isAndroid ? 'Android' : 'iOS';
|
|
headMap["req-sys-origin"] =
|
|
"origin=${ATGlobalConfig.origin};originChild=${ATGlobalConfig.originChild}";
|
|
if (token.isNotEmpty) {
|
|
headMap["Authorization"] = "Bearer $token";
|
|
}
|
|
|
|
///加密
|
|
headMap["Req-Atyou"] = true;
|
|
options.headers.addAll(headMap);
|
|
|
|
handler.next(options);
|
|
}
|
|
|
|
@override
|
|
void onResponse(Response response, ResponseInterceptorHandler handler) async {
|
|
if (response.statusCode == 200) {
|
|
final ResponseData respData = ResponseData.fromJson(response.data);
|
|
if (respData.success) {
|
|
handler.next(response);
|
|
} else {
|
|
// if (respData.code == 210||respData.code == 212) { // 登录失效
|
|
// if (!inLoginPage) {
|
|
// Fluttertoast.showToast(msg: respData.msg ?? "需要重新登录");
|
|
// SpUtils.sp?.clear();
|
|
// navigatorKey.currentState?.pushAndRemoveUntil(
|
|
// MaterialPageRoute(builder: (_) {
|
|
// Provider.of<RtmProvider>(_, listen: false).destoryRtm();
|
|
// Provider.of<ProfileManager>(_, listen: false).logout();
|
|
// Provider.of<RtcProvider>(_, listen: false).engine?.leaveChannel();
|
|
// return const LoginPage();
|
|
// }),
|
|
// (route) => false // 清除所有路由
|
|
// );
|
|
//
|
|
// inLoginPage = true;
|
|
// }
|
|
// } else {
|
|
// Fluttertoast.showToast(msg: respData.msg ?? "操作失败");
|
|
// }
|
|
|
|
// 使用 DioException 传递错误
|
|
handler.reject(
|
|
DioException(
|
|
requestOptions: response.requestOptions,
|
|
error: NotSuccessException.fromRespData(respData),
|
|
response: response,
|
|
message: respData.errorMsg,
|
|
),
|
|
);
|
|
}
|
|
} else {
|
|
Fluttertoast.showToast(msg: 'Network error, please check the status.');
|
|
handler.reject(
|
|
DioException(
|
|
requestOptions: response.requestOptions,
|
|
error: 'HTTP ${response.statusCode} 错误',
|
|
response: response,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 响应数据结构
|
|
class ResponseData<T> {
|
|
final bool status;
|
|
final int? errorCode;
|
|
final String? errorCodeName;
|
|
final String? errorMsg;
|
|
final T? body;
|
|
final num? time;
|
|
|
|
bool get success => status;
|
|
|
|
ResponseData({
|
|
required this.status,
|
|
this.errorCode,
|
|
this.errorCodeName,
|
|
this.errorMsg,
|
|
this.body,
|
|
this.time,
|
|
});
|
|
|
|
// 特殊处理基础类型
|
|
static T? _parseData<T>(dynamic data, T Function(dynamic) fromJsonT) {
|
|
if (data == null) return null;
|
|
|
|
// 布尔值特殊处理
|
|
if (T == bool) {
|
|
if (data is bool) return data as T;
|
|
if (data is int) return (data != 0) as T;
|
|
if (data is String) return (data.toLowerCase() == 'true') as T;
|
|
}
|
|
|
|
// 其他基础类型
|
|
if (T == int || T == double || T == String || T == bool) {
|
|
return data as T;
|
|
}
|
|
|
|
// 复杂类型使用转换函数
|
|
return fromJsonT(data);
|
|
}
|
|
|
|
factory ResponseData.fromJson(
|
|
Map<String, dynamic> json, {
|
|
T Function(dynamic)? fromJsonT,
|
|
}) {
|
|
if (fromJsonT == null) {
|
|
return ResponseData(
|
|
status: json['status'],
|
|
errorCode: json['errorCode'],
|
|
errorCodeName: json['errorCodeName'],
|
|
errorMsg: json['errorMsg'],
|
|
body: json['body'],
|
|
time: json['time'],
|
|
);
|
|
} else {
|
|
return ResponseData(
|
|
status: json['status'],
|
|
errorCode: json['errorCode'],
|
|
errorCodeName: json['errorCodeName'],
|
|
errorMsg: json['errorMsg'],
|
|
body: json['body'] != null ? _parseData(json['body'], fromJsonT) : null,
|
|
time: json['time'],
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 超时拦截器
|
|
class TimeOutInterceptor extends Interceptor {
|
|
static const int globalTimeoutSeconds = 15;
|
|
final Map<String, Timer> _timers = {};
|
|
|
|
@override
|
|
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
|
|
|
if (err.type == DioExceptionType.connectionTimeout ||
|
|
err.type == DioExceptionType.receiveTimeout ||
|
|
err.type == DioExceptionType.sendTimeout) {
|
|
print('超时错误: URL => ${err.requestOptions.baseUrl}');
|
|
print('当前使用的Host: ${ATGlobalConfig.apiHost}');
|
|
}
|
|
|
|
// 请求出错,取消计时器
|
|
final String requestKey = err.requestOptions.uri.toString();
|
|
_timers[requestKey]?.cancel();
|
|
_timers.remove(requestKey);
|
|
|
|
handler.next(err);
|
|
}
|
|
|
|
@override
|
|
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
|
// 为每个请求创建超时计时器
|
|
final cancelToken = options.cancelToken ?? CancelToken();
|
|
final String requestKey = options.uri.toString();
|
|
|
|
// 设置15秒后自动取消
|
|
_timers[requestKey] = Timer(
|
|
const Duration(seconds: globalTimeoutSeconds),
|
|
() {
|
|
ATLoadingManager.veilRoutine();
|
|
if (!cancelToken.isCancelled) {
|
|
cancelToken.cancel('请求超时(${globalTimeoutSeconds}秒)');
|
|
// 从映射中移除计时器
|
|
_timers.remove(requestKey);
|
|
}
|
|
}
|
|
);
|
|
handler.next(options);
|
|
}
|
|
|
|
@override
|
|
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
|
// 请求成功完成,取消计时器
|
|
final String requestKey = response.requestOptions.uri.toString();
|
|
_timers[requestKey]?.cancel();
|
|
_timers.remove(requestKey);
|
|
handler.next(response);
|
|
}
|
|
}
|