Files
Ghost 3317c84b5e feat: 管理端与用户端批量更新(订单/邀请双码/tabBar/审核与分销等)
- 管理端:用户详情、企业看板双小程序码、超管设置与用户列表等
- 后端:订单接口、邀请码企业版+个人版、分析与测试相关调整
- 微信小程序/抖音:自定义 tabBar 居中与拍摄钮上移、相机页与结果页、订单页、支付与统计
- 新增 faceResultDetail 工具与 mp 分析迁移脚本

Made-with: Cursor
2026-03-28 16:46:00 +08:00

152 lines
3.5 KiB
JavaScript

/**
* 抖音小程序埋点:批量上报 /api/analytics/events
* - session_id 区分每次启动
* - 自动采集平台、网络、设备、场景值
* - 定时 flush + 页面隐藏 flush
*/
const { request } = require('./request.js')
const MAX_BATCH = 30
const FLUSH_INTERVAL = 10000
const queue = []
let lastPageReport = { path: '', t: 0 }
const sessionId = 'dy_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6)
let _deviceInfo = null
function getDeviceInfo() {
if (_deviceInfo) return _deviceInfo
try {
const sys = tt.getSystemInfoSync()
_deviceInfo = {
brand: sys.brand,
model: sys.model,
system: sys.system,
platform: sys.platform,
version: sys.version,
SDKVersion: sys.SDKVersion,
screenWidth: sys.screenWidth,
screenHeight: sys.screenHeight,
pixelRatio: sys.pixelRatio
}
} catch (e) {
_deviceInfo = {}
}
return _deviceInfo
}
let _networkType = 'unknown'
function refreshNetwork() {
try {
tt.getNetworkType({
success(res) { _networkType = res.networkType || 'unknown' }
})
} catch (e) {}
}
refreshNetwork()
function getAppSafe() {
try { return getApp() } catch (e) { return null }
}
function getCurrentRoute() {
const pages = getCurrentPages()
const p = pages[pages.length - 1]
return p && p.route ? p.route : ''
}
function getScene() {
const app = getAppSafe()
return (app && app.globalData && app.globalData.scene) || ''
}
function track(eventName, props) {
if (!eventName || typeof eventName !== 'string') return
const app = getAppSafe()
const openId = app && app.globalData
? (app.globalData.openId || (app.globalData.userInfo && (app.globalData.userInfo.openid || app.globalData.userInfo.openId)) || '')
: ''
queue.push({
event_name: eventName,
page_path: getCurrentRoute(),
props: props && typeof props === 'object' ? props : {},
client_ts: Date.now(),
openid: openId || undefined,
session_id: sessionId,
platform: 'douyin',
network: _networkType,
scene: getScene()
})
if (queue.length >= MAX_BATCH) {
flush()
}
}
function reportPageView(extraProps) {
const path = getCurrentRoute()
if (!path) return
const now = Date.now()
if (path === lastPageReport.path && now - lastPageReport.t < 2000) return
lastPageReport = { path, t: now }
track('page_view', { path, ...(extraProps || {}) })
}
function flush() {
if (queue.length === 0) return
const events = queue.splice(0, queue.length)
request({
url: '/api/analytics/events',
method: 'POST',
needAuth: true,
data: { events, device: getDeviceInfo() },
allow401: false,
success() {},
fail() {}
})
}
function reportAppLaunch(launchOptions) {
const opts = launchOptions || {}
track('app_launch', {
scene: opts.scene,
query: opts.query || {},
referrerInfo: opts.referrerInfo || {},
device: getDeviceInfo()
})
}
function reportShare(channel, extra) {
track('share', { channel: channel || 'friend', ...(extra || {}) })
flush()
}
function reportPayResult(success, extra) {
track(success ? 'pay_success' : 'pay_fail', extra || {})
flush()
}
let _flushTimer = null
function startAutoFlush() {
if (_flushTimer) return
_flushTimer = setInterval(flush, FLUSH_INTERVAL)
}
function stopAutoFlush() {
if (_flushTimer) {
clearInterval(_flushTimer)
_flushTimer = null
}
}
startAutoFlush()
module.exports = {
track,
flush,
reportPageView,
reportAppLaunch,
reportShare,
reportPayResult,
getCurrentRoute,
startAutoFlush,
stopAutoFlush
}