基础库
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 

356 řádky
10 KiB

  1. import 'package:dio/dio.dart';
  2. import 'package:dio/adapter.dart';
  3. import 'package:zhiying_comm/util/empty_util.dart';
  4. import 'dart:io';
  5. import 'dart:ui';
  6. import 'dart:convert';
  7. import 'package:zhiying_comm/zhiying_comm.dart';
  8. import 'package:package_info/package_info.dart';
  9. import 'package:device_info/device_info.dart';
  10. import 'package:fluttertoast/fluttertoast.dart';
  11. import 'global_config.dart';
  12. import 'shared_prefe_util.dart';
  13. import 'time_util.dart';
  14. typedef OnSuccess = dynamic Function(dynamic data);
  15. typedef OnError = dynamic Function(String e);
  16. typedef OnCache = dynamic Function(dynamic data);
  17. class NetUtil {
  18. Dio _dio;
  19. NetUtil._();
  20. static NetUtil _instance;
  21. static NetUtil getInstance() {
  22. if (_instance == null) {
  23. _instance = NetUtil._();
  24. }
  25. return _instance;
  26. }
  27. get dio async {
  28. if (_dio == null) {
  29. var setting = await NativeUtil.getSetting();
  30. String domain = setting['domain']; //'http://www.hairuyi.com/';
  31. _config(domain);
  32. }
  33. return _dio;
  34. }
  35. /// 配置网络请求,基础地址,代理地址,如:192.168.0.123:8888(代理地址生产环境无效)
  36. /// apiVersion 接口版本
  37. void _config(String baseUrl, {String proxyUrl}) {
  38. _dio = Dio(BaseOptions(
  39. method: "post",
  40. baseUrl: baseUrl,
  41. connectTimeout: 15000,
  42. receiveTimeout: 15000,
  43. contentType: Headers.jsonContentType,
  44. followRedirects: true));
  45. _dio.interceptors.add(_NetInterceptors());
  46. _dio.interceptors.add(LogInterceptor());
  47. const bool inProduction = const bool.fromEnvironment("dart.vm.product");
  48. if (proxyUrl != null && proxyUrl != '' && !inProduction) {
  49. _setProxy(proxyUrl);
  50. }
  51. }
  52. /// 设置代理
  53. void _setProxy(String proxyUrl) {
  54. (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
  55. (HttpClient client) {
  56. client.findProxy = (uri) {
  57. return "PROXY $proxyUrl";
  58. };
  59. client.badCertificateCallback =
  60. (X509Certificate cert, String host, int port) => true;
  61. };
  62. }
  63. /// 同步请求
  64. static Future<dynamic> post(String path,
  65. {Map<String, dynamic> params}) async {
  66. if (params == null) {
  67. params = {};
  68. }
  69. Map<String, dynamic> sign = await signParams(params);
  70. Response response;
  71. try {
  72. Dio dio = await NetUtil.getInstance().dio;
  73. response = await dio.post(path, data: sign);
  74. } on DioError catch (e) {
  75. _formatError(e);
  76. // Logger.error(e);
  77. }
  78. if (response == null) {
  79. return null;
  80. }
  81. if (response.statusCode != 200) {
  82. // 请求错误
  83. const bool inProduction = const bool.fromEnvironment("dart.vm.product");
  84. if (!inProduction) {
  85. Fluttertoast.showToast(msg: response.statusMessage);
  86. }
  87. // log.e(response.statusMessage);
  88. return null;
  89. }
  90. var result = response.data;
  91. if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  92. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  93. result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  94. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') {
  95. Logger.error(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  96. Fluttertoast.showToast(
  97. msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG],
  98. toastLength: Toast.LENGTH_SHORT,
  99. gravity: ToastGravity.BOTTOM,
  100. );
  101. }
  102. return result;
  103. }
  104. /// 异步请求
  105. static void request(String path,
  106. {String method = 'POST',
  107. Map<String, dynamic> params,
  108. Map<String, File> multiFiles,
  109. OnSuccess onSuccess,
  110. OnError onError,
  111. OnCache onCache}) async {
  112. if (params == null) {
  113. params = {};
  114. }
  115. // 根据请求参数,获取缓存的Key
  116. String cacheKey = _getRequestParamsCachedKey(params, path);
  117. // // 读取缓存回调
  118. await _onCallBackCacheData(onCache, cacheKey);
  119. // 参数签名
  120. Map<String, dynamic> sign = await signParams(params);
  121. Response response;
  122. try {
  123. Dio dio = await NetUtil.getInstance().dio;
  124. // 文件
  125. if (multiFiles != null && multiFiles.length > 0) {
  126. for (String key in multiFiles.keys) {
  127. var file = await MultipartFile.fromFile(multiFiles[key].path);
  128. sign[key] = file;
  129. }
  130. }
  131. FormData formData = FormData.fromMap(sign);
  132. response = await dio.request(path,
  133. data: formData, options: Options(method: method));
  134. } on DioError catch (e) {
  135. _formatError(e);
  136. }
  137. if (response == null) {
  138. return null;
  139. }
  140. var result = jsonDecode(response.data);
  141. //TODO 加密?
  142. if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  143. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  144. result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  145. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') {
  146. if (onSuccess != null) {
  147. onSuccess(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  148. // 缓存返回的数据
  149. _setCallBackCacheData(cacheKey, response.data);
  150. }
  151. return;
  152. }
  153. Logger.error('error: ' + result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  154. Fluttertoast.showToast(
  155. msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG],
  156. toastLength: Toast.LENGTH_SHORT,
  157. gravity: ToastGravity.BOTTOM,
  158. );
  159. if (onError != null) {
  160. onError(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG] ?? '未知错误');
  161. }
  162. return;
  163. }
  164. /// 统一添加必要的参数
  165. static Future<Map<String, dynamic>> signParams(
  166. Map<String, dynamic> params) async {
  167. // 应用信息
  168. PackageInfo packageInfo = await PackageInfo.fromPlatform();
  169. // 原生传的信息
  170. Map setting = await NativeUtil.getSetting();
  171. if (Platform.isIOS) {
  172. IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
  173. // 设备
  174. params["platform"] = "ios";
  175. // 设备系统版本
  176. params["system_version"] = iosInfo?.systemVersion;
  177. // 设备型号 如:iPhone 11pro
  178. params['device_model'] = iosInfo?.name;
  179. // 设备型号,如:MLMF2LL/A
  180. // params['device_number'] = iosInfo?.model;
  181. // 设备ID
  182. params['device_id'] = iosInfo?.identifierForVendor;
  183. } else if (Platform.isAndroid) {
  184. AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
  185. // 设备
  186. params["platform"] = "android";
  187. // 设备系统版本
  188. params["system_version"] = "Android ${androidInfo?.version?.release}";
  189. // 设备型号 如:iPhone 11pro
  190. params['device_model'] = androidInfo?.model;
  191. // 设备型号,如:MLMF2LL/A
  192. // params['device_number'] = androidInfo?.id;
  193. // 设备Id
  194. params['device_id'] = androidInfo?.androidId;
  195. }
  196. // 应用版本号
  197. params["app_version"] = packageInfo.version;
  198. // 分辨率
  199. params["solution"] =
  200. "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}";
  201. // 站长ID
  202. String masterId = setting['master_id'];
  203. if (null != null &&
  204. masterId != '' &&
  205. (!params.containsKey('master_id') || params['master_id'] == '')) {
  206. params['master_id'] = masterId;
  207. }
  208. // secret_key
  209. params['secret_key'] = setting['secret_key'];
  210. // token
  211. String token = setting['token'];
  212. if (token != null &&
  213. token != '' &&
  214. (!params.containsKey('token') || params['token'] == '')) {
  215. params['token'] = token;
  216. }
  217. // 当前时间戳:秒
  218. params["time"] = TimeUtil.getNowTime();
  219. // 过滤空字段,过滤首尾空格
  220. Map<String, dynamic> filters = Map<String, dynamic>();
  221. params.forEach((key, value) {
  222. if (key != '' && value != '') {
  223. filters[key] = (value is String) ? value.trim() : value;
  224. }
  225. });
  226. params = filters;
  227. List<String> list = List<String>();
  228. params.forEach((key, value) {
  229. list.add(key.toString() + value.toString());
  230. });
  231. params["sign"] = signWithArray(list);
  232. return params;
  233. }
  234. /// 获取请求缓存成功的数据
  235. static Future<void> _onCallBackCacheData(OnCache onCache, String cacheKey) async {
  236. // 读取缓存
  237. Map<String, dynamic> cacheMap = await SharedPreferencesUtil.getNetCacheResult(cacheKey);
  238. if (!EmptyUtil.isEmpty(cacheMap) &&
  239. cacheMap.containsKey(GlobalConfig.RESPONSE_SUCCESS_CODE) &&
  240. (cacheMap[GlobalConfig.RESPONSE_SUCCESS_CODE] ==
  241. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  242. cacheMap[GlobalConfig.RESPONSE_SUCCESS_CODE] ==
  243. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') &&
  244. null != cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]) {
  245. onCache(cacheMap['data']);
  246. return;
  247. }
  248. return;
  249. }
  250. /// 缓存请求成功的数据
  251. static void _setCallBackCacheData(String cacheKey, String value) async {
  252. SharedPreferencesUtil.setNetCacheResult(cacheKey, value);
  253. }
  254. /// 根据请求参数,获取缓存的Key
  255. static String _getRequestParamsCachedKey(
  256. Map<String, dynamic> map, String path) {
  257. if (EmptyUtil.isEmpty(map)) {
  258. return EncodeUtil.generateMd5(path);
  259. }
  260. return EncodeUtil.generateMd5(path + map.toString());
  261. }
  262. /// 签名
  263. static String signWithArray(List<String> params) {
  264. // 字母升序
  265. params.sort();
  266. String result = "";
  267. // result += secret_key;
  268. params.forEach((param) {
  269. result += param;
  270. });
  271. // result += secret_key;
  272. return EncodeUtil.generateMd5(result);
  273. }
  274. /*
  275. * error统一处理
  276. */
  277. static void _formatError(DioError e) {
  278. if (e.type == DioErrorType.CONNECT_TIMEOUT) {
  279. Logger.error('连接超时: ${e.toString()}');
  280. } else if (e.type == DioErrorType.SEND_TIMEOUT) {
  281. Logger.error('请求超时: ${e.toString()}');
  282. } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
  283. Logger.error('响应超时: ${e.toString()}');
  284. } else if (e.type == DioErrorType.RESPONSE) {
  285. Logger.error('出现异常: ${e.toString()}');
  286. } else if (e.type == DioErrorType.CANCEL) {
  287. Logger.error('请求取消: ${e.toString()}');
  288. } else {
  289. Logger.error('未知错误: ${e.toString()}');
  290. }
  291. }
  292. }
  293. /**
  294. * @description: 网络请求拦截器
  295. * @param {type}
  296. * @return:
  297. */
  298. class _NetInterceptors extends InterceptorsWrapper {
  299. @override
  300. Future onRequest(RequestOptions options) {
  301. Logger.net(options?.path, data: options.data.toString());
  302. // TODO 加密?
  303. return super.onRequest(options);
  304. }
  305. @override
  306. Future onResponse(Response response) {
  307. Logger.endNet(response.realUri.toString(), data: response?.data?.toString() ?? '');
  308. // TODO 解密?
  309. return super.onResponse(response);
  310. }
  311. @override
  312. Future onError(DioError err) {
  313. // Logger.error(err);
  314. return super.onError(err);
  315. }
  316. }