本次提交对多个外部系统路由进行了多维度优化: 1. 统一分页参数:将所有路由的`page = offset//limit +1`、`page_size=limit`替换为标准的`limit`+`offset`分页格式 2. 完善接口文档:补充多个端点的功能说明、参数含义与返回字段解释 3. 增强参数校验:新增字段长度限制、正则校验、枚举类型约束与业务逻辑校验 4. 优化代码复用:提取重复逻辑为辅助函数,减少样板代码 5. 修复接口问题:修正工具健康检查端点路径参数类型,优化导出接口响应格式 6. 补充异常处理:为批量操作添加异常捕获与日志记录,避免流程中断
792 lines
30 KiB
Python
792 lines
30 KiB
Python
"""外部系统限界上下文 - System 子域 Router。
|
||
|
||
挂载到聚合 router 的 /systems 前缀下,覆盖系统 CRUD / 克隆 / 导出 /
|
||
状态 / 健康 / 指标用例。Request Schema 与 Input DTO 不共享类,Router 内
|
||
显式构造 DTO,操作人字段由 current_user.uid 填充。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
from datetime import UTC, datetime
|
||
from typing import Any
|
||
|
||
from fastapi import APIRouter, Depends, Query
|
||
from fastapi.responses import StreamingResponse
|
||
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.system import (
|
||
BatchCloneSystemsInput,
|
||
BulkEnableSystemsInput,
|
||
CloneSystemInput,
|
||
CreateSystemInput,
|
||
DeleteSystemInput,
|
||
ExportSystemsInput,
|
||
GetCircuitBreakerStatusInput,
|
||
GetGovernanceConfigInput,
|
||
GetSystemHealthInput,
|
||
GetSystemImpactAnalysisInput,
|
||
GetSystemInput,
|
||
GetSystemMetricsInput,
|
||
GetSystemResourceSummaryInput,
|
||
GetSystemStatsInput,
|
||
ListSystemsInput,
|
||
ResetCircuitBreakerInput,
|
||
SetSystemEnabledInput,
|
||
UpdateCircuitBreakerInput,
|
||
UpdateObservabilityInput,
|
||
UpdatePoolConfigInput,
|
||
UpdateRateLimitInput,
|
||
UpdateRetryPolicyInput,
|
||
UpdateSecretRefsInput,
|
||
UpdateSystemInput,
|
||
ValidateSystemConfigInput,
|
||
)
|
||
from yuxi.storage.postgres.models_business import User
|
||
|
||
from server.utils.auth_middleware import get_admin_user, get_db, get_required_user
|
||
|
||
system_router = APIRouter(prefix="/systems", tags=["external-systems-system"])
|
||
|
||
|
||
# =============================================================================
|
||
# === 治理配置结构化 Schema(Request 边界校验)===
|
||
# =============================================================================
|
||
# 字段对齐 framework/runtime 各组件实际消费的配置键:
|
||
# - RateLimiter: max_qps / max_concurrency / burst_capacity
|
||
# - CircuitBreaker: failure_threshold / recovery_timeout / half_open_max_calls
|
||
# (state / opened_at 为运行时状态,禁止通过 API 写入)
|
||
# - ObservabilityConfig: trace_enabled / trace_sample_rate / metrics_enabled / audit_enabled
|
||
# - retry_policy(executor): max_attempts / retry_delay / retry_backoff /
|
||
# retry_status_codes / retry_on_timeout / retry_on_connection_error
|
||
# 所有字段均 ``T | None = None``,通过 ``model_dump(exclude_none=True)`` 转换为
|
||
# 仅含显式设置键的 dict,与运行时 ``.get(key, default)`` 消费模式对齐。
|
||
|
||
|
||
class RateLimitConfigSchema(BaseModel):
|
||
"""限流配置 schema。``None`` 表示不限制该维度。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
max_qps: int | None = Field(default=None, ge=0)
|
||
max_concurrency: int | None = Field(default=None, ge=0)
|
||
burst_capacity: int | None = Field(default=None, ge=0)
|
||
|
||
|
||
class CircuitBreakerConfigSchema(BaseModel):
|
||
"""熔断器配置 schema。
|
||
|
||
仅包含可配置参数,运行时状态(``state`` / ``opened_at``)由框架内部维护,
|
||
禁止通过 API 写入——此 schema 中不声明这些字段即自动拦截。
|
||
"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
failure_threshold: int | None = Field(default=None, ge=1)
|
||
recovery_timeout: int | None = Field(default=None, ge=1)
|
||
half_open_max_calls: int | None = Field(default=None, ge=1)
|
||
|
||
|
||
class ObservabilityConfigSchema(BaseModel):
|
||
"""可观测性配置 schema。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
trace_enabled: bool | None = None
|
||
trace_sample_rate: float | None = Field(default=None, ge=0, le=1)
|
||
metrics_enabled: bool | None = None
|
||
audit_enabled: bool | None = None
|
||
|
||
|
||
class RetryPolicyConfigSchema(BaseModel):
|
||
"""重试策略配置 schema。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
max_attempts: int | None = Field(default=None, ge=1, le=10)
|
||
retry_delay: float | None = Field(default=None, ge=0)
|
||
retry_backoff: float | None = Field(default=None, ge=1)
|
||
retry_status_codes: list[int] | None = None
|
||
retry_on_timeout: bool | None = None
|
||
retry_on_connection_error: bool | None = None
|
||
|
||
|
||
# =============================================================================
|
||
# === Request Schemas(与 Input DTO 不共享类)===
|
||
# =============================================================================
|
||
|
||
|
||
class CreateSystemRequest(BaseModel):
|
||
"""创建系统请求体。字段对齐 ``CreateSystemInput``(不含 created_by)。
|
||
|
||
字段长度约束对齐 ``ExternalSystem`` ORM 列定义,在边界层拦截非法输入:
|
||
``category`` 对齐 String(64)、``adapter_type``/``auth_type`` 对齐 String(32)、
|
||
``source_type`` 对齐 String(64)、``timeout`` 对齐工具子域取值范围。
|
||
治理配置(rate_limit/circuit_breaker/observability/retry_policy)使用结构化
|
||
schema 在边界校验字段名与取值范围,``pool_config`` 因无运行时解析器保持 dict。
|
||
"""
|
||
|
||
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 = ""
|
||
category: str = Field(default="default", max_length=64)
|
||
adapter_type: str = Field(default="http", max_length=32)
|
||
source_type: str | None = Field(default=None, max_length=64)
|
||
enabled: bool = True
|
||
connection_config: dict[str, Any] = Field(default_factory=dict)
|
||
auth_type: str = Field(default="none", max_length=32)
|
||
auth_config: dict[str, Any] | None = None
|
||
secret_refs: dict[str, Any] = Field(default_factory=dict)
|
||
rate_limit: RateLimitConfigSchema = Field(default_factory=RateLimitConfigSchema)
|
||
circuit_breaker: CircuitBreakerConfigSchema = Field(default_factory=CircuitBreakerConfigSchema)
|
||
pool_config: dict[str, Any] = Field(default_factory=dict)
|
||
observability: ObservabilityConfigSchema = Field(default_factory=ObservabilityConfigSchema)
|
||
timeout: int = Field(default=30, ge=1, le=300)
|
||
retry_policy: RetryPolicyConfigSchema = Field(default_factory=RetryPolicyConfigSchema)
|
||
|
||
|
||
class UpdateSystemRequest(BaseModel):
|
||
"""更新系统请求体。字段对齐 ``UpdateSystemInput``(不含 id 与 updated_by)。
|
||
|
||
字段长度约束对齐 ``ExternalSystem`` ORM 列定义,仅透传客户端显式设置的字段
|
||
(通过 ``exclude_unset=True``),未设置字段保持 ``None`` 以保留部分更新语义。
|
||
治理配置使用结构化 schema 校验,``circuit_breaker`` 不接受运行时状态字段。
|
||
"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
name: str | None = Field(default=None, min_length=1, max_length=128)
|
||
description: str | None = None
|
||
category: str | None = Field(default=None, max_length=64)
|
||
adapter_type: str | None = Field(default=None, max_length=32)
|
||
source_type: str | None = Field(default=None, max_length=64)
|
||
enabled: bool | None = None
|
||
connection_config: dict[str, Any] | None = None
|
||
auth_type: str | None = Field(default=None, max_length=32)
|
||
auth_config: dict[str, Any] | None = None
|
||
secret_refs: dict[str, Any] | None = None
|
||
rate_limit: RateLimitConfigSchema | None = None
|
||
circuit_breaker: CircuitBreakerConfigSchema | None = None
|
||
pool_config: dict[str, Any] | None = None
|
||
observability: ObservabilityConfigSchema | None = None
|
||
timeout: int | None = Field(default=None, ge=1, le=300)
|
||
retry_policy: RetryPolicyConfigSchema | None = None
|
||
|
||
|
||
class CloneSystemRequest(BaseModel):
|
||
"""克隆系统请求体。字段对齐 ``CloneSystemInput``(不含 id 与 created_by)。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
new_slug: str = Field(..., min_length=1, max_length=128, pattern=r"^[a-zA-Z_][a-zA-Z0-9_-]{0,127}$")
|
||
new_name: str | None = None
|
||
clone_tools: bool = True
|
||
clone_environments: bool = True
|
||
|
||
|
||
class SetSystemEnabledRequest(BaseModel):
|
||
"""设置系统启用状态请求体。字段对齐 ``SetSystemEnabledInput``(不含 id 与 updated_by)。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
enabled: bool
|
||
|
||
|
||
class UpdateRateLimitRequest(BaseModel):
|
||
"""更新限流配置请求体。字段对齐 ``UpdateRateLimitInput``(不含 system_id 与 updated_by)。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
rate_limit: RateLimitConfigSchema
|
||
|
||
|
||
class UpdateCircuitBreakerRequest(BaseModel):
|
||
"""更新熔断器配置请求体。字段对齐 ``UpdateCircuitBreakerInput``(不含 system_id 与 updated_by)。
|
||
|
||
仅接受可配置参数,运行时状态(``state`` / ``opened_at``)通过 schema 字段缺省自动拦截。
|
||
"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
circuit_breaker: CircuitBreakerConfigSchema
|
||
|
||
|
||
class UpdatePoolConfigRequest(BaseModel):
|
||
"""更新连接池配置请求体。字段对齐 ``UpdatePoolConfigInput``(不含 system_id 与 updated_by)。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
pool_config: dict[str, Any]
|
||
|
||
|
||
class UpdateObservabilityRequest(BaseModel):
|
||
"""更新可观测性配置请求体。字段对齐 ``UpdateObservabilityInput``(不含 system_id 与 updated_by)。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
observability: ObservabilityConfigSchema
|
||
|
||
|
||
class UpdateRetryPolicyRequest(BaseModel):
|
||
"""更新重试策略请求体。字段对齐 ``UpdateRetryPolicyInput``(不含 system_id 与 updated_by)。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
retry_policy: RetryPolicyConfigSchema
|
||
|
||
|
||
class UpdateSecretRefsRequest(BaseModel):
|
||
"""更新密钥引用请求体。字段对齐 ``UpdateSecretRefsInput``(不含 system_id 与 updated_by)。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
secret_refs: dict[str, Any]
|
||
|
||
|
||
class BatchEnabledRequest(BaseModel):
|
||
"""批量启用/禁用请求体。字段对齐 ``BulkEnableSystemsInput``(不含 updated_by)。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
ids: list[int] = Field(..., min_length=1, max_length=100)
|
||
enabled: bool
|
||
|
||
|
||
class BatchCloneRequest(BaseModel):
|
||
"""批量克隆请求体。字段对齐 ``BatchCloneSystemsInput``(不含 created_by)。
|
||
|
||
``slug_suffix`` 长度上限 64,为 ``source.slug``(最长 128)预留拼接余量,
|
||
拼接后的 ``new_slug`` 由 service 层 ``validate_slug`` 校验总长与格式。
|
||
"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
source_system_ids: list[int] = Field(..., min_length=1, max_length=100)
|
||
slug_suffix: str = Field(..., min_length=1, max_length=64)
|
||
override_config: dict[str, Any] | None = None
|
||
|
||
|
||
# =============================================================================
|
||
# === 静态路径端点(必须在 /{system_id} 之前声明) ===
|
||
# =============================================================================
|
||
|
||
|
||
@system_router.get("/export")
|
||
async def export_systems(
|
||
ids: list[int] | None = Query(None),
|
||
category: str | None = Query(None),
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> StreamingResponse:
|
||
"""导出系统列表为 JSON 字节流。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = ExportSystemsInput(
|
||
ids=ids,
|
||
category=category,
|
||
)
|
||
output = await use_cases.system_service.export_systems(input_dto)
|
||
|
||
payload = json.dumps(
|
||
[item.model_dump() for item in output.items],
|
||
ensure_ascii=False,
|
||
default=str,
|
||
).encode("utf-8")
|
||
timestamp = datetime.now(UTC).strftime("%Y%m%d%H%M%S")
|
||
|
||
async def _stream():
|
||
yield payload
|
||
|
||
return StreamingResponse(
|
||
_stream(),
|
||
media_type="application/octet-stream",
|
||
headers={"Content-Disposition": f"attachment; filename=systems_{timestamp}.json"},
|
||
)
|
||
|
||
|
||
@system_router.get("", response_model=dict)
|
||
async def list_systems(
|
||
limit: int = Query(100, ge=1, le=500),
|
||
offset: int = Query(0, ge=0),
|
||
category: str | None = Query(None),
|
||
adapter_type: str | None = Query(None),
|
||
source_type: str | None = Query(None),
|
||
enabled: bool | None = Query(None),
|
||
keyword: str | None = Query(None),
|
||
include_health: bool = Query(False, description="是否附带每系统最近健康检查摘要"),
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""分页列出外部系统。
|
||
|
||
``include_health=true`` 时在 ``data.health_summaries`` 中返回当前页每系统最近一次
|
||
健康检查记录(单次窗口函数批量查询,无 N+1)。
|
||
"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = ListSystemsInput(
|
||
limit=limit,
|
||
offset=offset,
|
||
category=category,
|
||
adapter_type=adapter_type,
|
||
source_type=source_type,
|
||
enabled=enabled,
|
||
keyword=keyword,
|
||
include_health=include_health,
|
||
)
|
||
output = await use_cases.system_service.list_systems(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.post("", response_model=dict)
|
||
async def create_system(
|
||
body: CreateSystemRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""创建外部系统。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = CreateSystemInput(**body.model_dump(exclude_none=True), created_by=current_user.uid)
|
||
output = await use_cases.system_service.create_system(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/stats", response_model=dict)
|
||
async def get_system_stats(
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取系统聚合统计概览。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetSystemStatsInput()
|
||
output = await use_cases.system_service.get_system_stats(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.post("/batch-enabled", response_model=dict)
|
||
async def batch_set_enabled(
|
||
body: BatchEnabledRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""批量启用或禁用外部系统。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = BulkEnableSystemsInput(
|
||
ids=body.ids,
|
||
enabled=body.enabled,
|
||
updated_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.batch_set_enabled(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.post("/batch-clone", response_model=dict)
|
||
async def batch_clone_systems(
|
||
body: BatchCloneRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""批量克隆外部系统配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = BatchCloneSystemsInput(
|
||
source_system_ids=body.source_system_ids,
|
||
slug_suffix=body.slug_suffix,
|
||
override_config=body.override_config,
|
||
created_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.batch_clone_systems(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/circuit-breakers/status", response_model=dict)
|
||
async def get_circuit_breaker_status(
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取全局熔断器状态总览。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetCircuitBreakerStatusInput()
|
||
output = await use_cases.system_service.get_circuit_breaker_status(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
# =============================================================================
|
||
# === 动态路径端点 /{system_id} ===
|
||
# =============================================================================
|
||
|
||
|
||
@system_router.get("/{system_id}", response_model=dict)
|
||
async def get_system(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取系统详情。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetSystemInput(id=system_id)
|
||
output = await use_cases.system_service.get_system(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.put("/{system_id}", response_model=dict)
|
||
async def update_system(
|
||
system_id: int,
|
||
body: UpdateSystemRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""更新外部系统。仅透传客户端显式设置的字段,保留部分更新语义。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = UpdateSystemInput(
|
||
id=system_id,
|
||
updated_by=current_user.uid,
|
||
**body.model_dump(exclude_unset=True, exclude_none=True),
|
||
)
|
||
output = await use_cases.system_service.update_system(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.delete("/{system_id}", response_model=dict)
|
||
async def delete_system(
|
||
system_id: int,
|
||
cascade: bool = Query(False),
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""删除外部系统。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = DeleteSystemInput(id=system_id, cascade=cascade, deleted_by=current_user.uid)
|
||
output = await use_cases.system_service.delete_system(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.patch("/{system_id}/enabled", response_model=dict)
|
||
async def set_system_enabled(
|
||
system_id: int,
|
||
body: SetSystemEnabledRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""设置系统启用/禁用状态。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = SetSystemEnabledInput(
|
||
id=system_id,
|
||
enabled=body.enabled,
|
||
updated_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.set_system_enabled(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.post("/{system_id}/clone", response_model=dict)
|
||
async def clone_system(
|
||
system_id: int,
|
||
body: CloneSystemRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""克隆外部系统。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = CloneSystemInput(
|
||
**body.model_dump(),
|
||
id=system_id,
|
||
created_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.clone_system(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/health", response_model=dict, deprecated=True)
|
||
async def get_system_health(
|
||
system_id: int,
|
||
env_key: str | None = Query(None),
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取系统健康状态(已废弃)。
|
||
|
||
.. deprecated::
|
||
请使用 ``GET /system/external-systems/health-checks/by-system/{system_id}/latest`` 替代。
|
||
新端点响应字段更完整(含 id 与审计字段),本端点保留用于前端迁移期间的向后兼容。
|
||
"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetSystemHealthInput(id=system_id, env_key=env_key)
|
||
output = await use_cases.system_service.get_system_health(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/metrics", response_model=dict, deprecated=True)
|
||
async def get_system_metrics(
|
||
system_id: int,
|
||
env_key: str | None = Query(None),
|
||
start_at: datetime | None = Query(None, description="ISO8601 开始时间"),
|
||
end_at: datetime | None = Query(None, description="ISO8601 结束时间"),
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取系统调用指标(已废弃)。
|
||
|
||
.. deprecated::
|
||
由 ``GET /system/external-systems/metrics/aggregate`` 替代,
|
||
新端点响应字段对齐 ORM aggregate 返回,支持 ``adapter_type`` 过滤。
|
||
前端迁移完成后将移除本端点。
|
||
"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetSystemMetricsInput(
|
||
id=system_id,
|
||
env_key=env_key,
|
||
start_at=start_at,
|
||
end_at=end_at,
|
||
)
|
||
output = await use_cases.system_service.get_system_metrics(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
# =============================================================================
|
||
# === 运维治理配置管理 ===
|
||
# =============================================================================
|
||
|
||
|
||
@system_router.get("/{system_id}/rate-limit", response_model=dict)
|
||
async def get_rate_limit(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询系统限流配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetGovernanceConfigInput(system_id=system_id)
|
||
output = await use_cases.system_service.get_rate_limit(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.put("/{system_id}/rate-limit", response_model=dict)
|
||
async def update_rate_limit(
|
||
system_id: int,
|
||
body: UpdateRateLimitRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""更新系统限流配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = UpdateRateLimitInput(
|
||
system_id=system_id,
|
||
rate_limit=body.rate_limit.model_dump(exclude_none=True),
|
||
updated_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.update_rate_limit(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/circuit-breaker", response_model=dict)
|
||
async def get_circuit_breaker(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询系统熔断器配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetGovernanceConfigInput(system_id=system_id)
|
||
output = await use_cases.system_service.get_circuit_breaker(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.put("/{system_id}/circuit-breaker", response_model=dict)
|
||
async def update_circuit_breaker(
|
||
system_id: int,
|
||
body: UpdateCircuitBreakerRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""更新系统熔断器配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = UpdateCircuitBreakerInput(
|
||
system_id=system_id,
|
||
circuit_breaker=body.circuit_breaker.model_dump(exclude_none=True),
|
||
updated_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.update_circuit_breaker(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.post("/{system_id}/circuit-breaker/reset", response_model=dict)
|
||
async def reset_circuit_breaker(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""重置系统熔断器运行时状态。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = ResetCircuitBreakerInput(system_id=system_id, reset_by=current_user.uid)
|
||
output = await use_cases.system_service.reset_circuit_breaker(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/connection-pool", response_model=dict)
|
||
async def get_connection_pool(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询系统连接池配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetGovernanceConfigInput(system_id=system_id)
|
||
output = await use_cases.system_service.get_pool_config(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.put("/{system_id}/connection-pool", response_model=dict)
|
||
async def update_connection_pool(
|
||
system_id: int,
|
||
body: UpdatePoolConfigRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""更新系统连接池配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = UpdatePoolConfigInput(
|
||
system_id=system_id,
|
||
pool_config=body.pool_config,
|
||
updated_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.update_pool_config(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/observability", response_model=dict)
|
||
async def get_observability(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询系统可观测性配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetGovernanceConfigInput(system_id=system_id)
|
||
output = await use_cases.system_service.get_observability(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.put("/{system_id}/observability", response_model=dict)
|
||
async def update_observability(
|
||
system_id: int,
|
||
body: UpdateObservabilityRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""更新系统可观测性配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = UpdateObservabilityInput(
|
||
system_id=system_id,
|
||
observability=body.observability.model_dump(exclude_none=True),
|
||
updated_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.update_observability(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/retry-policy", response_model=dict)
|
||
async def get_retry_policy(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询系统重试策略配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetGovernanceConfigInput(system_id=system_id)
|
||
output = await use_cases.system_service.get_retry_policy(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.put("/{system_id}/retry-policy", response_model=dict)
|
||
async def update_retry_policy(
|
||
system_id: int,
|
||
body: UpdateRetryPolicyRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""更新系统重试策略。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = UpdateRetryPolicyInput(
|
||
system_id=system_id,
|
||
retry_policy=body.retry_policy.model_dump(exclude_none=True),
|
||
updated_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.update_retry_policy(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/secret-refs", response_model=dict)
|
||
async def get_secret_refs(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""查询系统密钥引用配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetGovernanceConfigInput(system_id=system_id)
|
||
output = await use_cases.system_service.get_secret_refs(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.put("/{system_id}/secret-refs", response_model=dict)
|
||
async def update_secret_refs(
|
||
system_id: int,
|
||
body: UpdateSecretRefsRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""更新系统密钥引用配置。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = UpdateSecretRefsInput(
|
||
system_id=system_id,
|
||
secret_refs=body.secret_refs,
|
||
updated_by=current_user.uid,
|
||
)
|
||
output = await use_cases.system_service.update_secret_refs(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
# =============================================================================
|
||
# === 配置验证与依赖分析 ===
|
||
# =============================================================================
|
||
|
||
|
||
@system_router.post("/{system_id}/validate-config", response_model=dict)
|
||
async def validate_system_config(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""校验系统配置完整性。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = ValidateSystemConfigInput(system_id=system_id, validated_by=current_user.uid)
|
||
output = await use_cases.system_service.validate_system_config(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/resource-summary", response_model=dict)
|
||
async def get_system_resource_summary(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取系统资源汇总。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetSystemResourceSummaryInput(system_id=system_id)
|
||
output = await use_cases.system_service.get_system_resource_summary(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@system_router.get("/{system_id}/impact-analysis", response_model=dict)
|
||
async def get_system_impact_analysis(
|
||
system_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_required_user),
|
||
) -> dict[str, Any]:
|
||
"""获取系统变更影响分析。"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = GetSystemImpactAnalysisInput(system_id=system_id)
|
||
output = await use_cases.system_service.get_system_impact_analysis(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|