diff --git a/lib/network/request_interceptor.dart b/lib/network/request_interceptor.dart index 46f43aea..c6c87ba2 100755 --- a/lib/network/request_interceptor.dart +++ b/lib/network/request_interceptor.dart @@ -28,5 +28,10 @@ FutureOr requestInterceptor(Request request) async { xToken = LoginData.fromJson(jsonDecode(data)).accessToken; } request.headers['Authorization'] = "Bearer ${xToken ?? ''}"; + + final String? deviceID = await Storage.getString(appDeviceID); + if (deviceID != null && deviceID.isNotEmpty) { + request.headers['deviceID'] = deviceID; + } return request; } diff --git a/lib/tools/bindings/app_binding.dart b/lib/tools/bindings/app_binding.dart index f87b295b..82dd0b8e 100755 --- a/lib/tools/bindings/app_binding.dart +++ b/lib/tools/bindings/app_binding.dart @@ -1,12 +1,20 @@ - import 'package:get/get.dart'; - -import '../../network/api_provider.dart'; -import '../../network/api_repository.dart'; +import 'package:star_lock/tools/storage.dart'; +import 'package:uuid/uuid.dart'; class AppBindings extends Bindings { @override void dependencies() { + initDeviceId(); } -} \ No newline at end of file + Future initDeviceId() async { + final String? deviceID = await Storage.getString(appDeviceID); + final bool isNullOrBlank = GetUtils.isNullOrBlank(deviceID) ?? true; + if (isNullOrBlank) { + final String uuidV4 = const Uuid().v4(); + print('initDeviceId UUID:v4: $uuidV4'); + await Storage.setString(appDeviceID, uuidV4); + } + } +} diff --git a/lib/tools/debounce_throttle_tool.dart b/lib/tools/debounce_throttle_tool.dart new file mode 100644 index 00000000..f42f0d12 --- /dev/null +++ b/lib/tools/debounce_throttle_tool.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:flutter/widgets.dart'; + +class DebounceThrottleTool { + DebounceThrottleTool(this._debounceTime, this._callback); + + Timer? _timer; + final Duration _debounceTime; + final void Function(T param) _callback; + + void trigger(T param) { + _timer?.cancel(); + _timer = Timer(_debounceTime, () { + _callback(param); + _timer = null; + }); + } + + void cancel() { + _timer?.cancel(); + } +} diff --git a/lib/tools/push/xs_jPhush.dart b/lib/tools/push/xs_jPhush.dart index 701fee30..7639dd86 100755 --- a/lib/tools/push/xs_jPhush.dart +++ b/lib/tools/push/xs_jPhush.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:ffi'; import 'dart:io'; import 'package:flutter/foundation.dart'; @@ -9,6 +10,7 @@ import 'package:star_lock/flavors.dart'; import 'package:star_lock/mine/minePersonInfo/minePersonInfoEditAccount/minePersonInfoEditAccount/mineUnbindPhoneOrEmail_entity.dart'; import 'package:star_lock/network/api_repository.dart'; import 'package:star_lock/tools/baseGetXController.dart'; +import 'package:star_lock/tools/debounce_throttle_tool.dart'; import 'package:star_lock/tools/push/message_management.dart'; import 'package:star_lock/tools/push/notification_service.dart'; import 'package:star_lock/tools/storage.dart'; @@ -28,31 +30,32 @@ class XSJPushProvider { 9: 'jiguang' }; final JPush jpush = JPush(); - Completer>? _jpushRegistrationIdCompleter; - Completer>? _vendorTokenCompleter; + DebounceThrottleTool? _debounceThrottleTool; Future resetJPushService() async { debugPrint("resetJPushService start"); - jpush.setup( - appKey: '', - channel: 'flutter_channel', - production: F.isProductionEnv, - debug: !F.isProductionEnv, - ); + for (final MapEntry entry in channelTypeMapping.entries) { + await Storage.removeData('old_${entry.value}'); + } + debugPrint("resetJPushService end"); } // appKey: 251fc8074820d122b6de58d2--鑫泓佳AppKey // appKey: 7ff37d174c1a568a89e98dad--sky Future initJPushService() async { debugPrint("initJPushService start"); - _jpushRegistrationIdCompleter = Completer>(); - _vendorTokenCompleter = Completer>(); final String? data = await Storage.getString(saveUserLoginData); if (data == null || data.isEmpty) { AppLog.log('No user data found.'); return; } + _debounceThrottleTool = DebounceThrottleTool( + const Duration(milliseconds: 1500), + (dynamic param) { + bindPushChannels(); + }, + ); AppLog.log('jPushKey ${F.jPushKey}'); // final String? bundleIdentifier = // await NativeInteractionTool().getBundleIdentifier(); @@ -86,35 +89,20 @@ class XSJPushProvider { case CMD_GET_REGISTRATION_ID: final bool isNullOrBlank = GetUtils.isNullOrBlank(data['message']) ?? true; - if (!(_jpushRegistrationIdCompleter?.isCompleted ?? true) && - !isNullOrBlank) { - await Storage.setString(pushDeviceID, data['message']); - AppLog.log('flutter get registration id : ${data['message']}'); - _jpushRegistrationIdCompleter?.complete({ - 'channel': 'jiguang', - 'channelToken': data['message'] - }); - final String? channel2TokenStr = - await Storage.getString(vendorPushChannelInfo); - if (Platform.isAndroid && - !(_vendorTokenCompleter?.isCompleted ?? true) && - channel2TokenStr != null) { - _vendorTokenCompleter?.complete(jsonDecode(channel2TokenStr)); - } + if (!isNullOrBlank) { + AppLog.log( + 'flutter get jiguang registration id : ${data['message']}'); + await Storage.setString('jiguang', data['message']); + _debounceThrottleTool?.trigger(data['message']); } break; case CMD_GET_TOKEN: final bool isNullOrBlank = GetUtils.isNullOrBlank(data['token']) ?? true; - if (!(_vendorTokenCompleter?.isCompleted ?? true) && - !isNullOrBlank) { - final Map channel2Token = { - 'channel': channelTypeMapping[data['platform']], - 'channelToken': data['token'] - }; + if (!isNullOrBlank) { await Storage.setString( - vendorPushChannelInfo, jsonEncode(channel2Token)); - _vendorTokenCompleter?.complete(channel2Token); + channelTypeMapping[data['platform']] ?? '', data['token']); + _debounceThrottleTool?.trigger(data['token']); } break; } @@ -147,31 +135,54 @@ class XSJPushProvider { return Future.value(); }, ); - bindPushChannels(); } // jpush 统一推送通道设备绑定 Future bindPushChannels() async { try { - if (_jpushRegistrationIdCompleter == null || - _vendorTokenCompleter == null) { + debugPrint("BindPushChannels start"); + bool needReBindPushToken = false; + final Map newVendorsPushToken = {}; + final Map oldVendorsPushToken = {}; + for (final String channel in channelTypeMapping.values) { + debugPrint("BindPushChannels channel $channel"); + final String? newVendorToken = await Storage.getString(channel); + debugPrint("BindPushChannels newVendorToken $newVendorToken"); + newVendorsPushToken[channel] = newVendorToken ?? ''; + final String? oldVendorToken = await Storage.getString('old_$channel'); + debugPrint("BindPushChannels oldVendorToken $oldVendorToken"); + oldVendorsPushToken['old_$channel'] = oldVendorToken ?? ''; + if (newVendorToken != oldVendorToken) { + needReBindPushToken = true; + } + } + if (!needReBindPushToken) { + AppLog.log('vendorToken 未变动,无需重新绑定'); return; } - debugPrint("await PushChannels start"); - final List> channels = await Future.wait([ - _jpushRegistrationIdCompleter!.future, - _vendorTokenCompleter!.future - ]); - final String? registrationId = await Storage.getString(pushDeviceID); - debugPrint("await PushChannels end"); + + final List> channels = newVendorsPushToken.entries + .where((entry) => !(GetUtils.isNullOrBlank(entry.value) ?? true)) + .map((entry) => { + 'channel': entry.key, + 'channelToken': entry.value + }) + .toList(); + debugPrint("BindPushChannels ${channels}"); + final String? deviceID = await Storage.getString(appDeviceID); final MineUnbindPhoneOrEmailEntity entity = - await ApiRepository.to.pushBindChannels(registrationId!, channels); + await ApiRepository.to.pushBindChannels(deviceID!, channels); if (entity.errorCode!.codeIsSuccessful) { - AppLog.log('绑定成功'); + AppLog.log('vendorToken绑定成功,准备更新本地缓存'); + await Future.wait(channels.map((Map entry) => + Storage.setString( + 'old_${entry['channel']}', entry['channelToken']))); + AppLog.log('vendorToken绑定成功,更新本地缓存完成'); } else { AppLog.log('绑定失败'); } } catch (e) { + AppLog.log('vendorToken绑定失败,下次重启应用重新绑定'); AppLog.log('Error binding device ID: $e'); } } diff --git a/lib/tools/storage.dart b/lib/tools/storage.dart index 5132a39c..2ed936aa 100755 --- a/lib/tools/storage.dart +++ b/lib/tools/storage.dart @@ -24,7 +24,8 @@ const String isAgreeCamera = 'isAgreeCamera'; //是否同意获取相机/相册 const String isShowUpdateVersion = 'isShowUpdateVersion'; //是否更新弹窗 const String saveLockAlias = 'saveLockAlias'; //锁别名 -const String pushDeviceID = 'pushDeviceID'; //推送设备ID +const String appDeviceID = 'appDeviceID'; //推送设备ID +const String pushDeviceID = 'pushDeviceID'; //Jpush推送设备ID const String vendorPushChannelInfo = 'pushChannelInfo'; //推送设备ID const String saveIsVip = 'saveIsVip'; //是否是VIP