system_config_manager.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. """
  2. 系统配置管理器
  3. 提供全局配置读取功能,支持缓存和默认值回退
  4. 业务代码通过此模块读取数据库中的系统配置
  5. """
  6. import json
  7. import logging
  8. from typing import Any, Optional, Dict
  9. from datetime import datetime, timedelta
  10. from threading import Lock
  11. from sqlalchemy.orm import Session
  12. from app.database import SessionLocal
  13. from app.models.config import SystemConfig
  14. logger = logging.getLogger(__name__)
  15. class SystemConfigManager:
  16. """系统配置管理器(单例模式)"""
  17. _instance = None
  18. _lock = Lock()
  19. # 配置缓存
  20. _cache: Dict[str, Any] = {}
  21. _cache_time: Dict[str, datetime] = {}
  22. _cache_ttl = timedelta(minutes=5) # 缓存5分钟
  23. # 默认值定义
  24. DEFAULT_VALUES = {
  25. # 基础配置
  26. "system_name": "智创空间",
  27. "system_logo": "",
  28. "contact_email": "",
  29. "icp_number": "",
  30. # 功能开关
  31. "enable_registration": True,
  32. "enable_search": True,
  33. "enable_verification_reminder": True,
  34. "enable_local_models": True,
  35. "enable_openclaw": True,
  36. "enable_openclaw_client": True,
  37. "enable_openclaw_web": True,
  38. "openclaw_client_url": "",
  39. # 限制配置
  40. "max_tokens_per_request": 4000,
  41. "max_images_per_request": 4,
  42. "max_audio_chars": 5000,
  43. "max_video_duration": 10,
  44. # 第三方配置(敏感,默认为空)
  45. "dashscope_api_key": "",
  46. "alipay_app_id": "",
  47. "oss_bucket": "",
  48. "oss_endpoint": "",
  49. }
  50. def __new__(cls):
  51. if cls._instance is None:
  52. with cls._lock:
  53. if cls._instance is None:
  54. cls._instance = super().__new__(cls)
  55. return cls._instance
  56. def _get_db(self) -> Session:
  57. """获取数据库会话"""
  58. return SessionLocal()
  59. def _parse_value(self, value_str: str, value_type: str) -> Any:
  60. """解析配置值"""
  61. try:
  62. return json.loads(value_str)
  63. except (json.JSONDecodeError, TypeError):
  64. return value_str
  65. def get(self, key: str, default: Any = None) -> Any:
  66. """
  67. 获取配置值
  68. 优先级:Redis缓存 > 本地内存缓存 > 数据库 > 默认值
  69. Redis 不可用时自动降级到内存缓存。
  70. """
  71. # 1. 检查本地内存缓存(最快)
  72. if key in self._cache:
  73. cache_time = self._cache_time.get(key)
  74. if cache_time and datetime.now() - cache_time < self._cache_ttl:
  75. return self._cache[key]
  76. # 2. 检查 Redis 缓存
  77. try:
  78. from app.core.redis import redis_manager
  79. r = redis_manager.get_sync_client()
  80. if r:
  81. raw = r.get(f"sysconfig:{key}")
  82. if raw:
  83. import json as _json
  84. value = _json.loads(raw)
  85. # 同步到本地内存缓存
  86. self._cache[key] = value
  87. self._cache_time[key] = datetime.now()
  88. return value
  89. except Exception:
  90. pass
  91. # 3. 从数据库读取
  92. db = self._get_db()
  93. try:
  94. config = db.query(SystemConfig).filter(
  95. SystemConfig.config_key == key
  96. ).first()
  97. if config:
  98. value = self._parse_value(config.config_value, config.config_type)
  99. # 写入本地内存缓存
  100. self._cache[key] = value
  101. self._cache_time[key] = datetime.now()
  102. # 写入 Redis 缓存(TTL 5 分钟)
  103. try:
  104. from app.core.redis import redis_manager
  105. import json as _json
  106. r = redis_manager.get_sync_client()
  107. if r:
  108. r.setex(f"sysconfig:{key}", 300, _json.dumps(value))
  109. except Exception:
  110. pass
  111. return value
  112. except Exception as e:
  113. logger.warning(f"读取配置 {key} 失败: {e}")
  114. finally:
  115. db.close()
  116. # 4. 返回默认值
  117. if default is not None:
  118. return default
  119. return self.DEFAULT_VALUES.get(key)
  120. def get_string(self, key: str, default: str = "") -> str:
  121. """获取字符串配置"""
  122. value = self.get(key, default)
  123. return str(value) if value is not None else default
  124. def get_int(self, key: str, default: int = 0) -> int:
  125. """获取整数配置"""
  126. value = self.get(key, default)
  127. try:
  128. return int(value)
  129. except (ValueError, TypeError):
  130. return default
  131. def get_float(self, key: str, default: float = 0.0) -> float:
  132. """获取浮点数配置"""
  133. value = self.get(key, default)
  134. try:
  135. return float(value)
  136. except (ValueError, TypeError):
  137. return default
  138. def get_bool(self, key: str, default: bool = False) -> bool:
  139. """获取布尔配置"""
  140. value = self.get(key, default)
  141. if isinstance(value, bool):
  142. return value
  143. if isinstance(value, str):
  144. return value.lower() in ('true', '1', 'yes')
  145. return bool(value)
  146. def clear_cache(self, key: Optional[str] = None):
  147. """清除缓存(内存 + Redis)"""
  148. if key:
  149. self._cache.pop(key, None)
  150. self._cache_time.pop(key, None)
  151. try:
  152. from app.core.redis import redis_manager
  153. r = redis_manager.get_sync_client()
  154. if r:
  155. r.delete(f"sysconfig:{key}")
  156. except Exception:
  157. pass
  158. else:
  159. self._cache.clear()
  160. self._cache_time.clear()
  161. try:
  162. from app.core.redis import redis_manager
  163. r = redis_manager.get_sync_client()
  164. if r:
  165. # 删除所有 sysconfig: 前缀的 key
  166. keys = r.keys("sysconfig:*")
  167. if keys:
  168. r.delete(*keys)
  169. except Exception:
  170. pass
  171. def refresh(self, key: str) -> Any:
  172. """强制刷新配置"""
  173. self.clear_cache(key)
  174. return self.get(key)
  175. # 全局单例实例
  176. config_manager = SystemConfigManager()
  177. # 便捷函数
  178. def get_config(key: str, default: Any = None) -> Any:
  179. """获取配置值"""
  180. return config_manager.get(key, default)
  181. def get_config_string(key: str, default: str = "") -> str:
  182. """获取字符串配置"""
  183. return config_manager.get_string(key, default)
  184. def get_config_int(key: str, default: int = 0) -> int:
  185. """获取整数配置"""
  186. return config_manager.get_int(key, default)
  187. def get_config_float(key: str, default: float = 0.0) -> float:
  188. """获取浮点数配置"""
  189. return config_manager.get_float(key, default)
  190. def get_config_bool(key: str, default: bool = False) -> bool:
  191. """获取布尔配置"""
  192. return config_manager.get_bool(key, default)
  193. def clear_config_cache(key: Optional[str] = None):
  194. """清除配置缓存"""
  195. config_manager.clear_cache(key)