ForcePilot/backend/package/yuxi/external_systems/integrations/operation_registry.py
Kris 830a3c1458 refactor: 完成多模块代码优化与功能迭代
本次提交包含多项核心改进:
1. 统一列表查询分页范式,替换page/page_size为limit/offset标准分页
2. 新增多项仓库方法与服务接口,完善统计与测试能力
3. 优化审计日志事务处理逻辑,保证业务与审计数据原子性
4. 统一适配器类型与源类型排序契约,添加排序注释说明
5. 完善通知渠道、健康检查、告警等模块的DTO与实体字段
6. 优化测试用例执行回调,透传调用方标识
7. 修复资产导入时的校验逻辑,重新计算size与checksum
8. 拆分UpsertQuotaInput的审计字段,区分创建人与修改人
2026-07-11 06:55:19 +08:00

129 lines
5.5 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.

"""IntegrationOperationRegistry厂商操作分派注册表。
实现 ``core/contracts.py::IntegrationOperationRegistry`` 端口(结构化子类型)。
按 ``(operation, source_type)`` 二元组分派 handler。支持 discover / preview_tools /
create_tools 三类操作。
handler 签名(``OperationHandler````(source_type: str, system_config: dict[str, Any]) -> Awaitable[Any]``
2 参数纯 dict不依赖 ORM/DB。Token/解密/DB 由 use_cases 层预处理后注入 ``system_config``。
source_type 解析职责由 use_cases 层完成(端口注释 L842-L844本注册表只接收
已解析的 source_type。草稿持久化职责由 use_cases 层统一处理(端口注释 L846-L847
键冲突策略:同一 ``(operation, source_type)`` 重复注册且 handler 不同时抛
``DomainValidationError``,与 framework 层注册表策略一致。
"""
from __future__ import annotations
from collections.abc import Awaitable, Callable
from typing import Any
from yuxi.external_systems.exceptions import (
DomainValidationError,
IntegrationOperationNotRegisteredError,
)
from yuxi.external_systems.integrations.schemas import GeneratedToolsDraft
# handler 类型别名与端口签名一致2 参数 + Awaitable 返回
OperationHandler = Callable[
[str, dict[str, Any]],
Awaitable[Any],
]
# 允许的 operation 集合
_ALLOWED_OPERATIONS = frozenset({"discover", "preview_tools", "create_tools"})
class IntegrationOperationRegistry:
"""厂商操作分派注册表,实现 core/contracts.py::IntegrationOperationRegistry 端口。
实现 Protocol 的 3 个分派方法discover / preview_tools / create_tools
并额外提供 register / get / list_operations / list_source_types / clear
供框架内部使用(与 AdapterRegistry 模式一致)。
"""
_handlers: dict[tuple[str, str], OperationHandler] = {}
@classmethod
def register(cls, operation: str, source_type: str, handler: OperationHandler) -> None:
"""注册 (operation, source_type) 对应的 handler。
键冲突抛 DomainValidationError。
operation 限定为 discover / preview_tools / create_tools 三者之一。
"""
if operation not in _ALLOWED_OPERATIONS:
raise DomainValidationError(f"不支持的集成操作: {operation},仅允许 {_ALLOWED_OPERATIONS}")
key = (operation, source_type)
existing = cls._handlers.get(key)
if existing is not None and existing is not handler:
raise DomainValidationError(
f"集成操作 handler 冲突: (operation={operation}, source_type={source_type}) "
f"已注册 {getattr(existing, '__name__', existing)}"
f"尝试注册 {getattr(handler, '__name__', handler)}"
)
cls._handlers[key] = handler
@classmethod
def get(cls, operation: str, source_type: str) -> OperationHandler:
"""获取 handler未注册抛 IntegrationOperationNotRegisteredError。"""
handler = cls._handlers.get((operation, source_type))
if handler is None:
raise IntegrationOperationNotRegisteredError(
f"集成操作未注册: (operation={operation}, source_type={source_type})"
)
return handler
@classmethod
async def discover(cls, source_type: str, system_config: dict[str, Any]) -> list[dict[str, Any]]:
"""分派 discover 操作(实现端口)。"""
handler = cls.get("discover", source_type)
return await handler(source_type, system_config)
@classmethod
async def preview_tools(cls, source_type: str, system_config: dict[str, Any]) -> list[dict[str, Any]]:
"""分派 preview_tools 操作(实现端口)。"""
handler = cls.get("preview_tools", source_type)
return await handler(source_type, system_config)
@classmethod
async def create_tools(cls, source_type: str, system_config: dict[str, Any]) -> GeneratedToolsDraft:
"""分派 create_tools 操作(实现端口)。
返回 GeneratedToolsDraft由 use_cases 层统一持久化。
"""
handler = cls.get("create_tools", source_type)
result = await handler(source_type, system_config)
if isinstance(result, GeneratedToolsDraft):
return result
# 容错handler 返回 dict 时构造 GeneratedToolsDraft便于渐进迁移
if isinstance(result, dict):
return GeneratedToolsDraft.model_validate(result)
raise DomainValidationError(
f"create_tools handler 返回类型非法: 期望 GeneratedToolsDraft / dict实际 {type(result).__name__}"
)
@classmethod
def list_operations(cls, source_type: str) -> list[str]:
"""列出指定 source_type 已注册的操作(供调试与前端展示)。"""
return sorted(op for (op, st) in cls._handlers if st == source_type)
@classmethod
def list_source_types(cls) -> list[str]:
"""列出所有已注册 handler 涉及的 source_type去重按字母升序排序
与 ``list_operations`` 排序契约一致:所有 list_* 方法均返回排序后的
结果,调用方无需重复排序。
"""
seen: dict[str, None] = {}
for _op, st in cls._handlers:
if st not in seen:
seen[st] = None
return sorted(seen.keys())
@classmethod
def clear(cls) -> None:
"""清空注册表,仅供测试使用。"""
cls._handlers.clear()