"""配置管理服务 - 对应Go的internal/service/config.go""" import platform import psutil import threading from datetime import datetime, timedelta from typing import Optional, List, Callable, Dict, Any from app.models import ( ConfigListRequest, ConfigListData, ConfigSection, ConfigItem, ConfigUpdateRequest, ConfigUpdateData, ConfigType, ReloadRequest, ReloadData, SystemStatusData, MemoryInfo ) from app.core.config import get_config, reload_config, save_config, Config from app.core.logger import info class ConfigService: """配置管理服务""" def __init__(self): self.config = get_config() self.start_time = datetime.now() self.version = "1.0.0" self.callbacks: Dict[ConfigType, List[Callable]] = {} self.lock = threading.RLock() def get_config_list(self, req: ConfigListRequest) -> ConfigListData: """获取配置列表""" sections = [] # 服务器配置 if not req.type or req.type == ConfigType.SERVER: sections.append(ConfigSection( name="服务器配置", type=ConfigType.SERVER, description="HTTP服务器相关配置", items=[ ConfigItem( key="port", value=self.config.server.port, type="int", description="服务端口", editable=True, required=True ), ConfigItem( key="mode", value=self.config.server.mode, type="string", description="运行模式: debug/release", editable=True, required=True ), ConfigItem( key="api_key", value=self.config.server.api_key, type="string", description="API认证密钥", editable=True, required=True ), ] )) # 数据库配置 if not req.type or req.type == ConfigType.DATABASE: sections.append(ConfigSection( name="数据库配置", type=ConfigType.DATABASE, description="PostgreSQL数据库连接配置", items=[ ConfigItem( key="host", value=self.config.database.host, type="string", description="数据库主机地址", editable=True, required=True ), ConfigItem( key="port", value=self.config.database.port, type="int", description="数据库端口", editable=True, required=True ), ConfigItem( key="user", value=self.config.database.user, type="string", description="数据库用户名", editable=True, required=True ), ConfigItem( key="password", value="********", type="password", description="数据库密码", editable=True, required=True ), ConfigItem( key="database", value=self.config.database.database, type="string", description="数据库名", editable=True, required=True ), ] )) # Redis配置 if not req.type or req.type == ConfigType.REDIS: sections.append(ConfigSection( name="Redis配置", type=ConfigType.REDIS, description="Redis缓存配置", items=[ ConfigItem( key="host", value=self.config.redis.host, type="string", description="Redis主机地址", editable=True, required=False ), ConfigItem( key="port", value=self.config.redis.port, type="int", description="Redis端口", editable=True, required=False ), ConfigItem( key="password", value="********", type="password", description="Redis密码", editable=True, required=False ), ConfigItem( key="db", value=self.config.redis.db, type="int", description="Redis数据库编号", editable=True, required=False ), ] )) # 数据源配置 if not req.type or req.type == ConfigType.SOURCE: sections.append(ConfigSection( name="数据源配置", type=ConfigType.SOURCE, description="股票和期货数据源配置", items=[ ConfigItem( key="stock_active", value=self.config.sources.stock.active, type="string", description="股票数据源适配器", editable=True, required=True ), ConfigItem( key="futures_active", value=self.config.sources.futures.active, type="string", description="期货数据源适配器", editable=True, required=True ), ] )) return ConfigListData( sections=sections, version=self.version, updated=datetime.now() ) def update_config(self, req: ConfigUpdateRequest) -> ConfigUpdateData: """更新配置""" need_restart = False with self.lock: if req.type == ConfigType.SERVER: if "port" in req.items: self.config.server.port = int(req.items["port"]) need_restart = True if "mode" in req.items: self.config.server.mode = req.items["mode"] if "api_key" in req.items: self.config.server.api_key = req.items["api_key"] elif req.type == ConfigType.DATABASE: if "host" in req.items: self.config.database.host = req.items["host"] need_restart = True if "port" in req.items: self.config.database.port = int(req.items["port"]) need_restart = True if "user" in req.items: self.config.database.user = req.items["user"] need_restart = True if "password" in req.items: password = req.items["password"] if password != "********": self.config.database.password = password need_restart = True if "database" in req.items: self.config.database.database = req.items["database"] need_restart = True elif req.type == ConfigType.SOURCE: if "stock_active" in req.items: self.config.sources.stock.active = req.items["stock_active"] if "futures_active" in req.items: self.config.sources.futures.active = req.items["futures_active"] # 保存到文件 try: save_config(self.config) self._trigger_callbacks(req.type) message = "配置更新成功" if need_restart: message += ",部分配置需要重启服务后生效" return ConfigUpdateData( success=True, need_restart=need_restart, message=message ) except Exception as e: return ConfigUpdateData( success=False, need_restart=False, message=f"配置保存失败: {e}" ) def reload_config(self, req: ReloadRequest) -> ReloadData: """热加载配置""" try: with self.lock: new_config = reload_config() # 根据类型选择性更新 if req.config_type is None: self.config = new_config else: if req.config_type == ConfigType.SERVER: self.config.server = new_config.server elif req.config_type == ConfigType.DATABASE: self.config.database = new_config.database elif req.config_type == ConfigType.REDIS: self.config.redis = new_config.redis elif req.config_type == ConfigType.SOURCE: self.config.sources = new_config.sources self._trigger_callbacks(req.config_type) return ReloadData( success=True, message="配置热加载成功" ) except Exception as e: return ReloadData( success=False, message=f"加载配置失败: {e}" ) def get_system_status(self) -> SystemStatusData: """获取系统状态""" # 获取内存信息 mem = psutil.virtual_memory() # 计算运行时长 uptime = datetime.now() - self.start_time uptime_str = self._format_duration(uptime) return SystemStatusData( status="running", version=self.version, start_time=self.start_time, uptime=uptime_str, python_version=platform.python_version(), memory=MemoryInfo( alloc=mem.used, total_alloc=mem.total, sys=mem.total, num_gc=0 # Python不需要显式GC计数 ), threads=threading.active_count() ) def _format_duration(self, d: timedelta) -> str: """格式化持续时间""" days = d.days hours, remainder = divmod(d.seconds, 3600) minutes, _ = divmod(remainder, 60) if days > 0: return f"{days}天{hours}小时{minutes}分钟" if hours > 0: return f"{hours}小时{minutes}分钟" return f"{minutes}分钟" def register_callback(self, config_type: ConfigType, callback: Callable): """注册配置变更回调""" with self.lock: if config_type not in self.callbacks: self.callbacks[config_type] = [] self.callbacks[config_type].append(callback) def _trigger_callbacks(self, config_type: Optional[ConfigType]): """触发回调""" with self.lock: # 触发特定类型的回调 if config_type and config_type in self.callbacks: for cb in self.callbacks[config_type]: try: cb() except Exception as e: info(f"Callback error: {e}")