video-flow-b/utils/analytics.ts
2025-09-28 18:11:29 +08:00

335 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Google Analytics 4 工具函数
* 提供标准化的事件跟踪和页面访问监控
*/
// 扩展全局Window接口
declare global {
interface Window {
gtag: (...args: any[]) => void;
dataLayer: any[];
}
}
/**
* GA4事件参数类型定义
*/
export interface GAEventParameters {
event_category?: string;
event_label?: string;
value?: number;
custom_parameters?: Record<string, any>;
}
/**
* 页面访问参数类型定义
*/
export interface GAPageViewParameters {
page_title?: string;
page_location?: string;
custom_parameters?: Record<string, any>;
}
/**
* 规范化/序列化事件参数,避免 [object Object]
*/
const normalizeEventParams = (
params: Record<string, any>
): Record<string, string | number | boolean> => {
const result: Record<string, string | number | boolean> = {};
const assignPrimitive = (key: string, value: any) => {
if (value === undefined) return;
if (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
) {
result[key] = value;
} else if (value === null) {
result[key] = 'null';
} else {
try {
result[key] = JSON.stringify(value);
} catch (_) {
result[key] = String(value);
}
}
};
for (const [key, value] of Object.entries(params || {})) {
if (key === 'custom_parameters' && value && typeof value === 'object') {
for (const [ck, cv] of Object.entries(value)) {
assignPrimitive(ck, cv);
}
continue;
}
assignPrimitive(key, value);
}
return result;
};
/**
* 检查GA是否可用
*/
export const isGAAvailable = (): boolean => {
return typeof window !== 'undefined' &&
typeof window.gtag === 'function' &&
process.env.NEXT_PUBLIC_GA_ENABLED === 'true';
};
/**
* 获取GA测量ID
*/
export const getGAMeasurementId = (): string => {
return process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID || 'G-4BDXV6TWF4';
};
/**
* 跟踪自定义事件
* @param eventName - 事件名称
* @param parameters - 事件参数
*/
export const trackEvent = (
eventName: string,
parameters?: GAEventParameters
): void => {
if (!isGAAvailable()) {
console.warn('Google Analytics not available');
return;
}
try {
const eventParamsRaw = {
event_category: parameters?.event_category || 'general',
event_label: parameters?.event_label,
value: parameters?.value,
...parameters?.custom_parameters,
};
const eventParams = normalizeEventParams(eventParamsRaw);
window.gtag('event', eventName, eventParams);
// 开发环境下打印日志
if (process.env.NODE_ENV === 'development') {
console.log('GA Event:', eventName, eventParams);
}
} catch (error) {
console.error('Error tracking GA event:', error);
}
};
/**
* 跟踪页面访问
* @param pagePath - 页面路径
* @param pageTitle - 页面标题
* @param parameters - 额外参数
*/
export const trackPageView = (
pagePath: string,
pageTitle?: string,
parameters?: GAPageViewParameters
): void => {
if (!isGAAvailable()) {
console.warn('Google Analytics not available');
return;
}
try {
const pageParamsRaw = {
page_path: pagePath,
page_title: pageTitle,
page_location: parameters?.page_location || window.location.href,
...parameters?.custom_parameters,
};
const pageParams = normalizeEventParams(pageParamsRaw);
window.gtag('config', getGAMeasurementId(), pageParams);
// 开发环境下打印日志
if (process.env.NODE_ENV === 'development') {
console.log('GA Page View:', pagePath, pageParams);
}
} catch (error) {
console.error('Error tracking GA page view:', error);
}
};
/**
* 跟踪用户注册事件
* @param method - 注册方式 (email, google, etc.)
*/
export const trackUserRegistration = (method: string): void => {
trackEvent('user_registration', {
event_category: 'user',
event_label: method,
custom_parameters: {
registration_method: method,
},
});
};
/**
* 跟踪用户登录事件
* @param method - 登录方式 (email, google, etc.)
*/
export const trackUserLogin = (method: string): void => {
trackEvent('user_login', {
event_category: 'user',
event_label: method,
custom_parameters: {
login_method: method,
},
});
};
/**
* 跟踪视频创建开始事件
* @param templateType - 模板类型
* @param aspectRatio - 视频比例
*/
export const trackVideoCreationStart = (
templateType: string,
aspectRatio?: string
): void => {
trackEvent('video_creation_start', {
event_category: 'video',
event_label: templateType,
custom_parameters: {
template_type: templateType,
aspect_ratio: aspectRatio,
},
});
};
/**
* 跟踪视频生成完成事件
* @param duration - 视频时长
* @param templateType - 模板类型
*/
export const trackVideoGenerationComplete = (
duration: number,
templateType: string
): void => {
trackEvent('video_generation_complete', {
event_category: 'video',
value: duration,
custom_parameters: {
template_type: templateType,
video_duration: duration,
},
});
};
/**
* 跟踪支付事件
* @param paymentType - 支付类型 (subscription, token)
* @param amount - 支付金额
* @param currency - 货币类型
*/
export const trackPayment = (
paymentType: string,
amount: number,
currency: string = 'USD'
): void => {
trackEvent('purchase', {
event_category: 'ecommerce',
value: amount,
custom_parameters: {
payment_type: paymentType,
currency: currency,
},
});
};
/**
* 跟踪模板选择事件
* @param templateId - 模板ID
* @param templateName - 模板名称
*/
export const trackTemplateSelection = (
templateId: string,
templateName: string
): void => {
trackEvent('template_selection', {
event_category: 'template',
event_label: templateName,
custom_parameters: {
template_id: templateId,
template_name: templateName,
},
});
};
/**
* 跟踪功能使用事件
* @param featureName - 功能名称
* @param action - 操作类型
*/
export const trackFeatureUsage = (
featureName: string,
action: string
): void => {
trackEvent('feature_usage', {
event_category: 'feature',
event_label: featureName,
custom_parameters: {
feature_name: featureName,
action: action,
},
});
};
/**
* 跟踪错误事件
* @param errorType - 错误类型
* @param errorMessage - 错误信息
* @param errorLocation - 错误位置
*/
export const trackError = (
errorType: string,
errorMessage: string,
errorLocation?: string
): void => {
trackEvent('error', {
event_category: 'error',
event_label: errorType,
custom_parameters: {
error_type: errorType,
error_message: errorMessage,
error_location: errorLocation,
},
});
};
/**
* 跟踪用户属性
* @param userId - 用户ID
* @param userProperties - 用户属性
*/
export const setUserProperties = (
userId: string,
userProperties: Record<string, any>
): void => {
if (!isGAAvailable()) {
console.warn('Google Analytics not available');
return;
}
try {
// GA4 推荐:通过 config 设置 user_id通过 set user_properties 设置用户属性
window.gtag('config', getGAMeasurementId(), {
user_id: userId,
});
window.gtag('set', 'user_properties', normalizeEventParams(userProperties));
} catch (error) {
console.error('Error setting user properties:', error);
}
};