feat:新增基于 pydantic-settings 的统一配置中心
This commit is contained in:
10
apps/server/core/__init__.py
Normal file
10
apps/server/core/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""核心基础模块 (配置、日志、异常等)。"""
|
||||
|
||||
from .settings import (
|
||||
Settings,
|
||||
get_settings,
|
||||
SERVER_DIR,
|
||||
PROJECT_ROOT,
|
||||
)
|
||||
|
||||
__all__ = ["Settings", "get_settings", "SERVER_DIR", "PROJECT_ROOT"]
|
||||
175
apps/server/core/settings.py
Normal file
175
apps/server/core/settings.py
Normal file
@@ -0,0 +1,175 @@
|
||||
"""统一应用配置 (MVP-1 / 新增)
|
||||
|
||||
基于 pydantic-settings 的多环境配置管理,集中收敛原先散落在
|
||||
``main.py``、各 service 模块中的环境变量、路径、阈值等配置。
|
||||
|
||||
加载顺序 (优先级从高到低)::
|
||||
|
||||
1. 显式构造参数
|
||||
2. 环境变量 (大小写不敏感)
|
||||
3. ``.env`` 文件 (位于 apps/server/.env)
|
||||
4. 默认值
|
||||
|
||||
使用方式::
|
||||
|
||||
from core.settings import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
print(settings.api.port)
|
||||
print(settings.detection.default_confidence)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
# 项目根 (apps/server)
|
||||
SERVER_DIR: Path = Path(__file__).resolve().parent.parent
|
||||
PROJECT_ROOT: Path = SERVER_DIR.parent.parent
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 子配置
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class APISettings(BaseSettings):
|
||||
"""API 与服务器相关配置。"""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="API_", extra="ignore")
|
||||
|
||||
host: str = Field(default="0.0.0.0", description="监听地址")
|
||||
port: int = Field(default=8000, description="监听端口")
|
||||
reload: bool = Field(default=True, description="开发模式自动重载")
|
||||
cors_origins: List[str] = Field(
|
||||
default_factory=lambda: ["*"], description="允许的跨域来源"
|
||||
)
|
||||
|
||||
|
||||
class DetectionSettings(BaseSettings):
|
||||
"""检测相关全局默认值。"""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="DETECTION_", extra="ignore")
|
||||
|
||||
default_confidence: float = Field(default=0.5, ge=0.0, le=1.0)
|
||||
default_iou: float = Field(default=0.45, ge=0.0, le=1.0)
|
||||
# 决策引擎过滤的最低置信度
|
||||
min_confidence: float = Field(default=0.3, ge=0.0, le=1.0)
|
||||
|
||||
|
||||
class ActionDetectionSettings(BaseSettings):
|
||||
"""ppTSM 行为识别 (Docker) 服务配置。"""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="ACTION_DETECTION_", extra="ignore")
|
||||
|
||||
api_url: str = Field(default="http://localhost:8081")
|
||||
timeout: int = Field(default=30, ge=1)
|
||||
|
||||
|
||||
class EventEngineSettings(BaseSettings):
|
||||
"""事件决策 + 聚合 + 规则引擎配置。"""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="EVENT_", extra="ignore")
|
||||
|
||||
# 时间窗口去重 (秒),同一 (source_id, event_type, track_id) 在窗口内只产生一条
|
||||
dedup_window_seconds: float = Field(default=30.0, ge=0.0)
|
||||
# 规则 YAML 目录 (相对 server 根)
|
||||
rules_dir: str = Field(default="config/rules")
|
||||
# 事件聚合最大活跃事件数 (超过将按 LRU 淘汰)
|
||||
max_active_events: int = Field(default=1000, ge=1)
|
||||
|
||||
|
||||
class LoggingSettings(BaseSettings):
|
||||
"""日志配置。"""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="LOG_", extra="ignore")
|
||||
|
||||
level: str = Field(default="INFO")
|
||||
json_format: bool = Field(default=False)
|
||||
|
||||
|
||||
class PathSettings(BaseSettings):
|
||||
"""关键路径 (静态资源 / 外部模型) 配置。"""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="PATH_", extra="ignore")
|
||||
|
||||
server_dir: Path = SERVER_DIR
|
||||
project_root: Path = PROJECT_ROOT
|
||||
static_dir: Path = SERVER_DIR / "static"
|
||||
results_dir: Path = SERVER_DIR / "static" / "results"
|
||||
temp_dir: Path = SERVER_DIR / "static" / "temp"
|
||||
uploads_dir: Path = SERVER_DIR / "static" / "uploads"
|
||||
external_paddle: Path = (
|
||||
PROJECT_ROOT / "external" / "video-recognition-system" / "PaddlePaddle"
|
||||
)
|
||||
|
||||
def ensure(self) -> None:
|
||||
"""确保所有需要的目录存在 (启动时调用)。"""
|
||||
|
||||
for p in (self.static_dir, self.results_dir, self.temp_dir, self.uploads_dir):
|
||||
p.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 总配置
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""应用总配置。
|
||||
|
||||
子配置统一在此聚合,便于以 ``settings.api.port`` 这种命名空间方式访问。
|
||||
"""
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=str(SERVER_DIR / ".env"),
|
||||
env_file_encoding="utf-8",
|
||||
env_nested_delimiter="__",
|
||||
case_sensitive=False,
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
env: str = Field(default="development", description="运行环境")
|
||||
debug: bool = Field(default=True)
|
||||
|
||||
api: APISettings = Field(default_factory=APISettings)
|
||||
detection: DetectionSettings = Field(default_factory=DetectionSettings)
|
||||
action_detection: ActionDetectionSettings = Field(
|
||||
default_factory=ActionDetectionSettings
|
||||
)
|
||||
event_engine: EventEngineSettings = Field(default_factory=EventEngineSettings)
|
||||
logging: LoggingSettings = Field(default_factory=LoggingSettings)
|
||||
paths: PathSettings = Field(default_factory=PathSettings)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_settings() -> Settings:
|
||||
"""获取全局 Settings 单例 (带缓存)。
|
||||
|
||||
测试场景下可调用 ``get_settings.cache_clear()`` 重新加载。
|
||||
"""
|
||||
|
||||
settings = Settings()
|
||||
settings.paths.ensure()
|
||||
return settings
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Settings",
|
||||
"APISettings",
|
||||
"DetectionSettings",
|
||||
"ActionDetectionSettings",
|
||||
"EventEngineSettings",
|
||||
"LoggingSettings",
|
||||
"PathSettings",
|
||||
"get_settings",
|
||||
"SERVER_DIR",
|
||||
"PROJECT_ROOT",
|
||||
]
|
||||
Reference in New Issue
Block a user