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