基础库
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 

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