fix: 修复连接池泄漏(conn.close改为put_db) + put_db处理已关闭连接 + 添加日线同步cron
This commit is contained in:
+15
-15
@@ -2,7 +2,7 @@
|
||||
管理后台 API 路由
|
||||
"""
|
||||
from flask import Blueprint, request, jsonify, session, render_template
|
||||
from db import get_db
|
||||
from db import get_db, put_db
|
||||
from psycopg2.extras import RealDictCursor
|
||||
from werkzeug.security import generate_password_hash
|
||||
import functools
|
||||
@@ -182,7 +182,7 @@ def admin_required(f):
|
||||
if not row or not row[0]:
|
||||
return jsonify({'success': False, 'error': '无管理员权限'}), 403
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
return f(*args, **kwargs)
|
||||
return decorated
|
||||
|
||||
@@ -201,7 +201,7 @@ def admin_page():
|
||||
if not row or not row[0]:
|
||||
return '无权访问', 403
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
return render_template('admin.html')
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ def dashboard():
|
||||
|
||||
return jsonify({'success': True, 'data': stats})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ========== 用户管理 ==========
|
||||
@@ -339,7 +339,7 @@ def list_users():
|
||||
|
||||
return jsonify({'success': True, 'data': users})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/api/admin/users/<int:user_id>', methods=['GET'])
|
||||
@@ -437,7 +437,7 @@ def get_user_detail(user_id):
|
||||
}
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/api/admin/users/<int:user_id>/reset_password', methods=['POST'])
|
||||
@@ -461,7 +461,7 @@ def reset_user_password(user_id):
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/api/admin/users/<int:user_id>/toggle_admin', methods=['POST'])
|
||||
@@ -483,7 +483,7 @@ def toggle_admin(user_id):
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/api/admin/users/<int:user_id>', methods=['DELETE'])
|
||||
@@ -509,7 +509,7 @@ def delete_user(user_id):
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ========== 用户数据管理 ==========
|
||||
@@ -526,7 +526,7 @@ def delete_user_watchlist(user_id, code):
|
||||
conn.commit()
|
||||
return jsonify({'success': True})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/api/admin/users/<int:user_id>/trades/<int:trade_id>', methods=['DELETE'])
|
||||
@@ -541,7 +541,7 @@ def delete_user_trade(user_id, trade_id):
|
||||
conn.commit()
|
||||
return jsonify({'success': True})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ========== 系统数据 ==========
|
||||
@@ -568,7 +568,7 @@ def scan_history():
|
||||
""")
|
||||
return jsonify({'success': True, 'data': [dict(r) for r in cur.fetchall()]})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/api/admin/data_stats', methods=['GET'])
|
||||
@@ -603,7 +603,7 @@ def data_stats():
|
||||
|
||||
return jsonify({'success': True, 'data': {'tables': result, 'logs': logs}})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ========== 全景扫描管理 ==========
|
||||
@@ -786,7 +786,7 @@ def kline_sync_status():
|
||||
total_stocks = cur.fetchone()[0]
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
is_running = _is_kline_sync_running()
|
||||
|
||||
@@ -1308,7 +1308,7 @@ def admin_scan_status():
|
||||
triggered = cur.fetchone()[0]
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
is_running = _is_scan_running()
|
||||
|
||||
|
||||
@@ -313,7 +313,7 @@ def ai_analyze_stream(stock_code):
|
||||
from flask import Response
|
||||
from services.doubao_api import analyze_stock_stream, format_fund_flow, format_market_cap
|
||||
from services.mairui_api import get_realtime_price as mairui_price, get_financial_indicators
|
||||
from db import get_db
|
||||
from db import get_db, put_db
|
||||
|
||||
def generate():
|
||||
# 获取股票数据
|
||||
@@ -384,7 +384,7 @@ def ai_analyze_stream(stock_code):
|
||||
stock_data['triggered_count'] = scan_row['triggered_count'] or 0
|
||||
cur2.close()
|
||||
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
except Exception as e:
|
||||
print(f"获取数据失败: {e}")
|
||||
|
||||
@@ -399,7 +399,7 @@ def ai_analyze_stream(stock_code):
|
||||
_cur.execute("INSERT INTO ai_call_log (user_id, stock_code, stock_name) VALUES (%s, %s, %s)",
|
||||
(_uid, stock_code, stock_name))
|
||||
_conn.commit()
|
||||
_conn.close()
|
||||
_put_db(conn)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -421,7 +421,7 @@ def ai_analyze(stock_code):
|
||||
try:
|
||||
from services.doubao_api import analyze_stock
|
||||
from services.mairui_api import get_realtime_price as mairui_price, get_financial_indicators
|
||||
from db import get_db
|
||||
from db import get_db, put_db
|
||||
|
||||
# 获取股票数据
|
||||
stock_data = {}
|
||||
@@ -467,7 +467,7 @@ def ai_analyze(stock_code):
|
||||
'super_pct': float(row[3]) if row[3] else 0,
|
||||
})
|
||||
stock_data['fund_flow_3days'] = fund_flow
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
except Exception as e:
|
||||
print(f"获取资金流向失败: {e}")
|
||||
|
||||
@@ -603,7 +603,7 @@ def batch_technical_signals():
|
||||
scan_map[row['code']] = row
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
except Exception as e:
|
||||
print(f"批量扫描获取数据失败: {e}")
|
||||
|
||||
@@ -925,7 +925,7 @@ def get_scan_results():
|
||||
recommend_counts[disp] = recommend_counts.get(disp, 0) + 1
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
@@ -1003,7 +1003,7 @@ def signal_alerts():
|
||||
WHERE code IN ({placeholders})
|
||||
""", stock_codes)
|
||||
price_rows = cur.fetchall()
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
price_map = {}
|
||||
change_map = {}
|
||||
@@ -1153,7 +1153,7 @@ def get_scan_status():
|
||||
triggered = cur.fetchone()[0]
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
@@ -1241,7 +1241,7 @@ def get_scan_strategy():
|
||||
""", (scan_date,))
|
||||
rows = cur.fetchall()
|
||||
cur.close()
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
tier1, tier2, tier3, tier4 = [], [], [], []
|
||||
for r in rows:
|
||||
@@ -1376,7 +1376,7 @@ def get_bull_stocks():
|
||||
for p in cur.fetchall():
|
||||
price_map[p['code']] = {'price': float(p['price']), 'change_pct': float(p.get('change_pct') or 0)}
|
||||
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
# ---- 批量计算综合评分 ----
|
||||
scores_map = None
|
||||
|
||||
@@ -6,7 +6,7 @@ import pandas as pd
|
||||
from datetime import datetime, timedelta
|
||||
from services.stock_service import get_stock_fund_flow, load_cached_data
|
||||
from services.stock_algorithms import get_kline_data as algo_get_kline_data
|
||||
from db import get_db
|
||||
from db import get_db, put_db
|
||||
|
||||
bp = Blueprint('market', __name__, url_prefix='/api')
|
||||
|
||||
@@ -40,7 +40,7 @@ def db_realtime_price(stock_code):
|
||||
'data': dict(row)
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/db/realtime_prices', methods=['POST'])
|
||||
@@ -71,7 +71,7 @@ def db_realtime_prices():
|
||||
'data': [dict(row) for row in rows]
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/db/fund_flow_today/<stock_code>', methods=['GET'])
|
||||
@@ -102,7 +102,7 @@ def db_fund_flow_today(stock_code):
|
||||
'data': dict(row)
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/db/fund_flow_today_batch', methods=['POST'])
|
||||
@@ -135,7 +135,7 @@ def db_fund_flow_today_batch():
|
||||
'data': [dict(row) for row in rows]
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/db/data_status', methods=['GET'])
|
||||
@@ -171,7 +171,7 @@ def db_data_status():
|
||||
'recent_logs': [dict(log) for log in logs]
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ============ 原有API(兼容) ============
|
||||
@@ -305,7 +305,7 @@ def get_fund_flow_rank():
|
||||
return jsonify({'success': True, 'data': flow_data, 'total': len(flow_data),
|
||||
'source': 'cache'})
|
||||
finally:
|
||||
_conn.close()
|
||||
_put_db(conn)
|
||||
|
||||
return jsonify({'success': True, 'data': [], 'total': 0})
|
||||
except Exception as e:
|
||||
@@ -389,7 +389,7 @@ def get_fundamental(stock_code):
|
||||
'triggered_count': sig_row[2] or 0,
|
||||
}
|
||||
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
except Exception as e:
|
||||
print(f"获取数据失败: {e}")
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def init_user_config(user_id):
|
||||
cur.execute("SELECT * FROM sim_config WHERE user_id = %s", (user_id,))
|
||||
return cur.fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/config', methods=['GET'])
|
||||
@@ -82,7 +82,7 @@ def update_config():
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/trades', methods=['GET'])
|
||||
@@ -114,7 +114,7 @@ def get_trades():
|
||||
trades = cur.fetchall()
|
||||
return jsonify({'success': True, 'trades': trades})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/positions', methods=['GET'])
|
||||
@@ -140,7 +140,7 @@ def get_positions():
|
||||
positions = cur.fetchall()
|
||||
return jsonify({'success': True, 'positions': positions})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/stats', methods=['GET'])
|
||||
@@ -261,7 +261,7 @@ def get_stats():
|
||||
'history': list(reversed(history)) if history else []
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/execute', methods=['POST'])
|
||||
@@ -381,7 +381,7 @@ def execute_trade():
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/auto_execute', methods=['POST'])
|
||||
@@ -532,7 +532,7 @@ def auto_execute():
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/update_prices', methods=['POST'])
|
||||
@@ -588,7 +588,7 @@ def update_prices():
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/reset', methods=['POST'])
|
||||
@@ -612,7 +612,7 @@ def reset_simulation():
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/trigger_trade', methods=['POST'])
|
||||
@@ -628,7 +628,7 @@ def trigger_trade():
|
||||
conn = get_db()
|
||||
if conn:
|
||||
result = execute_smart_trade(conn, user_id, scan_date=None)
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
if result.get('success'):
|
||||
results = result.get('results', [])
|
||||
buy_count = len([r for r in results if r['type'] == 'buy'])
|
||||
@@ -651,7 +651,7 @@ def trigger_trade():
|
||||
cur.execute("SELECT trade_quantity FROM sim_config WHERE user_id = %s", (user_id,))
|
||||
config = cur.fetchone()
|
||||
trade_quantity = config['trade_quantity'] if config else 1000
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
else:
|
||||
trade_quantity = 1000
|
||||
|
||||
@@ -730,4 +730,4 @@ def get_today_trades():
|
||||
}
|
||||
})
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
@@ -43,7 +43,7 @@ def get_algo_templates():
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════
|
||||
@@ -78,7 +78,7 @@ def get_algo_config():
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/config', methods=['POST'])
|
||||
@@ -111,7 +111,7 @@ def save_algo_config():
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
@bp.route('/apply_template', methods=['POST'])
|
||||
@@ -139,7 +139,7 @@ def apply_template():
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════
|
||||
@@ -164,7 +164,7 @@ def get_status():
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════
|
||||
@@ -205,7 +205,7 @@ def trigger_smart_trade():
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════
|
||||
@@ -242,7 +242,7 @@ def get_signals():
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════
|
||||
@@ -284,4 +284,4 @@ def get_position_meta():
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
finally:
|
||||
conn.close()
|
||||
put_db(conn)
|
||||
|
||||
Reference in New Issue
Block a user