diff --git a/iti/applications/service/erp/__init__.py b/iti/applications/service/erp/__init__.py index 90c228d..9c4971d 100644 --- a/iti/applications/service/erp/__init__.py +++ b/iti/applications/service/erp/__init__.py @@ -28,6 +28,20 @@ __all__ = [ ] def init_erp(app) -> None: - # 初始化ERP管理器 - erp_odbc.init_app(app) - erp_api.init_app(app) + """初始化ERP管理器(ODBC和API)""" + import logging + logger = logging.getLogger(__name__) + + # 初始化ODBC管理器(失败不中断启动) + try: + erp_odbc.init_app(app) + except Exception as e: + logger.error(f"ERP ODBC管理器初始化失败: {e}", exc_info=True) + logger.warning("应用将继续启动,但ODBC功能将不可用") + + # 初始化API管理器(失败不中断启动) + try: + erp_api.init_app(app) + except Exception as e: + logger.error(f"ERP API管理器初始化失败: {e}", exc_info=True) + logger.warning("应用将继续启动,但API功能将不可用") diff --git a/iti/applications/service/erp/odbc_manager.py b/iti/applications/service/erp/odbc_manager.py index 92a3525..fa9ffd0 100644 --- a/iti/applications/service/erp/odbc_manager.py +++ b/iti/applications/service/erp/odbc_manager.py @@ -19,7 +19,7 @@ class PyODBCConnectionPool: """纯pyodbc连接池实现""" def __init__(self, dsn: str, pool_size: int = 5, max_overflow: int = 10, - pool_timeout: int = 30, pool_recycle: int = 3600): + pool_timeout: int = 30, pool_recycle: int = 3600, lazy_init: bool = False): """ 初始化连接池 @@ -29,12 +29,14 @@ class PyODBCConnectionPool: max_overflow: 最大溢出连接数 pool_timeout: 获取连接超时(秒) pool_recycle: 连接回收时间(秒) + lazy_init: 是否延迟初始化(不预创建连接) """ self.dsn = dsn self.pool_size = pool_size self.max_overflow = max_overflow self.pool_timeout = pool_timeout self.pool_recycle = pool_recycle + self._is_available = False # 连接池队列 self._pool = Queue(maxsize=pool_size) @@ -42,12 +44,19 @@ class PyODBCConnectionPool: self._overflow_count = 0 self._overflow_lock = Lock() - # 预创建连接 - for _ in range(pool_size): - conn = self._create_connection() - self._pool.put((conn, time.time())) - - logger.info(f"ODBC连接池初始化成功: DSN={dsn}, 池大小={pool_size}") + # 预创建连接(如果不是延迟初始化) + if not lazy_init: + try: + for _ in range(pool_size): + conn = self._create_connection() + self._pool.put((conn, time.time())) + self._is_available = True + logger.info(f"ODBC连接池初始化成功: DSN={dsn}, 池大小={pool_size}") + except Exception as e: + logger.error(f"ODBC连接池初始化失败: {e}") + self._is_available = False + else: + logger.info(f"ODBC连接池延迟初始化: DSN={dsn}, 池大小={pool_size}") def _create_connection(self): """创建新连接""" @@ -75,9 +84,29 @@ class PyODBCConnectionPool: except: return False + def is_available(self) -> bool: + """检查连接池是否可用""" + return self._is_available + + def test_connection(self) -> bool: + """测试连接是否可用""" + try: + conn = self._create_connection() + conn.close() + self._is_available = True + return True + except Exception as e: + logger.error(f"ODBC连接测试失败: {e}") + self._is_available = False + return False + @contextmanager def get_connection(self): """获取连接(上下文管理器)""" + # 检查连接池是否可用 + if not self._is_available: + raise Exception("ODBC连接池不可用,请检查数据源配置") + conn = None created_time = None is_overflow = False @@ -150,6 +179,7 @@ class ERPODBCManager: _instance = None _pool = None + _initialized = False def __new__(cls): """单例模式""" @@ -159,22 +189,54 @@ class ERPODBCManager: def init_app(self, app): """初始化(在app启动时调用)""" + if self._initialized: + logger.warning("ERP ODBC管理器已初始化,跳过重复初始化") + return + config = app.config.get("ERP_ODBC_CONFIG", {}) - # 创建连接池 - self._pool = PyODBCConnectionPool( - dsn=config.get("dsn", "YHC-test"), - pool_size=config.get("pool_size", 5), - max_overflow=config.get("max_overflow", 10), - pool_timeout=config.get("pool_timeout", 30), - pool_recycle=config.get("pool_recycle", 3600), - ) + # 检查是否启用ODBC + if not config.get("enabled", True): + logger.info("ERP ODBC功能已禁用(配置项 enabled=False)") + self._pool = None + self._initialized = True + return - logger.info("ERP ODBC管理器初始化成功") + try: + # 创建连接池(延迟初始化) + self._pool = PyODBCConnectionPool( + dsn=config.get("dsn", "YHC-test"), + pool_size=config.get("pool_size", 5), + max_overflow=config.get("max_overflow", 10), + pool_timeout=config.get("pool_timeout", 30), + pool_recycle=config.get("pool_recycle", 3600), + lazy_init=True, # 延迟初始化,不预创建连接 + ) + + # 测试连接 + if self._pool.test_connection(): + logger.info("ERP ODBC管理器初始化成功,连接测试通过") + else: + logger.warning("ERP ODBC管理器初始化完成,但连接测试失败,ODBC功能将不可用") + + self._initialized = True + + except Exception as e: + logger.error(f"ERP ODBC管理器初始化失败: {e}", exc_info=True) + logger.warning("应用将继续启动,但ODBC功能将不可用") + self._pool = None + self._initialized = True + + def is_available(self) -> bool: + """检查ODBC连接是否可用""" + return self._pool is not None and self._pool.is_available() @contextmanager def get_connection(self): """获取连接(上下文管理器)""" + if not self.is_available(): + raise Exception("ODBC连接不可用,请检查数据源配置或联系管理员") + with self._pool.get_connection() as conn: yield conn @@ -188,7 +250,13 @@ class ERPODBCManager: Returns: 查询结果列表(字典格式) + + Raises: + Exception: 当ODBC连接不可用或查询失败时 """ + if not self.is_available(): + raise Exception("ODBC连接不可用,无法执行查询") + with self.get_connection() as conn: cursor = conn.cursor() try: @@ -224,7 +292,13 @@ class ERPODBCManager: Returns: 影响的行数 + + Raises: + Exception: 当ODBC连接不可用或执行失败时 """ + if not self.is_available(): + raise Exception("ODBC连接不可用,无法执行操作") + with self.get_connection() as conn: cursor = conn.cursor() try: diff --git a/iti/config.py b/iti/config.py index 33707ea..2aea596 100644 --- a/iti/config.py +++ b/iti/config.py @@ -219,6 +219,7 @@ class BaseConfig: # ERP ODBC配置 ERP_ODBC_CONFIG = { + "enabled": _get_bool_env("ERP_ODBC_ENABLED", True), # 是否启用ODBC "dsn": "YHC-test", # ODBC数据源名称 "pool_size": 5, # 连接池大小 "max_overflow": 10, # 最大溢出连接数