import asyncio from typing import Dict, Optional import os import time from sqlalchemy import select, update, insert from sqlalchemy.ext.asyncio import AsyncSession from utils.logger import session_logger from pydantic import BaseModel from database.models import DeviceConfig as DBDeviceConfig from services.database_service_base import DatabaseServiceBase class DeviceConfig(BaseModel): selected_role_key: str preferred_language: Optional[str] = None last_update_time: float = None def __init__(self, **data): if 'last_update_time' not in data: data['last_update_time'] = time.time() super().__init__(**data) def to_dict(self): """将配置转换为可序列化的字典""" return { "selected_role_key": self.selected_role_key, "preferred_language": self.preferred_language, } @classmethod def from_db_model(cls, db_model: DBDeviceConfig): """从数据库模型创建配置对象""" return cls( selected_role_key=db_model.selected_role_key, preferred_language=db_model.preferred_language, last_update_time=db_model.last_update_time ) class DeviceConfigManager(DatabaseServiceBase): def __init__(self): super().__init__(service_name="device_config") self.device_configs: Dict[str, DeviceConfig] = {} self.lock = asyncio.Lock() self.worker_id = os.environ.get("UVICORN_WID", "0") self.config_last_updated = {} async def _is_config_updated(self, device_id: str, async_session: AsyncSession) -> bool: """检查配置是否已在数据库中更新""" try: query = select(DBDeviceConfig.last_update_time).where(DBDeviceConfig.device_id == device_id) result = await async_session.execute(query) db_last_updated = result.scalar_one_or_none() if db_last_updated is None: return False local_last_updated = self.config_last_updated.get(device_id, 0) return db_last_updated > local_last_updated except Exception as e: session_logger.error(device_id, "config", f"检查配置更新失败: {str(e)}") return False async def _load_config_from_db(self, device_id: str, async_session: AsyncSession): """从数据库加载设备配置""" try: query = select(DBDeviceConfig).where(DBDeviceConfig.device_id == device_id) result = await async_session.execute(query) db_config = result.scalar_one_or_none() if db_config: device_config = DeviceConfig.from_db_model(db_config) self.config_last_updated[device_id] = db_config.last_update_time async with self.lock: self.device_configs[device_id] = device_config session_logger.info( device_id, "config", f"Worker {self.worker_id}: 从数据库加载设备配置,角色: {device_config.selected_role_key}, 语言: {device_config.preferred_language or '未设置'}" ) return device_config return None except Exception as e: session_logger.error(device_id, "config", f"从数据库加载配置失败: {str(e)}") return None async def _save_config_to_db(self, device_id: str, config: DeviceConfig, async_session: AsyncSession): """保存设备配置到数据库""" try: query = select(DBDeviceConfig).where(DBDeviceConfig.device_id == device_id) result = await async_session.execute(query) existing_config = result.scalar_one_or_none() current_time = time.time() if existing_config: stmt = update(DBDeviceConfig).where( DBDeviceConfig.device_id == device_id ).values( selected_role_key=config.selected_role_key, preferred_language=config.preferred_language, last_update_time=current_time ) else: stmt = insert(DBDeviceConfig).values( device_id=device_id, selected_role_key=config.selected_role_key, preferred_language=config.preferred_language, last_update_time=current_time ) await async_session.execute(stmt) await async_session.commit() self.config_last_updated[device_id] = current_time session_logger.info( device_id, "config", f"Worker {self.worker_id}: 设备配置已保存到数据库" ) except Exception as e: await async_session.rollback() session_logger.error( device_id, "config", f"Worker {self.worker_id}: 保存设备配置到数据库失败: {str(e)}" ) raise async def get_config(self, device_id: str, force_refresh: bool = False) -> DeviceConfig: """获取设备配置,如果force_refresh=True则强制从数据库读取""" await self._init_database() config = None config_updated = False db_session = await self.db_manager.get_session() try: if force_refresh or await self._is_config_updated(device_id, db_session): config = await self._load_config_from_db(device_id, db_session) config_updated = True if not config_updated: async with self.lock: config = self.device_configs.get(device_id) if not config: from config import settings default_language = "en" config = DeviceConfig( selected_role_key=settings.selected_role_key, preferred_language=default_language ) session_logger.info( device_id, "config", f"Worker {self.worker_id}: 为新设备创建配置,默认角色: {settings.selected_role_key},默认语言: {default_language}" ) await self.set_config(device_id, config) elif config_updated: session_logger.info( device_id, "config", f"Worker {self.worker_id}: 已从数据库刷新设备配置,当前角色: {config.selected_role_key}, 语言: {config.preferred_language or '未设置'}" ) return config except Exception as e: session_logger.error(device_id, "config", f"获取设备配置失败: {str(e)}") raise finally: await db_session.close() async def set_config(self, device_id: str, config: DeviceConfig): """设置并保存设备配置""" await self._init_database() config.last_update_time = time.time() db_session = await self.db_manager.get_session() try: async with self.lock: old_config = self.device_configs.get(device_id) self.device_configs[device_id] = config if old_config: changes = [] if old_config.selected_role_key != config.selected_role_key: changes.append(f"角色: {old_config.selected_role_key} -> {config.selected_role_key}") if old_config.preferred_language != config.preferred_language: changes.append(f"语言: {old_config.preferred_language or '未设置'} -> {config.preferred_language or '未设置'}") if changes: session_logger.info(device_id, "config", f"设备配置已更新: {', '.join(changes)}") await self._save_config_to_db(device_id, config, db_session) except Exception as e: session_logger.error(device_id, "config", f"设置设备配置失败: {str(e)}") raise finally: await db_session.close() device_config_manager = DeviceConfigManager()