guru_sdk/guru_app/lib/api/guru_api.dart

159 lines
5.6 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:guru_app/account/account_data_store.dart';
import 'package:guru_app/account/model/user.dart';
import 'package:guru_app/guru_app.dart';
import 'package:guru_app/property/app_property.dart';
import 'package:guru_utils/device/device_info.dart';
import 'package:guru_utils/device/device_utils.dart';
import 'package:retrofit/retrofit.dart';
import 'custom_transformer.dart';
import 'data/orders/orders_model.dart';
/// Created by Haoyi on 6/3/21
part 'modules/guru_api_extension.dart';
part 'guru_api.g.dart';
abstract class DioBuilder {
Dio build();
}
class GuruDioBuilder extends DioBuilder {
final AccountDataStore accountDataStore;
GuruDioBuilder() : accountDataStore = AccountDataStore.instance;
Options toOptions(RequestOptions ro) {
return Options(
method: ro.method,
sendTimeout: ro.sendTimeout,
receiveTimeout: ro.receiveTimeout,
extra: ro.extra,
headers: ro.headers,
responseType: ro.responseType,
contentType: ro.contentType,
validateStatus: ro.validateStatus,
receiveDataWhenStatusError: ro.receiveDataWhenStatusError,
followRedirects: ro.followRedirects,
maxRedirects: ro.maxRedirects,
requestEncoder: ro.requestEncoder,
responseDecoder: ro.responseDecoder,
listFormat: ro.listFormat);
}
@override
Dio build() {
Dio dio = Dio()
..transformer = CustomTransformer()
..options.connectTimeout = Duration(milliseconds: GuruApp.instance.appSpec.deployment.apiConnectTimeout)
..options.receiveTimeout = Duration(milliseconds: GuruApp.instance.appSpec.deployment.apiReceiveTimeout);
dio.interceptors.add(InterceptorsWrapper(
onRequest: (RequestOptions options, RequestInterceptorHandler handler) async {
DeviceInfo? deviceInfo = accountDataStore.currentDevice;
final deviceId = await AppProperty.getInstance().getDeviceId();
deviceInfo ??=
await DeviceUtils.buildDeviceInfo(deviceId: deviceId, firebasePushToken: "", uid: "");
final token = accountDataStore.saasToken;
options.headers
.addAll({"X-APP-ID": GuruApp.instance.details.saasAppId, "X-ACCESS-TOKEN": token ?? ''});
options.headers.addAll({"X-DEVICE-INFO": Uri.encodeFull(deviceInfo.toXDeviceInfo())});
handler.next(options);
}, onResponse: (Response response, ResponseInterceptorHandler handler) {
// Log.v("### onResponse ${response.data}");
response.data = response.data["data"] ?? response.data;
handler.next(response);
}, onError: (DioError err, ErrorInterceptorHandler handler) async {
final token = accountDataStore.saasToken;
final response = err.response;
Log.v("### onError ${err.toString()}");
if (response != null && token != null && response.statusCode == 401) {
// dio.lock();
try {
Log.v("accountDataStore.refreshAuth()");
await accountDataStore.refreshAuth(); //获取新token
} catch (e) {
// Log.v("[NETWORK]: RefreshToken Failed.");
handler.reject(err);
} finally {
// dio.unlock();
}
final options = err.requestOptions.copyWith();
options.headers["X-ACCESS-TOKEN"] = accountDataStore.saasToken;
try {
final response = await dio.request(options.path,
data: options.data,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
onReceiveProgress: options.onReceiveProgress,
options: toOptions(options));
handler.resolve(response);
} catch (error, stacktrace) {
Log.v("re-request error:$error $stacktrace");
handler.reject(err);
}
} else {
handler.reject(err);
}
}));
dio.interceptors
.add(LogInterceptor(requestBody: true, responseBody: true, logPrint: platformLogPrint));
return dio;
}
}
@RestApi()
abstract class GuruApiMethods {
factory GuruApiMethods(Dio dio, {String baseUrl}) = _GuruApiMethods;
static GuruApiMethods create(GuruDioBuilder dioBuilder, String baseUrl) {
return GuruApiMethods(dioBuilder.build(), baseUrl: baseUrl);
}
// 上报 Device, 初次或者 token 更新后
@POST("/device/api/v1/devices")
Future reportDevice(@Body() DeviceInfo body);
// Auth
@POST("/auth/api/v1/tokens/provider/secret")
Future<SaasUser> signInWithAnonymous(@Body() AnonymousLoginReqBody body);
@POST("/auth/api/v1/renewals/token")
Future<SaasUser> refreshSaasToken();
@POST("/auth/api/v1/renewals/firebase")
Future<FirebaseTokenData> renewFirebaseToken();
@POST("/order/api/v1/orders/ios")
Future<OrdersResponse> iOSOrdersReport(@Body() OrdersReport body);
@POST("/order/api/v1/orders/android")
Future<OrdersResponse> androidOrdersReport(@Body() OrdersReport body);
}
class GuruApi {
static const String _saasApiDevHost = "https://dev.saas.castbox.fm";
static const String _saasApiReleaseHost = "https://saas.castbox.fm";
static bool useReleaseApi = kReleaseMode;
static final GuruApi _releaseApi =
GuruApi._(GuruApiMethods.create(GuruDioBuilder(), _saasApiReleaseHost));
static final GuruApi _debugApi =
GuruApi._(GuruApiMethods.create(GuruDioBuilder(), _saasApiDevHost));
final GuruApiMethods _methods;
static GuruApi get instance => useReleaseApi ? _releaseApi : _debugApi;
static String get saasApiHost => useReleaseApi ? _saasApiReleaseHost : _saasApiDevHost;
GuruApiMethods get methods => _methods;
GuruApi._(this._methods);
}