修改总结
This commit is contained in:
131
CLAUDE.md
131
CLAUDE.md
@@ -4,40 +4,61 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
This is a hybrid React dashboard application with a Flask/Python backend. The project is built on the Argon Dashboard Chakra PRO template and includes financial/trading analysis features.
|
This is a hybrid React dashboard application with a Flask/Python backend for financial/trading analysis. Built on the Argon Dashboard Chakra PRO template with extensive customization.
|
||||||
|
|
||||||
### Frontend (React + Chakra UI)
|
### Frontend (React + Chakra UI)
|
||||||
- **Framework**: React 18.3.1 with Chakra UI 2.8.2
|
- **Framework**: React 18.3.1 with Chakra UI 2.8.2
|
||||||
|
- **State Management**: Redux Toolkit (@reduxjs/toolkit)
|
||||||
|
- **Routing**: React Router DOM v6 with lazy loading for code splitting
|
||||||
- **Styling**: Tailwind CSS + custom Chakra theme
|
- **Styling**: Tailwind CSS + custom Chakra theme
|
||||||
- **Build Tool**: React Scripts with custom Gulp tasks
|
- **Build Tool**: CRACO (Create React App Configuration Override) with custom webpack optimizations
|
||||||
- **Charts**: ApexCharts, ECharts, and custom visualization components
|
- **Charts**: ApexCharts, ECharts, Recharts, D3
|
||||||
|
- **UI Components**: Ant Design (antd) alongside Chakra UI
|
||||||
|
- **Other Libraries**: Three.js (@react-three), FullCalendar, Leaflet maps
|
||||||
|
|
||||||
### Backend (Flask/Python)
|
### Backend (Flask/Python)
|
||||||
- **Framework**: Flask with SQLAlchemy ORM
|
- **Framework**: Flask with SQLAlchemy ORM
|
||||||
- **Database**: ClickHouse for analytics + MySQL/PostgreSQL
|
- **Database**: ClickHouse for analytics queries + MySQL/PostgreSQL
|
||||||
- **Features**: Real-time data processing, trading analysis, user authentication
|
- **Real-time**: Flask-SocketIO for WebSocket connections
|
||||||
- **Task Queue**: Celery for background processing
|
- **Task Queue**: Celery with Redis for background processing
|
||||||
|
- **External APIs**: Tencent Cloud SMS, WeChat Pay integration
|
||||||
|
|
||||||
## Development Commands
|
## Development Commands
|
||||||
|
|
||||||
### Frontend Development
|
### Frontend Development
|
||||||
```bash
|
```bash
|
||||||
npm start # Start development server (port 3000, proxies to localhost:5001)
|
npm start # Start with mock data (.env.mock), proxies to localhost:5001
|
||||||
npm run build # Production build with license headers
|
npm run start:real # Start with real backend (.env.local)
|
||||||
npm test # Run React test suite
|
npm run start:dev # Start with development config (.env.development)
|
||||||
npm run lint:check # Check ESLint rules
|
npm run start:test # Starts both backend (app_2.py) and frontend (.env.test) concurrently
|
||||||
|
npm run dev # Alias for 'npm start'
|
||||||
|
npm run backend # Start Flask server only (python app_2.py)
|
||||||
|
|
||||||
|
npm run build # Production build with Gulp license headers
|
||||||
|
npm run build:analyze # Build with webpack bundle analyzer
|
||||||
|
npm test # Run React test suite with CRACO
|
||||||
|
|
||||||
|
npm run lint:check # Check ESLint rules (exits 0)
|
||||||
npm run lint:fix # Auto-fix ESLint issues
|
npm run lint:fix # Auto-fix ESLint issues
|
||||||
npm run install:clean # Clean install (removes node_modules and package-lock)
|
npm run clean # Remove node_modules and package-lock.json
|
||||||
|
npm run reinstall # Clean install (runs clean + install)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Backend Development
|
### Backend Development
|
||||||
```bash
|
```bash
|
||||||
python app_2.py # Start Flask server (main backend)
|
python app.py # Main Flask server (newer version)
|
||||||
python simulation_background_processor.py # Background data processor
|
python app_2.py # Flask server (appears to be current main)
|
||||||
|
python simulation_background_processor.py # Background data processor for simulations
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
```bash
|
||||||
|
npm run deploy # Executes scripts/deploy-from-local.sh
|
||||||
|
npm run deploy:setup # Setup deployment (scripts/setup-deployment.sh)
|
||||||
|
npm run rollback # Rollback deployment (scripts/rollback-from-local.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Python Dependencies
|
### Python Dependencies
|
||||||
Install from requirements.txt:
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
@@ -45,47 +66,69 @@ pip install -r requirements.txt
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Frontend Structure
|
### Frontend Structure
|
||||||
- `src/layouts/` - Main layout components (Admin, Auth, Home)
|
- **src/App.js** - Main application entry with route definitions (routing moved from src/routes.js)
|
||||||
- `src/views/` - Page components organized by feature (Dashboard, Company, Community, etc.)
|
- **src/layouts/** - Layout wrappers (Auth, Home, MainLayout)
|
||||||
- `src/components/` - Reusable UI components (Charts, Cards, Buttons, etc.)
|
- **src/views/** - Page components (Community, Company, TradingSimulation, etc.)
|
||||||
- `src/theme/` - Chakra UI theme customization
|
- **src/components/** - Reusable UI components
|
||||||
- `src/routes.js` - Application routing configuration
|
- **src/contexts/** - React contexts (AuthContext, NotificationContext, IndustryContext)
|
||||||
- `src/contexts/` - React context providers
|
- **src/store/** - Redux store with slices (posthogSlice, etc.)
|
||||||
- `src/services/` - API service layer
|
- **src/services/** - API service layer
|
||||||
|
- **src/theme/** - Chakra UI theme customization
|
||||||
|
- **src/mocks/** - MSW (Mock Service Worker) handlers for development
|
||||||
|
- src/mocks/handlers/ - Request handlers by domain
|
||||||
|
- src/mocks/data/ - Mock data files
|
||||||
|
- src/mocks/browser.js - MSW browser setup
|
||||||
|
|
||||||
### Backend Structure
|
### Backend Structure
|
||||||
- `app_2.py` - Main Flask application with routes and business logic
|
- **app.py / app_2.py** - Main Flask application with routes, authentication, and business logic
|
||||||
- `simulation_background_processor.py` - Background data processing service
|
- **simulation_background_processor.py** - Background processor for trading simulations
|
||||||
- `wechat_pay.py` / `wechat_pay_config.py` - Payment integration
|
- **wechat_pay.py / wechat_pay_config.py** - WeChat payment integration
|
||||||
- `tdays.csv` - Trading days data
|
- **concept_api.py** - API for concept/industry analysis
|
||||||
|
- **tdays.csv** - Trading days calendar data (loaded into memory at startup)
|
||||||
|
|
||||||
### Key Integrations
|
### Key Integrations
|
||||||
- ClickHouse for high-performance analytics queries
|
- **ClickHouse** - High-performance analytics queries
|
||||||
- Celery + Redis for background task processing
|
- **Celery + Redis** - Background task processing
|
||||||
- Flask-SocketIO for real-time data updates
|
- **Flask-SocketIO** - Real-time data updates via WebSocket
|
||||||
- Tencent Cloud services (SMS, etc.)
|
- **Tencent Cloud** - SMS services
|
||||||
- WeChat Pay integration
|
- **WeChat Pay** - Payment processing
|
||||||
|
- **PostHog** - Analytics (initialized in Redux)
|
||||||
|
- **MSW** - API mocking for development/testing
|
||||||
|
|
||||||
|
### Routing & Code Splitting
|
||||||
|
- Routing is defined in **src/App.js** (not src/routes.js - that file is deprecated)
|
||||||
|
- Heavy components use React.lazy() for code splitting (Community, TradingSimulation, etc.)
|
||||||
|
- Protected routes use ProtectedRoute and ProtectedRouteRedirect components
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Proxy Setup
|
|
||||||
The React dev server proxies API calls to `http://localhost:5001` (see package.json).
|
|
||||||
|
|
||||||
### Environment Files
|
### Environment Files
|
||||||
- `.env` - Environment variables for both frontend and backend
|
Multiple environment configurations available:
|
||||||
|
- **.env.mock** - Mock data mode (default for `npm start`)
|
||||||
|
- **.env.local** - Real backend connection
|
||||||
|
- **.env.development** - Development environment
|
||||||
|
- **.env.test** - Test environment
|
||||||
|
|
||||||
### Build Process
|
### Build Configuration (craco.config.js)
|
||||||
The build process includes custom Gulp tasks that add Creative Tim license headers to JS, CSS, and HTML files.
|
- **Webpack caching**: Filesystem cache for faster rebuilds (50-80% improvement)
|
||||||
|
- **Code splitting**: Aggressive chunk splitting by library (react-vendor, charts-lib, chakra-ui, antd-lib, three-lib, etc.)
|
||||||
|
- **Path aliases**: `@` → src/, `@components` → src/components/, `@views` → src/views/, `@assets` → src/assets/, `@contexts` → src/contexts/
|
||||||
|
- **Optimizations**: ESLint plugin removed from build for speed, Babel caching enabled, moment locale stripping
|
||||||
|
- **Source maps**: Disabled in production, eval-cheap-module-source-map in development
|
||||||
|
- **Dev server proxy**: `/api` requests proxy to http://49.232.185.254:5001
|
||||||
|
|
||||||
### Styling Architecture
|
### Important Build Notes
|
||||||
- Tailwind CSS for utility classes
|
- Uses NODE_OPTIONS='--openssl-legacy-provider --max_old_space_size=4096' for Node compatibility
|
||||||
- Custom Chakra UI theme with extended color palette
|
- Gulp task adds Creative Tim license headers post-build
|
||||||
- Component-specific SCSS files in `src/assets/scss/`
|
- Bundle analyzer available via `ANALYZE=true npm run build:analyze`
|
||||||
|
- Pre-build: kills any process on port 3000
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
- React Testing Library setup for frontend components
|
- **React Testing Library** for component tests
|
||||||
- Test command: `npm test`
|
- **MSW** (Mock Service Worker) for API mocking during tests
|
||||||
|
- Run tests: `npm test`
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
- Build: `npm run build`
|
- Deployment scripts in **scripts/** directory
|
||||||
- Deploy: `npm run deploy` (builds the project)
|
- Build output processed by Gulp for licensing
|
||||||
|
- Supports rollback via scripts/rollback-from-local.sh
|
||||||
73
app.py
73
app.py
@@ -97,6 +97,38 @@ def get_trading_day_near_date(target_date):
|
|||||||
return trading_days[-1] if trading_days else None
|
return trading_days[-1] if trading_days else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_previous_trading_day(target_date):
|
||||||
|
"""
|
||||||
|
获取指定日期的上一个交易日
|
||||||
|
如果目标日期不是交易日,先找到对应的交易日,然后返回前一个交易日
|
||||||
|
"""
|
||||||
|
if not trading_days:
|
||||||
|
load_trading_days()
|
||||||
|
|
||||||
|
if not trading_days:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 如果目标日期是datetime,转换为date
|
||||||
|
if isinstance(target_date, datetime):
|
||||||
|
target_date = target_date.date()
|
||||||
|
|
||||||
|
# 确保目标日期是交易日
|
||||||
|
if target_date not in trading_days_set:
|
||||||
|
target_date = get_trading_day_near_date(target_date)
|
||||||
|
if not target_date:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 查找上一个交易日
|
||||||
|
try:
|
||||||
|
index = trading_days.index(target_date)
|
||||||
|
if index > 0:
|
||||||
|
return trading_days[index - 1]
|
||||||
|
else:
|
||||||
|
return None # 没有上一个交易日
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# 应用启动时加载交易日数据
|
# 应用启动时加载交易日数据
|
||||||
load_trading_days()
|
load_trading_days()
|
||||||
|
|
||||||
@@ -6595,6 +6627,9 @@ def api_get_events():
|
|||||||
event_status = request.args.get('status', 'active')
|
event_status = request.args.get('status', 'active')
|
||||||
importance = request.args.get('importance', 'all')
|
importance = request.args.get('importance', 'all')
|
||||||
|
|
||||||
|
# 交易日筛选参数
|
||||||
|
tday = request.args.get('tday') # 交易日,格式:YYYY-MM-DD 或 YYYY/M/D
|
||||||
|
|
||||||
# 日期筛选参数
|
# 日期筛选参数
|
||||||
start_date = request.args.get('start_date')
|
start_date = request.args.get('start_date')
|
||||||
end_date = request.args.get('end_date')
|
end_date = request.args.get('end_date')
|
||||||
@@ -6680,7 +6715,41 @@ def api_get_events():
|
|||||||
text(f"JSON_SEARCH(keywords, 'one', '%{search_query}%') IS NOT NULL")
|
text(f"JSON_SEARCH(keywords, 'one', '%{search_query}%') IS NOT NULL")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if recent_days:
|
|
||||||
|
# 交易日筛选逻辑
|
||||||
|
if tday:
|
||||||
|
from datetime import datetime, timedelta, time
|
||||||
|
try:
|
||||||
|
# 解析交易日参数,支持 YYYY-MM-DD 和 YYYY/M/D 格式
|
||||||
|
if '/' in tday:
|
||||||
|
target_tday = datetime.strptime(tday, '%Y/%m/%d').date()
|
||||||
|
else:
|
||||||
|
target_tday = datetime.strptime(tday, '%Y-%m-%d').date()
|
||||||
|
|
||||||
|
# 获取该交易日的上一个交易日
|
||||||
|
prev_tday = get_previous_trading_day(target_tday)
|
||||||
|
|
||||||
|
if prev_tday:
|
||||||
|
# 计算时间范围:[前一个交易日 15:00, 当前交易日 15:00]
|
||||||
|
start_datetime = datetime.combine(prev_tday, time(15, 0, 0))
|
||||||
|
end_datetime = datetime.combine(target_tday, time(15, 0, 0))
|
||||||
|
|
||||||
|
query = query.filter(
|
||||||
|
Event.created_at >= start_datetime,
|
||||||
|
Event.created_at <= end_datetime
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# 如果没有上一个交易日,则筛选当天的事件
|
||||||
|
start_datetime = datetime.combine(target_tday, time(0, 0, 0))
|
||||||
|
end_datetime = datetime.combine(target_tday, time(15, 0, 0))
|
||||||
|
query = query.filter(
|
||||||
|
Event.created_at >= start_datetime,
|
||||||
|
Event.created_at <= end_datetime
|
||||||
|
)
|
||||||
|
except (ValueError, TypeError) as e:
|
||||||
|
# 日期格式错误,忽略该参数
|
||||||
|
app.logger.warning(f"无效的交易日参数: {tday}, 错误: {e}")
|
||||||
|
elif recent_days:
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
cutoff_date = datetime.now() - timedelta(days=recent_days)
|
cutoff_date = datetime.now() - timedelta(days=recent_days)
|
||||||
query = query.filter(Event.created_at >= cutoff_date)
|
query = query.filter(Event.created_at >= cutoff_date)
|
||||||
@@ -6786,6 +6855,8 @@ def api_get_events():
|
|||||||
applied_filters['type'] = event_type
|
applied_filters['type'] = event_type
|
||||||
if importance != 'all':
|
if importance != 'all':
|
||||||
applied_filters['importance'] = importance
|
applied_filters['importance'] = importance
|
||||||
|
if tday:
|
||||||
|
applied_filters['tday'] = tday
|
||||||
if start_date:
|
if start_date:
|
||||||
applied_filters['start_date'] = start_date
|
applied_filters['start_date'] = start_date
|
||||||
if end_date:
|
if end_date:
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
"""
|
|
||||||
测试脚本:手动创建事件到数据库
|
|
||||||
用于测试 WebSocket 实时推送功能
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
|
||||||
from sqlalchemy import create_engine, Column, Integer, String, Text, Float, DateTime
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
|
|
||||||
# 数据库连接(从 app.py 复制)
|
|
||||||
DATABASE_URI = 'mysql+pymysql://root:Zzl5588161!@111.198.58.126:33060/stock?charset=utf8mb4'
|
|
||||||
|
|
||||||
engine = create_engine(DATABASE_URI, echo=False)
|
|
||||||
Session = sessionmaker(bind=engine)
|
|
||||||
session = Session()
|
|
||||||
|
|
||||||
Base = declarative_base()
|
|
||||||
|
|
||||||
# Event 模型(简化版,只包含必要字段)
|
|
||||||
class Event(Base):
|
|
||||||
__tablename__ = 'events'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
title = Column(String(500), nullable=False)
|
|
||||||
description = Column(Text)
|
|
||||||
event_type = Column(String(100))
|
|
||||||
importance = Column(String(10))
|
|
||||||
status = Column(String(50), default='active')
|
|
||||||
hot_score = Column(Float, default=0)
|
|
||||||
view_count = Column(Integer, default=0)
|
|
||||||
created_at = Column(DateTime, default=datetime.now)
|
|
||||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
||||||
|
|
||||||
|
|
||||||
def create_test_event():
|
|
||||||
"""创建一个测试事件"""
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
event_types = ['policy', 'market', 'tech', 'industry', 'finance']
|
|
||||||
importances = ['S', 'A', 'B', 'C']
|
|
||||||
|
|
||||||
test_event = Event(
|
|
||||||
title=f'测试事件 - {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}',
|
|
||||||
description=f'这是一个用于测试 WebSocket 实时推送的事件,创建于 {datetime.now()}',
|
|
||||||
event_type=random.choice(event_types),
|
|
||||||
importance=random.choice(importances),
|
|
||||||
status='active',
|
|
||||||
hot_score=round(random.uniform(50, 100), 2),
|
|
||||||
view_count=random.randint(100, 1000)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
session.add(test_event)
|
|
||||||
session.commit()
|
|
||||||
|
|
||||||
print("✅ 测试事件创建成功!")
|
|
||||||
print(f" ID: {test_event.id}")
|
|
||||||
print(f" 标题: {test_event.title}")
|
|
||||||
print(f" 类型: {test_event.event_type}")
|
|
||||||
print(f" 重要性: {test_event.importance}")
|
|
||||||
print(f" 热度: {test_event.hot_score}")
|
|
||||||
print(f"\n💡 提示: 轮询将在 2 分钟内检测到此事件并推送到前端")
|
|
||||||
print(f" (如果需要立即推送,请将轮询间隔改为更短)")
|
|
||||||
|
|
||||||
return test_event.id
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
session.rollback()
|
|
||||||
print(f"❌ 创建事件失败: {e}")
|
|
||||||
return None
|
|
||||||
finally:
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
|
|
||||||
def create_multiple_events(count=3):
|
|
||||||
"""创建多个测试事件"""
|
|
||||||
print(f"正在创建 {count} 个测试事件...\n")
|
|
||||||
|
|
||||||
for i in range(count):
|
|
||||||
event_id = create_test_event()
|
|
||||||
if event_id:
|
|
||||||
print(f"[{i+1}/{count}] 事件 #{event_id} 创建成功\n")
|
|
||||||
else:
|
|
||||||
print(f"[{i+1}/{count}] 创建失败\n")
|
|
||||||
|
|
||||||
print(f"\n✅ 完成!共创建 {count} 个事件")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("=" * 60)
|
|
||||||
print("WebSocket 事件推送测试 - 手动创建事件")
|
|
||||||
print("=" * 60)
|
|
||||||
print()
|
|
||||||
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
try:
|
|
||||||
count = int(sys.argv[1])
|
|
||||||
create_multiple_events(count)
|
|
||||||
except ValueError:
|
|
||||||
print("❌ 参数必须是数字")
|
|
||||||
print("用法: python test_create_event.py [数量]")
|
|
||||||
else:
|
|
||||||
# 默认创建 1 个事件
|
|
||||||
create_test_event()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
147
test_events_api.py
Normal file
147
test_events_api.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
"""
|
||||||
|
测试 /api/events 接口的分页和交易日筛选功能
|
||||||
|
"""
|
||||||
|
import requests
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
# 接口地址
|
||||||
|
BASE_URL = "http://localhost:5001"
|
||||||
|
EVENTS_API = f"{BASE_URL}/api/events"
|
||||||
|
|
||||||
|
def test_pagination():
|
||||||
|
"""测试分页功能"""
|
||||||
|
print("\n=== 测试分页功能 ===")
|
||||||
|
|
||||||
|
# 测试第一页
|
||||||
|
params = {
|
||||||
|
'page': 1,
|
||||||
|
'per_page': 5
|
||||||
|
}
|
||||||
|
response = requests.get(EVENTS_API, params=params)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data.get('success'):
|
||||||
|
pagination = data['data']['pagination']
|
||||||
|
print(f"✓ 第一页请求成功")
|
||||||
|
print(f" - 当前页: {pagination['page']}")
|
||||||
|
print(f" - 每页数量: {pagination['per_page']}")
|
||||||
|
print(f" - 总记录数: {pagination['total']}")
|
||||||
|
print(f" - 总页数: {pagination['pages']}")
|
||||||
|
print(f" - 是否有下一页: {pagination['has_next']}")
|
||||||
|
print(f" - 本页事件数: {len(data['data']['events'])}")
|
||||||
|
|
||||||
|
# 测试第二页
|
||||||
|
if pagination['has_next']:
|
||||||
|
params['page'] = 2
|
||||||
|
response = requests.get(EVENTS_API, params=params)
|
||||||
|
data = response.json()
|
||||||
|
if data.get('success'):
|
||||||
|
print(f"✓ 第二页请求成功,返回 {len(data['data']['events'])} 个事件")
|
||||||
|
else:
|
||||||
|
print(f"✗ 请求失败: {data.get('error')}")
|
||||||
|
|
||||||
|
def test_trading_day_filter():
|
||||||
|
"""测试交易日筛选功能"""
|
||||||
|
print("\n=== 测试交易日筛选功能 ===")
|
||||||
|
|
||||||
|
# 测试使用 YYYY-MM-DD 格式
|
||||||
|
tday = "2024-11-01" # 使用一个交易日
|
||||||
|
params = {
|
||||||
|
'tday': tday,
|
||||||
|
'per_page': 10
|
||||||
|
}
|
||||||
|
response = requests.get(EVENTS_API, params=params)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data.get('success'):
|
||||||
|
print(f"✓ 交易日筛选成功 (格式: YYYY-MM-DD)")
|
||||||
|
print(f" - 交易日: {tday}")
|
||||||
|
print(f" - 筛选到的事件数: {len(data['data']['events'])}")
|
||||||
|
print(f" - 总记录数: {data['data']['pagination']['total']}")
|
||||||
|
|
||||||
|
if data['data']['events']:
|
||||||
|
print(f" - 第一个事件创建时间: {data['data']['events'][0]['created_at']}")
|
||||||
|
|
||||||
|
# 检查 applied_filters
|
||||||
|
filters = data['data']['filters']['applied_filters']
|
||||||
|
if 'tday' in filters:
|
||||||
|
print(f" - 应用的交易日筛选: {filters['tday']}")
|
||||||
|
else:
|
||||||
|
print(f"✗ 请求失败: {data.get('error')}")
|
||||||
|
|
||||||
|
# 测试使用 YYYY/M/D 格式
|
||||||
|
tday2 = "2024/11/1"
|
||||||
|
params['tday'] = tday2
|
||||||
|
response = requests.get(EVENTS_API, params=params)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data.get('success'):
|
||||||
|
print(f"✓ 交易日筛选成功 (格式: YYYY/M/D)")
|
||||||
|
print(f" - 交易日: {tday2}")
|
||||||
|
print(f" - 筛选到的事件数: {len(data['data']['events'])}")
|
||||||
|
else:
|
||||||
|
print(f"✗ 请求失败: {data.get('error')}")
|
||||||
|
|
||||||
|
def test_combined_filters():
|
||||||
|
"""测试组合筛选功能"""
|
||||||
|
print("\n=== 测试组合筛选 (分页 + 交易日) ===")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'tday': '2024-10-31',
|
||||||
|
'page': 1,
|
||||||
|
'per_page': 3,
|
||||||
|
'status': 'active'
|
||||||
|
}
|
||||||
|
response = requests.get(EVENTS_API, params=params)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data.get('success'):
|
||||||
|
print(f"✓ 组合筛选成功")
|
||||||
|
print(f" - 筛选到的事件数: {len(data['data']['events'])}")
|
||||||
|
print(f" - 应用的筛选条件: {data['data']['filters']['applied_filters']}")
|
||||||
|
|
||||||
|
if data['data']['events']:
|
||||||
|
for i, event in enumerate(data['data']['events'], 1):
|
||||||
|
print(f" - 事件{i}: {event['title'][:30]}... (创建时间: {event['created_at']})")
|
||||||
|
else:
|
||||||
|
print(f"✗ 请求失败: {data.get('error')}")
|
||||||
|
|
||||||
|
def test_latest_trading_day():
|
||||||
|
"""测试获取最新数据(不传 tday 参数)"""
|
||||||
|
print("\n=== 测试获取最新数据 ===")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'page': 1,
|
||||||
|
'per_page': 5,
|
||||||
|
'sort': 'new'
|
||||||
|
}
|
||||||
|
response = requests.get(EVENTS_API, params=params)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data.get('success'):
|
||||||
|
print(f"✓ 获取最新数据成功")
|
||||||
|
print(f" - 返回事件数: {len(data['data']['events'])}")
|
||||||
|
if data['data']['events']:
|
||||||
|
print(f" - 最新事件: {data['data']['events'][0]['title'][:50]}")
|
||||||
|
print(f" - 创建时间: {data['data']['events'][0]['created_at']}")
|
||||||
|
else:
|
||||||
|
print(f"✗ 请求失败: {data.get('error')}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("开始测试 /api/events 接口")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 测试各项功能
|
||||||
|
test_pagination()
|
||||||
|
test_trading_day_filter()
|
||||||
|
test_combined_filters()
|
||||||
|
test_latest_trading_day()
|
||||||
|
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
print("测试完成!")
|
||||||
|
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
print("\n✗ 连接失败:请确保后端服务正在运行 (python app.py)")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n✗ 测试出错: {e}")
|
||||||
Reference in New Issue
Block a user