"""数据库表定义""" from sqlalchemy import ( MetaData, Table, Column, Integer, Float, Text, Boolean, DateTime, func ) metadata = MetaData() recommendations_table = Table( "recommendations", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("ts_code", Text, nullable=False), Column("name", Text, nullable=False), Column("sector", Text), Column("score", Float), Column("market_temp_score", Float), Column("sector_score", Float), Column("capital_score", Float), Column("technical_score", Float), Column("supply_demand_score", Float, default=0), Column("price_action_score", Float, default=0), Column("position_score", Float), Column("valuation_score", Float), Column("signal", Text), Column("entry_price", Float), Column("target_price", Float), Column("stop_loss", Float), Column("reasons", Text), Column("risk_note", Text, default=""), Column("action_plan", Text, default="观察"), Column("trigger_condition", Text, default=""), Column("invalidation_condition", Text, default=""), Column("suggested_position_pct", Float, default=0), Column("review_after_days", Integer, default=3), Column("lifecycle_status", Text, default="candidate"), Column("data_freshness", Text, default=""), Column("llm_analysis", Text, default=""), Column("strategy", Text, default="trend_breakout"), Column("entry_signal_type", Text, default="none"), Column("entry_timing", Text, default=""), Column("llm_score", Float, default=None), Column("recall_tags", Text, default="[]"), Column("prefilter_decision", Text, default=""), Column("prefilter_reason", Text, default=""), Column("focus_points", Text, default="[]"), Column("decision_trace", Text, default="{}"), Column("scan_session", Text), Column("created_at", DateTime, server_default=func.now()), ) sector_heat_table = Table( "sector_heat", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("sector_code", Text, nullable=False), Column("sector_name", Text, nullable=False), Column("board_type", Text, default="theme"), Column("theme_id", Text, default=""), Column("theme_name", Text, default=""), Column("theme_aliases", Text, default="[]"), Column("pct_change", Float), Column("capital_inflow", Float), Column("limit_up_count", Integer), Column("heat_score", Float), Column("stage", Text), Column("days_continuous", Integer), Column("member_count", Integer), Column("leading_stocks", Text), # JSON string Column("pct_trend", Text), # JSON string Column("turnover_avg", Float), Column("main_force_ratio", Float), Column("trade_date", Text, nullable=False), Column("created_at", DateTime, server_default=func.now()), ) market_temperature_table = Table( "market_temperature", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("trade_date", Text, nullable=False, unique=True), Column("up_count", Integer), Column("down_count", Integer), Column("limit_up_count", Integer), Column("limit_down_count", Integer), Column("max_streak", Integer), Column("broken_rate", Float), Column("temperature", Float), Column("created_at", DateTime, server_default=func.now()), ) recommendation_tracking_table = Table( "recommendation_tracking", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("recommendation_id", Integer), Column("track_date", Text, nullable=False), Column("current_price", Float), Column("pct_from_entry", Float), Column("max_price", Float), Column("min_price", Float), Column("max_return_pct", Float), Column("max_drawdown_pct", Float), Column("days_since_recommendation", Integer, default=0), Column("hit_target", Boolean, default=False), Column("hit_stop_loss", Boolean, default=False), Column("close_reason", Text, default=""), Column("review_note", Text, default=""), Column("status", Text, default="active"), Column("created_at", DateTime, server_default=func.now()), ) users_table = Table( "users", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("username", Text, nullable=False, unique=True), Column("email", Text, nullable=False, unique=True), Column("password_hash", Text, nullable=False), Column("role", Text, default="user"), Column("is_active", Boolean, default=True), Column("invite_code_used", Text, default=""), Column("created_at", DateTime, server_default=func.now()), Column("updated_at", DateTime, server_default=func.now()), ) email_verification_codes_table = Table( "email_verification_codes", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("email", Text, nullable=False), Column("code", Text, nullable=False), Column("purpose", Text, nullable=False, default="register"), Column("expires_at", DateTime, nullable=False), Column("used", Boolean, default=False), Column("created_at", DateTime, server_default=func.now()), ) invite_codes_table = Table( "invite_codes", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("code", Text, nullable=False, unique=True), Column("description", Text, default=""), Column("is_active", Boolean, default=True), Column("max_uses", Integer, default=1), Column("used_count", Integer, default=0), Column("expires_at", DateTime, default=None), Column("created_by", Integer, default=None), Column("created_at", DateTime, server_default=func.now()), Column("updated_at", DateTime, server_default=func.now()), ) stock_diagnoses_table = Table( "stock_diagnoses", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("ts_code", Text, nullable=False), Column("name", Text, nullable=False), Column("diagnosis_mode", Text, default="entry"), Column("diagnosis", Text, nullable=False), Column("created_at", DateTime, server_default=func.now()), ) user_watchlists_table = Table( "user_watchlists", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("user_id", Integer, nullable=False), Column("ts_code", Text, nullable=False), Column("name", Text, nullable=False), Column("note", Text, default=""), Column("watch_group", Text, default="observe"), Column("cost_price", Float, default=None), Column("is_active", Boolean, default=True), Column("created_at", DateTime, server_default=func.now()), Column("updated_at", DateTime, server_default=func.now()), ) watchlist_analyses_table = Table( "watchlist_analyses", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("user_id", Integer, nullable=False), Column("watchlist_id", Integer, nullable=False), Column("ts_code", Text, nullable=False), Column("name", Text, nullable=False), Column("conclusion", Text, default="观察"), Column("advice", Text, default=""), Column("trigger_condition", Text, default=""), Column("risk_note", Text, default=""), Column("summary", Text, default=""), Column("full_analysis", Text, default=""), Column("score_reference", Float, default=0), Column("analysis_mode", Text, default="scheduled"), Column("created_at", DateTime, server_default=func.now()), ) error_logs_table = Table( "error_logs", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("source", Text, nullable=False), # 模块来源,如 "recommender", "screener" Column("level", Text, default="error"), # error / warning Column("message", Text, nullable=False), # 错误消息 Column("detail", Text, default=""), # 完整异常信息(traceback) Column("created_at", DateTime, server_default=func.now()), ) strategy_configs_table = Table( "strategy_configs", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("strategy_id", Text, nullable=False), Column("version", Integer, nullable=False, default=1), Column("config_json", Text, nullable=False), Column("is_active", Boolean, default=True), Column("source", Text, default="manual"), # manual / auto_review / rollback / default_seed Column("change_reason", Text, default=""), Column("evidence_json", Text, default="{}"), Column("effective_from", DateTime, server_default=func.now()), Column("created_at", DateTime, server_default=func.now()), ) prompt_configs_table = Table( "prompt_configs", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("prompt_key", Text, nullable=False), Column("version", Integer, nullable=False, default=1), Column("content", Text, nullable=False), Column("is_active", Boolean, default=True), Column("source", Text, default="manual"), # manual / candidate / rollback / default_seed Column("change_reason", Text, default=""), Column("evidence_json", Text, default="{}"), Column("created_at", DateTime, server_default=func.now()), ) strategy_config_changes_table = Table( "strategy_config_changes", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("change_type", Text, nullable=False), # auto_applied / pending / rollback / manual Column("status", Text, default="pending"), # pending / applied / rejected Column("strategy_id", Text, default=""), Column("prompt_key", Text, default=""), Column("base_version", Integer, default=0), Column("new_version", Integer, default=0), Column("diff_json", Text, default="{}"), Column("evidence_json", Text, default="{}"), Column("reason", Text, default=""), Column("created_at", DateTime, server_default=func.now()), Column("applied_at", DateTime), ) catalysts_table = Table( "catalysts", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("title", Text, nullable=False), Column("summary", Text, default=""), Column("source", Text, default="manual"), Column("url", Text, default=""), Column("published_at", DateTime), Column("catalyst_type", Text, default="news"), Column("strength", Float, default=0), Column("freshness", Float, default=0), Column("confidence", Float, default=0), Column("is_active", Boolean, default=True), Column("raw_text", Text, default=""), Column("llm_reason", Text, default=""), Column("created_at", DateTime, server_default=func.now()), ) theme_catalysts_table = Table( "theme_catalysts", metadata, Column("id", Integer, primary_key=True, autoincrement=True), Column("catalyst_id", Integer, nullable=False), Column("theme_id", Text, nullable=False), Column("theme_name", Text, nullable=False), Column("relevance", Float, default=0), Column("reason", Text, default=""), Column("created_at", DateTime, server_default=func.now()), )