基础库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

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