ForcePilot/backend/server/routers/external_systems/integration_tool_router.py

203 lines
7.9 KiB
Python
Raw Normal View History

"""外部系统限界上下文 - 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, max_length=64),
adapter_type: str | None = Query(None, max_length=32),
enabled: bool | None = Query(None),
keyword: str | None = Query(None, max_length=128),
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()}