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:
Misaka
2026-02-09 22:39:14 +08:00
parent 04b99292ad
commit aaa46ef282
22 changed files with 2545 additions and 563 deletions

142
db/mysql_connection.py Normal file
View File

@@ -0,0 +1,142 @@
"""
MySQL 数据库连接组件
提供 MySQL 数据库连接和查询接口
"""
import mysql.connector
from mysql.connector import Error
from typing import List, Dict, Any, Optional
from db.base_connection import BaseDatabaseConnection
class MySQLConnection(BaseDatabaseConnection):
"""MySQL 数据库连接类"""
def __init__(self, config: Optional[Dict[str, Any]] = None):
"""
初始化数据库连接
Args:
config: 数据库配置字典
- host: 服务器地址
- port: 端口号(默认 3306
- database: 数据库名称
- username: 用户名
- password: 密码
- charset: 字符集(默认 utf8mb4
"""
super().__init__(config)
def connect(self):
"""
建立数据库连接
Returns:
mysql.connector.connection.MySQLConnection: 数据库连接对象
"""
if self.connection is not None:
return self.connection
try:
self.connection = mysql.connector.connect(
host=self.config.get('host', 'localhost'),
port=self.config.get('port', 3306),
database=self.config['database'],
user=self.config['username'],
password=self.config['password'],
charset=self.config.get('charset', 'utf8mb4'),
autocommit=False
)
print(
f"成功连接到 MySQL 数据库: {self.config.get('host', 'localhost')}"
f":{self.config.get('port', 3306)}/{self.config['database']}"
)
return self.connection
except Error as e:
print(f"MySQL 数据库连接失败: {e}")
raise
def disconnect(self):
"""关闭数据库连接"""
if self.connection and self.connection.is_connected():
self.connection.close()
self.connection = None
print("MySQL 数据库连接已关闭")
def execute_query(
self, sql: str, params: Optional[tuple] = None
) -> List[Dict[str, Any]]:
"""
执行查询语句并返回结果
Args:
sql: SQL 查询语句
params: 查询参数(可选)
Returns:
List[Dict[str, Any]]: 查询结果列表,每个元素为一行数据的字典
"""
if not self.connection or not self.connection.is_connected():
self.connect()
cursor = None
try:
cursor = self.connection.cursor(dictionary=True)
if params:
cursor.execute(sql, params)
else:
cursor.execute(sql)
# 直接获取字典列表
results = cursor.fetchall()
return results
except Error as e:
print(f"查询执行失败: {e}")
raise
finally:
if cursor:
cursor.close()
def execute_update(self, sql: str, params: Optional[tuple] = None) -> int:
"""
执行更新/插入/删除语句
Args:
sql: SQL 语句
params: 参数(可选)
Returns:
int: 受影响的行数
"""
if not self.connection or not self.connection.is_connected():
self.connect()
cursor = None
try:
cursor = self.connection.cursor()
if params:
cursor.execute(sql, params)
else:
cursor.execute(sql)
self.connection.commit()
return cursor.rowcount
except Error as e:
self.connection.rollback()
print(f"执行失败,已回滚: {e}")
raise
finally:
if cursor:
cursor.close()
def get_placeholder(self) -> str:
"""
获取参数占位符
Returns:
MySQL 使用 "%s" 作为参数占位符
"""
return "%s"