community增加事件详情
This commit is contained in:
76
app.py
76
app.py
@@ -11004,9 +11004,12 @@ def get_events_effectiveness_stats():
|
||||
|
||||
按交易日统计事件数据,展示事件预测的有效性
|
||||
|
||||
交易日定义:上一交易日15:00到当前交易日15:00
|
||||
例如:周一15:00到周二15:00为"周二交易日"的事件
|
||||
|
||||
参数:
|
||||
date: 指定日期(YYYY-MM-DD格式,可选,默认今天)
|
||||
days: 统计天数(默认7天)
|
||||
days: 统计天数(默认7天,days=1表示当前交易日)
|
||||
|
||||
返回:
|
||||
{
|
||||
@@ -11041,21 +11044,78 @@ def get_events_effectiveness_stats():
|
||||
days = request.args.get('days', 7, type=int)
|
||||
days = min(max(days, 1), 30) # 限制1-30天
|
||||
|
||||
# 确定查询日期范围
|
||||
# 确定基准时间
|
||||
if date_str:
|
||||
try:
|
||||
end_date = datetime.strptime(date_str, '%Y-%m-%d')
|
||||
base_date = datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
end_date = datetime.now()
|
||||
base_date = datetime.now()
|
||||
else:
|
||||
end_date = datetime.now()
|
||||
base_date = datetime.now()
|
||||
|
||||
start_date = end_date - timedelta(days=days)
|
||||
# 使用交易日15:00作为分界点计算时间范围
|
||||
# 当前交易日的结束时间:当天15:00(如果当前时间<15:00)或下一交易日15:00(如果当前时间>=15:00)
|
||||
current_time = base_date.time() if isinstance(base_date, datetime) else dt_time(12, 0)
|
||||
current_date = base_date.date() if isinstance(base_date, datetime) else base_date
|
||||
|
||||
# 确保交易日数据已加载
|
||||
if not trading_days:
|
||||
load_trading_days()
|
||||
|
||||
# 判断当前是否是交易日以及是否收盘后
|
||||
is_trading = current_date in trading_days_set
|
||||
is_after_close = current_time >= dt_time(15, 0)
|
||||
|
||||
# 确定当前交易日
|
||||
if is_trading:
|
||||
if is_after_close:
|
||||
# 交易日收盘后,当前交易日就是今天
|
||||
current_trading_day = current_date
|
||||
else:
|
||||
# 交易日盘中,当前交易日是今天
|
||||
current_trading_day = current_date
|
||||
else:
|
||||
# 非交易日,找最近的上一个交易日
|
||||
current_trading_day = None
|
||||
for td in reversed(trading_days):
|
||||
if td <= current_date:
|
||||
current_trading_day = td
|
||||
break
|
||||
|
||||
if not current_trading_day:
|
||||
current_trading_day = current_date
|
||||
|
||||
# 计算时间范围:每个交易日从上一交易日15:00到当天15:00
|
||||
# 对于 days=1(当前交易日),范围是:上一交易日15:00 到 当前交易日15:00
|
||||
|
||||
# 找到往前 days 个交易日
|
||||
try:
|
||||
current_idx = trading_days.index(current_trading_day)
|
||||
start_trading_day_idx = max(0, current_idx - days + 1)
|
||||
start_trading_day = trading_days[start_trading_day_idx]
|
||||
|
||||
# 找 start_trading_day 的前一个交易日
|
||||
if start_trading_day_idx > 0:
|
||||
prev_start_day = trading_days[start_trading_day_idx - 1]
|
||||
else:
|
||||
prev_start_day = start_trading_day - timedelta(days=1)
|
||||
except ValueError:
|
||||
# current_trading_day 不在列表中,使用简单计算
|
||||
start_trading_day = current_trading_day - timedelta(days=days)
|
||||
prev_start_day = start_trading_day - timedelta(days=1)
|
||||
|
||||
# 查询时间范围:从 prev_start_day 15:00 到 current_trading_day 15:00
|
||||
start_datetime = datetime.combine(prev_start_day, dt_time(15, 0))
|
||||
end_datetime = datetime.combine(current_trading_day, dt_time(15, 0))
|
||||
|
||||
# 如果当前时间还没到15:00,结束时间用当前时间
|
||||
if is_trading and not is_after_close:
|
||||
end_datetime = base_date if isinstance(base_date, datetime) else datetime.combine(current_date, dt_time(23, 59))
|
||||
|
||||
# 查询事件数据
|
||||
events_query = db.session.query(Event).filter(
|
||||
Event.created_at >= start_date,
|
||||
Event.created_at <= end_date + timedelta(days=1),
|
||||
Event.created_at >= start_datetime,
|
||||
Event.created_at <= end_datetime,
|
||||
Event.status == 'active'
|
||||
).order_by(Event.created_at.desc()).all()
|
||||
|
||||
|
||||
@@ -24,6 +24,17 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { getApiBase } from '@utils/apiConfig';
|
||||
|
||||
/**
|
||||
* 生成事件详情页 URL
|
||||
* @param {number} eventId - 事件ID
|
||||
* @returns {string} 事件详情页 URL
|
||||
*/
|
||||
const getEventDetailUrl = (eventId) => {
|
||||
// 使用 base64 编码 ID,格式:ev-{id} -> base64
|
||||
const encodedId = btoa(`ev-${eventId}`);
|
||||
return `/event-detail?id=${encodedId}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化涨跌幅
|
||||
*/
|
||||
@@ -99,48 +110,61 @@ const CompactStatCard = ({ label, value, icon, color = '#FFD700', subText, progr
|
||||
);
|
||||
|
||||
/**
|
||||
* TOP事件列表项
|
||||
* TOP事件列表项 - 支持点击跳转
|
||||
*/
|
||||
const TopEventItem = ({ event, rank }) => (
|
||||
<HStack
|
||||
spacing={2}
|
||||
py={1.5}
|
||||
px={2}
|
||||
bg="rgba(0,0,0,0.2)"
|
||||
borderRadius="md"
|
||||
_hover={{ bg: 'rgba(255,215,0,0.08)' }}
|
||||
transition="all 0.15s"
|
||||
>
|
||||
<Badge
|
||||
colorScheme={rank === 1 ? 'yellow' : rank === 2 ? 'gray' : 'orange'}
|
||||
fontSize="2xs"
|
||||
px={1.5}
|
||||
borderRadius="full"
|
||||
minW="18px"
|
||||
textAlign="center"
|
||||
const TopEventItem = ({ event, rank }) => {
|
||||
const handleClick = () => {
|
||||
if (event.id) {
|
||||
// 在新标签页打开事件详情
|
||||
window.open(getEventDetailUrl(event.id), '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<HStack
|
||||
spacing={2}
|
||||
py={1.5}
|
||||
px={2}
|
||||
bg="rgba(0,0,0,0.2)"
|
||||
borderRadius="md"
|
||||
_hover={{ bg: 'rgba(255,215,0,0.12)', cursor: 'pointer' }}
|
||||
transition="all 0.15s"
|
||||
onClick={handleClick}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleClick()}
|
||||
>
|
||||
{rank}
|
||||
</Badge>
|
||||
<Tooltip label={event.title} placement="top" hasArrow>
|
||||
<Badge
|
||||
colorScheme={rank === 1 ? 'yellow' : rank === 2 ? 'gray' : 'orange'}
|
||||
fontSize="2xs"
|
||||
px={1.5}
|
||||
borderRadius="full"
|
||||
minW="18px"
|
||||
textAlign="center"
|
||||
>
|
||||
{rank}
|
||||
</Badge>
|
||||
<Tooltip label={event.title} placement="top" hasArrow>
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color="gray.300"
|
||||
flex="1"
|
||||
noOfLines={1}
|
||||
_hover={{ color: '#FFD700' }}
|
||||
>
|
||||
{event.title}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color="gray.300"
|
||||
flex="1"
|
||||
noOfLines={1}
|
||||
cursor="default"
|
||||
fontWeight="bold"
|
||||
color={getChgColor(event.maxChg)}
|
||||
>
|
||||
{event.title}
|
||||
{formatChg(event.maxChg)}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
<Text
|
||||
fontSize="xs"
|
||||
fontWeight="bold"
|
||||
color={getChgColor(event.maxChg)}
|
||||
>
|
||||
{formatChg(event.maxChg)}
|
||||
</Text>
|
||||
</HStack>
|
||||
);
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
const EventDailyStats = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -215,9 +239,7 @@ const EventDailyStats = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const { summary, topPerformers = [], dailyStats = [] } = stats;
|
||||
// 获取当日TOP事件
|
||||
const todayTopEvents = dailyStats[0]?.topEvents || topPerformers.slice(0, 3);
|
||||
const { summary, topPerformers = [] } = stats;
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -329,19 +351,44 @@ const EventDailyStats = () => {
|
||||
{/* 分割线 */}
|
||||
<Box h="1px" bg="rgba(255,215,0,0.1)" />
|
||||
|
||||
{/* TOP表现事件 */}
|
||||
<Box>
|
||||
{/* TOP表现事件 - 显示 TOP10 */}
|
||||
<Box flex="1" overflow="hidden">
|
||||
<HStack spacing={1.5} mb={2}>
|
||||
<TrophyOutlined style={{ color: '#FFD700', fontSize: '12px' }} />
|
||||
<Text fontSize="xs" fontWeight="bold" color="gray.400">
|
||||
今日 TOP 表现
|
||||
</Text>
|
||||
<Text fontSize="2xs" color="gray.600">
|
||||
(点击查看详情)
|
||||
</Text>
|
||||
</HStack>
|
||||
<VStack spacing={1.5} align="stretch">
|
||||
{todayTopEvents.slice(0, 3).map((event, idx) => (
|
||||
<VStack
|
||||
spacing={1}
|
||||
align="stretch"
|
||||
maxH="220px"
|
||||
overflowY="auto"
|
||||
pr={1}
|
||||
css={{
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '4px',
|
||||
},
|
||||
'&::-webkit-scrollbar-track': {
|
||||
background: 'rgba(255,255,255,0.05)',
|
||||
borderRadius: '2px',
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
background: 'rgba(255,215,0,0.3)',
|
||||
borderRadius: '2px',
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
background: 'rgba(255,215,0,0.5)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{topPerformers.slice(0, 10).map((event, idx) => (
|
||||
<TopEventItem key={event.id || idx} event={event} rank={idx + 1} />
|
||||
))}
|
||||
{todayTopEvents.length === 0 && (
|
||||
{topPerformers.length === 0 && (
|
||||
<Text fontSize="xs" color="gray.600" textAlign="center" py={2}>
|
||||
暂无数据
|
||||
</Text>
|
||||
|
||||
Reference in New Issue
Block a user