|
@@ -2,12 +2,16 @@ import 'dart:convert';
|
|
|
import 'dart:io';
|
|
|
|
|
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
|
|
+import 'package:dart_json_mapper/dart_json_mapper.dart';
|
|
|
import 'package:dio/dio.dart';
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
import 'package:logger/logger.dart';
|
|
|
import 'package:naiyouwl/app/common/LogHelper.dart';
|
|
|
+import 'package:synchronized/synchronized.dart' as synchronized;
|
|
|
+
|
|
|
|
|
|
import '../common/SharedPreferencesUtil.dart';
|
|
|
+import '../data/model/LocalUser.dart';
|
|
|
//import 'custom_interceptors.dart';
|
|
|
|
|
|
class DioClient {
|
|
@@ -18,7 +22,6 @@ class DioClient {
|
|
|
final Logger _logger = Logger();
|
|
|
|
|
|
DioClient._internal() {
|
|
|
-
|
|
|
_dio = Dio(BaseOptions(
|
|
|
baseUrl: 'https://api.androidrj01.top', // 你的API地址
|
|
|
connectTimeout: 5000,
|
|
@@ -36,21 +39,18 @@ class DioClient {
|
|
|
));
|
|
|
return true;
|
|
|
}());
|
|
|
- final customInterceptor = CustomInterceptors(_dio);
|
|
|
- //token
|
|
|
- _dio.interceptors.add(TokenInterceptor());
|
|
|
- // 添加拦截器
|
|
|
- _dio.interceptors.add(customInterceptor);
|
|
|
|
|
|
- // 添加响应拦截器
|
|
|
+ // 修改拦截器添加顺序
|
|
|
+ // 1. 先添加 Token 拦截器
|
|
|
+ _dio.interceptors.add(TokenInterceptor(_dio));
|
|
|
+
|
|
|
+ // 2. 再添加响应处理拦截器
|
|
|
_dio.interceptors.add(InterceptorsWrapper(
|
|
|
onResponse: (Response<dynamic> response, ResponseInterceptorHandler handler) async {
|
|
|
final responseData = response.data as Map<String, dynamic>;
|
|
|
- //LogHelper().d("当前地址:==== ${response.requestOptions.baseUrl} ");
|
|
|
- LogHelper().d("data:==== ${responseData} ");
|
|
|
+ //LogHelper().d("data:==== ${responseData} ");
|
|
|
await SharedPreferencesUtil().setString("last_successful_url", response.requestOptions.baseUrl);
|
|
|
if (responseData['ret'] == 1) {
|
|
|
- customInterceptor.resetRetryCount(); // 当请求成功时重置重试计数器
|
|
|
handler.next(
|
|
|
Response<dynamic>(
|
|
|
data: responseData['data'],
|
|
@@ -70,7 +70,9 @@ class DioClient {
|
|
|
},
|
|
|
));
|
|
|
|
|
|
-
|
|
|
+ // 3. 最后添加自定义拦截器(处理备用地址)
|
|
|
+ final customInterceptor = CustomInterceptors(_dio);
|
|
|
+ _dio.interceptors.add(customInterceptor);
|
|
|
}
|
|
|
void updateBaseUrl(String newBaseUrl) {
|
|
|
_dio.options.baseUrl = newBaseUrl;
|
|
@@ -79,17 +81,100 @@ class DioClient {
|
|
|
}
|
|
|
|
|
|
class TokenInterceptor extends Interceptor {
|
|
|
- // 这里假设您有一个方法来从安全存储中获取Token
|
|
|
- Future<String?> getToken() async {
|
|
|
- // 从您存储Token的地方获取Token,例如从SharedPreferences或SecureStorage
|
|
|
- String? token = await SharedPreferencesUtil().getString("token");
|
|
|
- return token;
|
|
|
- //return "Your_Token";
|
|
|
+ final _lock = synchronized.Lock();
|
|
|
+ bool _isRefreshing = false;
|
|
|
+ final Dio _dio; // 添加 Dio 实例引用
|
|
|
+
|
|
|
+ TokenInterceptor(this._dio); // 添加构造函数
|
|
|
+
|
|
|
+ Future<String?> _refreshToken() async {
|
|
|
+ try {
|
|
|
+ // 从本地获取用户信息
|
|
|
+ final localUserStr = await SharedPreferencesUtil().getString("localUser");
|
|
|
+ if (localUserStr == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析本地用户信息
|
|
|
+ final localUser = JsonMapper.deserialize<LocalUser>(localUserStr);
|
|
|
+ if (localUser == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用保存的邮箱和密码重新登录
|
|
|
+ final response = await _dio.post(
|
|
|
+ '/api/client/v3/login',
|
|
|
+ data: {
|
|
|
+ 'email': localUser.email?.trim(),
|
|
|
+ 'password': localUser.password,
|
|
|
+ },
|
|
|
+ );
|
|
|
+
|
|
|
+ // 直接使用 response.data,因为响应拦截器已经处理过了
|
|
|
+ final accessToken = response.data['access_token'];
|
|
|
+ if (accessToken != null) {
|
|
|
+ // 更新本地用户信息
|
|
|
+ await SharedPreferencesUtil().setString(
|
|
|
+ "localUser",
|
|
|
+ JsonMapper.serialize(
|
|
|
+ LocalUser(
|
|
|
+ email: localUser.email,
|
|
|
+ password: localUser.password,
|
|
|
+ accessToken: accessToken,
|
|
|
+ )
|
|
|
+ )
|
|
|
+ );
|
|
|
+ return accessToken;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ } catch (e) {
|
|
|
+ LogHelper().e("刷新token失败: $e");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Future<void> onError(DioError err, ErrorInterceptorHandler handler) async {
|
|
|
+ // 只处理401错误,其他错误传递给下一个拦截器
|
|
|
+ if (err.response?.statusCode == 401 && !err.requestOptions.path.contains('/user/login')) {
|
|
|
+ LogHelper().d("Token过期,尝试刷新...");
|
|
|
+
|
|
|
+ RequestOptions options = err.requestOptions;
|
|
|
+
|
|
|
+ return await _lock.synchronized(() async {
|
|
|
+ try {
|
|
|
+ if (!_isRefreshing) {
|
|
|
+ _isRefreshing = true;
|
|
|
+ final newToken = await _refreshToken();
|
|
|
+ if (newToken != null) {
|
|
|
+ // 更新token
|
|
|
+ await SharedPreferencesUtil().setString("token", newToken);
|
|
|
+ // 使用新token重试原请求
|
|
|
+ options.headers["Authorization"] = "Bearer $newToken";
|
|
|
+
|
|
|
+ // 创建新的dio实例来避免拦截器循环
|
|
|
+ final clonedDio = Dio()..options = _dio.options;
|
|
|
+ final response = await clonedDio.fetch(options);
|
|
|
+
|
|
|
+ _isRefreshing = false;
|
|
|
+ return handler.resolve(response);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ LogHelper().e("刷新token失败: $e");
|
|
|
+ }
|
|
|
+ _isRefreshing = false;
|
|
|
+ err.error = AppException(message: "登录已过期,请重新登录");
|
|
|
+ return handler.next(err);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return handler.next(err);
|
|
|
}
|
|
|
|
|
|
+ // 添加请求拦截器来设置token
|
|
|
@override
|
|
|
Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
|
|
|
- final token = await getToken();
|
|
|
+ final token = await SharedPreferencesUtil().getString("token");
|
|
|
if (token != null) {
|
|
|
options.headers["Authorization"] = "Bearer $token";
|
|
|
}
|
|
@@ -111,7 +196,7 @@ class CustomInterceptors extends Interceptor {
|
|
|
}
|
|
|
@override
|
|
|
Future<void> onError(DioError err, ErrorInterceptorHandler handler) async {
|
|
|
- LogHelper().d("onError === $_retryCount");
|
|
|
+ //LogHelper().d("onError === $_retryCount");
|
|
|
// 检查网络连接状态
|
|
|
bool isConnectNetWork = await isConnected();
|
|
|
|
|
@@ -120,7 +205,7 @@ class CustomInterceptors extends Interceptor {
|
|
|
err.error = AppException(message: "当前网络不可用,请检查您的网络");
|
|
|
return handler.next(err);
|
|
|
} else if (err.error is SocketException || err.type == DioErrorType.other || err.type == DioErrorType.connectTimeout || err.type == DioErrorType.sendTimeout || err.type == DioErrorType.receiveTimeout) {
|
|
|
- LogHelper().d("错误类型:==== ${err.type}");
|
|
|
+ //LogHelper().d("错误类型:==== ${err.type}");
|
|
|
if (_retryCount < _backupUrls.length) {
|
|
|
// 有网络连接但请求失败,尝试使用备用地址
|
|
|
err.requestOptions.baseUrl = _backupUrls[_retryCount];
|
|
@@ -147,7 +232,7 @@ class CustomInterceptors extends Interceptor {
|
|
|
|
|
|
// 其他错误,统一处理
|
|
|
AppException appException = AppException.create(err);
|
|
|
- debugPrint('DioError===: ${appException.toString()}');
|
|
|
+ // debugPrint('DioError===: ${appException.toString()}');
|
|
|
err.error = appException;
|
|
|
return handler.next(err);
|
|
|
}
|