""" 期货智析数据库 - 独立存储 """ import logging from pathlib import Path from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, declarative_base from datetime import datetime from app.mysql_database import MySQLSessionLocal from app.storage_manager import get_storage_manager logger = logging.getLogger(__name__) # 数据目录 DATA_DIR = Path(__file__).resolve().parent.parent / "data" DATA_DIR.mkdir(parents=True, exist_ok=True) ANALYSIS_DB_PATH = DATA_DIR / "futures_analysis.db" analysis_engine = create_engine( f"sqlite:///{ANALYSIS_DB_PATH}", connect_args={"check_same_thread": False}, pool_pre_ping=True, ) AnalysisSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=analysis_engine) AnalysisBase = declarative_base() def get_analysis_db(): """获取期货智析数据库会话。 根据 MySQL 可用性动态选择后端: - MySQL 可用时返回 MySQL Session - MySQL 不可用时返回 SQLite Session(兜底) """ storage = get_storage_manager() if MySQLSessionLocal is not None and storage.check_mysql(): db = None try: db = MySQLSessionLocal() yield db except Exception as e: logger.warning(f"创建 MySQL analysis session 失败,降级到 SQLite: {e}") if db is not None: try: db.close() except Exception: pass db = AnalysisSessionLocal() yield db finally: if db is not None: try: db.close() except Exception: pass else: db = AnalysisSessionLocal() try: yield db finally: db.close() def init_analysis_db(): """初始化期货智析数据库表(SQLite 兜底)。""" # 确保导入所有模型类,使其注册到 AnalysisBase from app import analysis_models # 直接导入 analysis_models 模块中的所有类 from app.analysis_models import ( FuturesAnalysis, WatchedSymbol, AIModelConfig, AnalysisSettings, AIAnalysisCache, ReviewDate, SymbolRanking, TradingPlan, SymbolScoreV2, TradingPlanV2, SectorHeat, ReviewPlanV2, TradeRecord, TradeImportBatch, TradePair, TradeReflection, DailyReflection, TradeTag, TradeRecordTag, TradeExperience, ) AnalysisBase.metadata.create_all(bind=analysis_engine) # 迁移:为已有表添加新增列(SQLite 不支持 IF NOT EXISTS,用异常处理) _migrate_add_columns() # 初始化预设标签 _init_preset_tags() def init_analysis_mysql(mysql_engine): """初始化期货智析数据库表(MySQL)。""" from app import analysis_models from app.analysis_models import ( FuturesAnalysis, WatchedSymbol, AIModelConfig, AnalysisSettings, AIAnalysisCache, ReviewDate, SymbolRanking, TradingPlan, SymbolScoreV2, TradingPlanV2, SectorHeat, ReviewPlanV2, TradeRecord, TradeImportBatch, TradePair, TradeReflection, DailyReflection, TradeTag, TradeRecordTag, TradeExperience, ) AnalysisBase.metadata.create_all(bind=mysql_engine) logger.info("MySQL analysis 表结构初始化完成") def _migrate_add_columns(): """为已有表添加新增列(兼容旧数据库)""" import sqlite3 conn = sqlite3.connect(str(ANALYSIS_DB_PATH)) cursor = conn.cursor() migrations = [ ("symbol_scores_v2", "data_date", "VARCHAR(16)"), ("review_plans_v2", "actual_data_date", "VARCHAR(16)"), ("review_plans_v2", "data_date_matches", "INTEGER"), ] for table, column, col_type in migrations: try: cursor.execute(f"ALTER TABLE {table} ADD COLUMN {column} {col_type}") except sqlite3.OperationalError: pass # 列已存在 conn.commit() conn.close() def _init_preset_tags(): """初始化预设标签""" from app.analysis_models import TradeTag from sqlalchemy import inspect session = get_analysis_session() try: # 检查表是否存在 inspector = inspect(session.get_bind()) if 'trade_tags' not in inspector.get_table_names(): return # 检查是否已有预设标签 existing = session.query(TradeTag).filter_by(is_preset=True).first() if existing: return preset_tags = [ # 操作类 TradeTag(name="追涨杀跌", category="operation", is_preset=True), TradeTag(name="逆市操作", category="operation", is_preset=True), TradeTag(name="提前入场", category="operation", is_preset=True), TradeTag(name="延迟出场", category="operation", is_preset=True), TradeTag(name="加仓过早", category="operation", is_preset=True), TradeTag(name="减仓过晚", category="operation", is_preset=True), # 心态类 TradeTag(name="情绪化交易", category="mindset", is_preset=True), TradeTag(name="恐惧平仓", category="mindset", is_preset=True), TradeTag(name="贪婪持仓", category="mindset", is_preset=True), TradeTag(name="侥幸心理", category="mindset", is_preset=True), # 纪律类 TradeTag(name="严格执行计划", category="discipline", is_preset=True), TradeTag(name="违反止损", category="discipline", is_preset=True), TradeTag(name="超仓交易", category="discipline", is_preset=True), TradeTag(name="频繁交易", category="discipline", is_preset=True), # 技术类 TradeTag(name="突破交易", category="technical", is_preset=True), TradeTag(name="回调交易", category="technical", is_preset=True), TradeTag(name="趋势跟踪", category="technical", is_preset=True), TradeTag(name="震荡交易", category="technical", is_preset=True), TradeTag(name="反转交易", category="technical", is_preset=True), ] session.add_all(preset_tags) session.commit() except Exception: session.rollback() finally: session.close()