import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart' as http; class ApiException implements Exception { const ApiException(this.code, this.message); final int code; final String message; @override String toString() => message; } class ApiClient { ApiClient( this._tokenProvider, { http.Client? client, String? baseUrl, }) : _client = client ?? http.Client(), baseUrl = baseUrl ?? const String.fromEnvironment( 'API_BASE_URL', defaultValue: 'http://10.0.2.2:12426', ); final String baseUrl; final String Function() _tokenProvider; final http.Client _client; Future get(String path, {bool authenticated = true}) => _send('GET', path, authenticated: authenticated); Future post( String path, { Map? body, bool authenticated = true, }) => _send('POST', path, body: body, authenticated: authenticated); Future put(String path, {Map? body}) => _send('PUT', path, body: body); Future delete(String path, {Map? body}) => _send('DELETE', path, body: body); Future _send( String method, String path, { Map? body, bool authenticated = true, }) async { final request = http.Request(method, Uri.parse('$baseUrl$path')); request.headers[HttpHeaders.acceptHeader] = 'application/json'; if (authenticated) { final token = _tokenProvider(); if (token.isNotEmpty) request.headers[HttpHeaders.authorizationHeader] = token; } if (body != null) { request.headers[HttpHeaders.contentTypeHeader] = 'application/json; charset=UTF-8'; request.body = jsonEncode(body); } final streamed = await _client.send(request); final response = await http.Response.fromStream(streamed); if (response.statusCode < 200 || response.statusCode >= 300) { throw ApiException(response.statusCode, '网络请求失败(${response.statusCode})'); } final decoded = jsonDecode(response.body); if (decoded is! Map) { throw const ApiException(500, '服务端响应格式错误'); } final code = (decoded['code'] as num?)?.toInt() ?? 500; if (code != 0) { throw ApiException(code, decoded['message'] as String? ?? '操作失败'); } return decoded['details']; } } Map jsonMap(Object? value) { if (value is Map) return value; if (value is Map) return value.map((key, item) => MapEntry(key.toString(), item)); throw const ApiException(500, '服务端数据格式错误'); } List> jsonList(Object? value) { if (value is! List) return const []; return value.map>(jsonMap).toList(growable: false); }