feat: 整体常用控件封装,常用工具类。首页数据对接

This commit is contained in:
chengsiwen committed 2026-06-27 14:15:25 +08:00
1 parent d90d023e85
commit fbcd33ea2b
50 files changed
+4558 -264

No files matched your search

+99
View File
@@ -0,0 +1,99 @@
final class ApiConst {
ApiConst._();
static const appName = 'skywood';
static const int pageSize = 12;
static const String apiLink = 'https://api-quickeltv.quickeltv.com/quickeltv';
static String webPrefix = "quickeltv.com";
static String webIndex = "https://$webPrefix";
static String webPrivacy = "https://$webPrefix/private";
static String webAgreement = "https://$webPrefix/user_policy";
static String webmemberAgreement = "https://$webPrefix/member_ship_agreement";
static String webHelpIndex = "https://campaign.$webPrefix/pages/leave/index";
static String webHelpList = "https://campaign.$webPrefix/pages/leave/list";
static String webHelpDetail =
"https://campaign.$webPrefix/pages/leave/detail";
static String iosDownloadUrl = "";
static String androidDownloadUrl = "";
}
class ApiString {
static const String login = "/customer/login";
// /customer/register
static const String register = "/customer/register";
// /customer/info
static const String userInfo = "/customer/info";
// /customer/signout
static const String signOut = "/customer/signout";
// /customer/logoff
static const String logOff = "/customer/logoff";
/// events
// eventCreateHistory POST /createHistory 上报播放历史
// eventUserEnterApp POST /customer/enterTheApp 用户进入 App
// eventUserOnline POST /customer/onLine 用户在线,可带 prevToken
// eventUserLeaveApp POST /customer/leaveApp 用户离开 App,可带 prevToken
// eventPlaySeconds POST /uploadHistorySeconds 上报单集播放秒数
// eventWatchTime POST /watchShortDuration 上报短剧观看时长
// eventReport POST /event/add 通用事件上报,目前枚举只有 force_update
static const String eventCreateHistory = "/createHistory";
static const String eventUserEnterApp = "/customer/enterTheApp";
static const String eventUserOnline = "/customer/onLine";
static const String eventUserLeaveApp = "/customer/leaveApp";
static const String eventPlaySeconds = "/uploadHistorySeconds";
static const String eventWatchTime = "/watchShortDuration";
static const String eventReport = "/event/add";
}
class ApiStringPlay {
// getRecommandsList GET /getRecommands 推荐列表,分页参数 current_page/page_size,可带 revolution
// getVideoDetail GET /getVideoDetails 获取剧详情,query: short_play_id, video_id
// getDetailsRecommand GET /getDetailsRecommand 详情页推荐列表;注意方法参数 page/pageSize/data 当前没有传入请求
static const String getRecommandsList = "/getRecommands";
static const String getVideoDetail = "/getVideoDetails";
static const String getDetailsRecommand = "/getDetailsRecommand";
// getHomeModules GET /home/all-modules 首页全部模块数据
// getRankingList POST /homeRanking 首页榜单,body 带 type,支持 most_trending/top_searched/new_releases
// getCategoriesList GET /getCategories 分类列表
// getCategoriesDetail GET /videoList 分类详情/剧列表,分页 + extraParams
// getWaterflowList POST /newShortPlay 首页瀑布流,分页 + extraParams
static const String getHomeModules = "/home/all-modules";
static const String getRankingList = "/homeRanking";
static const String getCategoriesList = "/getCategories";
static const String getCategoriesDetail = "/videoList";
static const String getWaterflowList = "/newShortPlay";
// getSearchHots GET /search/hots 搜索热门列表
// getSearch GET /search 关键词搜索,query: search
// collect POST /collect 收藏,body: short_play_id, video_id
// cancelCollect POST /cancelCollect 取消收藏,body: short_play_id
// getMyCollections GET /myCollections 我的收藏列表,分页
// getMyHistorys GET /myHistorys 播放历史列表,分页
// getNoticeNum POST /noticeNum 获取通知数量
// versionControl GET /customer/versionControl 版本检查
// getLanguges GET /languges 获取语言列表,接口名拼写就是 languges
// getTranslates GET /translates 获取语言包,query: lang_key,默认 en
static const String getSearchHots = "/search/hots";
static const String getSearch = "/search";
static const String collect = "/collect";
static const String cancelCollect = "/cancelCollect";
static const String getMyCollections = "/myCollections";
static const String getMyHistorys = "/myHistorys";
static const String getNoticeNum = "/noticeNum";
static const String versionControl = "/customer/versionControl";
static const String getLanguges = "/languges";
static const String getTranslates = "/translates";
}
+617 -142
View File
@@ -1,154 +1,629 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:skywood/global/request/api_const.dart';
import 'package:skywood/global/request/network_interceptor.dart';
import 'package:skywood/utils/tools/app_toast.dart';
import 'package:talker_dio_logger/talker_dio_logger.dart';
class AppRequest {
import '../../utils/tools/token_manage.dart';
static const Duration defaultTimeout = Duration(seconds: 15);
static final AppRequest instance = AppRequest();
final Dio dio;
/// HTTP 请求工具类(单例)
class AppService {
static AppService? _instance;
late Dio _dio;
AppRequest({
Dio? dio,
String baseUrl = '',
Duration connectTimeout = defaultTimeout,
Duration sendTimeout = defaultTimeout,
Duration receiveTimeout = defaultTimeout,
}) : dio =
dio ??
Dio(
BaseOptions(
baseUrl: baseUrl,
connectTimeout: connectTimeout,
sendTimeout: sendTimeout,
receiveTimeout: receiveTimeout,
responseType: ResponseType.json,
),
);
void setBaseUrl(String baseUrl) {
dio.options.baseUrl = baseUrl;
static AppService get instance {
_instance ??= AppService._internal();
return _instance!;
}
void setHeader(String key, Object? value) {
dio.options.headers[key] = value;
AppService._internal() {
_initDio();
}
void removeHeader(String key) {
dio.options.headers.remove(key);
Dio get dio => _dio;
/// 初始化 Dio 配置
void _initDio() {
_dio = Dio(BaseOptions(
baseUrl: ApiConst.apiLink,
connectTimeout: const Duration(seconds: 15),
receiveTimeout: const Duration(seconds: 15),
sendTimeout: const Duration(seconds: 15),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
responseType: ResponseType.json,
));
// 添加自定义拦截器
_dio.interceptors.add(NetworkInterceptor());
// 开发环境添加日志
// if (!kReleaseMode) {
// _dio.interceptors.add(TalkerDioLogger(
// settings: TalkerDioLoggerSettings(
// printRequestHeaders: true,
// printRequestData: true,
// printResponseData: true,
// printResponseHeaders: false,
// ),
// ));
// }
}
void setToken(String token, {String prefix = 'Bearer'}) {
final value = prefix.isEmpty ? token : '$prefix $token';
setHeader('Authorization', value);
}
void clearToken() {
removeHeader('Authorization');
}
Future<T?> get<T>(
String path, {
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onReceiveProgress,
}) {
return request<T>(
path,
method: 'GET',
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onReceiveProgress: onReceiveProgress,
);
}
Future<T?> post<T>(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) {
return request<T>(
path,
method: 'POST',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
}
Future<T?> put<T>(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) {
return request<T>(
path,
method: 'PUT',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
}
Future<T?> delete<T>(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) {
return request<T>(
path,
method: 'DELETE',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
}
Future<T?> request<T>(
String path, {
required String method,
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) async {
final response = await dio.request<T>(
path,
data: data,
queryParameters: queryParameters,
options: _withMethod(options, method),
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
return response.data;
}
Options _withMethod(Options? options, String method) {
return options == null
? Options(method: method)
: options.copyWith(method: method);
/// 更新配置
void updateBaseOptions(BaseOptions options) {
_dio.options = options;
}
}
/// ============ 请求方法封装 ============
class AppRequest {
static final Dio _dio = AppService.instance.dio;
/// GET 请求(带重试)
static Future<HttpResult<T>> get<T>(
String path, {
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
int? retry,
}) async {
try {
final response = await _executeRequest(
() => _dio.get(
path,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
),
);
return HttpResult<T>.fromResponse(response);
} on DioException catch (e) {
return _handleError<T>(e);
} catch (e) {
return HttpResult<T>.error(e.toString());
}
}
/// POST 请求(带重试)
static Future<HttpResult<T>> post<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
int? retry,
}) async {
try {
final response = await _executeRequest(
() => _dio.post(
path,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
),
);
return HttpResult<T>.fromResponse(response);
} on DioException catch (e) {
return _handleError<T>(e);
} catch (e) {
return HttpResult<T>.error(e.toString());
}
}
/// PUT 请求(带重试)
static Future<HttpResult<T>> put<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
int? retry,
}) async {
try {
final response = await _executeRequest(
() => _dio.put(
path,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
),
);
return HttpResult<T>.fromResponse(response);
} on DioException catch (e) {
return _handleError<T>(e);
} catch (e) {
return HttpResult<T>.error(e.toString());
}
}
/// DELETE 请求(带重试)
static Future<HttpResult<T>> delete<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
int? retry,
}) async {
try {
final response = await _executeRequest(
// retries: retry,
() => _dio.delete(
path,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
),
);
return HttpResult<T>.fromResponse(response);
} on DioException catch (e) {
return _handleError<T>(e);
} catch (e) {
return HttpResult<T>.error(e.toString());
}
}
/// 文件上传(带重试)
static Future<HttpResult<T>> fileUpload<T>(
String path, {
required File file,
String fieldName = 'file',
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
int? retry,
}) async {
try {
final formData = FormData.fromMap({
fieldName: await MultipartFile.fromFile(file.path),
});
final response = await _executeRequest(
() => _dio.post(
path,
data: formData,
queryParameters: queryParameters,
options: options ?? Options(contentType: 'multipart/form-data'),
cancelToken: cancelToken,
onSendProgress: onSendProgress,
),
);
return HttpResult<T>.fromResponse(response);
} on DioException catch (e) {
return _handleError<T>(e);
} catch (e) {
return HttpResult<T>.error(e.toString());
}
}
/// ============ 分页请求(原项目的 fetchPage) ============
static Future<List<T>> fetchPage<T>(
String url, {
int? page,
int? pageSize,
Map<String, dynamic>? extraParams,
required T Function(dynamic json) fromJson,
bool error = true,
String method = 'get',
int? retry,
}) async {
try {
final params = {
'current_page': page?.toString() ?? '1',
'page_size': pageSize?.toString() ?? '20',
...?extraParams,
};
Response response;
if (method.toLowerCase() == 'get') {
response = await _executeRequest(
() => _dio.get(url, queryParameters: params),
);
} else {
response = await _executeRequest(
() => _dio.post(url, data: params),
);
}
if (response.statusCode != 200) {
throw Exception("HTTP ${response.statusCode}");
}
final data = response.data;
if (data is Map<String, dynamic>) {
final base = BasePageResponse.fromJson(data);
if (!base.success) {
if (error) {
// 这里可以触发 Toast
AppToast.showError(base.msg);
}
throw Exception(base.msg);
}
final result = base.data;
if (result is List) {
return result.map<T>((e) => fromJson(e)).toList();
} else if (result is Map && result['list'] is List) {
return (result['list'] as List).map<T>((e) => fromJson(e)).toList();
} else {
throw Exception("data error");
}
} else {
throw Exception("format error");
}
} catch (e) {
// _handleError();
rethrow;
}
}
/// 直接执行请求,不重试
static Future<Response> _executeRequest(
Future<Response> Function() requestFn,
) async {
try {
final response = await requestFn();
// 如果状态码不是 200,直接抛出异常
if (response.statusCode != 200) {
throw DioException(
requestOptions: response.requestOptions,
response: response,
type: DioExceptionType.badResponse,
error: 'HTTP ${response.statusCode}',
);
}
return response;
} catch (e) {
// 直接重新抛出异常,不重试
rethrow;
}
}
// ============ 重试机制 ============
static Future<Response> _retryRequest(
Future<Response> Function() requestFn, {
int? retries,
}) async {
int attempt = 0;
final maxRetries = retries ?? 2;
while (attempt <= maxRetries) {
try {
final response = await requestFn();
if (response.statusCode == 200) {
return response;
}
// 401/402 特殊处理,由拦截器处理
if (response.statusCode == 401 || response.statusCode == 402) {
if (attempt == maxRetries) {
throw DioException(
requestOptions: response.requestOptions,
response: response,
type: DioExceptionType.badResponse,
error: 'errorCode:${response.statusCode}',
);
}
}
if (attempt == maxRetries) {
throw DioException(
requestOptions: response.requestOptions,
response: response,
type: DioExceptionType.badResponse,
error: 'HTTP ${response.statusCode}',
);
}
} catch (e) {
if (attempt == maxRetries) {
rethrow;
}
}
attempt++;
await Future.delayed(const Duration(milliseconds: 300));
}
throw Exception("retry failed (unreachable)");
}
// ============ 错误处理 ============
static HttpResult<T> _handleError<T>(DioException e) {
final Response? resp = e.response;
// 尝试从响应中解析错误信息
if (resp != null && resp.data != null) {
try {
final parsed = HttpResult<T>.fromResponse(resp);
if (!parsed.success) {
// 401/402 错误,触发重新登录
if (resp.statusCode == 401 || resp.statusCode == 402) {
TokenManager().update();
if (resp.statusCode == 402) {
AppToast.showError('账号已在其他设备登录,请重新登录');
}
}
return parsed;
}
} catch (_) {}
}
String message = _getErrorMessage(e);
return HttpResult<T>.error(message, httpStatus: resp?.statusCode);
}
static String _getErrorMessage(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
return '连接超时,请稍后重试';
case DioExceptionType.sendTimeout:
return '发送超时,请稍后重试';
case DioExceptionType.receiveTimeout:
return '接收超时,请稍后重试';
case DioExceptionType.badResponse:
final statusCode = e.response?.statusCode;
if (statusCode == 401) {
TokenManager().update();
return '未授权,请重新登录';
}
if (statusCode == 402) {
TokenManager().update();
return '账号已在其他设备登录,请重新登录';
}
return _handleStatusCode(statusCode);
case DioExceptionType.cancel:
return '请求已取消';
case DioExceptionType.connectionError:
return '网络连接失败,请检查网络设置';
default:
return '网络错误,请稍后重试';
}
}
static String _handleStatusCode(int? statusCode) {
switch (statusCode) {
case 400:
return '请求错误';
case 403:
return '拒绝访问';
case 404:
return '请求资源不存在';
case 500:
return '服务器内部错误';
case 502:
return '网关错误';
case 503:
return '服务不可用';
default:
return '请求失败';
}
}
}
/// 统一响应:业务成功见 [isSuccess]
class HttpResult<T> {
final int? status;
final bool success;
final String? message;
final T? data;
final int? errorCode;
const HttpResult({
this.status,
this.success = false,
this.message,
this.data,
this.errorCode,
});
bool get isSuccess => success;
factory HttpResult.fromResponse(Response response) {
try {
final int httpStatus = response.statusCode ?? 0;
final dynamic raw = response.data;
final Map<String, dynamic>? root = _jsonMap(raw);
// 明确失败包:{ "success": false, "error": { ... } }
if (root != null && root['success'] == false) {
final payload = ApiErrorPayload.tryParse(root);
return HttpResult<T>(
status: httpStatus,
success: false,
message: payload?.message ?? root['message']?.toString() ?? '请求失败',
errorCode: payload?.code,
data: null,
);
}
// HTTP 2xx:body 即为业务数据
if (httpStatus >= 200 && httpStatus < 300) {
return HttpResult<T>(
status: httpStatus,
success: true,
message: null,
data: raw as T?,
errorCode: null,
);
}
// 非 2xx:尽量从 body 里拆 error
if (root != null) {
final payload = ApiErrorPayload.tryParse(root);
if (payload != null) {
return HttpResult<T>(
status: httpStatus,
success: false,
message: payload.message,
errorCode: payload.code,
data: null,
);
}
}
return HttpResult<T>(
status: httpStatus,
success: false,
message: _handleStatusCode(httpStatus),
data: null,
);
} catch (e) {
return HttpResult<T>(
status: response.statusCode,
success: false,
message: '数据解析失败: $e',
data: null,
);
}
}
factory HttpResult.error(String message, {int? httpStatus}) {
return HttpResult<T>(
success: false,
status: httpStatus,
message: message,
);
}
factory HttpResult.networkError() {
return HttpResult<T>(
success: false,
status: -1,
message: '网络连接失败,请检查网络后重试',
);
}
// ============ 私有方法 ============
static Map<String, dynamic>? _jsonMap(dynamic value) {
if (value is Map<String, dynamic>) return value;
if (value is Map) {
try {
return value.cast<String, dynamic>();
} catch (_) {
return Map<String, dynamic>.from(value as Map);
}
}
return null;
}
static String _handleStatusCode(int? statusCode) {
switch (statusCode) {
case 400:
return '请求错误';
case 401:
return '未授权,请重新登录';
case 403:
return '拒绝访问';
case 404:
return '请求资源不存在';
case 500:
return '服务器内部错误';
case 502:
return '网关错误';
case 503:
return '服务不可用';
default:
return '请求失败';
}
}
}
/// 后台错误体:`{ "error": { "code", "details", "message" }, "success": false }`
class ApiErrorPayload {
final int? code;
final dynamic details;
final String? message;
const ApiErrorPayload({this.code, this.details, this.message});
static ApiErrorPayload? tryParse(Map<String, dynamic>? root) {
if (root == null) return null;
final err = root['error'];
if (err is! Map) return null;
final m = _jsonMap(err);
if (m == null) return null;
return ApiErrorPayload(
code: _parseInt(m['code']),
details: m['details'],
message: m['message']?.toString(),
);
}
static Map<String, dynamic>? _jsonMap(dynamic value) {
if (value is Map<String, dynamic>) return value;
if (value is Map) {
try {
return value.cast<String, dynamic>();
} catch (_) {
return Map<String, dynamic>.from(value as Map);
}
}
return null;
}
static int? _parseInt(dynamic v) {
if (v == null) return null;
if (v is int) return v;
if (v is num) return v.toInt();
return int.tryParse(v.toString());
}
}
/// 分页响应模型(原项目的 GlimzoGetxBaseResponse 改造)
class BasePageResponse<T> {
final int code;
final String msg;
final T? data;
BasePageResponse({
required this.code,
required this.msg,
this.data,
});
bool get success => code == 200;
factory BasePageResponse.fromJson(Map<String, dynamic> json) {
return BasePageResponse(
code: json['code'] ?? -1,
msg: json['msg'] ?? '',
data: json['data'],
);
}
}
/// Map 数据提取工具(原项目的 GlimzoMapRes 改造)
class MapRes {
static List<T> getList<T>({
required Map<dynamic, dynamic> mapData,
required String alias,
required T Function(Map<String, dynamic>) fromJson,
}) {
final rawList = mapData[alias];
if (rawList is List) {
return rawList
.whereType<Map<String, dynamic>>()
.map(fromJson)
.toList();
}
return <T>[];
}
static T toModel<T>({
required Map<String, dynamic> json,
required T Function(Map<String, dynamic>) fromJson,
}) {
return fromJson(json);
}
}
+143
View File
@@ -0,0 +1,143 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:skywood/global/request/api_const.dart';
import 'package:skywood/utils/tools/sky_device_info.dart';
import 'package:skywood/utils/tools/sky_time_tool.dart';
import 'package:skywood/utils/tools/app_sp.dart';
import 'package:logger/logger.dart';
import '../../utils/tools/token_manage.dart';
import '../tool/sky_encrypt.dart';
/// 网络请求拦截器
class NetworkInterceptor extends Interceptor {
final bool enableLog = !kReleaseMode;
final Logger _logger = Logger();
@override
Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
// 添加公共请求头
await _addRequestHeaders(options);
// 打印请求日志
_logInfo("🔶 REQUEST [${options.method.toUpperCase()}] ${options.uri}");
_logDebug("Headers: ${options.headers}");
if (options.data != null) {
_logDebug("Payload: ${options.data}");
}
handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
_logInfo("🟢 RESPONSE [${response.statusCode}] ${response.requestOptions.uri}");
// _logDebug("Body: ${response.data}");
// 处理 401 / 402 Token 过期
if (response.statusCode == 401 || response.statusCode == 402) {
// 触发 Token 刷新
TokenManager().update();
if (response.statusCode == 402) {
_handleError('errorCode:${response.statusCode}', true);
}
handler.reject(DioException(
requestOptions: response.requestOptions,
response: response,
type: DioExceptionType.badResponse,
error: 'errorCode:${response.statusCode}',
));
return;
}
// Release 模式解密响应体
if (kReleaseMode && response.data is String) {
try {
final deStr = SkyEncrypt.deStr(response.data);
response.data = jsonDecode(deStr);
} catch (e) {
_logError("解密失败: $e");
}
}
handler.next(response);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
_logError("🔴 Error: ${err.message}");
handler.next(err);
}
// ============ 私有方法 ============
Future<void> _addRequestHeaders(RequestOptions options) async {
// 开发模式添加 security: false
if (!kReleaseMode) {
options.headers['security'] = 'false';
}
// 注入 Token
final tokenManager = TokenManager();
final token = tokenManager.get();
// print('查看token: $token');
if (token.isNotEmpty) {
options.headers['Authorization'] = 'Bearer $token';
}
// 其他公共参数
String langKey = await AppSp().getString('GLIMZO_STORE_LANGKEY') ?? 'en';
final deviceInfo = SkyDeviceInfo.instance;
options.headers.addAll({
'product-prefix': 'glimzo',
'device-id': deviceInfo.deviceId,
'system-type': deviceInfo.systemType,
'model': deviceInfo.model,
'system-version': deviceInfo.systemVersion,
'brand': deviceInfo.brand,
'app-version': deviceInfo.appVersion,
'app-name': deviceInfo.appName,
'lang-key': langKey,
'time-zone': SkyTimeTool.getTimeZone(),
'idfa': '',
'idfv': deviceInfo.idfv,
'device-gaid': '',
});
// 处理 prevToken(从请求体中提取)
await _handleOldToken(options);
}
Future<void> _handleOldToken(RequestOptions options) async {
try {
if (options.data is Map<String, dynamic>) {
final body = options.data as Map<String, dynamic>;
final prevToken = body['prevToken'];
if (prevToken != null && prevToken.toString().isNotEmpty) {
options.headers['Authorization'] = prevToken.toString();
}
}
} catch (_) {
// 非 JSON 请求跳过
}
}
void _logInfo(String message) {
if (enableLog) _logger.i(message);
}
void _logDebug(String message) {
if (enableLog) _logger.d('💡 $message');
}
void _logError(String message) {
if (enableLog) _logger.e(message);
}
void _handleError(dynamic e, bool error) {
_logError("🔴 Error: $e");
// 这里可以根据需要处理错误提示
}
}