1. 完整的用户旅程
- 从进入网站 → 浏览内容 → 使用功能 → 遇到付费墙 → 付费转化
2. 核心业务指标
- DAU/MAU(活跃用户)
- 功能使用率(哪些功能最受欢迎)
- 搜索热度(用户需求洞察)
- Revenue转化漏斗(付费转化分析)
- 用户参与度(Profile更新、设置变更)
3. 产品优化方向
- 哪些功能需要优化?
- 用户在哪个环节流失?
- 哪些内容最受欢迎?
- 如何提高付费转化率?
335 lines
9.5 KiB
JavaScript
335 lines
9.5 KiB
JavaScript
// src/hooks/useProfileEvents.js
|
||
// 个人资料和设置事件追踪 Hook
|
||
|
||
import { useCallback } from 'react';
|
||
import { usePostHogTrack } from './usePostHogRedux';
|
||
import { RETENTION_EVENTS } from '../lib/constants';
|
||
import { logger } from '../utils/logger';
|
||
|
||
/**
|
||
* 个人资料和设置事件追踪 Hook
|
||
* @param {Object} options - 配置选项
|
||
* @param {string} options.pageType - 页面类型 ('profile' | 'settings' | 'security')
|
||
* @returns {Object} 事件追踪处理函数集合
|
||
*/
|
||
export const useProfileEvents = ({ pageType = 'profile' } = {}) => {
|
||
const { track } = usePostHogTrack();
|
||
|
||
/**
|
||
* 追踪个人资料字段编辑开始
|
||
* @param {string} fieldName - 字段名称 ('nickname' | 'email' | 'phone' | 'avatar' | 'bio')
|
||
*/
|
||
const trackProfileFieldEditStarted = useCallback((fieldName) => {
|
||
if (!fieldName) {
|
||
logger.warn('useProfileEvents', 'trackProfileFieldEditStarted: fieldName is required');
|
||
return;
|
||
}
|
||
|
||
track('Profile Field Edit Started', {
|
||
field_name: fieldName,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '✏️ Profile Field Edit Started', {
|
||
fieldName,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪个人资料更新成功
|
||
* @param {Array<string>} updatedFields - 更新的字段列表
|
||
* @param {Object} changes - 变更详情
|
||
*/
|
||
const trackProfileUpdated = useCallback((updatedFields = [], changes = {}) => {
|
||
if (!updatedFields || updatedFields.length === 0) {
|
||
logger.warn('useProfileEvents', 'trackProfileUpdated: updatedFields array is required');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.PROFILE_UPDATED, {
|
||
updated_fields: updatedFields,
|
||
field_count: updatedFields.length,
|
||
changes: changes,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '✅ Profile Updated', {
|
||
updatedFields,
|
||
fieldCount: updatedFields.length,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪个人资料更新失败
|
||
* @param {Array<string>} attemptedFields - 尝试更新的字段
|
||
* @param {string} errorMessage - 错误信息
|
||
*/
|
||
const trackProfileUpdateFailed = useCallback((attemptedFields = [], errorMessage = '') => {
|
||
track('Profile Update Failed', {
|
||
attempted_fields: attemptedFields,
|
||
error_message: errorMessage,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '❌ Profile Update Failed', {
|
||
attemptedFields,
|
||
errorMessage,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪头像上传
|
||
* @param {string} uploadMethod - 上传方式 ('file_upload' | 'url' | 'camera' | 'default_avatar')
|
||
* @param {number} fileSize - 文件大小(bytes)
|
||
*/
|
||
const trackAvatarUploaded = useCallback((uploadMethod = 'file_upload', fileSize = 0) => {
|
||
track('Avatar Uploaded', {
|
||
upload_method: uploadMethod,
|
||
file_size: fileSize,
|
||
file_size_mb: (fileSize / (1024 * 1024)).toFixed(2),
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '🖼️ Avatar Uploaded', {
|
||
uploadMethod,
|
||
fileSize,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪密码更改
|
||
* @param {boolean} success - 是否成功
|
||
* @param {string} errorReason - 失败原因
|
||
*/
|
||
const trackPasswordChanged = useCallback((success = true, errorReason = '') => {
|
||
track('Password Changed', {
|
||
success,
|
||
error_reason: errorReason || null,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', success ? '🔒 Password Changed Successfully' : '❌ Password Change Failed', {
|
||
success,
|
||
errorReason,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪邮箱验证发起
|
||
* @param {string} email - 邮箱地址
|
||
*/
|
||
const trackEmailVerificationSent = useCallback((email = '') => {
|
||
track('Email Verification Sent', {
|
||
email_provided: Boolean(email),
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '📧 Email Verification Sent', {
|
||
emailProvided: Boolean(email),
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪手机号验证发起
|
||
* @param {string} phone - 手机号
|
||
*/
|
||
const trackPhoneVerificationSent = useCallback((phone = '') => {
|
||
track('Phone Verification Sent', {
|
||
phone_provided: Boolean(phone),
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '📱 Phone Verification Sent', {
|
||
phoneProvided: Boolean(phone),
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪账号绑定(微信、邮箱、手机等)
|
||
* @param {string} accountType - 账号类型 ('wechat' | 'email' | 'phone')
|
||
* @param {boolean} success - 是否成功
|
||
*/
|
||
const trackAccountBound = useCallback((accountType, success = true) => {
|
||
if (!accountType) {
|
||
logger.warn('useProfileEvents', 'trackAccountBound: accountType is required');
|
||
return;
|
||
}
|
||
|
||
track('Account Bound', {
|
||
account_type: accountType,
|
||
success,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', success ? '🔗 Account Bound' : '❌ Account Bind Failed', {
|
||
accountType,
|
||
success,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪账号解绑
|
||
* @param {string} accountType - 账号类型
|
||
* @param {boolean} success - 是否成功
|
||
*/
|
||
const trackAccountUnbound = useCallback((accountType, success = true) => {
|
||
if (!accountType) {
|
||
logger.warn('useProfileEvents', 'trackAccountUnbound: accountType is required');
|
||
return;
|
||
}
|
||
|
||
track('Account Unbound', {
|
||
account_type: accountType,
|
||
success,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', success ? '🔓 Account Unbound' : '❌ Account Unbind Failed', {
|
||
accountType,
|
||
success,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪设置项更改
|
||
* @param {string} settingName - 设置名称
|
||
* @param {any} oldValue - 旧值
|
||
* @param {any} newValue - 新值
|
||
* @param {string} category - 设置分类 ('notification' | 'privacy' | 'display' | 'advanced')
|
||
*/
|
||
const trackSettingChanged = useCallback((settingName, oldValue, newValue, category = 'general') => {
|
||
if (!settingName) {
|
||
logger.warn('useProfileEvents', 'trackSettingChanged: settingName is required');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.SETTINGS_CHANGED, {
|
||
setting_name: settingName,
|
||
old_value: String(oldValue),
|
||
new_value: String(newValue),
|
||
category,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '⚙️ Setting Changed', {
|
||
settingName,
|
||
oldValue,
|
||
newValue,
|
||
category,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪通知偏好更改
|
||
* @param {Object} preferences - 通知偏好设置
|
||
* @param {boolean} preferences.email - 邮件通知
|
||
* @param {boolean} preferences.push - 推送通知
|
||
* @param {boolean} preferences.sms - 短信通知
|
||
*/
|
||
const trackNotificationPreferencesChanged = useCallback((preferences = {}) => {
|
||
track('Notification Preferences Changed', {
|
||
email_enabled: preferences.email || false,
|
||
push_enabled: preferences.push || false,
|
||
sms_enabled: preferences.sms || false,
|
||
total_enabled: Object.values(preferences).filter(Boolean).length,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '🔔 Notification Preferences Changed', {
|
||
preferences,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪隐私设置更改
|
||
* @param {string} privacySetting - 隐私设置名称
|
||
* @param {boolean} isPublic - 是否公开
|
||
*/
|
||
const trackPrivacySettingChanged = useCallback((privacySetting, isPublic = false) => {
|
||
if (!privacySetting) {
|
||
logger.warn('useProfileEvents', 'trackPrivacySettingChanged: privacySetting is required');
|
||
return;
|
||
}
|
||
|
||
track('Privacy Setting Changed', {
|
||
privacy_setting: privacySetting,
|
||
is_public: isPublic,
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '🔐 Privacy Setting Changed', {
|
||
privacySetting,
|
||
isPublic,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
/**
|
||
* 追踪账号删除请求
|
||
* @param {string} reason - 删除原因
|
||
*/
|
||
const trackAccountDeletionRequested = useCallback((reason = '') => {
|
||
track('Account Deletion Requested', {
|
||
reason,
|
||
has_reason: Boolean(reason),
|
||
page_type: pageType,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useProfileEvents', '🗑️ Account Deletion Requested', {
|
||
reason,
|
||
pageType,
|
||
});
|
||
}, [track, pageType]);
|
||
|
||
return {
|
||
// 个人资料编辑
|
||
trackProfileFieldEditStarted,
|
||
trackProfileUpdated,
|
||
trackProfileUpdateFailed,
|
||
trackAvatarUploaded,
|
||
|
||
// 安全和验证
|
||
trackPasswordChanged,
|
||
trackEmailVerificationSent,
|
||
trackPhoneVerificationSent,
|
||
|
||
// 账号绑定
|
||
trackAccountBound,
|
||
trackAccountUnbound,
|
||
|
||
// 设置更改
|
||
trackSettingChanged,
|
||
trackNotificationPreferencesChanged,
|
||
trackPrivacySettingChanged,
|
||
|
||
// 账号管理
|
||
trackAccountDeletionRequested,
|
||
};
|
||
};
|
||
|
||
export default useProfileEvents;
|