基础库
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

373 строки
11 KiB

  1. import 'package:dio/dio.dart';
  2. import 'package:dio/adapter.dart';
  3. import 'package:zhiying_comm/util/empty_util.dart';
  4. import 'dart:io';
  5. import 'dart:ui';
  6. import 'dart:convert';
  7. import 'package:zhiying_comm/zhiying_comm.dart';
  8. import 'package:package_info/package_info.dart';
  9. import 'package:device_info/device_info.dart';
  10. import 'package:fluttertoast/fluttertoast.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}) async {
  78. if (params == null) {
  79. params = {};
  80. }
  81. Map<String, dynamic> sign = await signParams(params);
  82. Response response;
  83. try {
  84. Dio dio = await NetUtil.getInstance().dio;
  85. response = await dio.post(path, data: sign);
  86. } on DioError catch (e) {
  87. _formatError(e);
  88. // Logger.error(e);
  89. }
  90. if (response == null) {
  91. return null;
  92. }
  93. if (response.statusCode != 200) {
  94. // 请求错误
  95. const bool inProduction = const bool.fromEnvironment("dart.vm.product");
  96. if (!inProduction) {
  97. Fluttertoast.showToast(msg: response.statusMessage);
  98. }
  99. // log.e(response.statusMessage);
  100. return null;
  101. }
  102. var result = response.data;
  103. if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  104. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  105. result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  106. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') {
  107. Logger.error(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  108. Fluttertoast.showToast(
  109. msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG],
  110. toastLength: Toast.LENGTH_SHORT,
  111. gravity: ToastGravity.BOTTOM,
  112. );
  113. }
  114. return result;
  115. }
  116. /// 异步请求
  117. static void request(String path,
  118. {NetMethod method = NetMethod.GET,
  119. Map<String, dynamic> params,
  120. Map<String, File> multiFiles,
  121. OnSuccess onSuccess,
  122. OnError onError,
  123. OnCache onCache}) async {
  124. if (params == null) {
  125. params = {};
  126. }
  127. // 根据请求参数,获取缓存的Key
  128. String cacheKey = _getRequestParamsCachedKey(params, path);
  129. // // 读取缓存回调
  130. if (onCache != null) {
  131. await _onCallBackCacheData(onCache, cacheKey);
  132. }
  133. // 参数签名
  134. Map<String, dynamic> sign = await signParams(params);
  135. Response response;
  136. try {
  137. Dio dio = await NetUtil.getInstance().dio;
  138. response = await dio.request(path,
  139. data: sign, options: Options(method: enumToString(method)));
  140. } on DioError catch (e) {
  141. _formatError(e);
  142. }
  143. try {
  144. var result =
  145. response.data is Map ? response.data : jsonDecode(response.data);
  146. // TODO 解密?
  147. if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  148. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  149. result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  150. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') {
  151. if (onSuccess != null) {
  152. onSuccess(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  153. // 缓存返回的数据
  154. _setCallBackCacheData(cacheKey,
  155. response.data is Map ? jsonEncode(response.data) : response.data);
  156. }
  157. return;
  158. }
  159. Logger.error('error: ' + result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  160. Fluttertoast.showToast(
  161. msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG],
  162. toastLength: Toast.LENGTH_SHORT,
  163. gravity: ToastGravity.BOTTOM,
  164. );
  165. if (onError != null) {
  166. onError(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG] ?? '未知错误');
  167. }
  168. } catch (e) {
  169. Logger.error('error: ' + e.toString());
  170. if (onError != null) {
  171. onError(e?.toString() ?? '未知错误');
  172. }
  173. }
  174. return;
  175. }
  176. /// 统一添加必要的参数
  177. static Future<Map<String, dynamic>> signParams(
  178. Map<String, dynamic> params) async {
  179. // 应用信息
  180. PackageInfo packageInfo = await PackageInfo.fromPlatform();
  181. // 原生传的信息
  182. Map setting = await NativeUtil.getSetting();
  183. if (Platform.isIOS) {
  184. IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
  185. // 设备
  186. params["platform"] = "ios";
  187. // 设备系统版本
  188. params["system_version"] = iosInfo?.systemVersion;
  189. // 设备型号 如:iPhone 11pro
  190. params['device_model'] = iosInfo?.name;
  191. // 设备型号,如:MLMF2LL/A
  192. // params['device_number'] = iosInfo?.model;
  193. // 设备ID
  194. params['device_id'] = iosInfo?.identifierForVendor;
  195. } else if (Platform.isAndroid) {
  196. AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
  197. // 设备
  198. params["platform"] = "android";
  199. // 设备系统版本
  200. params["system_version"] = "Android ${androidInfo?.version?.release}";
  201. // 设备型号 如:iPhone 11pro
  202. params['device_model'] = androidInfo?.model;
  203. // 设备型号,如:MLMF2LL/A
  204. // params['device_number'] = androidInfo?.id;
  205. // 设备Id
  206. params['device_id'] = androidInfo?.androidId;
  207. }
  208. // 应用版本号
  209. params["app_version"] = packageInfo.version;
  210. // 分辨率
  211. params["solution"] =
  212. "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}";
  213. // 站长ID
  214. String masterId = setting['master_id'];
  215. if (null != null &&
  216. masterId != '' &&
  217. (!params.containsKey('master_id') || params['master_id'] == '')) {
  218. params['master_id'] = masterId;
  219. }
  220. // secret_key
  221. params['secret_key'] = setting['secret_key'];
  222. // token
  223. String token = setting['token'];
  224. if (token != null &&
  225. token != '' &&
  226. (!params.containsKey('token') || params['token'] == '')) {
  227. params['token'] = token;
  228. }
  229. // 当前时间戳:秒
  230. params["time"] = TimeUtil.getNowTime();
  231. // 过滤空字段,过滤首尾空格
  232. Map<String, dynamic> filters = Map<String, dynamic>();
  233. params.forEach((key, value) {
  234. if (key != '' && value != '') {
  235. filters[key] = (value is String) ? value.trim() : value;
  236. }
  237. });
  238. params = filters;
  239. List<String> list = List<String>();
  240. params.forEach((key, value) {
  241. list.add(key.toString() + value.toString());
  242. });
  243. params["sign"] = signWithArray(list);
  244. return params;
  245. }
  246. /// 获取请求缓存成功的数据
  247. static Future<void> _onCallBackCacheData(
  248. OnCache onCache, String cacheKey) async {
  249. // 读取缓存
  250. Map<String, dynamic> cacheMap =
  251. await SharedPreferencesUtil.getNetCacheResult(cacheKey);
  252. if (!EmptyUtil.isEmpty(cacheMap) &&
  253. cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) &&
  254. (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  255. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  256. cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  257. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') &&
  258. null != cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]) {
  259. onCache(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  260. return;
  261. }
  262. return;
  263. }
  264. /// 缓存请求成功的数据
  265. static void _setCallBackCacheData(String cacheKey, String value) async {
  266. SharedPreferencesUtil.setNetCacheResult(cacheKey, value);
  267. }
  268. /// 根据请求参数,获取缓存的Key
  269. static String _getRequestParamsCachedKey(
  270. Map<String, dynamic> map, String path) {
  271. if (EmptyUtil.isEmpty(map)) {
  272. return EncodeUtil.generateMd5(path);
  273. }
  274. return EncodeUtil.generateMd5(path + map.toString());
  275. }
  276. /// 签名
  277. static String signWithArray(List<String> params) {
  278. // 字母升序
  279. params.sort();
  280. String result = "";
  281. // result += secret_key;
  282. params.forEach((param) {
  283. result += param;
  284. });
  285. // result += secret_key;
  286. return EncodeUtil.generateMd5(result);
  287. }
  288. /*
  289. * error统一处理
  290. */
  291. static void _formatError(DioError e) {
  292. if (e.type == DioErrorType.CONNECT_TIMEOUT) {
  293. Logger.error('连接超时: ${e.toString()}');
  294. } else if (e.type == DioErrorType.SEND_TIMEOUT) {
  295. Logger.error('请求超时: ${e.toString()}');
  296. } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
  297. Logger.error('响应超时: ${e.toString()}');
  298. } else if (e.type == DioErrorType.RESPONSE) {
  299. Logger.error('出现异常: ${e.toString()}');
  300. } else if (e.type == DioErrorType.CANCEL) {
  301. Logger.error('请求取消: ${e.toString()}');
  302. } else {
  303. Logger.error('未知错误: ${e.toString()}');
  304. }
  305. }
  306. }
  307. /**
  308. * @description: 网络请求拦截器
  309. * @param {type}
  310. * @return:
  311. */
  312. class _NetInterceptors extends InterceptorsWrapper {
  313. @override
  314. Future onRequest(RequestOptions options) {
  315. Logger.net(options?.uri?.toString(), data: options.data.toString());
  316. // TODO 加密?
  317. return super.onRequest(options);
  318. }
  319. @override
  320. Future onResponse(Response response) {
  321. Logger.endNet(response?.request?.uri?.toString(),
  322. data: response?.data?.toString() ?? '');
  323. // TODO 解密?
  324. return super.onResponse(response);
  325. }
  326. @override
  327. Future onError(DioError err) {
  328. // Logger.error(err);
  329. return super.onError(err);
  330. }
  331. }