diff --git a/src/views/Community/components/StockDetailPanel/hooks/useStockMonitoring.js b/src/views/Community/components/StockDetailPanel/hooks/useStockMonitoring.js new file mode 100644 index 00000000..9f988c4f --- /dev/null +++ b/src/views/Community/components/StockDetailPanel/hooks/useStockMonitoring.js @@ -0,0 +1,159 @@ +// src/views/Community/components/StockDetailPanel/hooks/useStockMonitoring.js +import { useSelector, useDispatch } from 'react-redux'; +import { useState, useEffect, useRef, useCallback } from 'react'; +import { fetchStockQuotes } from '../../../../../store/slices/stockSlice'; +import { message } from 'antd'; +import { logger } from '../../../../../utils/logger'; + +/** + * 股票实时监控 Hook + * 提供定时刷新股票行情的功能 + * + * @param {Array} stocks - 股票列表 + * @param {string} eventTime - 事件时间 + * @param {number} interval - 刷新间隔(毫秒),默认 5000ms + * @returns {Object} 监控状态和控制方法 + */ +export const useStockMonitoring = (stocks = [], eventTime = null, interval = 5000) => { + const dispatch = useDispatch(); + const [isMonitoring, setIsMonitoring] = useState(false); + const monitoringIntervalRef = useRef(null); + + // 从 Redux 获取行情数据和加载状态 + const quotes = useSelector(state => state.stock.quotes); + const quotesLoading = useSelector(state => state.stock.loading.quotes); + + /** + * 执行一次行情更新 + */ + const updateQuotes = useCallback(() => { + if (stocks.length === 0) { + logger.warn('useStockMonitoring', '股票列表为空,跳过更新'); + return; + } + + const codes = stocks.map(s => s.stock_code); + logger.debug('useStockMonitoring', '更新行情数据', { + stockCount: codes.length, + eventTime, + timestamp: new Date().toISOString() + }); + + dispatch(fetchStockQuotes({ codes, eventTime })); + }, [dispatch, stocks, eventTime]); + + /** + * 开启实时监控 + */ + const startMonitoring = useCallback(() => { + if (isMonitoring) { + logger.warn('useStockMonitoring', '监控已经在运行中'); + return; + } + + if (stocks.length === 0) { + message.warning('暂无股票数据,无法开启监控'); + return; + } + + logger.info('useStockMonitoring', '开启实时监控', { + interval, + stockCount: stocks.length + }); + + setIsMonitoring(true); + message.success(`已开启实时监控,每${interval / 1000}秒自动更新`); + + // 立即执行一次 + updateQuotes(); + }, [isMonitoring, stocks, interval, updateQuotes]); + + /** + * 停止实时监控 + */ + const stopMonitoring = useCallback(() => { + if (!isMonitoring) { + return; + } + + logger.info('useStockMonitoring', '停止实时监控'); + + setIsMonitoring(false); + message.info('已停止实时监控'); + }, [isMonitoring]); + + /** + * 切换监控状态 + */ + const toggleMonitoring = useCallback(() => { + if (isMonitoring) { + stopMonitoring(); + } else { + startMonitoring(); + } + }, [isMonitoring, startMonitoring, stopMonitoring]); + + /** + * 手动刷新一次 + */ + const manualRefresh = useCallback(() => { + logger.debug('useStockMonitoring', '手动刷新行情'); + updateQuotes(); + }, [updateQuotes]); + + // 监控定时器效果 + useEffect(() => { + // 清理旧的定时器 + if (monitoringIntervalRef.current) { + clearInterval(monitoringIntervalRef.current); + monitoringIntervalRef.current = null; + } + + if (isMonitoring && stocks.length > 0) { + // 设置定时器 + monitoringIntervalRef.current = setInterval(() => { + updateQuotes(); + }, interval); + + logger.debug('useStockMonitoring', '定时器已设置', { + interval, + stockCount: stocks.length + }); + } + + // 清理函数 + return () => { + if (monitoringIntervalRef.current) { + clearInterval(monitoringIntervalRef.current); + monitoringIntervalRef.current = null; + logger.debug('useStockMonitoring', '定时器已清理'); + } + }; + }, [isMonitoring, stocks.length, interval]); // 注意:不依赖 updateQuotes,避免重复创建定时器 + + // 组件卸载时自动停止监控 + useEffect(() => { + return () => { + if (isMonitoring) { + logger.debug('useStockMonitoring', '组件卸载,自动停止监控'); + setIsMonitoring(false); + } + }; + }, []); // 只在卸载时执行 + + return { + // 状态 + isMonitoring, + quotes, + quotesLoading, + + // 控制方法 + startMonitoring, + stopMonitoring, + toggleMonitoring, + manualRefresh, + + // 工具方法 + setIsMonitoring + }; +};