From 6fed1b40cd73c38a177aaf552554c53023a0afb0 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 29 Dec 2025 17:23:22 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E6=9C=AA=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=20Hooks/Services/Utils=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=20(=E7=AC=AC9=E6=89=B9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除以下未被引用的文件: - usePostHog.js (已被 usePostHogRedux.js 替代) - llmService.js - debugEventService.js / eventBus.js / tradingTimeUtils.js ⚠️ 已恢复误删的 predictionMarketService.js 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/hooks/usePostHog.js | 101 ------------ src/services/llmService.js | 278 --------------------------------- src/utils/debugEventService.js | 39 ----- src/utils/eventBus.js | 25 --- src/utils/tradingTimeUtils.js | 181 --------------------- 5 files changed, 624 deletions(-) delete mode 100644 src/hooks/usePostHog.js delete mode 100644 src/services/llmService.js delete mode 100644 src/utils/debugEventService.js delete mode 100644 src/utils/eventBus.js delete mode 100644 src/utils/tradingTimeUtils.js diff --git a/src/hooks/usePostHog.js b/src/hooks/usePostHog.js deleted file mode 100644 index eb51f4e9..00000000 --- a/src/hooks/usePostHog.js +++ /dev/null @@ -1,101 +0,0 @@ -// src/hooks/usePostHog.js -import { useCallback } from 'react'; -import { - getPostHog, - trackEvent, - trackPageView, - identifyUser, - setUserProperties, - resetUser, - optIn, - optOut, - hasOptedOut, - getFeatureFlag, - isFeatureEnabled, -} from '../lib/posthog'; - -/** - * Custom hook to access PostHog functionality - * Provides convenient methods for tracking events and managing user sessions - * - * @returns {object} PostHog methods - */ -export const usePostHog = () => { - // Get PostHog instance - const posthog = getPostHog(); - - // Track custom event - const track = useCallback((eventName, properties = {}) => { - trackEvent(eventName, properties); - }, []); - - // Track page view - const trackPage = useCallback((pagePath, properties = {}) => { - trackPageView(pagePath, properties); - }, []); - - // Identify user - const identify = useCallback((userId, userProperties = {}) => { - identifyUser(userId, userProperties); - }, []); - - // Set user properties - const setProperties = useCallback((properties) => { - setUserProperties(properties); - }, []); - - // Reset user session (logout) - const reset = useCallback(() => { - resetUser(); - }, []); - - // Opt out of tracking - const optOutTracking = useCallback(() => { - optOut(); - }, []); - - // Opt in to tracking - const optInTracking = useCallback(() => { - optIn(); - }, []); - - // Check if user has opted out - const isOptedOut = useCallback(() => { - return hasOptedOut(); - }, []); - - // Get feature flag value - const getFlag = useCallback((flagKey, defaultValue = false) => { - return getFeatureFlag(flagKey, defaultValue); - }, []); - - // Check if feature is enabled - const isEnabled = useCallback((flagKey) => { - return isFeatureEnabled(flagKey); - }, []); - - return { - // Core PostHog instance - posthog, - - // Tracking methods - track, - trackPage, - - // User management - identify, - setProperties, - reset, - - // Privacy controls - optOut: optOutTracking, - optIn: optInTracking, - isOptedOut, - - // Feature flags - getFlag, - isEnabled, - }; -}; - -export default usePostHog; diff --git a/src/services/llmService.js b/src/services/llmService.js deleted file mode 100644 index 22183f47..00000000 --- a/src/services/llmService.js +++ /dev/null @@ -1,278 +0,0 @@ -// src/services/llmService.js -// LLM服务层 - 集成AI模型进行对话和工具调用 - -import axios from 'axios'; -import { mcpService } from './mcpService'; -import { logger } from '../utils/logger'; - -/** - * LLM服务配置 - */ -const LLM_CONFIG = { - // 可以使用 OpenAI、Claude、通义千问等 - provider: 'openai', // 或 'claude', 'qwen' - apiKey: process.env.REACT_APP_OPENAI_API_KEY || '', - apiUrl: 'https://api.openai.com/v1/chat/completions', - model: 'gpt-4o-mini', // 更便宜的模型 -}; - -/** - * LLM服务类 - */ -class LLMService { - constructor() { - this.conversationHistory = []; - } - - /** - * 构建系统提示词 - */ - getSystemPrompt(availableTools) { - return `你是一个专业的金融投资助手。你可以使用以下工具来帮助用户查询信息: - -${availableTools.map(tool => ` -**${tool.name}** -描述:${tool.description} -参数:${JSON.stringify(tool.parameters, null, 2)} -`).join('\n')} - -用户提问时,请按照以下步骤: -1. 理解用户的意图 -2. 选择合适的工具(可以多个) -3. 提取工具需要的参数 -4. 调用工具后,用自然语言总结结果 - -回复格式: -- 如果需要调用工具,返回JSON格式:{"tool": "工具名", "arguments": {...}} -- 如果不需要工具,直接回复自然语言 - -注意: -- 贵州茅台的股票代码是 600519 -- 涨停是指股票当日涨幅达到10% -- 概念板块是指相同题材的股票分类`; - } - - /** - * 智能对话 - 使用LLM理解意图并调用工具 - */ - async chat(userMessage, conversationHistory = []) { - try { - // 1. 获取可用工具列表 - const toolsResult = await mcpService.listTools(); - if (!toolsResult.success) { - throw new Error('获取工具列表失败'); - } - - const availableTools = toolsResult.data; - - // 2. 构建对话历史 - const messages = [ - { - role: 'system', - content: this.getSystemPrompt(availableTools), - }, - ...conversationHistory.map(msg => ({ - role: msg.isUser ? 'user' : 'assistant', - content: msg.content, - })), - { - role: 'user', - content: userMessage, - }, - ]; - - // 3. 调用LLM - logger.info('LLMService', '调用LLM', { messageCount: messages.length }); - - // 注意:这里需要配置API密钥 - if (!LLM_CONFIG.apiKey) { - // 如果没有配置LLM,使用简单的关键词匹配 - logger.warn('LLMService', '未配置LLM API密钥,使用简单匹配'); - return await this.fallbackChat(userMessage); - } - - const response = await axios.post( - LLM_CONFIG.apiUrl, - { - model: LLM_CONFIG.model, - messages: messages, - temperature: 0.7, - max_tokens: 1000, - }, - { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${LLM_CONFIG.apiKey}`, - }, - timeout: 30000, - } - ); - - const aiResponse = response.data.choices[0].message.content; - logger.info('LLMService', 'LLM响应', { response: aiResponse }); - - // 4. 解析LLM响应 - // 如果LLM返回工具调用指令 - try { - const toolCall = JSON.parse(aiResponse); - if (toolCall.tool && toolCall.arguments) { - // 调用MCP工具 - const toolResult = await mcpService.callTool(toolCall.tool, toolCall.arguments); - - if (!toolResult.success) { - return { - success: false, - error: toolResult.error, - }; - } - - // 5. 让LLM总结工具结果 - const summaryMessages = [ - ...messages, - { - role: 'assistant', - content: aiResponse, - }, - { - role: 'system', - content: `工具 ${toolCall.tool} 返回的数据:\n${JSON.stringify(toolResult.data, null, 2)}\n\n请用自然语言总结这些数据,给用户一个简洁清晰的回复。`, - }, - ]; - - const summaryResponse = await axios.post( - LLM_CONFIG.apiUrl, - { - model: LLM_CONFIG.model, - messages: summaryMessages, - temperature: 0.7, - max_tokens: 500, - }, - { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${LLM_CONFIG.apiKey}`, - }, - timeout: 30000, - } - ); - - const summary = summaryResponse.data.choices[0].message.content; - - return { - success: true, - data: { - message: summary, - rawData: toolResult.data, - toolUsed: toolCall.tool, - }, - }; - } - } catch (parseError) { - // 不是JSON格式,说明是直接回复 - return { - success: true, - data: { - message: aiResponse, - }, - }; - } - - // 默认返回LLM的直接回复 - return { - success: true, - data: { - message: aiResponse, - }, - }; - } catch (error) { - logger.error('LLMService', 'chat error', error); - return { - success: false, - error: error.message || '对话处理失败', - }; - } - } - - /** - * 降级方案:简单的关键词匹配(当没有配置LLM时) - */ - async fallbackChat(userMessage) { - logger.info('LLMService', '使用降级方案', { message: userMessage }); - - // 使用原有的简单匹配逻辑 - if (userMessage.includes('新闻') || userMessage.includes('资讯')) { - const result = await mcpService.callTool('search_china_news', { - query: userMessage.replace(/新闻|资讯/g, '').trim(), - top_k: 5, - }); - return this.formatFallbackResponse(result, '新闻搜索'); - } else if (userMessage.includes('概念') || userMessage.includes('板块')) { - const query = userMessage.replace(/概念|板块/g, '').trim(); - const result = await mcpService.callTool('search_concepts', { - query, - size: 5, - sort_by: 'change_pct', - }); - return this.formatFallbackResponse(result, '概念搜索'); - } else if (userMessage.includes('涨停')) { - const query = userMessage.replace(/涨停/g, '').trim(); - const result = await mcpService.callTool('search_limit_up_stocks', { - query, - mode: 'hybrid', - page_size: 5, - }); - return this.formatFallbackResponse(result, '涨停分析'); - } else if (/^[0-9]{6}$/.test(userMessage.trim())) { - // 6位数字 = 股票代码 - const result = await mcpService.callTool('get_stock_basic_info', { - seccode: userMessage.trim(), - }); - return this.formatFallbackResponse(result, '股票信息'); - } else if (userMessage.includes('茅台') || userMessage.includes('贵州茅台')) { - // 特殊处理茅台 - const result = await mcpService.callTool('get_stock_basic_info', { - seccode: '600519', - }); - return this.formatFallbackResponse(result, '贵州茅台股票信息'); - } else { - // 默认:搜索新闻 - const result = await mcpService.callTool('search_china_news', { - query: userMessage, - top_k: 5, - }); - return this.formatFallbackResponse(result, '新闻搜索'); - } - } - - /** - * 格式化降级响应 - */ - formatFallbackResponse(result, action) { - if (!result.success) { - return { - success: false, - error: result.error, - }; - } - - return { - success: true, - data: { - message: `已为您完成${action},找到以下结果:`, - rawData: result.data, - }, - }; - } - - /** - * 清除对话历史 - */ - clearHistory() { - this.conversationHistory = []; - } -} - -// 导出单例 -export const llmService = new LLMService(); - -export default LLMService; diff --git a/src/utils/debugEventService.js b/src/utils/debugEventService.js deleted file mode 100644 index 9b366fb7..00000000 --- a/src/utils/debugEventService.js +++ /dev/null @@ -1,39 +0,0 @@ -// 调试文件 - 检查 eventService 的方法 -import { eventService } from '../services/eventService'; - -export const debugEventService = () => { - console.log('=== eventService 调试信息 ==='); - console.log('eventService 对象:', eventService); - console.log('eventService 类型:', typeof eventService); - console.log('eventService 的所有键:', Object.keys(eventService)); - - // 检查特定的方法 - const methods = [ - 'getPosts', - 'createPost', - 'deletePost', - 'likePost', - 'getPostComments', - 'addPostComment', - 'deleteComment', - 'likeComment' - ]; - - console.log('\n方法检查:'); - methods.forEach(method => { - console.log(`${method}:`, - eventService[method] ? '✓ 存在' : '✗ 不存在', - eventService[method] ? `(类型: ${typeof eventService[method]})` : '' - ); - }); - - // 检查是否有其他相似的方法名 - console.log('\n所有方法列表:'); - Object.keys(eventService).forEach(key => { - if (typeof eventService[key] === 'function') { - console.log(`- ${key}`); - } - }); - - return eventService; -}; diff --git a/src/utils/eventBus.js b/src/utils/eventBus.js deleted file mode 100644 index 71b9d82c..00000000 --- a/src/utils/eventBus.js +++ /dev/null @@ -1,25 +0,0 @@ -// 简单的事件总线,用于组件间通信 -class EventBus { - constructor() { - this.events = {}; - } - - on(event, callback) { - if (!this.events[event]) { - this.events[event] = []; - } - this.events[event].push(callback); - } - - off(event, callback) { - if (!this.events[event]) return; - this.events[event] = this.events[event].filter(cb => cb !== callback); - } - - emit(event, data) { - if (!this.events[event]) return; - this.events[event].forEach(callback => callback(data)); - } -} - -export default new EventBus(); diff --git a/src/utils/tradingTimeUtils.js b/src/utils/tradingTimeUtils.js deleted file mode 100644 index 4321b2b5..00000000 --- a/src/utils/tradingTimeUtils.js +++ /dev/null @@ -1,181 +0,0 @@ -// src/utils/tradingTimeUtils.js -// 交易时间相关工具函数 - -import dayjs from 'dayjs'; -import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; -import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; - -// 扩展 Day.js 插件 -dayjs.extend(isSameOrBefore); -dayjs.extend(isSameOrAfter); - -/** - * 获取当前时间应该显示的实时要闻时间范围 - * 规则: - * - 15:00 之前:显示昨日 15:00 - 今日 15:00 - * - 15:30 之后:显示今日 15:00 - 当前时间 - * - * @returns {{ startTime: Date, endTime: Date, description: string }} - */ -export const getCurrentTradingTimeRange = () => { - const now = dayjs(); - const currentHour = now.hour(); - const currentMinute = now.minute(); - - // 计算当前是第几分钟(方便比较) - const currentTimeInMinutes = currentHour * 60 + currentMinute; - const cutoffTime1500 = 15 * 60; // 15:00 = 900分钟 - const cutoffTime1530 = 15 * 60 + 30; // 15:30 = 930分钟 - - let startTime, endTime, description; - - if (currentTimeInMinutes < cutoffTime1500) { - // 15:00 之前:显示昨日 15:00 - 今日 15:00 - startTime = dayjs().subtract(1, 'day').hour(15).minute(0).second(0).millisecond(0).toDate(); - endTime = dayjs().hour(15).minute(0).second(0).millisecond(0).toDate(); - description = '昨日15:00 - 今日15:00'; - } else if (currentTimeInMinutes >= cutoffTime1530) { - // 15:30 之后:显示今日 15:00 - 当前时间 - startTime = dayjs().hour(15).minute(0).second(0).millisecond(0).toDate(); - endTime = now.toDate(); - description = '今日15:00 - 当前时间'; - } else { - // 15:00 - 15:30 之间:过渡期,保持显示昨日 15:00 - 今日 15:00 - startTime = dayjs().subtract(1, 'day').hour(15).minute(0).second(0).millisecond(0).toDate(); - endTime = dayjs().hour(15).minute(0).second(0).millisecond(0).toDate(); - description = '昨日15:00 - 今日15:00'; - } - - return { - startTime, - endTime, - description, - rangeType: currentTimeInMinutes >= cutoffTime1530 ? 'current_day' : 'full_day' - }; -}; - -/** - * 获取市场复盘的时间范围 - * 规则:显示最近一个完整的交易日(昨日 15:00 - 今日 15:00) - * - * @returns {{ startTime: Date, endTime: Date, description: string }} - */ -export const getMarketReviewTimeRange = () => { - const now = dayjs(); - const currentHour = now.hour(); - const currentMinute = now.minute(); - - // 计算当前是第几分钟 - const currentTimeInMinutes = currentHour * 60 + currentMinute; - const cutoffTime1530 = 15 * 60 + 30; // 15:30 = 930分钟 - - let startTime, endTime, description; - - if (currentTimeInMinutes >= cutoffTime1530) { - // 15:30 之后:显示昨日 15:00 - 今日 15:00(刚刚完成的交易日) - startTime = dayjs().subtract(1, 'day').hour(15).minute(0).second(0).millisecond(0).toDate(); - endTime = dayjs().hour(15).minute(0).second(0).millisecond(0).toDate(); - description = '昨日15:00 - 今日15:00'; - } else { - // 15:30 之前:显示前日 15:00 - 昨日 15:00(上一个完整交易日) - startTime = dayjs().subtract(2, 'days').hour(15).minute(0).second(0).millisecond(0).toDate(); - endTime = dayjs().subtract(1, 'day').hour(15).minute(0).second(0).millisecond(0).toDate(); - description = '前日15:00 - 昨日15:00'; - } - - return { - startTime, - endTime, - description, - rangeType: 'market_review' - }; -}; - -/** - * 根据时间范围过滤事件列表 - * - * @param {Array} events - 事件列表 - * @param {Date} startTime - 开始时间 - * @param {Date} endTime - 结束时间 - * @returns {Array} 过滤后的事件列表 - */ -export const filterEventsByTimeRange = (events, startTime, endTime) => { - if (!events || !Array.isArray(events)) { - return []; - } - - if (!startTime || !endTime) { - return events; - } - - const startMoment = dayjs(startTime); - const endMoment = dayjs(endTime); - - return events.filter(event => { - if (!event.created_at) { - return false; - } - - const eventTime = dayjs(event.created_at); - return eventTime.isSameOrAfter(startMoment) && eventTime.isSameOrBefore(endMoment); - }); -}; - -/** - * 判断当前是否应该显示市场复盘模块 - * 根据需求:市场复盘模块一直显示 - * - * @returns {boolean} - */ -export const shouldShowMarketReview = () => { - // 市场复盘模块始终显示 - return true; -}; - -/** - * 获取时间范围的描述文本 - * - * @param {Date} startTime - 开始时间 - * @param {Date} endTime - 结束时间 - * @returns {string} - */ -export const getTimeRangeDescription = (startTime, endTime) => { - if (!startTime || !endTime) { - return ''; - } - - const startStr = dayjs(startTime).format('MM-DD HH:mm'); - const endStr = dayjs(endTime).format('MM-DD HH:mm'); - - return `${startStr} - ${endStr}`; -}; - -/** - * 判断是否为交易日(简化版本,只判断周末) - * 注意:这里没有考虑节假日,如需精确判断需要接入交易日历API - * - * @param {Date} date - 日期 - * @returns {boolean} - */ -export const isTradingDay = (date) => { - const day = dayjs(date).day(); - // 0 = 周日, 6 = 周六 - return day !== 0 && day !== 6; -}; - -/** - * 获取上一个交易日(简化版本) - * - * @param {Date} date - 日期 - * @returns {Date} - */ -export const getPreviousTradingDay = (date) => { - let prevDay = dayjs(date).subtract(1, 'day'); - - // 如果是周末,继续往前找 - while (!isTradingDay(prevDay.toDate())) { - prevDay = prevDay.subtract(1, 'day'); - } - - return prevDay.toDate(); -};