|
@@ -1,4 +1,44 @@
|
|
|
|
+
|
|
|
|
+// 通用 JSBridge
|
|
|
|
+window.JSBridge = {
|
|
|
|
+ getOS: function () {
|
|
|
|
+ const ua = navigator.userAgent || navigator.vendor || window.opera;
|
|
|
|
+ if (/android/i.test(ua)) return 'Android';
|
|
|
|
+ if (/iPad|iPhone|iPod/.test(ua) && !window.MSStream) return 'iOS';
|
|
|
|
+ return 'Web';
|
|
|
|
+ },
|
|
|
|
+ getUserId: function (callback) {
|
|
|
|
+ // Android WebView
|
|
|
|
+ if (window.Android && typeof window.Android.getUserId === 'function') {
|
|
|
|
+ callback(window.Android.getUserId());
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // iOS WebView
|
|
|
|
+ if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.getUserId) {
|
|
|
|
+ window.webkit.messageHandlers.getUserId.postMessage(null);
|
|
|
|
+ window.onUserIdReceived = callback; // iOS原生需回调window.onUserIdReceived(userId)
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // Web环境(用 FingerprintJS 生成指纹)
|
|
|
|
+ if (window.FingerprintJS) {
|
|
|
|
+ FingerprintJS.load().then(fp => {
|
|
|
|
+ fp.get().then(result => {
|
|
|
|
+ callback(result.visitorId);
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ callback('unsupported');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+let userId = '';
|
|
|
|
+JSBridge.getUserId(function (id) {
|
|
|
|
+ userId = id;
|
|
|
|
+});
|
|
class Tracker {
|
|
class Tracker {
|
|
|
|
+
|
|
constructor(options = {}) {
|
|
constructor(options = {}) {
|
|
this.baseUrl = options.baseUrl || '';
|
|
this.baseUrl = options.baseUrl || '';
|
|
this.timer = null;
|
|
this.timer = null;
|
|
@@ -56,10 +96,12 @@ class Tracker {
|
|
|
|
|
|
startHeartbeat() {
|
|
startHeartbeat() {
|
|
this.timer = setInterval(() => {
|
|
this.timer = setInterval(() => {
|
|
|
|
+ console.log(userId);
|
|
|
|
+
|
|
this.sendData({
|
|
this.sendData({
|
|
eventType: 'heartbeat',
|
|
eventType: 'heartbeat',
|
|
duration: Math.round((Date.now() - this.startTime) / 1000),
|
|
duration: Math.round((Date.now() - this.startTime) / 1000),
|
|
- }, {heartbeatSeconds: this.heartbeatInterval});
|
|
|
|
|
|
+ }, { heartbeatSeconds: this.heartbeatInterval, userId: userId });
|
|
}, this.heartbeatInterval);
|
|
}, this.heartbeatInterval);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -95,7 +137,7 @@ class Tracker {
|
|
if (navigator.sendBeacon) {
|
|
if (navigator.sendBeacon) {
|
|
navigator.sendBeacon(this.baseUrl, payload);
|
|
navigator.sendBeacon(this.baseUrl, payload);
|
|
} else {
|
|
} else {
|
|
- this.sendData(payload, {heartbeatSeconds: this.heartbeatInterval});
|
|
|
|
|
|
+ this.sendData(payload, { heartbeatSeconds: this.heartbeatInterval, userId: userId });
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -109,7 +151,7 @@ class Tracker {
|
|
text: target.innerText || target.textContent || '',
|
|
text: target.innerText || target.textContent || '',
|
|
timestamp: new Date().toISOString(),
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
};
|
|
- this.sendData(trackInfo, {heartbeatSeconds: this.heartbeatInterval});
|
|
|
|
|
|
+ this.sendData(trackInfo, { heartbeatSeconds: this.heartbeatInterval, userId: userId });
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -158,7 +200,7 @@ class Tracker {
|
|
this.sendData({
|
|
this.sendData({
|
|
eventType: 'tracker_destroyed',
|
|
eventType: 'tracker_destroyed',
|
|
duration: Math.round((Date.now() - this.startTime) / 1000),
|
|
duration: Math.round((Date.now() - this.startTime) / 1000),
|
|
- }, {heartbeatSeconds: this.heartbeatInterval});
|
|
|
|
|
|
+ }, { heartbeatSeconds: this.heartbeatInterval, userId: userId });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -167,4 +209,4 @@ class Tracker {
|
|
// tracker.init();
|
|
// tracker.init();
|
|
|
|
|
|
// 暴露给全局或者作为模块导出
|
|
// 暴露给全局或者作为模块导出
|
|
-window.Tracker = Tracker;
|
|
|
|
|
|
+window.Tracker = Tracker;
|