基础库
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 

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