顾海波

【需求】针对大数据平台兼容,todo保存数据库恢复

... ... @@ -173,3 +173,44 @@ class PageA extends StatelessWidget {
}
```
```
{
"_track_id": 2102988140,
"time": 1738981685455,
"type": "track",
"distinct_id": "afa4c7a98b3f6467",
"anonymous_id": "afa4c7a98b3f6467",
"event": "$AppEnd",
"properties": {
"$carrier": "NONE",
"$os_version": "13",
"$model": "C310CS",
"$os": "Android",
"$screen_width": 1200,
"$brand": "BOE",
"$screen_height": 1920,
"$app_version": "2.0.2",
"$lib": "Android",
"$device_id": "afa4c7a98b3f6467",
"$app_name": "Ewin Reading",
"$lib_version": "5.3.3",
"$timezone_offset": -480,
"$app_id": "com.ewin.tech.reading",
"$mac": "020000000000",
"$manufacturer": "BOE",
"$sn": "C310CS014820000006",
"$wifi": true,
"$network_type": "WIFI",
"$screen_orientation": "portrait",
"$screen_brightness": 204,
"$screen_name": "com.boe.usercenterlibrary.LoginActivity",
"$title": "Ewin Reading",
"$event_duration": 0,
"$lib_method": "autoTrack",
"$is_first_day": true
}
}
```
... ...
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/central' }
google()
mavenCentral()
}
... ... @@ -12,6 +16,9 @@ buildscript {
allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/central' }
google()
mavenCentral()
}
... ...
... ... @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.5-all.zip
... ...
... ... @@ -19,20 +19,29 @@ class _MyAppState extends State<MyApp> {
void initState() {
AutoTrack()
.config(AutoTrackConfig(
samplingRate: 0.9, // 采样率
host: "https://sitigrs.boeart.cn",
appKey: "KEY_oVfNOQLQ",
appSecret: "w21tRLJt7LBVYJtD",
samplingRate: 1, // 采样率
enableUpload : true,
uploadInterval:5,
eventHandler: (model) => {print('event handler ${model.type}')},
pageConfigs: [
AutoTrackPageConfig<Home>(
pageID: 'home',
),
AutoTrackPageConfig<PageA>(
pageID: 'page_a',
),
]))
// .enablePageLeave()
// .enablePageView()
// .enableClick()
// .enableDrag()
// .enableIgnoreNullKey()
.enableLog()
.enable()
.enablePageLeave()
.enablePageView()
.enableClick()
.enableDrag()
.enableIgnoreNullKey()
.enableLog();
;
super.initState();
// AutoTrack().updateSampleRate(0.5); 更新采样率
... ...
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
android_id:
dependency: transitive
description:
name: android_id
sha256: "748ba5f93dd5c497e675d8eaa1404346ce4d1794464ea654576ff192d153b92a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.0"
archive:
dependency: transitive
description:
name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.6.1"
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.11.0"
auto_track:
... ... @@ -15,13 +31,13 @@ packages:
path: ".."
relative: true
source: path
version: "0.0.1"
version: "0.1.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
characters:
... ... @@ -29,7 +45,7 @@ packages:
description:
name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
clock:
... ... @@ -37,7 +53,7 @@ packages:
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
collection:
... ... @@ -45,57 +61,74 @@ packages:
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.18.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.3"
version: "3.0.6"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
url: "https://pub.dev"
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.6"
dio:
version: "1.0.8"
device_info_plus:
dependency: transitive
description:
path: "packages/device_info_plus/device_info_plus"
ref: HEAD
resolved-ref: ff774d947bd15d9be08629e3ccfb29dda2e864bd
url: "https://gitee.com/openharmony-sig/flutter_plus_plugins"
source: git
version: "9.1.0"
device_info_plus_platform_interface:
dependency: transitive
description:
name: dio
sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8"
url: "https://pub.dev"
name: device_info_plus_platform_interface
sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.4.1"
version: "7.0.2"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.3"
file:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.4"
version: "7.0.0"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
... ... @@ -111,7 +144,7 @@ packages:
description:
name: flutter_lints
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.3"
flutter_test:
... ... @@ -119,17 +152,30 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: transitive
description:
name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.2"
integration_test:
... ... @@ -137,70 +183,111 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
matcher:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
url: "https://pub.dev"
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.16"
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
url: "https://pub.dev"
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.0"
version: "0.8.0"
meta:
dependency: transitive
description:
name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev"
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
version: "1.12.0"
package_info_plus:
dependency: transitive
description:
path: "packages/package_info_plus/package_info_plus"
ref: HEAD
resolved-ref: ff774d947bd15d9be08629e3ccfb29dda2e864bd
url: "https://gitee.com/openharmony-sig/flutter_plus_plugins"
source: git
version: "4.2.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
path:
dependency: transitive
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev"
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.3"
version: "1.9.0"
platform:
dependency: transitive
description:
name: platform
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
url: "https://pub.dev"
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.2"
version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.8"
process:
dependency: transitive
description:
name: process
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
url: "https://pub.dev"
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.4"
version: "5.0.2"
sky_engine:
dependency: transitive
description: flutter
... ... @@ -211,7 +298,7 @@ packages:
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
sprintf:
... ... @@ -219,7 +306,7 @@ packages:
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.0.0"
stack_trace:
... ... @@ -227,7 +314,7 @@ packages:
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.11.1"
stream_channel:
... ... @@ -235,7 +322,7 @@ packages:
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
string_scanner:
... ... @@ -243,7 +330,7 @@ packages:
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
sync_http:
... ... @@ -251,7 +338,7 @@ packages:
description:
name: sync_http
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.1"
term_glyph:
... ... @@ -259,65 +346,81 @@ packages:
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.1"
version: "0.7.0"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.2"
uuid:
dependency: transitive
description:
name: uuid
sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
url: "https://pub.dev"
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.3.3"
version: "4.5.1"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
url: "https://pub.dev"
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.10.0"
version: "14.2.1"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.0"
version: "1.1.0"
webdriver:
dependency: transitive
description:
name: webdriver
sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49"
url: "https://pub.dev"
sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.3"
win32:
dependency: transitive
description:
name: win32
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.5.4"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
version: "1.1.5"
sdks:
dart: ">=3.2.3 <4.0.0"
flutter: ">=3.3.0"
dart: ">=3.4.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
... ...
... ... @@ -24,9 +24,9 @@ class AutoTrackConfig {
this.useCustomRoute = false, // 使用自定义路由
this.ignoreElementKeys = const [], // 忽略key列表
this.ignoreElementStringKeys = const [],
this.enablePageView = true, // 监听页面进入事件
this.enablePageView = false, // 监听页面进入事件
this.enablePageLeave = false, // 监听页面离开事件
this.enableClick = true, // 监听点击事件
this.enableClick = false, // 监听点击事件
this.enableDrag = false, // 监听拖拽事件
this.enableIgnoreNullKey = false, // 忽略空key事件
this.httpRequestConfig,
... ... @@ -43,6 +43,7 @@ class AutoTrackConfig {
String? trackId;
String? userId;
String? uniqueId;
String? token;
/// 采样率,默认 1 (100%)
double samplingRate;
... ...
... ... @@ -6,16 +6,25 @@ import 'package:crypto/crypto.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/widgets.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:http/http.dart' as http; // 使用 http 包
import '../log/logger.dart';
import '../utils/sign.dart';
import 'config.dart';
typedef UpdateConfigFunc = AutoTrackConfig Function(AutoTrackConfig);
const String GET_TOKEN = "/equipment-auth/equipment/getToken/v1";
const String UPLOAD = "/bd-datapoint/equipment/dataPoint/zip";
class AutoTrackConfigManager {
static final AutoTrackConfigManager instance = AutoTrackConfigManager._();
AutoTrackConfigManager._() {
PackageInfo.fromPlatform().then((value) => _appVersion = value.version);
PackageInfo.fromPlatform().then( (value) {
_appVersion = value.version;
_appName = value.appName;
});
DeviceInfoPlugin().deviceInfo.then((value) {
_deviceInfo = value.data;
_baseDeviceInfo = value;
... ... @@ -26,7 +35,14 @@ class AutoTrackConfigManager {
String _appVersion = '';
String get appVersion => _appVersion;
String _appName = '';
String get appName => _appName;
BaseDeviceInfo? _baseDeviceInfo;
BaseDeviceInfo? get baseDeviceInfo => _baseDeviceInfo;
String? _deviceId;
String? get deviceId => _deviceId;
... ... @@ -67,6 +83,9 @@ class AutoTrackConfigManager {
} else if (_baseDeviceInfo is MacOsDeviceInfo) {
_deviceId =
'${(_baseDeviceInfo as MacOsDeviceInfo).hostName}-${(_baseDeviceInfo as MacOsDeviceInfo).computerName}';
} else if (_baseDeviceInfo is OhosDeviceInfo) {
_deviceId =
'${(_baseDeviceInfo as OhosDeviceInfo).odID}${config.appKey}';
} else {
_deviceId = null;
}
... ... @@ -74,8 +93,72 @@ class AutoTrackConfigManager {
void enableAutoTrack(bool enable) {
_autoTrackEnable = enable;
//获取token
getToken(false);
}
bool _isGetting = false;
void getToken(bool refreshToken) async {
if (_isGetting) {
return;
}
_isGetting = true;
AutoTrackLogger.getInstance().debug('track getToken => start');
await Future.delayed(const Duration(milliseconds: 2000));
try {
int currentSeconds = DateTime.now().millisecondsSinceEpoch;
Map<String, String> sParams = {
"productKey": _config.appKey ?? '',
"equipmentId": _deviceId ?? '',
"otherInfo": _appName,
"time": currentSeconds.toString(),
};
AutoTrackLogger.getInstance().debug('track getToken => param $sParams');
String sign = SignUtil.sign(sParams, _config.appSecret!);
sParams["sign"] = sign;
// 发送 POST 请求(使用 http 包)
final response = await http
.post(
Uri.parse(_config.host! + GET_TOKEN),
body: sParams,
)
.timeout(const Duration(seconds: 10)); // 添加超时
// 处理响应
if (response.statusCode == 200) {
final jsonResponse = jsonDecode(response.body) as Map<String, dynamic>;
final code = jsonResponse['code'] as int;
if (code == 200) {
final tokenValue = jsonResponse['data'] as String;
AutoTrackLogger.getInstance().debug('track getToken => success $tokenValue');
_config.token = tokenValue;
} else {
AutoTrackLogger.getInstance().debug('track getToken => fail $code');
_reGetToken(refreshToken); // 重试
}
} else {
AutoTrackLogger.getInstance().debug('track getToken => failCode: ${response.statusCode}');
_reGetToken(refreshToken); // 非 200 状态码重试
}
} catch (e) {
print('BoeDataAPI Error: $e');
_reGetToken(refreshToken); // 异常重试
} finally {
_isGetting = false; // 无论成功与否,重置标志
}
}
void _reGetToken(bool refreshToken) {
Future.delayed(const Duration(seconds: 2), () => getToken(refreshToken));
}
List<AutoTrackPageConfig> get pageConfigs => _config.pageConfigs;
bool get useCustomRoute => _config.useCustomRoute;
... ... @@ -119,3 +202,4 @@ class AutoTrackConfigManager {
bool get ignoreNullKeyEnable => _config.enableIgnoreNullKey;
}
... ...
... ... @@ -5,6 +5,8 @@ import 'dart:math';
import 'package:auto_track/auto_track/config/manager.dart';
import 'package:auto_track/auto_track/utils/track_model.dart';
import 'package:archive/archive.dart';
import 'package:device_info_plus/device_info_plus.dart';
import '../log/logger.dart';
... ... @@ -39,11 +41,17 @@ class AutoTrackQueue {
_timer = null;
}
/**
* todo 存储数据库,目前会丢失数据
*/
void flush() {
AutoTrackLogger.getInstance().debug('start flush ${_queue.length}');
if (_queue.isEmpty) return;
final uploadList = List.from(_queue);
final List<TrackModel> uploadList = List.from(_queue);
_queue.clear();
final config = AutoTrackConfigManager.instance.config;
final baseDeviceInfo = AutoTrackConfigManager.instance.baseDeviceInfo;
final host = config.host;
if (config.samplingRate != 1) {
if (Random().nextDouble() > config.samplingRate) {
... ... @@ -51,31 +59,156 @@ class AutoTrackQueue {
return;
}
}
String? token = config.token;
if (token == null) {
return;
}
if (host != null) {
final t = DateTime.now().millisecondsSinceEpoch;
httpClient.postUrl(Uri.parse(host)).then((request) {
request.headers.contentType = ContentType.json;
request.write(json.encode({
'app_key': config.appKey ?? '',
'signature': config.signature!(t),
't': t,
'user_id': config.userId ?? '',
'track_id': config.trackId ?? '',
'unique_id':
config.uniqueId ?? AutoTrackConfigManager.instance.deviceId,
'device_id': AutoTrackConfigManager.instance.deviceId,
'data_list': uploadList.map((e) => e.toMap()).toList(),
'app_version': AutoTrackConfigManager.instance.appVersion,
'device_info': AutoTrackConfigManager.instance.deviceInfo
}));
List<Map> datas = [];
uploadList.forEach((event) {
Random random = Random.secure();
int id = random.nextInt(1 << 32); // 模拟 Java 的 nextInt()
final t = DateTime.now().millisecondsSinceEpoch;
String os = "";
String os_version = "";
String model = "";
String manufacturer = "";
if (baseDeviceInfo != null) {
if (baseDeviceInfo is AndroidDeviceInfo) {
os = 'android';
os_version = baseDeviceInfo.version.release;
model = baseDeviceInfo.model;
manufacturer = baseDeviceInfo.manufacturer;
} else if (baseDeviceInfo is IosDeviceInfo) {
os = 'ios';
os_version = baseDeviceInfo.systemVersion;
model = baseDeviceInfo.model;
manufacturer = 'apple';
} else if (baseDeviceInfo is OhosDeviceInfo) {
os = 'ohos';
os_version = baseDeviceInfo.versionId??"";
model = baseDeviceInfo.productModel??"";
manufacturer = 'huawei';
}
}
final properties = {
'\$os': os,
'\$os_version': os_version,
'\$model': model,
'\$lib': "Flutter",
'\$app_name': AutoTrackConfigManager.instance.appName,
'\$app_version': AutoTrackConfigManager.instance.appVersion,
'\$device_id': AutoTrackConfigManager.instance.deviceId,
'\$timezone_offset': getZoneOffset(),
"\$manufacturer": manufacturer,
// "$carrier": "NONE",
// "$os_version": "13",
// "$model": "C310CS",
// "$os": "Android",
// "$screen_width": 1200,
// "$brand": "BOE",
// "$screen_height": 1920,
// "$device_id": "afa4c7a98b3f6467",
// "$app_name": "Ewin Reading",
// "$lib_version": "5.3.3",
// "$timezone_offset": -480,
// "$app_id": "com.ewin.tech.reading",
// "$mac": "020000000000",
// "$manufacturer": "BOE",
// "$sn": "C310CS014820000006",
// "$wifi": true,
// "$network_type": "WIFI",
// "$screen_orientation": "portrait",
// "$screen_brightness": 204,
// "$event_duration": 0,
// "$lib_method": "autoTrack",
// "$is_first_day": true
};
event.params.forEach((k,v){
properties[k] = v;
});
datas.add({
'_track_id': id,
'time': t,
'type': 'track',
'distinct_id':
config.userId ?? AutoTrackConfigManager.instance.deviceId,
'anonymous_id': AutoTrackConfigManager.instance.deviceId,
'event':event.type,
'properties':properties
});
AutoTrackLogger.getInstance().debug('upload => data => $datas');
});
httpClient
.postUrl(Uri.parse(host + UPLOAD))
.then((HttpClientRequest request) {
request.headers
.set(HttpHeaders.contentTypeHeader, "application/json");
request.headers.set("token", token); // 设置 header
// 对数据进行压缩并进行 Base64 编码
final compressedData = encodeData(jsonEncode(datas));
final jsonPayload = jsonEncode({"base64Str": compressedData});
print("压缩数据:$jsonPayload");
request.write(jsonPayload);
return request.close();
}).then((response) {
AutoTrackLogger.getInstance()
.debug('upload status => ${response.statusCode}');
}).catchError((error) {
AutoTrackLogger.getInstance().error(error);
}).then((HttpClientResponse response) {
final responseCode = response.statusCode;
print("responseCode: $responseCode");
response.transform(utf8.decoder).join().then((responseBody) {
if (responseCode >= HttpStatus.ok &&
responseCode < HttpStatus.multipleChoices) {
// 状态码 200 - 300 认为是成功
print("response: $responseBody");
AutoTrackLogger.getInstance().debug('upload => success ret_code: $responseCode ret_content: $responseBody');
try {
final jsonResponse = jsonDecode(responseBody);
if (jsonResponse["code"] == 4005) {
AutoTrackConfigManager.instance.getToken(true);
}
} catch (e) {
print("JSON 解析错误: $e");
}
} else {
AutoTrackLogger.getInstance().debug('upload => fail ret_code: $responseCode ret_content: $responseBody');
}
if (responseCode < HttpStatus.ok ||
responseCode >= HttpStatus.multipleChoices) {
AutoTrackLogger.getInstance().debug('upload => fail ret_code: $responseCode ret_content: $responseBody');
}
});
});
}
}
int getZoneOffset() {
final now = DateTime.now();
final localOffset = now.timeZoneOffset.inMinutes; // 获取时区偏移量(分钟)
return -localOffset; // 取反,保持与 Java 代码一致
}
String encodeData(String rawMessage) {
try {
// 使用 GZip 压缩
List<int> compressed = GZipEncoder().encode(utf8.encode(rawMessage))!;
// Base64 编码
return base64.encode(compressed);
} catch (e) {
throw FormatException('Invalid data: ${e.toString()}');
}
}
}
... ...
... ... @@ -185,6 +185,7 @@ class _PageTask {
if (leavePage != null && !leavePage.pageInfo.ignore) {
leavePage.pageInfo.timer.end();
Track.instance.pageLeave(leavePage.pageInfo);
Track.instance.pageDuration(leavePage.pageInfo);
}
if (enterPage != null && !enterPage.pageInfo.ignore) {
enterPage.pageInfo.timer.start();
... ...
... ... @@ -15,9 +15,9 @@ class Track {
Map<String, dynamic> _appendPageInfo(Map<String, dynamic> params, PageInfo pageInfo) {
params['page_key'] = pageInfo.pageKey;
params['page_title'] = pageInfo.pageTitle;
params['\$title'] = pageInfo.pageTitle;
params['page_manual_key'] = pageInfo.pageManualKey;
params['page_path'] = pageInfo.pagePath;
params['\$screen_name'] = pageInfo.pageKey;
params['is_back'] = pageInfo.isBack ? 1 : 0;
return params;
}
... ... @@ -37,6 +37,19 @@ class Track {
AutoTrackLogger.getInstance().debug('track page_view => $params');
}
void pageDuration(PageInfo pageInfo) {
if (!AutoTrackConfigManager.instance.autoTrackEnable) {
return;
}
Map<String, dynamic> params = _appendPageInfo({}, pageInfo);
params['use_time'] = pageInfo.timer.duration.inMilliseconds;
params['use_name'] = pageInfo.pageKey;
_TrackPlugin.pageDuration(params);
AutoTrackLogger.getInstance().debug('track page_leave => $params');
}
void pageLeave(PageInfo pageInfo) {
if (!AutoTrackConfigManager.instance.autoTrackEnable) {
return;
... ... @@ -130,8 +143,15 @@ class _TrackPlugin {
AutoTrackQueue.instance.appendQueue(model);
}
static void pageDuration(Map<String, dynamic> params) {
var model = TrackModel('page_duration_event', DateTime.now().millisecondsSinceEpoch, params, params['page_manual_key']);
AutoTrackConfigManager.instance.config.eventHandler?.call(model);
AutoTrackQueue.instance.appendQueue(model);
}
static void click(Map<String, dynamic> params) {
var model = TrackModel('click', DateTime.now().millisecondsSinceEpoch, params, params['element_manual_key']);
var model = TrackModel('\$AppClick', DateTime.now().millisecondsSinceEpoch, params, params['element_manual_key']);
AutoTrackConfigManager.instance.config.eventHandler?.call(model);
AutoTrackQueue.instance.appendQueue(model);
}
... ...
import 'dart:convert';
import 'package:crypto/crypto.dart';
class SignUtil {
/// 签名方法
static String sign(Map<String, String> sPara, String appecret) {
String prestr = createLinkString(paraFilter(sPara)); // 把数组所有元素,
String mysign = md5Sign(prestr, appecret, "utf-8");
return mysign;
}
/// 除去数组中的空值和签名参数
static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = {};
if (sArray == null || sArray.isEmpty) {
return result;
}
sArray.forEach((key, value) {
if (value == null || value.isEmpty || key.toLowerCase() == "sign" || key.toLowerCase() == "sign_type") {
return;
}
result[key] = value;
});
return result;
}
/// 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
static String createLinkString(Map<String, String> params) {
List<String> keys = params.keys.toList();
keys.sort();
String prestr = "";
for (int i = 0; i < keys.length; i++) {
String key = keys[i];
String value = params[key]!;
if (i == keys.length - 1) {
// 拼接时,不包括最后一个&字符
prestr = "$prestr$key=$value";
} else {
prestr = "$prestr$key=$value&";
}
}
return prestr;
}
/// MD5签名
static String md5Sign(String prestr, String secret, String encoding) {
String toSign = prestr + secret;
var bytes = utf8.encode(toSign);
var digest = md5.convert(bytes);
return digest.toString();
}
}
... ...
... ... @@ -10,12 +10,24 @@ environment:
dependencies:
android_id: ^0.4.0
crypto: ^3.0.3
device_info_plus: ^10.1.2
# device_info_plus: ^10.1.2
device_info_plus:
git:
url: https://gitee.com/openharmony-sig/flutter_plus_plugins
path: packages/device_info_plus/device_info_plus
flutter:
sdk: flutter
package_info_plus: ^8.0.1
# package_info_plus: ^8.0.1
package_info_plus:
git:
url: https://gitee.com/openharmony-sig/flutter_plus_plugins
path: packages/package_info_plus/package_info_plus
plugin_platform_interface: ^2.0.2
uuid: ^4.3.3
archive: ^3.3.7 # 确保使用最新版本
dev_dependencies:
flutter_test:
... ...