基础库
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 

493 righe
16 KiB

  1. import 'dart:collection';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'dart:ui';
  5. import 'package:device_info/device_info.dart';
  6. import 'package:dio/adapter.dart';
  7. import 'package:dio/dio.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:fluttertoast/fluttertoast.dart';
  10. import 'package:imei_plugin/imei_plugin.dart';
  11. import 'package:package_info/package_info.dart';
  12. import 'package:zhiying_comm/util/empty_util.dart';
  13. import 'package:zhiying_comm/zhiying_comm.dart';
  14. import 'global_config.dart';
  15. import 'shared_prefe_util.dart';
  16. import 'time_util.dart';
  17. import 'package:provider/provider.dart';
  18. typedef OnSuccess = dynamic Function(dynamic data);
  19. typedef OnError = dynamic Function(String e);
  20. typedef OnCache = dynamic Function(dynamic data);
  21. final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
  22. enum NetMethod {
  23. GET,
  24. POST,
  25. PUT,
  26. DELETE,
  27. OPTIONS,
  28. PATCH,
  29. UPDATE,
  30. HEAD,
  31. }
  32. class NetUtil {
  33. Dio _dio;
  34. NetUtil._();
  35. static NetUtil _instance;
  36. static NetUtil getInstance() {
  37. if (_instance == null) {
  38. _instance = NetUtil._();
  39. }
  40. return _instance;
  41. }
  42. get dio async {
  43. if (_dio == null) {
  44. var setting = await NativeUtil.getSetting();
  45. String domain = setting['domain']; //'http://www.hairuyi.com/';
  46. _config(domain, proxyUrl: '192.168.0.66:8866');
  47. }
  48. return _dio;
  49. }
  50. /// 配置网络请求,基础地址,代理地址,如:192.168.0.123:8888(代理地址生产环境无效)
  51. /// apiVersion 接口版本
  52. void _config(String baseUrl, {String proxyUrl}) {
  53. _dio = Dio(BaseOptions(
  54. method: "post",
  55. baseUrl: baseUrl,
  56. connectTimeout: 15000,
  57. receiveTimeout: 15000,
  58. contentType: Headers.jsonContentType,
  59. followRedirects: true,
  60. headers: {'device': 'wx_applet', 'Platform': 'wx_applet'},
  61. validateStatus: (_) {
  62. return true;
  63. }));
  64. _dio.interceptors.add(_NetInterceptors());
  65. // _dio.interceptors.add(LogInterceptor());
  66. const bool inProduction = const bool.fromEnvironment("dart.vm.product");
  67. if (proxyUrl != null && proxyUrl != '' && !inProduction) {
  68. _setProxy(proxyUrl);
  69. }
  70. }
  71. /// 设置代理
  72. void _setProxy(String proxyUrl) {
  73. (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (HttpClient client) {
  74. client.findProxy = (uri) {
  75. return "PROXY $proxyUrl";
  76. };
  77. client.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
  78. };
  79. }
  80. /// 同步请求
  81. static Future<dynamic> post(String path, {Map<String, dynamic> params, NetMethod method = NetMethod.POST, bool cache = false}) async {
  82. if (params == null) {
  83. params = {};
  84. }
  85. // 根据请求参数,获取缓存的Key
  86. String cacheKey = getRequestParamsCachedKey(params, path);
  87. // 参数签名 TODO 加密?
  88. // post请求的参数
  89. Map<String, dynamic> bodyParams = params;
  90. // 请求头参数
  91. Map<String, dynamic> headParam = await _getMustHeadParams();
  92. Response response;
  93. try {
  94. Dio dio = await NetUtil.getInstance().dio;
  95. response = await dio.request(
  96. path,
  97. data: !EmptyUtil.isEmpty(bodyParams) ? bodyParams : null,
  98. options: Options(method: enumToString(method), headers: headParam),
  99. );
  100. } on DioError catch (e) {
  101. _formatError(e);
  102. }
  103. try {
  104. var result = response.data is Map ? response.data : jsonDecode(response.data);
  105. // TODO 解密?
  106. if (isSuccess(result)) {
  107. // 缓存成功的数据
  108. if (cache) {
  109. _setCallBackCacheData(cacheKey, response.data is Map ? jsonEncode(response.data) : response.data);
  110. }
  111. return result;
  112. // 缓存返回的数据
  113. } else {
  114. Logger.error('error: ' + result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  115. if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] != 401000) {
  116. Fluttertoast.showToast(
  117. msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG],
  118. toastLength: Toast.LENGTH_SHORT,
  119. gravity: ToastGravity.BOTTOM,
  120. );
  121. }
  122. // 退出登陆
  123. if(result[GlobalConfig.HTTP_RESPONSE_KEY_CODE]?.toString() == '401003'){
  124. try {
  125. Future.delayed(Duration(seconds: 0)).then((onValue) {
  126. BuildContext context = navigatorKey.currentState.overlay.context;
  127. Provider.of<UserInfoNotifier>(context, listen: false).unLogin();
  128. });
  129. }catch(e,s ){
  130. Logger.error(e,s );
  131. }
  132. }
  133. return result;
  134. }
  135. } catch (e) {
  136. return null;
  137. }
  138. }
  139. /// 异步请求
  140. static void request(String path, {NetMethod method = NetMethod.GET, Map<String, dynamic> params, OnSuccess onSuccess, OnError onError, OnCache onCache}) async {
  141. if (params == null) {
  142. params = {};
  143. }
  144. // 根据请求参数,获取缓存的Key
  145. String cacheKey = getRequestParamsCachedKey(params, path);
  146. // // 读取缓存回调
  147. if (onCache != null) {
  148. await _onCallBackCacheData(onCache, cacheKey);
  149. }
  150. try {
  151. Map result = await NetUtil.post(path, method: method, params: params);
  152. // TODO 解密?
  153. if (isSuccess(result)) {
  154. if (onSuccess != null) {
  155. onSuccess(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  156. }
  157. return;
  158. }
  159. if (onError != null) {
  160. onError(!EmptyUtil.isEmpty(result) ? !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]) ? result[GlobalConfig.HTTP_RESPONSE_KEY_MSG] : '未知错误' : '未知错误');
  161. }
  162. } catch (e) {
  163. Logger.error('error: ' + e.toString());
  164. if (onError != null) {
  165. onError(e?.toString() ?? '未知错误');
  166. }
  167. }
  168. return;
  169. }
  170. /// 统一添加必要的参数
  171. // static Future<Map<String, dynamic>> signParams(Map<String, dynamic> params) async {
  172. // // 应用信息
  173. // PackageInfo packageInfo = await PackageInfo.fromPlatform();
  174. // // 原生传的信息
  175. // Map setting = await NativeUtil.getSetting();
  176. //
  177. // if (Platform.isIOS) {
  178. // IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
  179. // // 设备
  180. // params["platform"] = "ios";
  181. // // 设备系统版本
  182. // params["system_version"] = iosInfo?.systemVersion;
  183. // // 设备型号 如:iPhone 11pro
  184. // params['device_model'] = iosInfo?.name;
  185. // // 设备型号,如:MLMF2LL/A
  186. // // params['device_number'] = iosInfo?.model;
  187. // // 设备ID
  188. // params['device_id'] = iosInfo?.identifierForVendor;
  189. // } else if (Platform.isAndroid) {
  190. // AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
  191. // // 设备
  192. // params["platform"] = "android";
  193. // // 设备系统版本
  194. // params["system_version"] = "Android ${androidInfo?.version?.release}";
  195. // // 设备型号 如:iPhone 11pro
  196. // params['device_model'] = androidInfo?.model;
  197. // // 设备型号,如:MLMF2LL/A
  198. // // params['device_number'] = androidInfo?.id;
  199. // // 设备Id
  200. // params['device_id'] = androidInfo?.androidId;
  201. // }
  202. // // 应用版本号
  203. // params["app_version_name"] = packageInfo.version;
  204. // params["app_version"] = -1 ;// packageInfo.buildNumber;
  205. // // 分辨率
  206. // params["solution"] =
  207. // "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}";
  208. //
  209. // // 站长ID
  210. // String masterId = setting['master_id'];
  211. // if (null != masterId &&
  212. // masterId != '' &&
  213. // (!params.containsKey('master_id') || params['master_id'] == '')) {
  214. // params['master_id'] = masterId;
  215. // }
  216. //
  217. // // secret_key
  218. // params['secret_key'] = setting['secret_key'];
  219. //
  220. // // token 读取SP缓存中的用户token
  221. // String token = await SharedPreferencesUtil.getStringValue(
  222. // GlobalConfig.SHARED_KEY_TOKEN);
  223. // if (!EmptyUtil.isEmpty(token)) {
  224. // params['token'] = token;
  225. // }
  226. //
  227. // // 当前时间戳:秒
  228. // params["time"] = TimeUtil.getNowTime();
  229. //
  230. // // 过滤空字段,过滤首尾空格
  231. // Map<String, dynamic> filters = Map<String, dynamic>();
  232. // params.forEach((key, value) {
  233. // if (key != '' && value != '') {
  234. // filters[key] = (value is String) ? value.trim() : value;
  235. // }
  236. // });
  237. // params = filters;
  238. //
  239. // List<String> list = List<String>();
  240. // params.forEach((key, value) {
  241. // list.add(key.toString() + value.toString());
  242. // });
  243. // params["sign"] = signWithArray(list);
  244. // return params;
  245. // }
  246. /// 获取必须的请求参数(用于请求头部)
  247. static Future<Map<String, String>> _getMustHeadParams() async {
  248. Map<String, String> params = new HashMap<String, String>();
  249. // 应用信息
  250. PackageInfo packageInfo = await PackageInfo.fromPlatform();
  251. // 原生传的信息
  252. Map setting = await NativeUtil.getSetting();
  253. if (Platform.isIOS) {
  254. IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
  255. // 设备
  256. params["platform"] = "ios";
  257. // 设备系统版本
  258. params["os_version"] = iosInfo?.systemVersion?.toString();
  259. // 设备型号 如:iPhone 11pro
  260. params['device_model'] = iosInfo?.name?.toString();
  261. // 设备ID
  262. params['device_id'] = iosInfo?.identifierForVendor?.toString();
  263. // idfa
  264. params['idfa'] = iosInfo?.identifierForVendor?.toString();
  265. } else if (Platform.isAndroid) {
  266. AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
  267. // 设备
  268. params["platform"] = "android";
  269. // 设备系统版本
  270. params["os_version"] = "Android ${androidInfo?.version?.release}";
  271. // 设备型号 如:iPhone 11pro
  272. params['device_model'] = androidInfo?.model?.toString();
  273. // 设备Id
  274. params['device_id'] = androidInfo?.androidId?.toString();
  275. // imei
  276. params['imei'] = await _getImei();
  277. }
  278. // 应用版本号
  279. params["app_version_name"] = packageInfo.version?.toString();
  280. params["app_version"] = packageInfo.buildNumber?.toString();
  281. // 分辨率
  282. params["solution"] = "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}";
  283. // 站长ID
  284. String masterId = setting['master_id'];
  285. if (null != masterId && masterId != '' && (!params.containsKey('master_id') || params['master_id'] == '')) {
  286. params['master_id'] = masterId ?? 'template_database';
  287. }
  288. // token 读取SP缓存中的用户token
  289. String token = await SharedPreferencesUtil.getStringValue(GlobalConfig.SHARED_KEY_TOKEN);
  290. if (!EmptyUtil.isEmpty(token)) {
  291. params['token'] = token;
  292. }
  293. // secret_key
  294. params['secret_key'] = setting['secret_key'] ?? '';
  295. // 当前时间戳:秒
  296. params["time"] = TimeUtil.getNowTime();
  297. // 过滤空字段,过滤首尾空格
  298. Map<String, String> filters = Map<String, String>();
  299. params.forEach((key, value) {
  300. if (key != '' && value != '') {
  301. filters[key] = (value is String) ? value.trim() : value;
  302. }
  303. });
  304. params = filters;
  305. List<String> list = List<String>();
  306. params.forEach((key, value) {
  307. list.add(key.toString() + '=' + value.toString() + '&');
  308. });
  309. params["sign"] = signWithArray(list);
  310. params.remove('secret_key');
  311. return params;
  312. }
  313. /// 获取Android imei
  314. static Future<String> _getImei() async{
  315. try{
  316. return await ImeiPlugin.getImei(shouldShowRequestPermissionRationale: true);
  317. }catch(e, s){
  318. Logger.error(e, s);
  319. }
  320. return null;
  321. }
  322. /// 获取请求缓存成功的数据
  323. static Future<void> _onCallBackCacheData(OnCache onCache, String cacheKey) async {
  324. // 读取缓存
  325. Map<String, dynamic> cacheMap = await SharedPreferencesUtil.getNetCacheResult(cacheKey);
  326. if (!EmptyUtil.isEmpty(cacheMap) &&
  327. cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) &&
  328. (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE ||
  329. cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}') &&
  330. !EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
  331. onCache(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  332. return;
  333. }
  334. return;
  335. }
  336. /// 缓存请求成功的数据
  337. static void _setCallBackCacheData(String cacheKey, String value) async {
  338. SharedPreferencesUtil.setNetCacheResult(cacheKey, value);
  339. }
  340. /// 根据请求参数,获取缓存的数据
  341. static Future<dynamic> getRequestCachedData(String url, {Map<String, dynamic> params}) async {
  342. Map<String, dynamic> cacheMap = await SharedPreferencesUtil.getNetCacheResult(getRequestParamsCachedKey(params, url));
  343. if (!EmptyUtil.isEmpty(cacheMap) &&
  344. cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) &&
  345. (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE ||
  346. cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}') &&
  347. !EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
  348. return cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA];
  349. }
  350. return null;
  351. }
  352. /// 判断后台返回是否成功
  353. static bool isSuccess(Map<String, dynamic> data) {
  354. try {
  355. if (!EmptyUtil.isEmpty(data) &&
  356. (data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == GlobalConfig.RESPONSE_SUCCESS_CODE ||
  357. data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] == '${GlobalConfig.RESPONSE_SUCCESS_CODE}')) {
  358. return true;
  359. }
  360. } catch (e) {
  361. return false;
  362. }
  363. return false;
  364. }
  365. /// 根据请求参数,获取缓存的Key
  366. static String getRequestParamsCachedKey(Map<String, dynamic> map, String path) {
  367. if (EmptyUtil.isEmpty(map)) {
  368. return EncodeUtil.generateMd5(path);
  369. }
  370. return EncodeUtil.generateMd5(path + map.toString());
  371. }
  372. // 七牛云文件上传
  373. static Future uploadFile(String url, File file, {String method = 'POST', Map<String, dynamic> params, OnSuccess onSuccess, OnError onError}) async {
  374. if (params == null) {
  375. params = {};
  376. }
  377. Dio dio = Dio(BaseOptions(
  378. method: "post",
  379. connectTimeout: 15000,
  380. receiveTimeout: 15000,
  381. contentType: Headers.jsonContentType,
  382. followRedirects: true,
  383. ));
  384. params['file'] = await MultipartFile.fromFile(file.path);
  385. FormData format = FormData.fromMap(params);
  386. return dio.request(url, data: format, options: Options(method: method));
  387. }
  388. /// 签名
  389. static String signWithArray(List<String> params) {
  390. // 字母升序
  391. params.sort();
  392. String result = "";
  393. params.forEach((param) {
  394. result += param;
  395. });
  396. result = result.substring(0, result.length - 1);
  397. return EncodeUtil.generateMd5(result);
  398. }
  399. /*
  400. * error统一处理
  401. */
  402. static void _formatError(DioError e) {
  403. if (e.type == DioErrorType.CONNECT_TIMEOUT) {
  404. Logger.error('连接超时: ${e.toString()}');
  405. } else if (e.type == DioErrorType.SEND_TIMEOUT) {
  406. Logger.error('请求超时: ${e.toString()}');
  407. } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
  408. Logger.error('响应超时: ${e.toString()}');
  409. } else if (e.type == DioErrorType.RESPONSE) {
  410. Logger.error('出现异常: ${e.toString()}');
  411. } else if (e.type == DioErrorType.CANCEL) {
  412. Logger.error('请求取消: ${e.toString()}');
  413. } else {
  414. Logger.error('未知错误: ${e.toString()}');
  415. }
  416. }
  417. }
  418. /**
  419. * @description: 网络请求拦截器
  420. * @param {type}
  421. * @return:
  422. */
  423. class _NetInterceptors extends InterceptorsWrapper {
  424. @override
  425. Future onRequest(RequestOptions options) {
  426. Logger.net(options?.uri?.toString(), data: options.data.toString());
  427. // TODO 加密?
  428. return super.onRequest(options);
  429. }
  430. @override
  431. Future onResponse(Response response) {
  432. Logger.endNet(response?.request?.uri?.toString(), data: response?.data?.toString() ?? '');
  433. // TODO 解密?
  434. return super.onResponse(response);
  435. }
  436. @override
  437. Future onError(DioError err) {
  438. // Logger.error(err);
  439. return super.onError(err);
  440. }
  441. }