Files
vf_react/src/services/eventService.js
2025-10-16 15:54:57 +08:00

400 lines
14 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/services/eventService.js
const apiRequest = async (url, options = {}) => {
try {
console.log(`Making API request to: ${url}`);
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers,
},
credentials: 'include', // 包含 cookies以便后端识别登录状态
});
if (!response.ok) {
const errorText = await response.text();
console.error(`API request failed: ${response.status} - ${errorText}`);
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(`API response from ${url}:`, data);
return data;
} catch (error) {
console.error(`API request failed for ${url}:`, error);
throw error;
}
};
export const eventService = {
getEvents: (params = {}) => {
// Filter out empty params
const cleanParams = Object.fromEntries(Object.entries(params).filter(([_, v]) => v != null && v !== ''));
const query = new URLSearchParams(cleanParams).toString();
return apiRequest(`/api/events/?${query}`);
},
getHotEvents: (params = {}) => {
const query = new URLSearchParams(params).toString();
return apiRequest(`/api/events/hot?${query}`);
},
getPopularKeywords: (limit = 20) => {
return apiRequest(`/api/events/keywords/popular?limit=${limit}`);
},
calendar: {
/**
* Fetches event counts for a given month, for display on the calendar grid.
* @param {number} year - The year to fetch counts for.
* @param {number} month - The month to fetch counts for (1-12).
* @returns {Promise<object>} A list of objects with date, count, and className.
*/
getEventCounts: (year, month) => {
// Note: The backend route is `/api/v1/calendar/event-counts`, not `/api/calendar-event-counts`
return apiRequest(`/api/v1/calendar/event-counts?year=${year}&month=${month}`);
},
/**
* Fetches a list of all events for a specific date.
* @param {string} date - The date in 'YYYY-MM-DD' format.
* @param {string} [type='all'] - The type of events to fetch ('event', 'data', or 'all').
* @returns {Promise<object>} A list of event objects for the given date.
*/
getEventsForDate: (date, type = 'all') => {
return apiRequest(`/api/v1/calendar/events?date=${date}&type=${type}`);
},
/**
* Fetches the full details of a single calendar event.
* @param {number} eventId - The ID of the calendar event.
* @returns {Promise<object>} The detailed calendar event object.
*/
getEventDetail: (eventId) => {
return apiRequest(`/api/v1/calendar/events/${eventId}`);
},
/**
* 切换未来事件的关注状态
* @param {number} eventId - 未来事件ID
* @returns {Promise<object>} 返回关注状态
*/
toggleFollow: async (eventId) => {
return await apiRequest(`/api/v1/calendar/events/${eventId}/follow`, {
method: 'POST'
});
},
/**
* 获取用户关注的所有未来事件
* @returns {Promise<object>} 返回关注的未来事件列表
*/
getFollowingEvents: async () => {
return await apiRequest(`/api/account/future-events/following`);
},
},
getEventDetail: async (eventId) => {
return await apiRequest(`/api/events/${eventId}`);
},
getRelatedStocks: async (eventId) => {
return await apiRequest(`/api/events/${eventId}/stocks`);
},
addRelatedStock: async (eventId, stockData) => {
return await apiRequest(`/api/events/${eventId}/stocks`, {
method: 'POST',
body: JSON.stringify(stockData),
});
},
deleteRelatedStock: async (stockId) => {
return await apiRequest(`/api/stocks/${stockId}`, {
method: 'DELETE',
});
},
getRelatedConcepts: async (eventId) => {
return await apiRequest(`/api/events/${eventId}/concepts`);
},
getHistoricalEvents: async (eventId) => {
return await apiRequest(`/api/events/${eventId}/historical`);
},
getExpectationScore: async (eventId) => {
return await apiRequest(`/api/events/${eventId}/expectation-score`);
},
getTransmissionChainAnalysis: async (eventId) => {
// This API is optimized: it filters isolated nodes and provides a clean data structure.
return await apiRequest(`/api/events/${eventId}/transmission`);
},
getChainNodeDetail: async (eventId, nodeId) => {
return await apiRequest(`/api/events/${eventId}/chain-node/${nodeId}`);
},
getSankeyData: async (eventId) => {
return await apiRequest(`/api/events/${eventId}/sankey-data`);
},
getSankeyNodeDetail: async (eventId, nodeInfo) => {
const params = new URLSearchParams({
node_name: nodeInfo.name,
node_type: nodeInfo.type,
});
if (nodeInfo.level !== undefined && nodeInfo.level !== null) {
params.append('node_level', nodeInfo.level);
}
const url = `/api/events/${eventId}/sankey-data?${params.toString()}`;
return await apiRequest(url);
},
toggleFollow: async (eventId, isFollowing) => {
return await apiRequest(`/api/events/${eventId}/follow`, {
method: 'POST',
body: JSON.stringify({ is_following: isFollowing }),
});
},
// 帖子相关API
getPosts: async (eventId, sortType = 'latest', page = 1, perPage = 20) => {
try {
return await apiRequest(`/api/events/${eventId}/posts?sort=${sortType}&page=${page}&per_page=${perPage}`);
} catch (error) {
console.error('获取帖子失败:', error);
return { success: false, data: [], pagination: {} };
}
},
createPost: async (eventId, postData) => {
try {
return await apiRequest(`/api/events/${eventId}/posts`, {
method: 'POST',
body: JSON.stringify(postData)
});
} catch (error) {
console.error('创建帖子失败:', error);
return { success: false, message: '创建帖子失败' };
}
},
deletePost: async (postId) => {
try {
return await apiRequest(`/api/posts/${postId}`, {
method: 'DELETE'
});
} catch (error) {
console.error('删除帖子失败:', error);
return { success: false, message: '删除帖子失败' };
}
},
likePost: async (postId) => {
try {
return await apiRequest(`/api/posts/${postId}/like`, {
method: 'POST'
});
} catch (error) {
console.error('点赞失败:', error);
return { success: false, message: '点赞失败' };
}
},
// 评论相关API
getPostComments: async (postId, sortType = 'latest') => {
try {
return await apiRequest(`/api/posts/${postId}/comments?sort=${sortType}`);
} catch (error) {
console.error('获取评论失败:', error);
return { success: false, data: [] };
}
},
addPostComment: async (postId, commentData) => {
try {
return await apiRequest(`/api/posts/${postId}/comments`, {
method: 'POST',
body: JSON.stringify(commentData)
});
} catch (error) {
console.error('添加评论失败:', error);
return { success: false, message: '添加评论失败' };
}
},
deleteComment: async (commentId) => {
try {
return await apiRequest(`/api/comments/${commentId}`, {
method: 'DELETE'
});
} catch (error) {
console.error('删除评论失败:', error);
return { success: false, message: '删除评论失败' };
}
},
likeComment: async (commentId) => {
try {
return await apiRequest(`/api/comments/${commentId}/like`, {
method: 'POST'
});
} catch (error) {
console.error('点赞失败:', error);
return { success: false, message: '点赞失败' };
}
},
// 兼容旧版本的评论API
getComments: async (eventId, sortType = 'latest') => {
console.warn('getComments 已废弃,请使用 getPosts');
// 直接调用 getPosts 的实现,避免循环引用
try {
return await apiRequest(`/api/events/${eventId}/posts?sort=${sortType}&page=1&per_page=20`);
} catch (error) {
console.error('获取帖子失败:', error);
return { success: false, data: [], pagination: {} };
}
},
addComment: async (eventId, commentData) => {
console.warn('addComment 已废弃,请使用 createPost');
// 直接调用 createPost 的实现,避免循环引用
try {
return await apiRequest(`/api/events/${eventId}/posts`, {
method: 'POST',
body: JSON.stringify(commentData)
});
} catch (error) {
console.error('创建帖子失败:', error);
return { success: false, message: '创建帖子失败' };
}
},
};
export const stockService = {
getQuotes: async (codes, eventTime = null) => {
try {
const requestBody = {
codes: codes
};
if (eventTime) {
requestBody.event_time = eventTime;
}
console.log(`获取股票报价,请求体:`, requestBody);
const response = await apiRequest(`/api/stock/quotes`, {
method: 'POST',
body: JSON.stringify(requestBody)
});
console.log('股票报价原始响应:', response);
console.log('response.success:', response.success);
console.log('response.data:', response.data);
if (response.success && response.data) {
console.log('返回股票报价数据:', response.data);
return response.data;
} else {
console.warn('股票报价响应格式异常:', response);
return {};
}
} catch (error) {
console.error('获取股票报价失败:', error);
throw error;
}
},
getForecastReport: async (stockCode) => {
return await apiRequest(`/api/stock/${stockCode}/forecast-report`);
},
getKlineData: async (stockCode, chartType = 'timeline', eventTime = null) => {
try {
const params = new URLSearchParams();
params.append('type', chartType);
if (eventTime) {
params.append('event_time', eventTime);
}
const url = `/api/stock/${stockCode}/kline?${params.toString()}`;
console.log(`获取K线数据: ${url}`);
const response = await apiRequest(url);
if (response.error) {
console.warn('K线数据响应包含错误:', response.error);
return response;
}
return response;
} catch (error) {
console.error('获取K线数据失败:', error);
throw error;
}
},
getTransmissionChainAnalysis: async (eventId) => {
return await apiRequest(`/api/events/${eventId}/transmission`);
},
async getSankeyNodeDetail(eventId, nodeInfo) {
try {
const params = new URLSearchParams({
node_name: nodeInfo.name,
node_type: nodeInfo.type,
node_level: nodeInfo.level
});
const response = await fetch(`/api/events/${eventId}/sankey-node-detail?${params}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching sankey node detail:', error);
throw error;
}
},
getChainNodeDetail: async (eventId, nodeId) => {
return await apiRequest(`/api/events/${eventId}/transmission/node/${nodeId}`);
},
getHistoricalEventStocks: async (eventId) => {
return await apiRequest(`/api/historical-events/${eventId}/stocks`);
},
};
export const indexService = {
getKlineData: async (indexCode, chartType = 'timeline', eventTime = null) => {
try {
const params = new URLSearchParams();
params.append('type', chartType);
if (eventTime) {
params.append('event_time', eventTime);
}
const url = `/api/index/${indexCode}/kline?${params.toString()}`;
console.log(`获取指数K线数据: ${url}`);
const response = await apiRequest(url);
return response;
} catch (error) {
console.error('获取指数K线数据失败:', error);
throw error;
}
},
};
export default {
eventService,
stockService,
indexService,
};