基础库
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 

388 рядки
12 KiB

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