UNPKG

@ked3/ktrace

Version:

跨平台埋点分析SDK

165 lines (140 loc) 5.13 kB
import { v4 as uuidv4 } from "uuid"; /** * 生成极短唯一ID(8-16字符) * 基于62进制编码,结合时间戳、随机数和实例标识 */ export function generateUltraShortId(): string { // 62进制编码表(避免特殊字符,兼容URL) const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; // 1. 时间戳(取最近28位毫秒级时间,约可表示17年) const timestamp = Date.now() - 1700000000000; // 减去起始时间,缩小数值范围 const timeStr = encodeToBase62(timestamp, chars, 6); // 固定6字符(约2^36范围) // 2. 随机数(32位,约5.3字符) const random = Math.floor(Math.random() * 0xFFFFFFFF); // 32位随机数 const randomStr = encodeToBase62(random, chars, 5); // 固定5字符 // 3. 拼接后共11字符(6+5) return timeStr + randomStr; } /** * 62进制编码工具函数 * @param num 待编码的数字 * @param chars 编码表 * @param minLength 最小长度(不足补前导字符) */ function encodeToBase62(num: number, chars: string, minLength: number): string { if (num === 0) return chars[0].repeat(minLength); let result = ''; const base = chars.length; while (num > 0) { result = chars[num % base] + result; num = Math.floor(num / base); } // 不足最小长度时补前导字符(确保固定长度) return result.padStart(minLength, chars[0]); } /** * 获取会话ID */ export function getSessionId(): string { let sessionId = sessionStorage.getItem("k_track_session_id") || ''; if (!sessionId) { sessionId = generateUltraShortId(); sessionStorage.setItem("k_track_session_id", sessionId); } return sessionId; } /** * 简单哈希函数,用于生成设备ID */ export function simpleHash(str: string): string { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // 转换为32位整数 } return Math.abs(hash).toString(36) + Date.now().toString(36); } /** * 获取设备ID,兼容electron和web环境 */ export function getDeviceId(): string { // 尝试从localStorage获取已存储的设备ID let deviceId = ''; try { deviceId = localStorage.getItem("k_track_device_id") || ''; } catch (e) { // 如果localStorage不可用,从sessionStorage获取 try { deviceId = sessionStorage.getItem("k_track_device_id") || ''; } catch (e2) { // 如果都不可用,则生成新的 } } if (!deviceId) { // 生成基于多种因素的设备ID const factors = []; // 检查是否为electron环境 const isElectron = typeof window !== 'undefined' && (window as any).process && (window as any).process.type === 'renderer'; if (isElectron) { // Electron环境下的设备标识 try { // 尝试获取electron特有信息 const { remote } = (window as any).require('electron'); if (remote) { factors.push(`electron_${remote.app.getVersion()}`); } } catch (e) { // 如果无法获取electron信息,使用通用方法 } } // 通用设备信息收集 if (typeof navigator !== 'undefined') { factors.push(navigator.userAgent); factors.push(navigator.language); factors.push(navigator.platform); if (navigator.hardwareConcurrency) { factors.push(navigator.hardwareConcurrency.toString()); } } if (typeof screen !== 'undefined') { factors.push(`${screen.width}x${screen.height}`); factors.push(screen.colorDepth.toString()); } // 时区信息 factors.push(Intl.DateTimeFormat().resolvedOptions().timeZone); // 如果没有足够的因素,使用随机UUID if (factors.length === 0) { deviceId = uuidv4(); } else { // 基于收集的信息生成稳定的设备ID const combined = factors.join('|'); deviceId = simpleHash(combined); } // 尝试存储设备ID try { localStorage.setItem("k_track_device_id", deviceId); } catch (e) { try { sessionStorage.setItem("k_track_device_id", deviceId); } catch (e2) { // 如果都不能存储,则每次都会重新生成 } } } return deviceId; } /** * 格式化日期为 yyyy-MM-dd HH:mm:ss 格式 */ export function formatDateTime(date: Date): string { const year = date.getFullYear(); const month = String(date.getMonth() + 1).length === 1 ? '0' + String(date.getMonth() + 1) : String(date.getMonth() + 1); const day = String(date.getDate()).length === 1 ? '0' + String(date.getDate()) : String(date.getDate()); const hours = String(date.getHours()).length === 1 ? '0' + String(date.getHours()) : String(date.getHours()); const minutes = String(date.getMinutes()).length === 1 ? '0' + String(date.getMinutes()) : String(date.getMinutes()); const seconds = String(date.getSeconds()).length === 1 ? '0' + String(date.getSeconds()) : String(date.getSeconds()); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; }