// src/views/TradingSimulation/hooks/useTradingSimulationEvents.js // 模拟盘交易事件追踪 Hook import { useCallback, useEffect } from 'react'; import { usePostHogTrack } from '../../../hooks/usePostHogRedux'; import { RETENTION_EVENTS } from '../../../lib/constants'; import { logger } from '../../../utils/logger'; /** * 模拟盘交易事件追踪 Hook * @param {Object} options - 配置选项 * @param {Object} options.portfolio - 账户信息 * @param {number} options.portfolio.totalValue - 总资产 * @param {number} options.portfolio.availableCash - 可用资金 * @param {number} options.portfolio.holdingsCount - 持仓数量 * @param {Function} options.navigate - 路由导航函数 * @returns {Object} 事件追踪处理函数集合 */ export const useTradingSimulationEvents = ({ portfolio, navigate } = {}) => { const { track } = usePostHogTrack(); // 🎯 页面浏览事件 - 页面加载时触发 useEffect(() => { track(RETENTION_EVENTS.TRADING_SIMULATION_ENTERED, { total_value: portfolio?.totalValue || 0, available_cash: portfolio?.availableCash || 0, holdings_count: portfolio?.holdingsCount || 0, has_holdings: Boolean(portfolio?.holdingsCount && portfolio.holdingsCount > 0), timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '🎮 Trading Simulation Entered', { totalValue: portfolio?.totalValue, holdingsCount: portfolio?.holdingsCount, }); }, [track, portfolio]); /** * 追踪股票搜索(模拟盘内) * @param {string} query - 搜索关键词 * @param {number} resultCount - 搜索结果数量 */ const trackSimulationStockSearched = useCallback((query, resultCount = 0) => { if (!query) return; track(RETENTION_EVENTS.SIMULATION_STOCK_SEARCHED, { query, result_count: resultCount, has_results: resultCount > 0, timestamp: new Date().toISOString(), }); // 如果没有搜索结果,额外追踪 if (resultCount === 0) { track(RETENTION_EVENTS.SEARCH_NO_RESULTS, { query, context: 'trading_simulation', timestamp: new Date().toISOString(), }); } logger.debug('useTradingSimulationEvents', '🔍 Simulation Stock Searched', { query, resultCount, }); }, [track]); /** * 追踪下单操作 * @param {Object} order - 订单信息 * @param {string} order.stockCode - 股票代码 * @param {string} order.stockName - 股票名称 * @param {string} order.direction - 买卖方向 ('buy' | 'sell') * @param {number} order.quantity - 数量 * @param {number} order.price - 价格 * @param {string} order.orderType - 订单类型 ('market' | 'limit') * @param {boolean} order.success - 是否成功 */ const trackSimulationOrderPlaced = useCallback((order) => { if (!order || !order.stockCode) { logger.warn('useTradingSimulationEvents', 'Order object is required'); return; } track(RETENTION_EVENTS.SIMULATION_ORDER_PLACED, { stock_code: order.stockCode, stock_name: order.stockName || '', direction: order.direction, quantity: order.quantity, price: order.price, order_type: order.orderType || 'market', order_value: order.quantity * order.price, success: order.success, error_message: order.errorMessage || null, timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '📝 Simulation Order Placed', { stockCode: order.stockCode, direction: order.direction, quantity: order.quantity, success: order.success, }); }, [track]); /** * 追踪持仓查看 * @param {Object} holdings - 持仓信息 * @param {number} holdings.count - 持仓数量 * @param {number} holdings.totalValue - 持仓总市值 * @param {number} holdings.totalCost - 持仓总成本 * @param {number} holdings.profitLoss - 总盈亏 */ const trackSimulationHoldingsViewed = useCallback((holdings = {}) => { track(RETENTION_EVENTS.SIMULATION_HOLDINGS_VIEWED, { holdings_count: holdings.count || 0, total_value: holdings.totalValue || 0, total_cost: holdings.totalCost || 0, profit_loss: holdings.profitLoss || 0, profit_loss_percent: holdings.totalCost ? ((holdings.profitLoss / holdings.totalCost) * 100).toFixed(2) : 0, has_profit: holdings.profitLoss > 0, timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '💼 Simulation Holdings Viewed', { count: holdings.count, profitLoss: holdings.profitLoss, }); }, [track]); /** * 追踪持仓股票点击 * @param {Object} holding - 持仓对象 * @param {string} holding.stockCode - 股票代码 * @param {string} holding.stockName - 股票名称 * @param {number} holding.profitLoss - 盈亏金额 * @param {number} position - 在列表中的位置 */ const trackHoldingClicked = useCallback((holding, position = 0) => { if (!holding || !holding.stockCode) { logger.warn('useTradingSimulationEvents', 'Holding object is required'); return; } track(RETENTION_EVENTS.STOCK_CLICKED, { stock_code: holding.stockCode, stock_name: holding.stockName || '', source: 'simulation_holdings', profit_loss: holding.profitLoss || 0, position, timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '🎯 Holding Clicked', { stockCode: holding.stockCode, position, }); }, [track]); /** * 追踪历史交易记录查看 * @param {Object} history - 历史记录信息 * @param {number} history.count - 交易记录数量 * @param {string} history.filterBy - 筛选条件 ('all' | 'buy' | 'sell') * @param {string} history.dateRange - 日期范围 */ const trackSimulationHistoryViewed = useCallback((history = {}) => { track(RETENTION_EVENTS.SIMULATION_HISTORY_VIEWED, { history_count: history.count || 0, filter_by: history.filterBy || 'all', date_range: history.dateRange || 'all', has_history: Boolean(history.count && history.count > 0), timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '📜 Simulation History Viewed', { count: history.count, filterBy: history.filterBy, }); }, [track]); /** * 追踪买入按钮点击 * @param {Object} stock - 股票对象 * @param {string} stock.code - 股票代码 * @param {string} stock.name - 股票名称 * @param {number} stock.price - 当前价格 * @param {string} source - 来源 ('search' | 'holdings' | 'stock_detail') */ const trackBuyButtonClicked = useCallback((stock, source = 'search') => { if (!stock || !stock.code) { logger.warn('useTradingSimulationEvents', 'Stock object is required'); return; } track('Simulation Buy Button Clicked', { stock_code: stock.code, stock_name: stock.name || '', current_price: stock.price || 0, source, timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '🟢 Buy Button Clicked', { stockCode: stock.code, source, }); }, [track]); /** * 追踪卖出按钮点击 * @param {Object} holding - 持仓对象 * @param {string} holding.stockCode - 股票代码 * @param {string} holding.stockName - 股票名称 * @param {number} holding.quantity - 持有数量 * @param {number} holding.profitLoss - 盈亏金额 * @param {string} source - 来源 ('holdings' | 'stock_detail') */ const trackSellButtonClicked = useCallback((holding, source = 'holdings') => { if (!holding || !holding.stockCode) { logger.warn('useTradingSimulationEvents', 'Holding object is required'); return; } track('Simulation Sell Button Clicked', { stock_code: holding.stockCode, stock_name: holding.stockName || '', quantity: holding.quantity || 0, profit_loss: holding.profitLoss || 0, source, timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '🔴 Sell Button Clicked', { stockCode: holding.stockCode, source, }); }, [track]); /** * 追踪账户重置 * @param {Object} beforeReset - 重置前的账户信息 * @param {number} beforeReset.totalValue - 总资产 * @param {number} beforeReset.profitLoss - 总盈亏 */ const trackAccountReset = useCallback((beforeReset = {}) => { track('Simulation Account Reset', { total_value_before: beforeReset.totalValue || 0, profit_loss_before: beforeReset.profitLoss || 0, holdings_count_before: beforeReset.holdingsCount || 0, timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '🔄 Account Reset', { totalValueBefore: beforeReset.totalValue, }); }, [track]); /** * 追踪标签页切换 * @param {string} tabName - 标签名称 ('trading' | 'holdings' | 'history') */ const trackTabClicked = useCallback((tabName) => { if (!tabName) { logger.warn('useTradingSimulationEvents', 'Tab name is required'); return; } track('Simulation Tab Clicked', { tab_name: tabName, timestamp: new Date().toISOString(), }); logger.debug('useTradingSimulationEvents', '📑 Tab Clicked', { tabName, }); }, [track]); return { // 搜索事件 trackSimulationStockSearched, // 交易事件 trackSimulationOrderPlaced, trackBuyButtonClicked, trackSellButtonClicked, // 持仓事件 trackSimulationHoldingsViewed, trackHoldingClicked, // 历史记录事件 trackSimulationHistoryViewed, // 账户管理事件 trackAccountReset, // UI交互事件 trackTabClicked, }; }; export default useTradingSimulationEvents;