基础库
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 

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