TravelContentCreator/core/config_loader.py
jinye_huang dcfd820ca4 feat(poster_v2): 智能海报生成引擎 v1.0
- 新增 PosterSmartEngine,AI 生成文案 + 海报渲染
- 5 种布局支持文本换行和自适应字体
- 修复按钮/标签颜色显示问题
- 优化渐变遮罩和内容区域计算
- Prompt 优化:标题格式为产品名+描述
2025-12-10 15:04:59 +08:00

158 lines
4.3 KiB
Python

#!/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, {})