"""下载器、媒体服务器和通知服务的应用层目录端口。""" from __future__ import annotations from collections.abc import Callable, Iterator from typing import Any, Dict, Generic, List, Optional, Type, TypeVar from app.schemas.system import ServiceInfo from app.schemas.types import ModuleType, SystemConfigKey TConf = TypeVar("TConf") ServiceConfigLoader = Callable[[SystemConfigKey, Type[Any]], list[Any]] RunningModuleLoader = Callable[[ModuleType], list[Any]] def _unconfigured_configs( _config_key: SystemConfigKey, _conf_type: Type[Any], ) -> list[Any]: """拒绝在启动组合根装配前隐式读取服务配置。""" raise RuntimeError("服务配置目录尚未由启动组合根配置") def _unconfigured_modules(_module_type: ModuleType) -> list[Any]: """拒绝在启动组合根装配前隐式抓取模块管理器。""" raise RuntimeError("运行模块目录尚未由启动组合根配置") _config_loader: ServiceConfigLoader = _unconfigured_configs _module_loader: RunningModuleLoader = _unconfigured_modules def configure_service_directory( *, configs: ServiceConfigLoader, modules: RunningModuleLoader, ) -> None: """由启动组合根注入服务配置和运行模块枚举端口。""" global _config_loader, _module_loader _config_loader = configs _module_loader = modules class ServiceBaseHelper(Generic[TConf]): """通过应用端口查询服务配置和对应运行实例。""" def __init__( self, config_key: SystemConfigKey, conf_type: Type[TConf], module_type: ModuleType, ) -> None: """绑定配置类型和模块能力类型,不抓取具体 Runtime 管理器。""" self.config_key = config_key self.conf_type = conf_type self.module_type = module_type def get_configs(self, include_disabled: bool = False) -> Dict[str, TConf]: """返回按名称索引的有效服务配置。""" configs = _config_loader(self.config_key, self.conf_type) return { config.name: config for config in configs if config.name and config.type and (config.enabled or include_disabled) } def get_config(self, name: str) -> Optional[TConf]: """按名称返回单个启用服务配置。""" return self.get_configs().get(name) if name else None def iterate_module_instances(self) -> Iterator[ServiceInfo]: """迭代当前类型所有运行模块实例及其配置投影。""" configs = self.get_configs() for module in _module_loader(self.module_type): if not module: continue instances = module.get_instances() if not isinstance(instances, dict): continue for name, instance in instances.items(): if not instance: continue config = configs.get(name) yield ServiceInfo( name=name, instance=instance, module=module, type=config.type if config else None, config=config, ) def get_services( self, type_filter: Optional[str] = None, name_filters: Optional[List[str]] = None, ) -> Dict[str, ServiceInfo]: """按服务类型和名称集合过滤运行实例。""" names = set(name_filters) if name_filters else None return { service.name: service for service in self.iterate_module_instances() if service.config and (type_filter is None or service.type == type_filter) and (names is None or service.name in names) } def get_service( self, name: str, type_filter: Optional[str] = None, ) -> Optional[ServiceInfo]: """按名称和可选类型返回单个运行服务。""" if not name: return None for service in self.iterate_module_instances(): if ( service.name == name and service.config and (type_filter is None or service.type == type_filter) ): return service return None