Files
CapacityReport/app/config.py
T

232 lines
7.7 KiB
Python

"""
配置管理模块
"""
import json
from datetime import datetime
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List
BASE_DIR = Path(__file__).resolve().parent.parent
CACHE_DIR = BASE_DIR / "cache"
CONFIG_FILE = BASE_DIR / "Configure.json"
SQL_SCRIPT = BASE_DIR / "ReportScript.sql"
@dataclass
class MySQLConfig:
host: str = "localhost"
port: int = 3306
user: str = "root"
passwd: str = ""
dbname: str = "CapacityReport"
@dataclass
class RemoteDataConfig:
enabled: bool = False
protocol: str = "sftp"
host: str = ""
port: int = 22
user: str = ""
passwd: str = ""
remote_dir: str = "/"
passive: bool = True
timeout: int = 30
auto_delete_source: bool = False
def normalized(self) -> "RemoteDataConfig":
protocol = self.protocol.lower().strip()
if protocol not in {"ftp", "sftp"}:
protocol = "sftp"
port = self.port or (22 if protocol == "sftp" else 21)
return RemoteDataConfig(
enabled=bool(self.enabled),
protocol=protocol,
host=self.host.strip(),
port=port,
user=self.user.strip(),
passwd=self.passwd,
remote_dir=(self.remote_dir or "/").strip() or "/",
passive=bool(self.passive),
timeout=max(int(self.timeout or 30), 1),
auto_delete_source=bool(self.auto_delete_source),
)
def to_dict(self, include_password: bool = False) -> Dict[str, Any]:
data = {
"enabled": self.enabled,
"protocol": self.protocol,
"host": self.host,
"port": self.port,
"user": self.user,
"remote_dir": self.remote_dir,
"passive": self.passive,
"timeout": self.timeout,
"auto_delete_source": self.auto_delete_source,
}
if include_password:
data["passwd"] = self.passwd
return data
@classmethod
def from_dict(cls, data: Dict[str, Any] | None) -> "RemoteDataConfig":
data = data or {}
protocol = str(data.get("protocol", "sftp")).lower()
default_port = 22 if protocol == "sftp" else 21
try:
port = int(data.get("port") or default_port)
except (TypeError, ValueError):
port = default_port
try:
timeout = int(data.get("timeout") or 30)
except (TypeError, ValueError):
timeout = 30
return cls(
enabled=bool(data.get("enabled", False)),
protocol=protocol,
host=str(data.get("host", "")),
port=port,
user=str(data.get("user", "")),
passwd=str(data.get("passwd", "")),
remote_dir=str(data.get("remote_dir", "/")),
passive=bool(data.get("passive", True)),
timeout=timeout,
auto_delete_source=bool(data.get("auto_delete_source", False)),
).normalized()
@dataclass
class HistoryRetentionConfig:
enabled: bool = False
keep_count: int = 20
def normalized(self) -> "HistoryRetentionConfig":
try:
keep_count = int(self.keep_count)
except (TypeError, ValueError):
keep_count = 20
return HistoryRetentionConfig(
enabled=bool(self.enabled),
keep_count=max(keep_count, 0),
)
def to_dict(self) -> Dict[str, Any]:
normalized = self.normalized()
return {
"enabled": normalized.enabled,
"keep_count": normalized.keep_count,
}
@classmethod
def from_dict(cls, data: Dict[str, Any] | None) -> "HistoryRetentionConfig":
data = data or {}
return cls(
enabled=bool(data.get("enabled", False)),
keep_count=data.get("keep_count", 20),
).normalized()
@dataclass
class AppConfig:
update: str = ""
mysql: MySQLConfig = field(default_factory=MySQLConfig)
remote_data: RemoteDataConfig = field(default_factory=RemoteDataConfig)
history_retention: HistoryRetentionConfig = field(default_factory=HistoryRetentionConfig)
sheet_filter: List[str] = field(default_factory=list)
extract_fields: List[Dict[str, Any]] = field(default_factory=list)
@classmethod
def load(cls) -> "AppConfig":
"""从 Configure.json 加载配置"""
if not CONFIG_FILE.exists():
return cls()
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
mysql_data = data.get("MySQL_DBInfo", {})
mysql_config = MySQLConfig(
host=mysql_data.get("host", "localhost"),
port=mysql_data.get("port", 3306),
user=mysql_data.get("user", "root"),
passwd=mysql_data.get("passwd", ""),
dbname=mysql_data.get("dbname", "CapacityReport")
)
remote_config = RemoteDataConfig.from_dict(data.get("RemoteData"))
history_retention = HistoryRetentionConfig.from_dict(data.get("HistoryRetention"))
return cls(
update=data.get("Update", ""),
mysql=mysql_config,
remote_data=remote_config,
history_retention=history_retention,
sheet_filter=data.get("SheetFilter", []),
extract_fields=data.get("ExtractField", [])
)
def save(self):
"""保存配置到 Configure.json,并自动更新 Update 时间"""
self.update = datetime.now().strftime("%Y/%m/%d %H:%M:%S")
data = self.to_file_dict()
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def to_file_dict(self) -> Dict[str, Any]:
"""转换为配置文件结构(包含敏感字段,用于保存和下载)"""
return {
"Update": self.update,
"MySQL_DBInfo": {
"host": self.mysql.host,
"port": self.mysql.port,
"user": self.mysql.user,
"passwd": self.mysql.passwd,
"dbname": self.mysql.dbname
},
"RemoteData": self.remote_data.normalized().to_dict(include_password=True),
"HistoryRetention": self.history_retention.normalized().to_dict(),
"SheetFilter": self.sheet_filter,
"ExtractField": self.extract_fields
}
def to_dict(self) -> Dict[str, Any]:
"""转换为字典(用于返回给前端,隐藏密码)"""
return {
"update": self.update,
"mysql": {
"host": self.mysql.host,
"port": self.mysql.port,
"user": self.mysql.user,
"dbname": self.mysql.dbname
},
"remote_data": self.remote_data.normalized().to_dict(),
"history_retention": self.history_retention.normalized().to_dict(),
"sheet_filter": self.sheet_filter,
"extract_fields": self.extract_fields
}
def to_dict_full(self) -> Dict[str, Any]:
"""转换为完整字典(包含密码,用于编辑时回显)"""
return {
"update": self.update,
"mysql": {
"host": self.mysql.host,
"port": self.mysql.port,
"user": self.mysql.user,
"passwd": self.mysql.passwd,
"dbname": self.mysql.dbname
},
"remote_data": self.remote_data.normalized().to_dict(include_password=True),
"history_retention": self.history_retention.normalized().to_dict(),
"sheet_filter": self.sheet_filter,
"extract_fields": self.extract_fields
}
# 确保缓存目录存在
CACHE_DIR.mkdir(exist_ok=True)