更新app.py

This commit is contained in:
2026-01-29 16:24:54 +08:00
parent 5e9c4b40e4
commit 1f2af549f5
3 changed files with 468 additions and 6 deletions

42
app.py
View File

@@ -11628,6 +11628,7 @@ def get_events_effectiveness_stats():
# 提取股票代码并获取前收盘价 # 提取股票代码并获取前收盘价
all_base_codes = [row[0].split('.')[0] for row in market_data] all_base_codes = [row[0].split('.')[0] for row in market_data]
all_prev_close = get_cached_prev_close(all_base_codes, prev_date_str) all_prev_close = get_cached_prev_close(all_base_codes, prev_date_str)
print(f'[effectiveness-stats] 前收盘价获取: 请求 {len(all_base_codes)} 个, 返回 {len(all_prev_close)}')
rising = 0 rising = 0
falling = 0 falling = 0
@@ -13863,6 +13864,47 @@ def broadcast_new_event(event):
traceback.print_exc() traceback.print_exc()
# ==================== 测试事件推送接口 ====================
@app.route('/api/test/push-event', methods=['POST'])
def test_push_event():
"""测试事件推送(仅用于调试)"""
try:
data = request.get_json() or {}
test_event_data = {
'id': data.get('id', 99999),
'title': data.get('title', '测试事件标题 - 这是一个测试'),
'description': data.get('description', '这是测试事件的详细描述内容,用于验证通知推送功能是否正常工作。'),
'event_type': data.get('event_type', 'market'),
'importance': data.get('importance', 'A'),
'status': 'active',
'created_at': datetime.now().isoformat(),
'hot_score': 100,
'view_count': 0,
'related_avg_chg': 2.5,
'related_max_chg': 5.0,
'keywords': ['测试', '调试'],
}
print(f'\n[测试推送] ========== 测试事件推送 ==========')
print(f'[测试推送] 事件数据: {test_event_data}')
socketio.emit('new_event', test_event_data, room='events_all', namespace='/')
print(f'[测试推送] ✓ 已发送到 events_all')
return jsonify({
'code': 200,
'message': '测试事件已推送',
'data': test_event_data
})
except Exception as e:
import traceback
traceback.print_exc()
return jsonify({
'code': 500,
'message': f'推送失败: {str(e)}'
}), 500
# ==================== WebSocket 轮询机制(检测新事件) ==================== # ==================== WebSocket 轮询机制(检测新事件) ====================
# Redis Key 用于多 Worker 协调 # Redis Key 用于多 Worker 协调

View File

@@ -9,16 +9,17 @@ from typing import Dict, List, Any, Optional
from datetime import datetime, date from datetime import datetime, date
from decimal import Decimal from decimal import Decimal
import json import json
import os
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# MySQL连接配置 # MySQL连接配置(支持环境变量,便于多服务器部署)
MYSQL_CONFIG = { MYSQL_CONFIG = {
'host': '127.0.0.1', 'host': os.environ.get('MYSQL_HOST', '127.0.0.1'),
'port': 3306, 'port': int(os.environ.get('MYSQL_PORT', 3306)),
'user': 'root', 'user': os.environ.get('MYSQL_USER', 'root'),
'password': 'Zzl33818!', 'password': os.environ.get('MYSQL_PASSWORD', 'Zzl33818!'),
'db': 'stock', 'db': os.environ.get('MYSQL_DATABASE', 'stock'),
'charset': 'utf8mb4', 'charset': 'utf8mb4',
'autocommit': True 'autocommit': True
} }

View File

@@ -142,10 +142,74 @@ server {
proxy_buffering off; proxy_buffering off;
} }
# ============================================
# MCP 服务(本地)
# ============================================
location /mcp/ {
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization, X-Requested-With, Cookie' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
# 隐藏后端返回的 CORS 头(避免重复)
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header Access-Control-Expose-Headers;
proxy_pass http://127.0.0.1:8900/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection '';
# 统一添加 CORS 头
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
# SSE 流式响应配置
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
gzip off;
add_header X-Accel-Buffering no;
proxy_connect_timeout 75s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# ============================================ # ============================================
# 数据服务 API 代理 (222.128.1.157) # 数据服务 API 代理 (222.128.1.157)
# ============================================ # ============================================
location /concept-api/ { location /concept-api/ {
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
# 隐藏后端返回的 CORS 头(避免重复)
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header Access-Control-Expose-Headers;
proxy_pass http://222.128.1.157:16801/; proxy_pass http://222.128.1.157:16801/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
@@ -153,11 +217,33 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
# 统一添加 CORS 头
add_header 'Access-Control-Allow-Origin' $cors_origin always; add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
} }
location /es-api/ { location /es-api/ {
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, HEAD' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header Access-Control-Expose-Headers;
proxy_pass http://222.128.1.157:19200/; proxy_pass http://222.128.1.157:19200/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
@@ -167,9 +253,31 @@ server {
add_header 'Access-Control-Allow-Origin' $cors_origin always; add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
} }
location /news-api/ { location /news-api/ {
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header Access-Control-Expose-Headers;
proxy_pass http://222.128.1.157:21891/; proxy_pass http://222.128.1.157:21891/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
@@ -179,9 +287,30 @@ server {
add_header 'Access-Control-Allow-Origin' $cors_origin always; add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_connect_timeout 90s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
} }
location /report-api/ { location /report-api/ {
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header Access-Control-Expose-Headers;
proxy_pass http://222.128.1.157:8811/; proxy_pass http://222.128.1.157:8811/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
@@ -191,9 +320,30 @@ server {
add_header 'Access-Control-Allow-Origin' $cors_origin always; add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
} }
location /category-api/ { location /category-api/ {
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header Access-Control-Expose-Headers;
proxy_pass http://222.128.1.157:18827/; proxy_pass http://222.128.1.157:18827/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
@@ -203,6 +353,275 @@ server {
add_header 'Access-Control-Allow-Origin' $cors_origin always; add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
proxy_buffering on;
proxy_buffer_size 128k;
proxy_buffers 8 256k;
proxy_busy_buffers_size 512k;
}
# ============================================
# Bytedesk 客服系统代理 (43.143.189.195)
# ============================================
location /bytedesk/ {
proxy_pass http://43.143.189.195/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
}
location /websocket {
proxy_pass http://43.143.189.195/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 86400s;
proxy_send_timeout 86400s;
proxy_read_timeout 86400s;
}
location /chat/ {
proxy_pass http://43.143.189.195/chat/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 替换响应内容中的 IP 地址
sub_filter 'http://43.143.189.195' 'https://www.valuefrontier.cn';
sub_filter_once off;
sub_filter_types text/css text/javascript application/javascript application/json;
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
}
location /config/ {
proxy_pass http://43.143.189.195/config/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
}
location ^~ /uploads/ {
proxy_pass http://43.143.189.195/uploads/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 隐藏后端返回的可能冲突的头
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header Cross-Origin-Resource-Policy;
# 解决 ORB 问题
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept' always;
add_header 'Cross-Origin-Resource-Policy' 'cross-origin' always;
proxy_cache_valid 200 1d;
expires 1d;
add_header Cache-Control "public, max-age=86400";
}
location ^~ /file/ {
proxy_pass http://43.143.189.195/file/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 隐藏后端返回的可能冲突的头
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header Cross-Origin-Resource-Policy;
# 解决 ORB 问题
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept' always;
add_header 'Cross-Origin-Resource-Policy' 'cross-origin' always;
proxy_cache_valid 200 1d;
expires 1d;
add_header Cache-Control "public, max-age=86400";
}
location /visitor/ {
proxy_pass http://43.143.189.195/visitor/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Accept-Encoding "";
# 替换 JSON 响应中的 IP 地址
sub_filter 'http://43.143.189.195' 'https://www.valuefrontier.cn';
sub_filter_once off;
sub_filter_types application/json;
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
}
location = /stomp {
# 代理到 bytedesk 的 /websocket 端点
proxy_pass http://43.143.189.195/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
proxy_buffering off;
}
location /stomp/ {
# 代理到 bytedesk 的 /websocket 端点
proxy_pass http://43.143.189.195/websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
proxy_buffering off;
}
location ^~ /avatars/ {
proxy_pass http://43.143.189.195/uploads/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 隐藏后端返回的可能冲突的头
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header Cross-Origin-Resource-Policy;
# 解决 ORB 问题
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept' always;
add_header 'Cross-Origin-Resource-Policy' 'cross-origin' always;
proxy_cache_valid 200 1d;
proxy_cache_bypass $http_cache_control;
}
location /assets/ {
proxy_pass http://43.143.189.195/assets/;
proxy_set_header Host $host;
expires 1d;
add_header Cache-Control "public, immutable";
} }
# ============================================ # ============================================