254 lines
7.6 KiB
Dart
254 lines
7.6 KiB
Dart
// api.dart
|
|
import 'dart:convert';
|
|
import 'package:dio/dio.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
|
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
|
import 'package:yumi/modules/auth/login_route.dart';
|
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
|
import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
|
import 'package:yumi/main.dart';
|
|
import 'package:yumi/shared/data_sources/sources/remote/net/network_client.dart';
|
|
|
|
import '../../../models/enum/sc_erro_code.dart';
|
|
|
|
export 'package:dio/dio.dart';
|
|
|
|
_parseAndDecode(String response) => jsonDecode(response);
|
|
|
|
parseJson(String text) => compute(_parseAndDecode, text);
|
|
|
|
class BaseNetworkClient {
|
|
final Dio dio;
|
|
|
|
BaseNetworkClient() : dio = Dio() {
|
|
_confD();
|
|
init();
|
|
}
|
|
|
|
void _confD() {
|
|
dio.transformer = BackgroundTransformer()..jsonDecodeCallback = parseJson;
|
|
dio.options = BaseOptions(
|
|
connectTimeout: const Duration(seconds: 12),
|
|
receiveTimeout: const Duration(seconds: 15),
|
|
contentType: 'application/json; charset=UTF-8',
|
|
);
|
|
}
|
|
|
|
void init() {}
|
|
|
|
// 通用 GET 请求
|
|
Future<T> get<T>(
|
|
String path, {
|
|
Map<String, dynamic>? queryParams,
|
|
required T Function(dynamic) fromJson,
|
|
CancelToken? cancelToken,
|
|
ProgressCallback? onSendProgress,
|
|
ProgressCallback? onReceiveProgress,
|
|
}) async {
|
|
return _req<T>(
|
|
path,
|
|
method: 'GET',
|
|
queryParams: queryParams,
|
|
fromJson: fromJson,
|
|
cancelToken: cancelToken,
|
|
onSendProgress: onSendProgress,
|
|
onReceiveProgress: onReceiveProgress,
|
|
);
|
|
}
|
|
|
|
// 通用 put 请求
|
|
Future<T> put<T>(
|
|
String path, {
|
|
dynamic data,
|
|
Map<String, dynamic>? queryParams,
|
|
required T Function(dynamic) fromJson,
|
|
CancelToken? cancelToken,
|
|
ProgressCallback? onSendProgress,
|
|
ProgressCallback? onReceiveProgress,
|
|
}) async {
|
|
return _req<T>(
|
|
path,
|
|
method: 'PUT',
|
|
data: data,
|
|
queryParams: queryParams,
|
|
fromJson: fromJson,
|
|
cancelToken: cancelToken,
|
|
onSendProgress: onSendProgress,
|
|
onReceiveProgress: onReceiveProgress,
|
|
);
|
|
}
|
|
|
|
// 通用 POST 请求
|
|
Future<T> post<T>(
|
|
String path, {
|
|
dynamic data,
|
|
Map<String, dynamic>? queryParams,
|
|
required T Function(dynamic) fromJson,
|
|
CancelToken? cancelToken,
|
|
ProgressCallback? onSendProgress,
|
|
ProgressCallback? onReceiveProgress,
|
|
}) async {
|
|
return _req<T>(
|
|
path,
|
|
method: 'POST',
|
|
data: data,
|
|
queryParams: queryParams,
|
|
fromJson: fromJson,
|
|
cancelToken: cancelToken,
|
|
onSendProgress: onSendProgress,
|
|
onReceiveProgress: onReceiveProgress,
|
|
);
|
|
}
|
|
|
|
// 通用 POST 请求
|
|
Future<T> delete<T>(
|
|
String path, {
|
|
dynamic data,
|
|
Map<String, dynamic>? queryParams,
|
|
required T Function(dynamic) fromJson,
|
|
CancelToken? cancelToken,
|
|
ProgressCallback? onSendProgress,
|
|
ProgressCallback? onReceiveProgress,
|
|
}) async {
|
|
return _req<T>(
|
|
path,
|
|
method: 'DELETE',
|
|
data: data,
|
|
queryParams: queryParams,
|
|
fromJson: fromJson,
|
|
cancelToken: cancelToken,
|
|
onSendProgress: onSendProgress,
|
|
onReceiveProgress: onReceiveProgress,
|
|
);
|
|
}
|
|
|
|
// 核心请求方法
|
|
Future<T> _req<T>(
|
|
String path, {
|
|
required String method,
|
|
dynamic data,
|
|
Map<String, dynamic>? queryParams,
|
|
CancelToken? cancelToken,
|
|
ProgressCallback? onSendProgress,
|
|
ProgressCallback? onReceiveProgress,
|
|
required T Function(dynamic) fromJson,
|
|
}) async {
|
|
try {
|
|
final response = await dio.request(
|
|
path,
|
|
data: data,
|
|
queryParameters: queryParams,
|
|
options: Options(method: method),
|
|
cancelToken: cancelToken,
|
|
onSendProgress: onSendProgress,
|
|
onReceiveProgress: onReceiveProgress,
|
|
);
|
|
|
|
// 处理基础响应
|
|
final baseResponse = ResponseData.fromJson(
|
|
response.data as Map<String, dynamic>,
|
|
fromJsonT: fromJson,
|
|
);
|
|
// 业务逻辑成功判断
|
|
if (baseResponse.success) {
|
|
if (baseResponse.body != null) {
|
|
return baseResponse.body!;
|
|
} else {
|
|
if (T == bool) return false as T;
|
|
if (T == int) return 0 as T;
|
|
if (T == String) return "" as T;
|
|
SCLoadingManager.hide();
|
|
throw Exception('Response data is null');
|
|
}
|
|
} else {
|
|
// 业务逻辑错误
|
|
SCLoadingManager.hide();
|
|
throw DioException(
|
|
requestOptions: response.requestOptions,
|
|
response: response,
|
|
error: '业务错误: ${baseResponse.errorMsg}',
|
|
);
|
|
}
|
|
} on DioException catch (e) {
|
|
// 网络错误处理
|
|
throw _hdlErr(e);
|
|
} catch (e) {
|
|
SCLoadingManager.hide();
|
|
throw Exception('未知错误: $e');
|
|
}
|
|
}
|
|
|
|
// 错误处理
|
|
DioException _hdlErr(DioException e) {
|
|
SCLoadingManager.hide();
|
|
switch (e.type) {
|
|
case DioExceptionType.connectionTimeout:
|
|
return DioException(requestOptions: e.requestOptions, error: '连接超时');
|
|
case DioExceptionType.sendTimeout:
|
|
return DioException(requestOptions: e.requestOptions, error: '发送超时');
|
|
case DioExceptionType.receiveTimeout:
|
|
return DioException(requestOptions: e.requestOptions, error: '接收超时');
|
|
case DioExceptionType.badResponse:
|
|
var errorCode = e.response?.data["errorCode"];
|
|
var errorMsg = e.response?.data["errorMsg"];
|
|
if (errorCode == SCErroCode.userNotRegistered.code) {
|
|
//用户还没有注册
|
|
SCTts.show("Please register an account first.");
|
|
BuildContext? context = navigatorKey.currentContext;
|
|
if (context != null) {
|
|
SCNavigatorUtils.push(
|
|
context,
|
|
LoginRouter.editProfile,
|
|
replace: false,
|
|
);
|
|
}
|
|
} else if (errorCode == SCErroCode.authUnauthorized.code) {
|
|
//token过期
|
|
BuildContext? context = navigatorKey.currentContext;
|
|
if (context != null) {
|
|
if (!inLoginPage) {
|
|
AccountStorage().logout(context);
|
|
inLoginPage = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (errorMsg.toString().endsWith("balance not made")) {
|
|
BuildContext? context = navigatorKey.currentContext;
|
|
if (context != null) {
|
|
SCRoomUtils.goRecharge(context);
|
|
}
|
|
} else {
|
|
SCTts.show(errorMsg);
|
|
}
|
|
}
|
|
return DioException(
|
|
requestOptions: e.requestOptions,
|
|
response: e.response,
|
|
error: '服务器错误: ${e.response?.data["errorCode"]}',
|
|
);
|
|
case DioExceptionType.cancel:
|
|
return DioException(requestOptions: e.requestOptions, error: 'Cancel');
|
|
default:
|
|
return DioException(
|
|
requestOptions: e.requestOptions,
|
|
error: 'Net fail',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
class NotSuccessException implements Exception {
|
|
final String message;
|
|
|
|
NotSuccessException(this.message);
|
|
|
|
factory NotSuccessException.fromRespData(ResponseData respData) {
|
|
return NotSuccessException(respData.errorMsg ?? "操作失败");
|
|
}
|
|
}
|