Files
vf_react/src/views/Concept/hooks/useConceptEvents.js
2025-10-29 19:49:20 +08:00

362 lines
10 KiB
JavaScript
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.

// src/views/Concept/hooks/useConceptEvents.js
// 概念中心页面事件追踪 Hook
import { useCallback, useEffect } from 'react';
import { usePostHogTrack } from '../../../hooks/usePostHogRedux';
import { RETENTION_EVENTS, REVENUE_EVENTS } from '../../../lib/constants';
import { logger } from '../../../utils/logger';
/**
* 概念中心事件追踪 Hook
* @param {Object} options - 配置选项
* @param {Function} options.navigate - 路由导航函数
* @returns {Object} 事件追踪处理函数集合
*/
export const useConceptEvents = ({ navigate } = {}) => {
const { track } = usePostHogTrack();
// 🎯 页面浏览事件 - 页面加载时触发
useEffect(() => {
track(RETENTION_EVENTS.CONCEPT_PAGE_VIEWED, {
timestamp: new Date().toISOString(),
});
logger.debug('useConceptEvents', '📊 Concept Page Viewed');
}, [track]);
/**
* 追踪概念列表数据查看
* @param {Array} concepts - 概念列表
* @param {Object} filters - 当前筛选条件
*/
const trackConceptListViewed = useCallback((concepts, filters = {}) => {
track(RETENTION_EVENTS.CONCEPT_LIST_VIEWED, {
concept_count: concepts.length,
sort_by: filters.sortBy,
view_mode: filters.viewMode,
has_search_query: !!filters.searchQuery,
selected_date: filters.selectedDate,
page: filters.page,
});
logger.debug('useConceptEvents', '📋 Concept List Viewed', {
count: concepts.length,
filters,
});
}, [track]);
/**
* 追踪搜索开始
*/
const trackSearchInitiated = useCallback(() => {
track(RETENTION_EVENTS.SEARCH_INITIATED, {
context: 'concept_center',
});
logger.debug('useConceptEvents', '🔍 Search Initiated');
}, [track]);
/**
* 追踪搜索查询提交
* @param {string} query - 搜索查询词
* @param {number} resultCount - 搜索结果数量
*/
const trackSearchQuerySubmitted = useCallback((query, resultCount = 0) => {
if (!query) return;
track(RETENTION_EVENTS.SEARCH_QUERY_SUBMITTED, {
query,
category: 'concept',
result_count: resultCount,
has_results: resultCount > 0,
});
// 如果没有搜索结果,额外追踪
if (resultCount === 0) {
track(RETENTION_EVENTS.SEARCH_NO_RESULTS, {
query,
context: 'concept_center',
});
}
logger.debug('useConceptEvents', '🔍 Search Query Submitted', {
query,
resultCount,
});
}, [track]);
/**
* 追踪排序方式变化
* @param {string} sortBy - 新的排序方式
* @param {string} previousSortBy - 之前的排序方式
*/
const trackSortChanged = useCallback((sortBy, previousSortBy = null) => {
track(RETENTION_EVENTS.SEARCH_FILTER_APPLIED, {
filter_type: 'sort',
filter_value: sortBy,
previous_value: previousSortBy,
context: 'concept_center',
});
logger.debug('useConceptEvents', '🔄 Sort Changed', {
sortBy,
previousSortBy,
});
}, [track]);
/**
* 追踪视图模式切换
* @param {string} viewMode - 新的视图模式 (grid/list)
* @param {string} previousViewMode - 之前的视图模式
*/
const trackViewModeChanged = useCallback((viewMode, previousViewMode = null) => {
track(RETENTION_EVENTS.SEARCH_FILTER_APPLIED, {
filter_type: 'view_mode',
filter_value: viewMode,
previous_value: previousViewMode,
context: 'concept_center',
});
logger.debug('useConceptEvents', '👁️ View Mode Changed', {
viewMode,
previousViewMode,
});
}, [track]);
/**
* 追踪日期选择变化
* @param {string} newDate - 新选择的日期
* @param {string} previousDate - 之前的日期
* @param {string} selectionMethod - 选择方式 (today/yesterday/week_ago/month_ago/custom)
*/
const trackDateChanged = useCallback((newDate, previousDate = null, selectionMethod = 'custom') => {
track(RETENTION_EVENTS.SEARCH_FILTER_APPLIED, {
filter_type: 'date',
filter_value: newDate,
previous_value: previousDate,
selection_method: selectionMethod,
context: 'concept_center',
});
logger.debug('useConceptEvents', '📅 Date Changed', {
newDate,
previousDate,
selectionMethod,
});
}, [track]);
/**
* 追踪分页变化
* @param {number} page - 新的页码
* @param {Object} filters - 当前筛选条件
*/
const trackPageChanged = useCallback((page, filters = {}) => {
track(RETENTION_EVENTS.CONCEPT_LIST_VIEWED, {
page,
sort_by: filters.sortBy,
view_mode: filters.viewMode,
has_search_query: !!filters.searchQuery,
});
logger.debug('useConceptEvents', '📄 Page Changed', { page, filters });
}, [track]);
/**
* 追踪概念卡片点击
* @param {Object} concept - 概念对象
* @param {number} position - 在列表中的位置
* @param {string} source - 来源 (list/stats_panel)
*/
const trackConceptClicked = useCallback((concept, position = 0, source = 'list') => {
track(RETENTION_EVENTS.CONCEPT_CLICKED, {
concept_name: concept.concept_name || concept.name,
concept_code: concept.concept_code || concept.code,
change_percent: concept.change_pct || concept.change_percent,
stock_count: concept.stock_count,
position,
source,
});
logger.debug('useConceptEvents', '🎯 Concept Clicked', {
concept: concept.concept_name || concept.name,
position,
source,
});
}, [track]);
/**
* 追踪概念下的股票标签点击
* @param {Object} stock - 股票对象
* @param {string} conceptName - 所属概念名称
*/
const trackConceptStockClicked = useCallback((stock, conceptName) => {
track(RETENTION_EVENTS.CONCEPT_STOCK_CLICKED, {
stock_code: stock.code || stock.stock_code,
stock_name: stock.name || stock.stock_name,
concept_name: conceptName,
source: 'concept_center_tag',
});
logger.debug('useConceptEvents', '🏷️ Concept Stock Tag Clicked', {
stock: stock.code || stock.stock_code,
concept: conceptName,
});
}, [track]);
/**
* 追踪概念详情查看时间轴Modal
* @param {string} conceptName - 概念名称
* @param {string} conceptId - 概念ID
*/
const trackConceptDetailViewed = useCallback((conceptName, conceptId) => {
track(RETENTION_EVENTS.CONCEPT_DETAIL_VIEWED, {
concept_name: conceptName,
concept_id: conceptId,
source: 'concept_center',
});
logger.debug('useConceptEvents', '📊 Concept Detail Viewed', {
conceptName,
conceptId,
});
}, [track]);
/**
* 追踪股票详情Modal打开
* @param {string} stockCode - 股票代码
* @param {string} stockName - 股票名称
*/
const trackStockDetailViewed = useCallback((stockCode, stockName) => {
track(RETENTION_EVENTS.STOCK_DETAIL_VIEWED, {
stock_code: stockCode,
stock_name: stockName,
source: 'concept_center_modal',
});
logger.debug('useConceptEvents', '👁️ Stock Detail Modal Opened', {
stockCode,
stockName,
});
}, [track]);
/**
* 追踪付费墙展示
* @param {string} feature - 需要付费的功能
* @param {string} requiredTier - 需要的订阅等级
*/
const trackPaywallShown = useCallback((feature, requiredTier = 'pro') => {
track(REVENUE_EVENTS.PAYWALL_SHOWN, {
feature,
required_tier: requiredTier,
page: 'concept_center',
});
logger.debug('useConceptEvents', '🔒 Paywall Shown', {
feature,
requiredTier,
});
}, [track]);
/**
* 追踪升级按钮点击
* @param {string} feature - 触发升级的功能
* @param {string} targetTier - 目标订阅等级
*/
const trackUpgradeClicked = useCallback((feature, targetTier = 'pro') => {
track(REVENUE_EVENTS.PAYWALL_UPGRADE_CLICKED, {
feature,
target_tier: targetTier,
source_page: 'concept_center',
});
logger.debug('useConceptEvents', '⬆️ Upgrade Button Clicked', {
feature,
targetTier,
});
}, [track]);
// ========== 别名函数 - 为保持向后兼容性 ==========
/**
* 追踪概念搜索(别名函数)
* @alias trackSearchQuerySubmitted
*/
const trackConceptSearched = useCallback((query, resultCount = 0) => {
return trackSearchQuerySubmitted(query, resultCount);
}, [trackSearchQuerySubmitted]);
/**
* 追踪筛选器应用(通用包装函数)
* @param {string} filterType - 筛选类型 (sort/date/view_mode)
* @param {any} filterValue - 筛选值
* @param {any} previousValue - 之前的值
*/
const trackFilterApplied = useCallback((filterType, filterValue, previousValue = null) => {
if (filterType === 'sort') {
return trackSortChanged(filterValue, previousValue);
} else if (filterType === 'date') {
return trackDateChanged(filterValue, previousValue);
} else if (filterType === 'view_mode') {
return trackViewModeChanged(filterValue, previousValue);
}
}, [trackSortChanged, trackDateChanged, trackViewModeChanged]);
/**
* 追踪概念股票列表查看
* @param {string} conceptName - 概念名称
* @param {number} stockCount - 股票数量
*/
const trackConceptStocksViewed = useCallback((conceptName, stockCount = 0) => {
track(RETENTION_EVENTS.CONCEPT_DETAIL_VIEWED, {
concept_name: conceptName,
stock_count: stockCount,
view_type: 'stocks_list',
source: 'concept_center',
});
logger.debug('useConceptEvents', '📈 Concept Stocks Viewed', {
conceptName,
stockCount,
});
}, [track]);
/**
* 追踪概念时间轴查看(别名函数)
* @alias trackConceptDetailViewed
*/
const trackConceptTimelineViewed = useCallback((conceptName, conceptId) => {
return trackConceptDetailViewed(conceptName, conceptId);
}, [trackConceptDetailViewed]);
/**
* 追踪分页变化(别名函数 - 不同时态)
* @alias trackPageChanged
*/
const trackPageChange = useCallback((page, filters = {}) => {
return trackPageChanged(page, filters);
}, [trackPageChanged]);
return {
// 原有函数
trackConceptListViewed,
trackSearchInitiated,
trackSearchQuerySubmitted,
trackSortChanged,
trackViewModeChanged,
trackDateChanged,
trackPageChanged,
trackConceptClicked,
trackConceptStockClicked,
trackConceptDetailViewed,
trackStockDetailViewed,
trackPaywallShown,
trackUpgradeClicked,
// 别名函数 - 为保持向后兼容性
trackConceptSearched,
trackFilterApplied,
trackConceptStocksViewed,
trackConceptTimelineViewed,
trackPageChange,
};
};