Files
pruchase_jhw/src/config/settings.py
T
2026-06-11 12:34:04 +08:00

112 lines
4.6 KiB
Python

import os
from functools import lru_cache
from typing import Annotated, Any
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict
from surugaya_common import redis_keys
# 根据 APP_ENV 确定环境配置文件(默认为 dev)
APP_ENV = os.getenv("APP_ENV", "dev").lower()
ENV_FILE = f".env.{APP_ENV}"
class Settings(BaseSettings):
"""
全局应用配置模型。
使用 pydantic-settings 校验类型并自动解析复杂数据类型。
"""
# 运行环境
app_env: str = Field(default="dev", alias="APP_ENV")
# 日志配置
log_level: str = Field(default="INFO", alias="LOG_LEVEL")
log_dir: str = Field(default="./logs", alias="LOG_DIR")
# 节点配置
node_id: str = Field(default="node_1", alias="NODE_ID")
# Redis 配置
redis_host: str = Field(default="127.0.0.1", alias="REDIS_HOST")
redis_port: int = Field(default=6379, alias="REDIS_PORT")
redis_password: str | None = Field(default=None, alias="REDIS_PASSWORD")
redis_db: int = Field(default=0, alias="REDIS_DB")
redis_stream_key: str = Field(default=redis_keys.PURCHASE_TASK_STREAM, alias="REDIS_STREAM_KEY")
redis_consumer_group: str = Field(default="surugaya_purchase_task_group", alias="REDIS_CONSUMER_GROUP")
redis_consumer_name: str = Field(default="surugaya_purchase_task_worker", alias="REDIS_CONSUMER_NAME")
redis_delay_zset_key: str = Field(default=redis_keys.TRADE_DELAY_ZSET, alias="REDIS_DELAY_ZSET_KEY")
redis_delay_poll_interval: int = Field(default=3600, alias="REDIS_DELAY_POLL_INTERVAL")
redis_delay_batch_size: int = Field(default=10, alias="REDIS_DELAY_BATCH_SIZE")
# 浏览器控制
browser_headless: bool = Field(default=True, alias="BROWSER_HEADLESS")
browser_executable_path: str | None = Field(default=None, alias="BROWSER_EXECUTABLE_PATH")
browser_keep_open: bool = Field(default=False, alias="BROWSER_KEEP_OPEN")
# 配置与资源路径
accounts_config_path: str = Field(default="./accounts.yaml", alias="ACCOUNTS_CONFIG_PATH")
screenshot_dir: str = Field(default="./archives", alias="SCREENSHOT_DIR")
# 任务上报配置
callback_url: str | None = Field(default=None, alias="CALLBACK_URL")
appid: str | None = Field(default=None, alias="APPID")
app_secret: str | None = Field(default=None, alias="APP_SECRET")
# PayPay 配置
paypay_phone: str | None = Field(default=None, alias="PAYPAY_PHONE")
paypay_password: str | None = Field(default=None, alias="PAYPAY_PASSWORD")
# 通知配置
notify_enabled: bool = Field(default=False, alias="NOTIFY_ENABLED")
notify_channels: Annotated[list[str], NoDecode] = Field(
default_factory=lambda: ["email"],
alias="NOTIFY_CHANNELS",
)
notify_on_task_failed: bool = Field(default=True, alias="NOTIFY_ON_TASK_FAILED")
# 邮件通知配置
mail_enabled: bool = Field(default=True, alias="MAIL_ENABLED")
mail_smtp_host: str | None = Field(default=None, alias="MAIL_SMTP_HOST")
mail_smtp_port: int = Field(default=465, alias="MAIL_SMTP_PORT")
mail_smtp_use_ssl: bool = Field(default=True, alias="MAIL_SMTP_USE_SSL")
mail_smtp_starttls: bool = Field(default=False, alias="MAIL_SMTP_STARTTLS")
mail_username: str | None = Field(default=None, alias="MAIL_USERNAME")
mail_password: str | None = Field(default=None, alias="MAIL_PASSWORD")
mail_from_address: str | None = Field(default=None, alias="MAIL_FROM_ADDRESS")
mail_from_name: str = Field(default="JP Purchase Bot", alias="MAIL_FROM_NAME")
mail_to_addresses: Annotated[list[str], NoDecode] = Field(
default_factory=list,
alias="MAIL_TO_ADDRESSES",
)
mail_subject_prefix: str = Field(default="[JP-Purchase]", alias="MAIL_SUBJECT_PREFIX")
mail_timeout_seconds: int = Field(default=10, alias="MAIL_TIMEOUT_SECONDS")
@field_validator("notify_channels", "mail_to_addresses", mode="before")
@classmethod
def _parse_csv_list(cls, value: Any) -> list[str]:
if value is None:
return []
if isinstance(value, str):
return [item.strip() for item in value.split(",") if item.strip()]
if isinstance(value, (list, tuple, set)):
return [str(item).strip() for item in value if str(item).strip()]
return [str(value).strip()] if str(value).strip() else []
# Pydantic Settings 配置项
model_config = SettingsConfigDict(
env_file=ENV_FILE,
env_file_encoding="utf-8",
extra="ignore" # 忽略其他系统环境变量
)
@lru_cache
def get_settings() -> Settings:
"""
获取缓存的全局配置单例。
"""
return Settings()