Files
ecm_sync_system/sync_state_machine/common/registry.py
T
2026-03-10 16:13:10 +08:00

291 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Domain 注册中心
提供统一的 domain 组件注册和查询接口。
各模块(Collection/DataSource/Strategy)按需获取。
使用方式:
1. 在 domain/__init__.py 中注册所有组件
2. 各模块按需查询
示例:
# 注册
from ..common.registry import DomainRegistry
from .contract import ContractSyncNode, ContractJsonlHandler
DomainRegistry.register(
node_type="contract",
schema=ContractResponse,
node_class=ContractSyncNode,
jsonl_handler_class=ContractJsonlHandler
)
# Collection 查询 SyncNode 类
node_class = DomainRegistry.get_node_class("contract")
# DataSource 查询 Handler
handler_class = DomainRegistry.get_jsonl_handler("contract")
"""
from typing import Dict, Type, Optional, Any
from dataclasses import dataclass, field
from pydantic import BaseModel
@dataclass
class DomainRegistration:
"""
单个 domain 的注册信息
包含:
- node_class: SyncNode 子类(用于 Collection 恢复)
- strategy_class: Strategy 子类(用于 SyncSystem 初始化)
- handlers: 各种 Handler(用于 DataSource 初始化)
"""
node_type: str
schema: Type[BaseModel]
node_class: Type[Any] # Type[SyncNode[T]],避免循环导入
strategy_class: Optional[Type[Any]] = None # 可选,部分 domain 可能只读
jsonl_handler_class: Optional[Type[Any]] = None
api_handler_class: Optional[Type[Any]] = None
db_handler_class: Optional[Type[Any]] = None
handler_classes: Dict[str, Type[Any]] = field(default_factory=dict)
metadata: Dict[str, Any] = field(default_factory=dict) # 额外元数据
class DomainRegistry:
"""
Domain 注册中心
职责分离:
- Collection: 只关心 SyncNode 类(恢复类型)
- DataSource: 只关心 Handler 类(执行层)
- Strategy: 只关心 Strategy 类(业务逻辑)
每个 domain 只注册一次,各模块按需获取。
"""
_registry: Dict[str, DomainRegistration] = {}
_BUILTIN_HANDLER_ATTRS: Dict[str, str] = {
"jsonl": "jsonl_handler_class",
"api": "api_handler_class",
"db": "db_handler_class",
}
@classmethod
def register(
cls,
node_type: str,
schema: Type[BaseModel],
node_class: Type[Any],
strategy_class: Optional[Type[Any]] = None,
jsonl_handler_class: Optional[Type[Any]] = None,
api_handler_class: Optional[Type[Any]] = None,
db_handler_class: Optional[Type[Any]] = None,
**metadata
) -> None:
"""
注册一个 domain
Args:
node_type: 节点类型(如 "contract"
schema: Pydantic Schema 类
node_class: SyncNode 子类
strategy_class: Strategy 子类(可选)
jsonl_handler_class: JSONL Handler 类(可选)
api_handler_class: API Handler 类(可选)
db_handler_class: DB Handler 类(可选)
**metadata: 额外的元数据
Raises:
ValueError: 如果 node_type 已注册
"""
if node_type in cls._registry:
raise ValueError(f"Domain '{node_type}' already registered")
reg = DomainRegistration(
node_type=node_type,
schema=schema,
node_class=node_class,
strategy_class=strategy_class,
jsonl_handler_class=jsonl_handler_class,
api_handler_class=api_handler_class,
db_handler_class=db_handler_class,
metadata=metadata
)
for datasource_type, attr_name in cls._BUILTIN_HANDLER_ATTRS.items():
handler_class = getattr(reg, attr_name)
if handler_class is not None:
reg.handler_classes[datasource_type] = handler_class
cls._registry[node_type] = reg
@classmethod
def get_node_class(cls, node_type: str) -> Type[Any]:
"""
获取 SyncNode 类(用于 Collection 恢复)
Args:
node_type: 节点类型
Returns:
SyncNode 子类
Raises:
ValueError: 如果 node_type 未注册
"""
reg = cls._registry.get(node_type)
if not reg:
raise ValueError(f"Unknown node_type: '{node_type}'. Available: {list(cls._registry.keys())}")
return reg.node_class
@classmethod
def get_strategy_class(cls, node_type: str) -> Optional[Type[Any]]:
"""
获取 Strategy 类(用于 SyncSystem
Args:
node_type: 节点类型
Returns:
Strategy 类,如果未注册则返回 None
"""
reg = cls._registry.get(node_type)
if not reg:
raise ValueError(f"Unknown node_type: '{node_type}'")
return reg.strategy_class
@classmethod
def get_handler(
cls,
node_type: str,
datasource_type: str,
) -> Optional[Type[Any]]:
"""按 datasource 类型获取 Handler 类。"""
reg = cls._registry.get(node_type)
if not reg:
raise ValueError(f"Unknown node_type: '{node_type}'")
handler_class = reg.handler_classes.get(datasource_type)
if handler_class is not None:
return handler_class
attr_name = cls._BUILTIN_HANDLER_ATTRS.get(datasource_type)
if attr_name is None:
return None
return getattr(reg, attr_name)
@classmethod
def register_handler(cls, node_type: str, datasource_type: str, handler_class: Type[Any]) -> None:
"""为已注册 domain 补充任意 datasource 类型的 Handler。"""
reg = cls._registry.get(node_type)
if not reg:
raise ValueError(f"Unknown node_type: '{node_type}'")
reg.handler_classes[datasource_type] = handler_class
attr_name = cls._BUILTIN_HANDLER_ATTRS.get(datasource_type)
if attr_name is not None:
setattr(reg, attr_name, handler_class)
@classmethod
def register_db_handler(cls, node_type: str, db_handler_class: Type[Any]) -> None:
"""为已注册 domain 补充 DB Handler。"""
cls.register_handler(node_type, "db", db_handler_class)
@classmethod
def get_schema(cls, node_type: str) -> Type[BaseModel]:
"""获取 Schema 类"""
reg = cls._registry.get(node_type)
if not reg:
raise ValueError(f"Unknown node_type: '{node_type}'")
return reg.schema
@classmethod
def get_registration(cls, node_type: str) -> DomainRegistration:
"""获取完整的注册信息"""
reg = cls._registry.get(node_type)
if not reg:
raise ValueError(f"Unknown node_type: '{node_type}'")
return reg
@classmethod
def is_registered(cls, node_type: str) -> bool:
"""检查 node_type 是否已注册"""
return node_type in cls._registry
@classmethod
def list_registered(cls) -> list[str]:
"""列出所有已注册的 node_type"""
return list(cls._registry.keys())
@classmethod
def get_registered_types(cls) -> list[str]:
"""列出所有已注册的 node_typelist_registered 的别名)"""
return cls.list_registered()
@classmethod
def clear(cls) -> None:
"""清空注册表(仅用于测试)"""
cls._registry.clear()
@classmethod
def print_registry(cls) -> None:
"""打印所有注册的 domain 信息(用于调试)"""
if not cls._registry:
print("⚠️ DomainRegistry is empty - no domains registered")
return
print(f"\n{'='*80}")
print(f"📋 DomainRegistry - {len(cls._registry)} domain(s) registered")
print(f"{'='*80}\n")
for node_type, reg in sorted(cls._registry.items()):
print(f"🔹 {node_type}")
print(f" Schema: {reg.schema.__name__}")
print(f" Node Class: {reg.node_class.__name__}")
print(f" Strategy Class: {reg.strategy_class.__name__ if reg.strategy_class else '❌ None'}")
print(f" JSONL Handler: {reg.jsonl_handler_class.__name__ if reg.jsonl_handler_class else '❌ None'}")
print(f" API Handler: {reg.api_handler_class.__name__ if reg.api_handler_class else '❌ None'}")
print(f" DB Handler: {reg.db_handler_class.__name__ if reg.db_handler_class else '❌ None'}")
extra_handler_types = sorted(
handler_type for handler_type in reg.handler_classes if handler_type not in cls._BUILTIN_HANDLER_ATTRS
)
if extra_handler_types:
print(f" Extra Handlers: {extra_handler_types}")
if reg.metadata:
print(f" Metadata: {reg.metadata}")
print()
print(f"{'='*80}\n")
@classmethod
def get_summary(cls) -> Dict[str, Dict[str, Any]]:
"""
获取注册摘要(用于自动化检查)
Returns:
字典,key 为 node_typevalue 为各组件的注册状态
Example:
{
"contract": {
"has_node": True,
"has_strategy": True,
"has_jsonl_handler": True,
"has_api_handler": True,
"has_db_handler": False
},
...
}
"""
summary = {}
for node_type, reg in cls._registry.items():
summary[node_type] = {
"has_node": reg.node_class is not None,
"has_strategy": reg.strategy_class is not None,
"has_jsonl_handler": reg.jsonl_handler_class is not None,
"has_api_handler": reg.api_handler_class is not None,
"has_db_handler": reg.db_handler_class is not None,
"handler_types": sorted(reg.handler_classes.keys()),
}
return summary