268 lines
9.0 KiB
Python
268 lines
9.0 KiB
Python
"""
|
||
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, Literal
|
||
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
|
||
metadata: Dict[str, Any] = field(default_factory=dict) # 额外元数据
|
||
|
||
|
||
class DomainRegistry:
|
||
"""
|
||
Domain 注册中心
|
||
|
||
职责分离:
|
||
- Collection: 只关心 SyncNode 类(恢复类型)
|
||
- DataSource: 只关心 Handler 类(执行层)
|
||
- Strategy: 只关心 Strategy 类(业务逻辑)
|
||
|
||
每个 domain 只注册一次,各模块按需获取。
|
||
"""
|
||
|
||
_registry: Dict[str, DomainRegistration] = {}
|
||
|
||
_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")
|
||
|
||
cls._registry[node_type] = 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
|
||
)
|
||
|
||
@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: Literal["jsonl", "api", "db"],
|
||
) -> Optional[Type[Any]]:
|
||
"""按 datasource 类型获取 Handler 类。"""
|
||
reg = cls._registry.get(node_type)
|
||
if not reg:
|
||
raise ValueError(f"Unknown node_type: '{node_type}'")
|
||
attr_name = cls._HANDLER_ATTRS.get(datasource_type)
|
||
if attr_name is None:
|
||
raise ValueError(
|
||
f"Unsupported datasource_type: '{datasource_type}'. "
|
||
f"Available: {sorted(cls._HANDLER_ATTRS.keys())}"
|
||
)
|
||
return getattr(reg, attr_name)
|
||
|
||
@classmethod
|
||
def register_db_handler(cls, node_type: str, db_handler_class: Type[Any]) -> None:
|
||
"""为已注册 domain 补充 DB Handler。"""
|
||
reg = cls._registry.get(node_type)
|
||
if not reg:
|
||
raise ValueError(f"Unknown node_type: '{node_type}'")
|
||
reg.db_handler_class = 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_type(list_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'}")
|
||
if reg.metadata:
|
||
print(f" Metadata: {reg.metadata}")
|
||
print()
|
||
|
||
print(f"{'='*80}\n")
|
||
|
||
@classmethod
|
||
def get_summary(cls) -> Dict[str, Dict[str, bool]]:
|
||
"""
|
||
获取注册摘要(用于自动化检查)
|
||
|
||
Returns:
|
||
字典,key 为 node_type,value 为各组件的注册状态
|
||
|
||
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,
|
||
}
|
||
return summary
|