chore: 删除未使用的 Hooks/Services/Utils 文件 (第9批)
删除以下未被引用的文件: - 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 <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
};
|
||||
Reference in New Issue
Block a user