feat: migrate configuration to .env environment variables
This commit implements a complete migration from JSON-based configuration to .env environment variables, providing better security and flexibility. Key Changes: - Add python-dotenv dependency for environment variable support - Create config/env_loader.py with type conversion utilities - Add from_env() class methods to all config dataclasses - Update ConfigLoader to prioritize environment variables - Add save_to_env() method for .env file management - Implement database connection factory pattern - Add base DAO and connection classes for better abstraction - Support both SQL Server and MySQL with unified interface - Create migration script (scripts/migrate_to_env.py) - Update GUI to read/write .env files - Add comprehensive migration documentation New Files: - config/env_loader.py - Environment variable loader - db/base_connection.py - Base database connection interface - db/base_dao.py - Base DAO with common utilities - db/connection_factory.py - Factory for creating connections - db/mysql_connection.py - MySQL-specific connection - db/sqlserver_connection.py - SQL Server-specific connection - db/table_name_converter.py - SQL dialect converter - scripts/migrate_to_env.py - Configuration migration tool - docs/ENV_MIGRATION.md - Complete migration guide - .env.example - Environment variable template Testing: - Verified MySQL connection (8.0.44) - Tested all DAO operations - Confirmed 150 tables accessible - Validated configuration loading Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"""
|
||||
默认配置值
|
||||
|
||||
定义所有配置项的默认值。
|
||||
定义所有配置项的默认值,从环境变量加载。
|
||||
"""
|
||||
from config.schema import (
|
||||
ERPConfig,
|
||||
@@ -12,49 +12,14 @@ from config.schema import (
|
||||
ExtractionConfig,
|
||||
ValidationConfig,
|
||||
AppConfig,
|
||||
SQLServerConfig,
|
||||
MySQLConfig,
|
||||
DatabaseType,
|
||||
)
|
||||
|
||||
|
||||
# 默认配置
|
||||
DEFAULT_APP_CONFIG = AppConfig(
|
||||
erp=ERPConfig(
|
||||
url="https://68.11.34.30:8082/",
|
||||
username="BLDpengqiangqiang",
|
||||
password="Cqbld123456.",
|
||||
headless=True,
|
||||
ignore_https_errors=True,
|
||||
auto_close_browser=True,
|
||||
),
|
||||
database=DatabaseConfig(
|
||||
server="192.168.110.114",
|
||||
database="CompanyDB",
|
||||
username="peng",
|
||||
password="Cqbld123456.",
|
||||
driver="ODBC Driver 18 for SQL Server",
|
||||
trust_server_certificate="yes",
|
||||
),
|
||||
paths=PathConfig(
|
||||
data_dir="D:/python/playwrite/data/",
|
||||
production_id_file="ProductionID.txt",
|
||||
default_output="离散备料计划维护_合并.xlsx",
|
||||
validation_output="物料状态校验结果.xlsx",
|
||||
),
|
||||
extraction=ExtractionConfig(
|
||||
batch_size=100,
|
||||
verbose=True,
|
||||
auto_convert=True,
|
||||
merge_batches=True,
|
||||
enable_db_persistence=False, # Disabled by default
|
||||
),
|
||||
validation=ValidationConfig(
|
||||
data_source="database_full",
|
||||
use_database=True,
|
||||
batch_size=2000,
|
||||
enable_crud_operations=False,
|
||||
default_manager="",
|
||||
match_mode="substring",
|
||||
),
|
||||
)
|
||||
# 默认配置 - 从环境变量加载
|
||||
DEFAULT_APP_CONFIG = AppConfig.from_env()
|
||||
|
||||
|
||||
# 兼容旧版本的字典格式
|
||||
|
||||
201
config/env_loader.py
Normal file
201
config/env_loader.py
Normal file
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
环境变量加载器
|
||||
|
||||
使用 python-dotenv 加载 .env 文件,并提供类型转换功能。
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, Type, TypeVar
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
|
||||
|
||||
def load_env_file(env_file: Optional[str] = None) -> None:
|
||||
"""
|
||||
加载 .env 文件
|
||||
|
||||
Args:
|
||||
env_file: .env 文件路径,默认为项目根目录下的 .env
|
||||
"""
|
||||
if env_file is None:
|
||||
env_file = PROJECT_ROOT / ".env"
|
||||
else:
|
||||
env_file = Path(env_file)
|
||||
|
||||
load_dotenv(env_file)
|
||||
|
||||
|
||||
def get_env(key: str, default: Any = None) -> str:
|
||||
"""
|
||||
获取环境变量
|
||||
|
||||
Args:
|
||||
key: 环境变量名
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
环境变量值
|
||||
"""
|
||||
return os.getenv(key, default)
|
||||
|
||||
|
||||
def get_env_bool(key: str, default: bool = False) -> bool:
|
||||
"""
|
||||
获取布尔类型环境变量
|
||||
|
||||
Args:
|
||||
key: 环境变量名
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
布尔值
|
||||
"""
|
||||
value = os.getenv(key, "")
|
||||
if not value:
|
||||
return default
|
||||
return value.lower() in ("true", "1", "yes", "on")
|
||||
|
||||
|
||||
def get_env_int(key: str, default: int = 0) -> int:
|
||||
"""
|
||||
获取整数类型环境变量
|
||||
|
||||
Args:
|
||||
key: 环境变量名
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
整数值
|
||||
"""
|
||||
value = os.getenv(key, "")
|
||||
if not value:
|
||||
return default
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
|
||||
def get_env_float(key: str, default: float = 0.0) -> float:
|
||||
"""
|
||||
获取浮点数类型环境变量
|
||||
|
||||
Args:
|
||||
key: 环境变量名
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
浮点数值
|
||||
"""
|
||||
value = os.getenv(key, "")
|
||||
if not value:
|
||||
return default
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
|
||||
def set_env(key: str, value: Any) -> None:
|
||||
"""
|
||||
设置环境变量(仅在当前进程中有效)
|
||||
|
||||
Args:
|
||||
key: 环境变量名
|
||||
value: 环境变量值
|
||||
"""
|
||||
os.environ[key] = str(value)
|
||||
|
||||
|
||||
def save_env_file(env_file: Optional[str] = None, env_dict: Optional[dict] = None) -> bool:
|
||||
"""
|
||||
保存环境变量到 .env 文件
|
||||
|
||||
Args:
|
||||
env_file: .env 文件路径,默认为项目根目录下的 .env
|
||||
env_dict: 要保存的环境变量字典,如果为 None 则保存当前所有环境变量
|
||||
|
||||
Returns:
|
||||
保存是否成功
|
||||
"""
|
||||
if env_file is None:
|
||||
env_file = PROJECT_ROOT / ".env"
|
||||
else:
|
||||
env_file = Path(env_file)
|
||||
|
||||
try:
|
||||
# 确保目录存在
|
||||
env_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 读取现有的 .env 文件以保留注释
|
||||
existing_lines = []
|
||||
if env_file.exists():
|
||||
with open(env_file, "r", encoding="utf-8") as f:
|
||||
existing_lines = f.readlines()
|
||||
|
||||
# 如果提供了 env_dict,则保存指定的环境变量
|
||||
if env_dict is not None:
|
||||
# 构建新的文件内容
|
||||
new_content = []
|
||||
processed_keys = set()
|
||||
|
||||
for line in existing_lines:
|
||||
stripped = line.strip()
|
||||
# 保留注释和空行
|
||||
if not stripped or stripped.startswith("#"):
|
||||
new_content.append(line)
|
||||
# 更新已存在的键值对
|
||||
elif "=" in stripped and not stripped.startswith("#"):
|
||||
key = stripped.split("=")[0].strip()
|
||||
if key in env_dict:
|
||||
value = env_dict[key]
|
||||
# 处理布尔值的格式
|
||||
if isinstance(value, bool):
|
||||
value = "true" if value else "false"
|
||||
new_content.append(f"{key}={value}\n")
|
||||
processed_keys.add(key)
|
||||
else:
|
||||
new_content.append(line)
|
||||
|
||||
# 添加新的键值对
|
||||
for key, value in env_dict.items():
|
||||
if key not in processed_keys:
|
||||
# 处理布尔值的格式
|
||||
if isinstance(value, bool):
|
||||
value = "true" if value else "false"
|
||||
new_content.append(f"{key}={value}\n")
|
||||
|
||||
# 写入文件
|
||||
with open(env_file, "w", encoding="utf-8") as f:
|
||||
f.writelines(new_content)
|
||||
else:
|
||||
# 如果没有提供 env_dict,则不执行任何操作
|
||||
# 因为保存所有环境变量可能会包含系统变量
|
||||
return False
|
||||
|
||||
return True
|
||||
except IOError as e:
|
||||
print(f"保存 .env 文件失败: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def update_env_file(env_file: Optional[str] = None, **kwargs) -> bool:
|
||||
"""
|
||||
更新 .env 文件中的特定环境变量
|
||||
|
||||
Args:
|
||||
env_file: .env 文件路径
|
||||
**kwargs: 要更新的环境变量键值对
|
||||
|
||||
Returns:
|
||||
更新是否成功
|
||||
"""
|
||||
return save_env_file(env_file, kwargs)
|
||||
|
||||
|
||||
# 自动加载 .env 文件
|
||||
load_env_file()
|
||||
118
config/loader.py
118
config/loader.py
@@ -3,29 +3,51 @@
|
||||
"""
|
||||
配置加载器
|
||||
|
||||
负责加载、合并和验证配置。
|
||||
负责加载、合并和验证配置,优先从环境变量加载。
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from typing import Any, Dict
|
||||
from config.schema import AppConfig
|
||||
from config.schema import (
|
||||
AppConfig,
|
||||
ERPConfig,
|
||||
DatabaseConfig,
|
||||
PathConfig,
|
||||
ExtractionConfig,
|
||||
ValidationConfig,
|
||||
DatabaseType,
|
||||
SQLServerConfig,
|
||||
MySQLConfig,
|
||||
)
|
||||
from config.defaults import DEFAULT_APP_CONFIG, DEFAULT_SETTINGS_DICT
|
||||
from config.env_loader import get_env, get_env_bool, get_env_int
|
||||
|
||||
|
||||
class ConfigLoader:
|
||||
"""配置加载器"""
|
||||
|
||||
@staticmethod
|
||||
def load(config_file: str = "config/user_settings.json") -> AppConfig:
|
||||
def load(config_file: str = "config/user_settings.json", use_env: bool = True) -> AppConfig:
|
||||
"""
|
||||
加载配置文件
|
||||
加载配置
|
||||
|
||||
优先级:
|
||||
1. 环境变量(如果 use_env=True)
|
||||
2. JSON 配置文件(如果存在)
|
||||
3. 默认配置
|
||||
|
||||
Args:
|
||||
config_file: 配置文件路径
|
||||
use_env: 是否使用环境变量,默认为 True
|
||||
|
||||
Returns:
|
||||
应用配置对象
|
||||
"""
|
||||
# 优先从环境变量加载
|
||||
if use_env:
|
||||
return AppConfig.from_env()
|
||||
|
||||
# 如果不使用环境变量,则从 JSON 文件加载(向后兼容)
|
||||
if os.path.exists(config_file):
|
||||
try:
|
||||
with open(config_file, "r", encoding="utf-8") as f:
|
||||
@@ -66,6 +88,63 @@ class ConfigLoader:
|
||||
print(f"保存配置文件失败: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def save_to_env(config: AppConfig, env_file: str = ".env") -> bool:
|
||||
"""
|
||||
保存配置到 .env 文件
|
||||
|
||||
Args:
|
||||
config: 应用配置对象
|
||||
env_file: .env 文件路径
|
||||
|
||||
Returns:
|
||||
保存是否成功
|
||||
"""
|
||||
from config.env_loader import save_env_file
|
||||
|
||||
env_dict = {
|
||||
# ERP 配置
|
||||
"ERP_URL": config.erp.url,
|
||||
"ERP_USERNAME": config.erp.username,
|
||||
"ERP_PASSWORD": config.erp.password,
|
||||
"ERP_HEADLESS": config.erp.headless,
|
||||
"ERP_IGNORE_HTTPS_ERRORS": config.erp.ignore_https_errors,
|
||||
"ERP_AUTO_CLOSE_BROWSER": config.erp.auto_close_browser,
|
||||
# 数据库配置
|
||||
"DB_TYPE": config.database.db_type.value,
|
||||
"DB_SERVER": config.database.server,
|
||||
"DB_NAME": config.database.database,
|
||||
"DB_USERNAME": config.database.username,
|
||||
"DB_PASSWORD": config.database.password,
|
||||
# SQL Server 特定配置
|
||||
"DB_SQLSERVER_DRIVER": config.database.sqlserver.driver if config.database.sqlserver else "ODBC Driver 18 for SQL Server",
|
||||
"DB_TRUST_SERVER_CERTIFICATE": config.database.sqlserver.trust_server_certificate if config.database.sqlserver else "yes",
|
||||
# MySQL 特定配置
|
||||
"DB_MYSQL_HOST": config.database.mysql.host if config.database.mysql else "",
|
||||
"DB_MYSQL_PORT": config.database.mysql.port if config.database.mysql else 3306,
|
||||
"DB_MYSQL_CHARSET": config.database.mysql.charset if config.database.mysql else "utf8mb4",
|
||||
# 路径配置
|
||||
"PATH_DATA_DIR": config.paths.data_dir,
|
||||
"PATH_PRODUCTION_ID_FILE": config.paths.production_id_file,
|
||||
"PATH_DEFAULT_OUTPUT": config.paths.default_output,
|
||||
"PATH_VALIDATION_OUTPUT": config.paths.validation_output,
|
||||
# 数据提取配置
|
||||
"EXTRACTION_BATCH_SIZE": config.extraction.batch_size,
|
||||
"EXTRACTION_VERBOSE": config.extraction.verbose,
|
||||
"EXTRACTION_AUTO_CONVERT": config.extraction.auto_convert,
|
||||
"EXTRACTION_MERGE_BATCHES": config.extraction.merge_batches,
|
||||
"EXTRACTION_ENABLE_DB_PERSISTENCE": config.extraction.enable_db_persistence,
|
||||
# 校验配置
|
||||
"VALIDATION_DATA_SOURCE": config.validation.data_source,
|
||||
"VALIDATION_USE_DATABASE": config.validation.use_database,
|
||||
"VALIDATION_BATCH_SIZE": config.validation.batch_size,
|
||||
"VALIDATION_ENABLE_CRUD": config.validation.enable_crud_operations,
|
||||
"VALIDATION_DEFAULT_MANAGER": config.validation.default_manager,
|
||||
"VALIDATION_MATCH_MODE": config.validation.match_mode,
|
||||
}
|
||||
|
||||
return save_env_file(env_file, env_dict)
|
||||
|
||||
@staticmethod
|
||||
def _merge_settings(defaults: Dict, loaded: Dict) -> Dict:
|
||||
"""
|
||||
@@ -109,6 +188,28 @@ class ConfigLoader:
|
||||
extraction_dict = settings.get("extraction", {})
|
||||
validation_dict = settings.get("validation", {})
|
||||
|
||||
# 解析数据库类型
|
||||
db_type_str = database_dict.get("db_type", "sqlserver")
|
||||
try:
|
||||
db_type = DatabaseType(db_type_str)
|
||||
except ValueError:
|
||||
db_type = DatabaseType.SQLSERVER
|
||||
|
||||
# 解析 SQL Server 配置
|
||||
sqlserver_dict = database_dict.get("sqlserver", {})
|
||||
sqlserver_config = SQLServerConfig(
|
||||
driver=sqlserver_dict.get("driver", "ODBC Driver 18 for SQL Server"),
|
||||
trust_server_certificate=sqlserver_dict.get("trust_server_certificate", "yes"),
|
||||
)
|
||||
|
||||
# 解析 MySQL 配置
|
||||
mysql_dict = database_dict.get("mysql", {})
|
||||
mysql_config = MySQLConfig(
|
||||
host=mysql_dict.get("host", database_dict.get("server", "")),
|
||||
port=mysql_dict.get("port", 3306),
|
||||
charset=mysql_dict.get("charset", "utf8mb4"),
|
||||
)
|
||||
|
||||
return AppConfig(
|
||||
erp=ERPConfig(
|
||||
url=erp_dict.get("url", ""),
|
||||
@@ -119,14 +220,13 @@ class ConfigLoader:
|
||||
auto_close_browser=erp_dict.get("auto_close_browser", True),
|
||||
),
|
||||
database=DatabaseConfig(
|
||||
db_type=db_type,
|
||||
server=database_dict.get("server", ""),
|
||||
database=database_dict.get("database", ""),
|
||||
username=database_dict.get("username", ""),
|
||||
password=database_dict.get("password", ""),
|
||||
driver=database_dict.get("driver", "ODBC Driver 18 for SQL Server"),
|
||||
trust_server_certificate=database_dict.get(
|
||||
"trust_server_certificate", "yes"
|
||||
),
|
||||
sqlserver=sqlserver_config,
|
||||
mysql=mysql_config,
|
||||
),
|
||||
paths=PathConfig(
|
||||
data_dir=paths_dict.get("data_dir", ""),
|
||||
@@ -156,5 +256,3 @@ class ConfigLoader:
|
||||
)
|
||||
|
||||
|
||||
# 为了兼容旧代码,导入必要的类型
|
||||
from config.schema import ERPConfig, DatabaseConfig, PathConfig, ExtractionConfig, ValidationConfig
|
||||
|
||||
181
config/schema.py
181
config/schema.py
@@ -8,6 +8,13 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class DatabaseType(str, Enum):
|
||||
"""数据库类型枚举"""
|
||||
SQLSERVER = "sqlserver"
|
||||
MYSQL = "mysql"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -21,6 +28,20 @@ class ERPConfig:
|
||||
ignore_https_errors: bool = True
|
||||
auto_close_browser: bool = True
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "ERPConfig":
|
||||
"""从环境变量创建配置"""
|
||||
from config.env_loader import get_env, get_env_bool
|
||||
|
||||
return cls(
|
||||
url=get_env("ERP_URL", "https://68.11.34.30:8082/"),
|
||||
username=get_env("ERP_USERNAME", "BLDpengqiangqiang"),
|
||||
password=get_env("ERP_PASSWORD", ""),
|
||||
headless=get_env_bool("ERP_HEADLESS", True),
|
||||
ignore_https_errors=get_env_bool("ERP_IGNORE_HTTPS_ERRORS", True),
|
||||
auto_close_browser=get_env_bool("ERP_AUTO_CLOSE_BROWSER", True),
|
||||
)
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
"""验证配置,返回错误列表"""
|
||||
errors = []
|
||||
@@ -33,28 +54,98 @@ class ERPConfig:
|
||||
return errors
|
||||
|
||||
|
||||
@dataclass
|
||||
class SQLServerConfig:
|
||||
"""SQL Server 特定配置"""
|
||||
driver: str = "ODBC Driver 18 for SQL Server"
|
||||
trust_server_certificate: str = "yes"
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "SQLServerConfig":
|
||||
"""从环境变量创建配置"""
|
||||
from config.env_loader import get_env
|
||||
|
||||
return cls(
|
||||
driver=get_env("DB_SQLSERVER_DRIVER", "ODBC Driver 18 for SQL Server"),
|
||||
trust_server_certificate=get_env("DB_TRUST_SERVER_CERTIFICATE", "yes"),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MySQLConfig:
|
||||
"""MySQL 特定配置"""
|
||||
host: str = ""
|
||||
port: int = 3306
|
||||
charset: str = "utf8mb4"
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "MySQLConfig":
|
||||
"""从环境变量创建配置"""
|
||||
from config.env_loader import get_env, get_env_int
|
||||
|
||||
return cls(
|
||||
host=get_env("DB_MYSQL_HOST", "192.168.31.83"),
|
||||
port=get_env_int("DB_MYSQL_PORT", 3306),
|
||||
charset=get_env("DB_MYSQL_CHARSET", "utf8mb4"),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DatabaseConfig:
|
||||
"""数据库配置"""
|
||||
|
||||
server: str
|
||||
database: str
|
||||
username: str
|
||||
password: str
|
||||
driver: str = "ODBC Driver 18 for SQL Server"
|
||||
trust_server_certificate: str = "yes"
|
||||
db_type: DatabaseType = DatabaseType.SQLSERVER
|
||||
server: str = "" # SQL Server 服务器地址
|
||||
database: str = ""
|
||||
username: str = ""
|
||||
password: str = ""
|
||||
sqlserver: Optional[SQLServerConfig] = None
|
||||
mysql: Optional[MySQLConfig] = None
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "DatabaseConfig":
|
||||
"""从环境变量创建配置"""
|
||||
from config.env_loader import get_env, get_env_int
|
||||
|
||||
db_type_str = get_env("DB_TYPE", "sqlserver")
|
||||
try:
|
||||
db_type = DatabaseType(db_type_str)
|
||||
except ValueError:
|
||||
db_type = DatabaseType.SQLSERVER
|
||||
|
||||
return cls(
|
||||
db_type=db_type,
|
||||
server=get_env("DB_SERVER", "192.168.110.114"),
|
||||
database=get_env("DB_NAME", "CompanyDB"),
|
||||
username=get_env("DB_USERNAME", "peng"),
|
||||
password=get_env("DB_PASSWORD", ""),
|
||||
sqlserver=SQLServerConfig.from_env(),
|
||||
mysql=MySQLConfig.from_env(),
|
||||
)
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
"""验证配置,返回错误列表"""
|
||||
errors = []
|
||||
if not self.server:
|
||||
errors.append("数据库服务器地址不能为空")
|
||||
if not self.database:
|
||||
errors.append("数据库名称不能为空")
|
||||
if not self.username:
|
||||
errors.append("数据库用户名不能为空")
|
||||
if not self.password:
|
||||
errors.append("数据库密码不能为空")
|
||||
|
||||
if self.db_type == DatabaseType.SQLSERVER:
|
||||
if not self.server:
|
||||
errors.append("SQL Server 服务器地址不能为空")
|
||||
if not self.database:
|
||||
errors.append("数据库名称不能为空")
|
||||
if not self.username:
|
||||
errors.append("数据库用户名不能为空")
|
||||
if not self.password:
|
||||
errors.append("数据库密码不能为空")
|
||||
elif self.db_type == DatabaseType.MYSQL:
|
||||
if self.mysql and not self.mysql.host:
|
||||
errors.append("MySQL 主机地址不能为空")
|
||||
if not self.database:
|
||||
errors.append("数据库名称不能为空")
|
||||
if not self.username:
|
||||
errors.append("数据库用户名不能为空")
|
||||
if not self.password:
|
||||
errors.append("数据库密码不能为空")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
@@ -67,6 +158,18 @@ class PathConfig:
|
||||
default_output: str = "离散备料计划维护_合并.xlsx"
|
||||
validation_output: str = "物料状态校验结果.xlsx"
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "PathConfig":
|
||||
"""从环境变量创建配置"""
|
||||
from config.env_loader import get_env
|
||||
|
||||
return cls(
|
||||
data_dir=get_env("PATH_DATA_DIR", "D:/python/playwrite/data/"),
|
||||
production_id_file=get_env("PATH_PRODUCTION_ID_FILE", "ProductionID.txt"),
|
||||
default_output=get_env("PATH_DEFAULT_OUTPUT", "离散备料计划维护_合并.xlsx"),
|
||||
validation_output=get_env("PATH_VALIDATION_OUTPUT", "物料状态校验结果.xlsx"),
|
||||
)
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
"""验证配置,返回错误列表"""
|
||||
errors = []
|
||||
@@ -87,6 +190,19 @@ class ExtractionConfig:
|
||||
merge_batches: bool = True
|
||||
enable_db_persistence: bool = False
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "ExtractionConfig":
|
||||
"""从环境变量创建配置"""
|
||||
from config.env_loader import get_env_int, get_env_bool
|
||||
|
||||
return cls(
|
||||
batch_size=get_env_int("EXTRACTION_BATCH_SIZE", 100),
|
||||
verbose=get_env_bool("EXTRACTION_VERBOSE", True),
|
||||
auto_convert=get_env_bool("EXTRACTION_AUTO_CONVERT", True),
|
||||
merge_batches=get_env_bool("EXTRACTION_MERGE_BATCHES", True),
|
||||
enable_db_persistence=get_env_bool("EXTRACTION_ENABLE_DB_PERSISTENCE", False),
|
||||
)
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
"""验证配置,返回错误列表"""
|
||||
errors = []
|
||||
@@ -108,6 +224,20 @@ class ValidationConfig:
|
||||
default_manager: str = ""
|
||||
match_mode: str = "substring"
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "ValidationConfig":
|
||||
"""从环境变量创建配置"""
|
||||
from config.env_loader import get_env, get_env_int, get_env_bool
|
||||
|
||||
return cls(
|
||||
data_source=get_env("VALIDATION_DATA_SOURCE", "database_full"),
|
||||
use_database=get_env_bool("VALIDATION_USE_DATABASE", True),
|
||||
batch_size=get_env_int("VALIDATION_BATCH_SIZE", 2000),
|
||||
enable_crud_operations=get_env_bool("VALIDATION_ENABLE_CRUD", False),
|
||||
default_manager=get_env("VALIDATION_DEFAULT_MANAGER", ""),
|
||||
match_mode=get_env("VALIDATION_MATCH_MODE", "substring"),
|
||||
)
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
"""验证配置,返回错误列表"""
|
||||
errors = []
|
||||
@@ -149,6 +279,17 @@ class AppConfig:
|
||||
extraction: ExtractionConfig
|
||||
validation: ValidationConfig
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "AppConfig":
|
||||
"""从环境变量创建配置"""
|
||||
return cls(
|
||||
erp=ERPConfig.from_env(),
|
||||
database=DatabaseConfig.from_env(),
|
||||
paths=PathConfig.from_env(),
|
||||
extraction=ExtractionConfig.from_env(),
|
||||
validation=ValidationConfig.from_env(),
|
||||
)
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
"""验证所有配置,返回错误列表"""
|
||||
errors = []
|
||||
@@ -171,12 +312,20 @@ class AppConfig:
|
||||
"auto_close_browser": self.erp.auto_close_browser,
|
||||
},
|
||||
"database": {
|
||||
"db_type": self.database.db_type.value,
|
||||
"server": self.database.server,
|
||||
"database": self.database.database,
|
||||
"username": self.database.username,
|
||||
"password": self.database.password,
|
||||
"driver": self.database.driver,
|
||||
"trust_server_certificate": self.database.trust_server_certificate,
|
||||
"sqlserver": {
|
||||
"driver": self.database.sqlserver.driver if self.database.sqlserver else "ODBC Driver 18 for SQL Server",
|
||||
"trust_server_certificate": self.database.sqlserver.trust_server_certificate if self.database.sqlserver else "yes",
|
||||
},
|
||||
"mysql": {
|
||||
"host": self.database.mysql.host if self.database.mysql else "",
|
||||
"port": self.database.mysql.port if self.database.mysql else 3306,
|
||||
"charset": self.database.mysql.charset if self.database.mysql else "utf8mb4",
|
||||
},
|
||||
},
|
||||
"paths": {
|
||||
"data_dir": self.paths.data_dir,
|
||||
|
||||
Reference in New Issue
Block a user