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

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