基础库
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.
 
 
 
 
 

442 wiersze
13 KiB

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