import 'package:dio/dio.dart'; import 'local_database.dart'; /// API 基础地址 const String baseUrl = 'http://10.4.185.103:5000'; /// Dio HTTP 客户端封装——带 token 注入、401 自动刷新 class ApiClient { final Dio _dio; final LocalDatabase _db; ApiClient({required LocalDatabase db}) : _db = db, _dio = Dio(BaseOptions( baseUrl: baseUrl, connectTimeout: const Duration(seconds: 15), receiveTimeout: const Duration(seconds: 60), headers: {'Content-Type': 'application/json'}, )) { _dio.interceptors.add(_AuthInterceptor(this)); _dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: false)); } Dio get dio => _dio; Future get accessToken => _db.read('access_token'); Future get refreshToken => _db.read('refresh_token'); Future saveTokens(String access, String refresh) async { await _db.write('access_token', access); await _db.write('refresh_token', refresh); } Future clearTokens() async { await _db.deleteAll(); } /// 带 token 的 GET 请求 Future get(String path, {Map? queryParameters}) async { return _dio.get(path, queryParameters: queryParameters); } /// 带 token 的 POST 请求 Future post(String path, {dynamic data}) async { return _dio.post(path, data: data); } /// 带 token 的 PUT 请求 Future put(String path, {dynamic data}) async { return _dio.put(path, data: data); } /// 带 token 的 DELETE 请求 Future delete(String path) async { return _dio.delete(path); } } /// 认证拦截器:自动注入 token + 401 刷新 class _AuthInterceptor extends Interceptor { final ApiClient _client; _AuthInterceptor(this._client); @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { if (!options.path.contains('/auth/')) { final token = await _client.accessToken; if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } } handler.next(options); } @override void onError(DioException err, ErrorInterceptorHandler handler) async { if (err.response?.statusCode == 401) { final refresh = await _client.refreshToken; if (refresh != null) { try { final response = await Dio(BaseOptions(baseUrl: baseUrl)) .post('/api/auth/refresh', data: {'refreshToken': refresh}); final data = response.data['data']; if (data != null) { await _client.saveTokens(data['accessToken'], data['refreshToken']); final opts = err.requestOptions; final token = data['accessToken']; opts.headers['Authorization'] = 'Bearer $token'; final retryResponse = await Dio(BaseOptions(baseUrl: baseUrl)).fetch(opts); return handler.resolve(retryResponse); } } catch (_) {} } await _client.clearTokens(); } handler.next(err); } }