ForcePilot/backend/server/routers/external_systems/integration_tool_router.py
Kris ba798a9bb5 feat(routers): add external systems management module and its sub routers
新增外部系统限界上下文的完整路由体系,包含聚合路由层与23个子领域路由,覆盖外部系统全生命周期管理、健康检查、指标监控、审计日志、Token管理、通知渠道、回收站等功能,并将外部系统路由挂载到全局API前缀下。
2026-06-20 22:16:07 +08:00

203 lines
7.8 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.

"""外部系统限界上下文 - IntegrationTool 子域 Router。
挂载到聚合 router 的 /integration-tools 前缀下覆盖厂商集成PRD FR-07/08
的工具生成流程编排list / discover / preview / generate / persist
Request Schema 与 Input DTO 不共享类Router 内显式构造 DTO
操作人字段由 current_user.uid 填充。
静态路径优先:/discover / /preview / /generate 必须在 POST ""persist之前声明
避免被空路径匹配。
discover / preview / generate 三个端点请求体字段完全一致,共用
``IntegrationToolFlowRequest``,对应 use_cases 层 ``IntegrationToolFlowInput``。
操作语义由端点路径与服务方法名区分。
"""
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy.ext.asyncio import AsyncSession
from yuxi.external_systems.infrastructure.container import create_use_cases_from_db
from yuxi.external_systems.use_cases.dto.tool import (
ExternalToolCreateInput,
IntegrationToolFlowInput,
ListToolsInput,
PersistGeneratedToolsInput,
)
from yuxi.storage.postgres.models_business import User
from server.utils.auth_middleware import get_admin_user, get_db, get_required_user
integration_tool_router = APIRouter(
prefix="/integration-tools",
tags=["external-systems-integration-tool"],
)
# =============================================================================
# === Request Schemas与 Input DTO 不共享类) ===
# =============================================================================
class ExternalToolItemRequest(BaseModel):
"""外部工具项请求体。字段对齐 ``ExternalToolCreateInput``。
用于持久化集成生成工具(``PersistIntegrationToolsRequest.tools``)。
"""
model_config = ConfigDict(frozen=True)
slug: str = Field(..., min_length=1, max_length=128, pattern=r"^[a-zA-Z_][a-zA-Z0-9_-]{0,127}$")
name: str = Field(..., min_length=1, max_length=128)
description: str = Field(..., min_length=1)
category: str = Field(default="default", max_length=64)
adapter_type: str = Field(default="http", max_length=32)
enabled: bool = True
timeout: int = Field(default=30, ge=1, le=300)
retry_policy: dict[str, Any] = Field(default_factory=dict)
auth_type: str = Field(default="none", max_length=32)
auth_config: dict[str, Any] = Field(default_factory=dict)
adapter_config: dict[str, Any] = Field(default_factory=dict)
class PersistIntegrationToolsRequest(BaseModel):
"""持久化集成生成工具请求体。字段对齐 ``PersistGeneratedToolsInput``(不含 created_by"""
model_config = ConfigDict(frozen=True)
system_id: int
tools: list[ExternalToolItemRequest] = Field(..., min_length=1)
class IntegrationToolFlowRequest(BaseModel):
"""集成工具生成流程请求体discover / preview / generate 共用)。
字段对齐 ``IntegrationToolFlowInput``。三个操作的请求参数完全一致,
共用一个 Schema操作语义由端点路径区分。
``source_type`` 为空时由 use_cases 层按 ``system.source_type`` 兜底解析。
``env_key`` 为空时由 use_cases 层解析系统默认环境。
``discovery_options`` 为用户选择,透传到 ``system_config``,各厂商字段名不同。
"""
model_config = ConfigDict(frozen=True)
system_id: int
source_type: str | None = None
env_key: str | None = None
discovery_options: dict[str, Any] | None = None
# =============================================================================
# === Endpoints ===
# =============================================================================
@integration_tool_router.get("", response_model=dict)
async def list_generated_tools(
limit: int = Query(100, ge=1, le=500),
offset: int = Query(0, ge=0),
system_id: int | None = Query(None),
category: str | None = Query(None),
adapter_type: str | None = Query(None),
enabled: bool | None = Query(None),
keyword: str | None = Query(None),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_required_user),
) -> dict[str, Any]:
"""分页列出已生成的集成工具。
与 ``tool_router.GET /tools`` 功能等价,保持 Router 边界清晰:
``integration_tool_router`` 仅依赖 ``integration_tool_service`` 端口。
``limit`` / ``offset`` 内部转换为 ``page`` / ``page_size``。
"""
use_cases = create_use_cases_from_db(db)
input_dto = ListToolsInput(
page=offset // limit + 1,
page_size=limit,
system_id=system_id,
category=category,
adapter_type=adapter_type,
enabled=enabled,
keyword=keyword,
)
output = await use_cases.integration_tool_service.list_generated_tools(input_dto)
return {"success": True, "data": output.model_dump()}
@integration_tool_router.post("/discover", response_model=dict)
async def discover_resources(
body: IntegrationToolFlowRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""发现外部系统可用资源(调用厂商集成的 ``discover`` handler"""
use_cases = create_use_cases_from_db(db)
input_dto = IntegrationToolFlowInput(
system_id=body.system_id,
source_type=body.source_type,
env_key=body.env_key,
discovery_options=body.discovery_options,
)
output = await use_cases.integration_tool_service.discover_resources(input_dto)
return {"success": True, "data": output.model_dump()}
@integration_tool_router.post("/preview", response_model=dict)
async def preview_tools(
body: IntegrationToolFlowRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""预览将生成的工具列表(调用厂商集成的 ``preview_tools`` handler"""
use_cases = create_use_cases_from_db(db)
input_dto = IntegrationToolFlowInput(
system_id=body.system_id,
source_type=body.source_type,
env_key=body.env_key,
discovery_options=body.discovery_options,
)
output = await use_cases.integration_tool_service.preview_tools(input_dto)
return {"success": True, "data": output.model_dump()}
@integration_tool_router.post("/generate", response_model=dict)
async def generate_tools_draft(
body: IntegrationToolFlowRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""生成工具草稿(调用厂商集成的 ``create_tools`` handler不持久化
草稿由前端确认后调用 ``POST /integration-tools``persist落库。
"""
use_cases = create_use_cases_from_db(db)
input_dto = IntegrationToolFlowInput(
system_id=body.system_id,
source_type=body.source_type,
env_key=body.env_key,
discovery_options=body.discovery_options,
)
output = await use_cases.integration_tool_service.generate_tools_draft(input_dto)
return {"success": True, "data": output.model_dump()}
@integration_tool_router.post("", response_model=dict)
async def persist_generated_tools(
body: PersistIntegrationToolsRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_admin_user),
) -> dict[str, Any]:
"""持久化厂商集成生成的工具草稿。created_by 由服务端以 current_user.uid 填充。"""
use_cases = create_use_cases_from_db(db)
input_dto = PersistGeneratedToolsInput(
system_id=body.system_id,
tools=[ExternalToolCreateInput(**item.model_dump()) for item in body.tools],
created_by=current_user.uid,
)
output = await use_cases.integration_tool_service.persist_generated_tools(input_dto)
return {"success": True, "data": output.model_dump()}