"""统一配置:从 .env / 环境变量读 MySQL、调度器、数据源开关等参数。""" from __future__ import annotations import os from pathlib import Path from typing import Optional from pydantic_settings import BaseSettings, SettingsConfigDict try: from dotenv import load_dotenv _HAS_DOTENV = True except ImportError: _HAS_DOTENV = False _PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent # 启动时自动把 .env 加载到 os.environ,使下游 `os.environ.get(...)` 代码 # (如 app/sources/mairui.py / xueqiu.py)能拿到 credentials。 if _HAS_DOTENV: _env_file = _PROJECT_ROOT / ".env" if _env_file.exists(): load_dotenv(_env_file, override=False) class Settings(BaseSettings): """应用配置。所有字段都可以通过环境变量或 .env 文件覆盖。""" # MySQL mysql_host: str = "127.0.0.1" mysql_port: int = 3306 mysql_user: str = "root" mysql_password: str = "" # 同步任务写入的目标库(原始市场数据) mysql_database: str = "market_data_sync_db" # 简版库(对照测试,可选) mysql_database_lite: str = "grid_seeker" # 业务主库(保留— 同步任务不再写入) mysql_database_business: str = "grid_seeker_model_base" # Scheduler scheduler_tick_seconds: int = 5 scheduler_timezone: str = "Asia/Shanghai" scheduler_auto_seed: bool = True # API api_host: str = "0.0.0.0" api_port: int = 8100 # Logging log_level: str = "INFO" log_dir: str = "./logs" # Datasources ds_baostock_enabled: bool = True ds_sina_enabled: bool = True xueqiu_token: str = "" # Trading calendar trading_holidays: str = "" model_config = SettingsConfigDict( env_file=str(_PROJECT_ROOT / ".env"), env_file_encoding="utf-8", case_sensitive=False, extra="ignore", ) @property def project_root(self) -> Path: return _PROJECT_ROOT @property def log_path(self) -> Path: p = Path(self.log_dir) if not p.is_absolute(): p = _PROJECT_ROOT / p p.mkdir(parents=True, exist_ok=True) return p @property def holidays_list(self) -> list[str]: if not self.trading_holidays: return [] return [d.strip() for d in self.trading_holidays.split(",") if d.strip()] def mysql_url(self, database: Optional[str] = None) -> str: """构造 SQLAlchemy URL。""" db = database or self.mysql_database return ( f"mysql+pymysql://{self.mysql_user}:{self.mysql_password}" f"@{self.mysql_host}:{self.mysql_port}/{db}?charset=utf8mb4" ) def pymysql_connect_kwargs(self, database: Optional[str] = None) -> dict: """构造 pymysql.connect 的关键字参数。 内网自签名证书场景自动禁用 SSL。 """ db = database or self.mysql_database return { "host": self.mysql_host, "port": self.mysql_port, "user": self.mysql_user, "password": self.mysql_password, "database": db, "charset": "utf8mb4", "autocommit": False, # 兼容自签名证书的内网 MySQL — 禁用 SSL "ssl": None, } _settings: Optional[Settings] = None def get_settings() -> Settings: """全局单例。""" global _settings if _settings is None: _settings = Settings() return _settings settings = get_settings()