2026-07-01 18:25:58 +08:00

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);
}
}