#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 统一配置加载器 从 config/app.yaml 读取所有配置,替代分散的 JSON 文件 """ import os import yaml import logging from pathlib import Path from typing import Any, Dict, Optional from functools import lru_cache logger = logging.getLogger(__name__) # 项目根目录 PROJECT_ROOT = Path(__file__).parent.parent CONFIG_FILE = PROJECT_ROOT / "config" / "app.yaml" class ConfigLoader: """统一配置加载器""" _instance: Optional['ConfigLoader'] = None _config: Dict[str, Any] = {} def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._load_config() return cls._instance def _load_config(self): """加载配置文件""" if not CONFIG_FILE.exists(): logger.warning(f"配置文件不存在: {CONFIG_FILE}") self._config = {} return try: with open(CONFIG_FILE, 'r', encoding='utf-8') as f: self._config = yaml.safe_load(f) or {} # 处理环境变量替换 self._process_env_vars(self._config) logger.info(f"配置加载成功: {CONFIG_FILE}") except Exception as e: logger.error(f"配置加载失败: {e}") self._config = {} def _process_env_vars(self, config: Dict, path: str = ""): """递归处理环境变量 ${VAR_NAME}""" for key, value in config.items(): if isinstance(value, dict): self._process_env_vars(value, f"{path}.{key}") elif isinstance(value, str) and value.startswith("${") and value.endswith("}"): env_var = value[2:-1] env_value = os.environ.get(env_var) if env_value: config[key] = env_value else: logger.warning(f"环境变量未设置: {env_var} (路径: {path}.{key})") def get(self, key: str, default: Any = None) -> Any: """ 获取配置值,支持点号分隔的路径 示例: config.get("ai_model.model") # 返回 "qwq-plus" config.get("engines.topic_generate.model.temperature") # 返回 0.2 """ keys = key.split(".") value = self._config for k in keys: if isinstance(value, dict) and k in value: value = value[k] else: return default return value def get_section(self, section: str) -> Dict[str, Any]: """获取整个配置节""" return self.get(section, {}) @property def ai_model(self) -> Dict[str, Any]: """AI 模型配置""" return self.get_section("ai_model") @property def engines(self) -> Dict[str, Any]: """引擎配置""" return self.get_section("engines") @property def poster(self) -> Dict[str, Any]: """海报配置""" return self.get_section("poster") @property def hotspot(self) -> Dict[str, Any]: """热点配置""" return self.get_section("hotspot") @property def paths(self) -> Dict[str, Any]: """路径配置""" return self.get_section("paths") @property def system(self) -> Dict[str, Any]: """系统配置""" return self.get_section("system") def reload(self): """重新加载配置""" self._load_config() # 全局配置实例 @lru_cache(maxsize=1) def get_config() -> ConfigLoader: """获取全局配置实例""" return ConfigLoader() # 便捷访问函数 def config_get(key: str, default: Any = None) -> Any: """便捷获取配置值""" return get_config().get(key, default) # 兼容旧代码的函数 def load_json_config(filename: str) -> Dict[str, Any]: """ 兼容旧的 JSON 配置加载 将请求映射到统一配置 """ config = get_config() mapping = { "ai_model.json": config.ai_model, "engines.json": config.engines, "poster_gen.json": config.poster, "paths.json": config.paths, "system.json": config.system, "database.json": {}, # 已废弃 "resource.json": {}, # 已废弃 } return mapping.get(filename, {})