123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- 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 {
- static final DioClient _instance = DioClient._internal();
- late Dio _dio;
- factory DioClient() => _instance;
- final Logger _logger = Logger();
- DioClient._internal() {
- _dio = Dio(BaseOptions(
- baseUrl: 'https://api.androidrj01.top', // 你的API地址
- connectTimeout: 5000,
- receiveTimeout: 3000,
- ));
- // // 仅在调试模式下添加日志拦截器
- assert(() {
- _dio.interceptors.add(LogInterceptor(
- request: true,
- requestBody: true,
- responseBody: true,
- error: true,
- logPrint: _logger.d, // 使用 Logger 插件打印日志
- ));
- return true;
- }());
- // 修改拦截器添加顺序
- // 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("data:==== ${responseData} ");
- await SharedPreferencesUtil().setString("last_successful_url", response.requestOptions.baseUrl);
- if (responseData['ret'] == 1) {
- handler.next(
- Response<dynamic>(
- data: responseData['data'],
- headers: response.headers,
- requestOptions: response.requestOptions,
- statusCode: response.statusCode,
- ),
- );
- } else {
- handler.reject(
- DioError(
- requestOptions: response.requestOptions,
- error: AppException(message: responseData['msg'] ?? 'Unknown Error',statusCode: responseData['ret'] ?? -1),
- ),
- );
- }
- },
- ));
- // 3. 最后添加自定义拦截器(处理备用地址)
- final customInterceptor = CustomInterceptors(_dio);
- _dio.interceptors.add(customInterceptor);
- }
- void updateBaseUrl(String newBaseUrl) {
- _dio.options.baseUrl = newBaseUrl;
- }
- Dio get dio => _dio;
- }
- class TokenInterceptor extends Interceptor {
- 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;
- // 添加响应处理拦截器
- clonedDio.interceptors.add(InterceptorsWrapper(
- onResponse: (Response<dynamic> response, ResponseInterceptorHandler handler) async {
- final responseData = response.data as Map<String, dynamic>;
- if (responseData['ret'] == 1) {
- handler.next(
- Response<dynamic>(
- data: responseData['data'],
- headers: response.headers,
- requestOptions: response.requestOptions,
- statusCode: response.statusCode,
- ),
- );
- } else {
- handler.reject(
- DioError(
- requestOptions: response.requestOptions,
- error: AppException(
- message: responseData['msg'] ?? 'Unknown Error',
- statusCode: responseData['ret'] ?? -1
- ),
- ),
- );
- }
- },
- ));
-
- 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 SharedPreferencesUtil().getString("token");
- if (token != null) {
- options.headers["Authorization"] = "Bearer $token";
- }
- return super.onRequest(options, handler);
- }
- }
- class CustomInterceptors extends Interceptor {
- int _retryCount = 0;
- final List<String> _backupUrls = ['http://107.148.49.147:8383','https://api.androidrj02.top','https://api.androidrj88.com','https://api.androidrj03.top'];
- final Dio _dio; // 添加 Dio 作为参数
- CustomInterceptors(this._dio);
- Future<bool> isConnected() async {
- var connectivityResult = await (Connectivity().checkConnectivity());
- return connectivityResult != ConnectivityResult.none;
- }
- void resetRetryCount() {
- _retryCount = 0;
- }
- @override
- Future<void> onError(DioError err, ErrorInterceptorHandler handler) async {
- //LogHelper().d("onError === $_retryCount");
- // 检查网络连接状态
- bool isConnectNetWork = await isConnected();
- if (!isConnectNetWork) {
- // 无网络连接,设置友好的错误消息
- 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}");
- if (_retryCount < _backupUrls.length) {
- // 有网络连接但请求失败,尝试使用备用地址
- err.requestOptions.baseUrl = _backupUrls[_retryCount];
- LogHelper().d("切换地址:==== ${err.requestOptions.baseUrl}");
- _retryCount++;
- try {
- final Response response = await _dio.fetch(err.requestOptions);
- return handler.resolve(response);
- } catch (e) {
- if (e is DioError) {
- return onError(e, handler); // Recursive call
- } else {
- // Handle other exceptions if needed or rethrow them
- rethrow;
- }
- }
- }
- }
- // 其他错误,统一处理
- AppException appException = AppException.create(err);
- // debugPrint('DioError===: ${appException.toString()}');
- err.error = appException;
- return handler.next(err);
- }
- }
- class AppException implements Exception {
- final int? statusCode; // 添加了 statusCode
- final String message;
- AppException({required this.message,this.statusCode});
- static AppException create(DioError err) {
- switch (err.type) {
- case DioErrorType.cancel:
- return AppException(message: '请求被取消');
- case DioErrorType.connectTimeout:
- return AppException(message: '连接超时');
- case DioErrorType.sendTimeout:
- return AppException(message: '请求超时');
- case DioErrorType.receiveTimeout:
- return AppException(message: '响应超时');
- case DioErrorType.response:
- return AppException(
- message: '服务器返回异常,状态码:${err.response?.statusCode}',
- statusCode: err.response?.statusCode, // 设置statusCode,即使在default中也可以选择设置
- );
- case DioErrorType.other:
- return AppException(
- message: '网络连接失败',
- statusCode: err.response?.statusCode, // 设置statusCode,即使在default中也可以选择设置
- );
- default:
- return AppException(
- message: err.message,
- statusCode: err.response?.statusCode, // 设置statusCode,即使在default中也可以选择设置
- );
- }
- }
- @override
- String toString() {
- return message;
- }
- }
- extension DioClientExtension on DioClient {
- Future<dynamic> get(String path, {Map<String, dynamic>? queryParameters}) async {
- final response = await _dio.get(path, queryParameters: queryParameters);
- return _handleResponse(response);
- }
- Future<dynamic> post(String path, {Map<String, dynamic>? data}) async {
- final response = await _dio.post(path, data: data);
- return _handleResponse(response);
- }
- Future<dynamic> put(String path, {Map<String, dynamic>? data}) async {
- final response = await _dio.put(path, data: data);
- return _handleResponse(response);
- }
- Future<void> download(String urlPath, String savePath) async {
- await _dio.download(urlPath, savePath);
- }
- dynamic _handleResponse(Response response) {
- if (response.data is List) {
- return response.data as List<dynamic>;
- } else if (response.data is Map) {
- return response.data as Map<String, dynamic>;
- } else {
- throw Exception('Unsupported data type');
- }
- }
- }
|