- 新增 PosterSmartEngine,AI 生成文案 + 海报渲染 - 5 种布局支持文本换行和自适应字体 - 修复按钮/标签颜色显示问题 - 优化渐变遮罩和内容区域计算 - Prompt 优化:标题格式为产品名+描述
158 lines
4.3 KiB
Python
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, {})
|