import 'dart:collection'; import 'dart:convert'; import 'dart:io'; import 'dart:ui'; import 'package:device_info/device_info.dart'; import 'package:dio/adapter.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:imei_plugin/imei_plugin.dart'; import 'package:package_info/package_info.dart'; import 'package:zhiying_comm/util/empty_util.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; import 'encode_util.dart'; import 'global_config.dart'; import 'shared_prefe_util.dart'; import 'time_util.dart'; import 'package:provider/provider.dart'; typedef OnSuccess = dynamic Function(dynamic data); typedef OnError = dynamic Function(String e); typedef OnCache = dynamic Function(dynamic data); final GlobalKey navigatorKey = new GlobalKey(); enum NetMethod { GET, POST, PUT, DELETE, OPTIONS, PATCH, UPDATE, HEAD, } class NetUtil { Dio _dio; NetUtil._(); static NetUtil _instance; static NetUtil getInstance() { if (_instance == null) { _instance = NetUtil._(); } return _instance; } get dio async { if (_dio == null) { var setting = await NativeUtil.getSetting(); String domain = setting['domain']; //'http://www.hairuyi.com/'; _config(domain, proxyUrl: '');// 192.168.0.66:8866 } return _dio; } /// 配置网络请求,基础地址,代理地址,如:192.168.0.123:8888(代理地址生产环境无效) /// apiVersion 接口版本 void _config(String baseUrl, {String proxyUrl}) { _dio = Dio(BaseOptions( method: "post", baseUrl: baseUrl, connectTimeout: 15000, receiveTimeout: 15000, contentType: Headers.jsonContentType, followRedirects: true, headers: {'device': 'wx_applet', 'Platform': 'wx_applet'}, validateStatus: (_) { return true; })); _dio.interceptors.add(_NetInterceptors()); // _dio.interceptors.add(LogInterceptor()); const bool inProduction = const bool.fromEnvironment("dart.vm.product"); if (proxyUrl != null && proxyUrl != '' && !inProduction) { _setProxy(proxyUrl); } } /// 设置代理 void _setProxy(String proxyUrl) { (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (HttpClient client) { client.findProxy = (uri) { return "PROXY $proxyUrl"; }; client.badCertificateCallback = (X509Certificate cert, String host, int port) => true; }; } /// 同步请求 static Future post(String path, {Map params, Map queryParameters, NetMethod method = NetMethod.POST, bool cache = false, bool showToast = true}) async { if (params == null) { params = {}; } // 根据请求参数,获取缓存的Key String cacheKey = getRequestParamsCachedKey(params, path); // 参数签名 TODO 加密? // post请求的参数 Map bodyParams = params; // 请求头参数 Map headParam = await _getMustHeadParams(); Response response; try { Dio dio = await NetUtil.getInstance().dio; response = await dio.request( path, data: !EmptyUtil.isEmpty(bodyParams) ? bodyParams : null, options: Options(method: enumToString(method), headers: headParam), queryParameters: !EmptyUtil.isEmpty(queryParameters) ? queryParameters : null, ); } on DioError catch (e) { _formatError(e); } try { var result = response.data is Map ? response.data : jsonDecode(response.data); // TODO 解密? if (isSuccess(result)) { // 缓存成功的数据 if (cache) { _setCallBackCacheData(cacheKey, response.data is Map ? jsonEncode(response.data) : response.data); } return result; // 缓存返回的数据 } else { Logger.error('error: ' + result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]); /// /// 401000 验证用户失败(不提示Toast) /// 404004 没有找到对应模块(跳空页面,不提示toast) /// if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] != 401000 && result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] != 404004) { if(showToast) { Fluttertoast.showToast( msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG], toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); } } /// /// 401003 用户被逼下线 /// 退出登陆(清理token等用户信息) /// if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE]?.toString() == '401003') { try { Future.delayed(Duration(seconds: 0)).then((onValue) { BuildContext context = navigatorKey.currentState.overlay.context; Provider.of(context, listen: false).unLogin(); }); } catch (e, s) { Logger.error(e, s); } } return result; } } catch (e) { return null; } } /// 异步请求 static void request(String path, {NetMethod method = NetMethod.GET, Map params,Map queryParameters, OnSuccess onSuccess, OnError onError, OnCache onCache, bool showToast = true}) async { if (params == null) { params = {}; } // 根据请求参数,获取缓存的Key String cacheKey = getRequestParamsCachedKey(params, path); // // 读取缓存回调 if (onCache != null) { await _onCallBackCacheData(onCache, cacheKey); } try { Map result = await NetUtil.post(path, method: method, params: params, queryParameters: queryParameters, showToast: showToast, cache: onCache != null); // TODO 解密? if (isSuccess(result)) { if (onSuccess != null) { onSuccess(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); } return; } if (onError != null) { onError(!EmptyUtil.isEmpty(result) ? !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]) ? result[GlobalConfig.HTTP_RESPONSE_KEY_MSG] : '未知错误' : '未知错误'); } } catch (e) { Logger.error('error: ' + e.toString()); if (onError != null) { onError(e?.toString() ?? '未知错误'); } } return; } /// 统一添加必要的参数 // static Future> signParams(Map params) async { // // 应用信息 // PackageInfo packageInfo = await PackageInfo.fromPlatform(); // // 原生传的信息 // Map setting = await NativeUtil.getSetting(); // // if (Platform.isIOS) { // IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo; // // 设备 // params["platform"] = "ios"; // // 设备系统版本 // params["system_version"] = iosInfo?.systemVersion; // // 设备型号 如:iPhone 11pro // params['device_model'] = iosInfo?.name; // // 设备型号,如:MLMF2LL/A // // params['device_number'] = iosInfo?.model; // // 设备ID // params['device_id'] = iosInfo?.identifierForVendor; // } else if (Platform.isAndroid) { // AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; // // 设备 // params["platform"] = "android"; // // 设备系统版本 // params["system_version"] = "Android ${androidInfo?.version?.release}"; // // 设备型号 如:iPhone 11pro // params['device_model'] = androidInfo?.model; // // 设备型号,如:MLMF2LL/A // // params['device_number'] = androidInfo?.id; // // 设备Id // params['device_id'] = androidInfo?.androidId; // } // // 应用版本号 // params["app_version_name"] = packageInfo.version; // params["app_version"] = -1 ;// packageInfo.buildNumber; // // 分辨率 // params["solution"] = // "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}"; // // // 站长ID // String masterId = setting['master_id']; // if (null != masterId && // masterId != '' && // (!params.containsKey('master_id') || params['master_id'] == '')) { // params['master_id'] = masterId; // } // // // secret_key // params['secret_key'] = setting['secret_key']; // // // token 读取SP缓存中的用户token // String token = await SharedPreferencesUtil.getStringValue( // GlobalConfig.SHARED_KEY_TOKEN); // if (!EmptyUtil.isEmpty(token)) { // params['token'] = token; // } // // // 当前时间戳:秒 // params["time"] = TimeUtil.getNowTime(); // // // 过滤空字段,过滤首尾空格 // Map filters = Map(); // params.forEach((key, value) { // if (key != '' && value != '') { // filters[key] = (value is String) ? value.trim() : value; // } // }); // params = filters; // // List list = List(); // params.forEach((key, value) { // list.add(key.toString() + value.toString()); // }); // params["sign"] = signWithArray(list); // return params; // } /// 获取必须的请求参数(用于请求头部) static Future> _getMustHeadParams() async { Map params = new HashMap(); // 应用信息 PackageInfo packageInfo = await PackageInfo.fromPlatform(); // 原生传的信息 Map setting = await NativeUtil.getSetting(); if (Platform.isIOS) { IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo; // 设备 params["platform"] = "ios"; // 设备系统版本 params["os_version"] = iosInfo?.systemVersion?.toString(); // 设备型号 如:iPhone 11pro params['device_model'] = EncodeUtil.encodeBase64(iosInfo?.name?.toString() ?? ''); // 设备ID params['device_id'] = iosInfo?.identifierForVendor?.toString(); // idfa params['idfa'] = iosInfo?.identifierForVendor?.toString(); } else if (Platform.isAndroid) { AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; // 设备 params["platform"] = "android"; // 设备系统版本 params["os_version"] = "Android ${androidInfo?.version?.release}"; // 设备型号 如:iPhone 11pro params['device_model'] = androidInfo?.model?.toString(); // 设备Id params['device_id'] = androidInfo?.androidId?.toString(); // imei params['imei'] = await _getImei(); } // 应用版本号 params["app_version_name"] = packageInfo.version?.toString(); params["app_version"] = packageInfo.buildNumber?.toString(); // 分辨率 params["solution"] = "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}"; // 站长ID String masterId = setting['master_id']; if (null != masterId && masterId != '' && (!params.containsKey('master_id') || params['master_id'] == '')) { params['master_id'] = masterId; //!EmptyUtil.isEmpty(masterId) ? masterId : 'template_database'; } // token 读取SP缓存中的用户token String token = await SharedPreferencesUtil.getStringValue(GlobalConfig.SHARED_KEY_TOKEN); if (!EmptyUtil.isEmpty(token)) { params['token'] = token; } // secret_key params['secret_key'] = setting['secret_key'] ?? ''; // 当前时间戳:秒 params["time"] = TimeUtil.getNowTime(); // 过滤空字段,过滤首尾空格 Map filters = Map(); params.forEach((key, value) { if (key != '' && value != '') { filters[key] = (value is String) ? value.trim() : value; } }); params = filters; List list = List(); params.forEach((key, value) { list.add(key.toString() + '=' + value.toString() + '&'); }); params["sign"] = signWithArray(list); params.remove('secret_key'); return params; } /// 获取Android imei static Future _getImei() async { try { return await ImeiPlugin.getImei(shouldShowRequestPermissionRationale: true); } catch (e, s) { Logger.error(e, s); } return null; } /// 获取请求缓存成功的数据 static Future _onCallBackCacheData(OnCache onCache, String cacheKey) async { // 读取缓存 Map cacheMap = await SharedPreferencesUtil.getNetCacheResult(cacheKey); if (!EmptyUtil.isEmpty(cacheMap) && cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) && (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE || cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}') && !EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { onCache(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); return; } return; } /// 缓存请求成功的数据 static void _setCallBackCacheData(String cacheKey, String value) async { SharedPreferencesUtil.setNetCacheResult(cacheKey, value); } /// 根据请求参数,获取缓存的数据 static Future getRequestCachedData(String url, {Map params}) async { Map cacheMap = await SharedPreferencesUtil.getNetCacheResult(getRequestParamsCachedKey(params, url)); if (!EmptyUtil.isEmpty(cacheMap) && cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) && (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE || cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}') && !EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { return cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]; } return null; } /// 判断后台返回是否成功 static bool isSuccess(Map data) { try { if (!EmptyUtil.isEmpty(data) && (data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE || data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}')) { return true; } } catch (e) { return false; } return false; } /// 根据请求参数,获取缓存的Key static String getRequestParamsCachedKey(Map map, String path) { if (EmptyUtil.isEmpty(map)) { return EncodeUtil.generateMd5(path); } return EncodeUtil.generateMd5(path + map.toString()); } // 七牛云文件上传 static Future uploadFile(String url, File file, {String method = 'POST', Map params, OnSuccess onSuccess, OnError onError}) async { if (params == null) { params = {}; } Dio dio = Dio(BaseOptions( method: "post", connectTimeout: 15000, receiveTimeout: 15000, contentType: Headers.jsonContentType, followRedirects: true, )); params['file'] = await MultipartFile.fromFile(file.path); FormData format = FormData.fromMap(params); return dio.request(url, data: format, options: Options(method: method)); } /// 签名 static String signWithArray(List params) { // 字母升序 params.sort(); String result = ""; params.forEach((param) { result += param; }); result = result.substring(0, result.length - 1); return EncodeUtil.generateMd5(result); } /* * error统一处理 */ static void _formatError(DioError e) { if (e.type == DioErrorType.CONNECT_TIMEOUT) { Logger.error('连接超时: ${e.toString()}'); } else if (e.type == DioErrorType.SEND_TIMEOUT) { Logger.error('请求超时: ${e.toString()}'); } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) { Logger.error('响应超时: ${e.toString()}'); } else if (e.type == DioErrorType.RESPONSE) { Logger.error('出现异常: ${e.toString()}'); } else if (e.type == DioErrorType.CANCEL) { Logger.error('请求取消: ${e.toString()}'); } else { Logger.error('未知错误: ${e.toString()}'); } } } /** * @description: 网络请求拦截器 * @param {type} * @return: */ class _NetInterceptors extends InterceptorsWrapper { @override Future onRequest(RequestOptions options) { Logger.net(options?.uri?.toString(), data: options.data.toString()); // TODO 加密? return super.onRequest(options); } @override Future onResponse(Response response) { Logger.endNet(response?.request?.uri?.toString(), data: response?.data?.toString() ?? ''); // TODO 解密? return super.onResponse(response); } @override Future onError(DioError err) { // Logger.error(err); return super.onError(err); } }