新增外部系统限界上下文的完整路由体系,包含聚合路由层与23个子领域路由,覆盖外部系统全生命周期管理、健康检查、指标监控、审计日志、Token管理、通知渠道、回收站等功能,并将外部系统路由挂载到全局API前缀下。
138 lines
4.6 KiB
Python
138 lines
4.6 KiB
Python
"""Trash 子域 Router。
|
||
|
||
外部系统限界上下文的回收站管理 API,覆盖回收站列表查询 / 恢复软删除资源 /
|
||
物理删除 / 清理过期回收站。所有端点通过 ``create_use_cases_from_db`` 装配
|
||
use_cases,经 ``trash_service`` 端口调用用例。
|
||
|
||
回收站为高危操作,所有端点使用 ``get_admin_user`` 鉴权。Request Schema 与
|
||
Input DTO 不共享类,Router 内显式构造 DTO,操作者字段(``restored_by`` /
|
||
``deleted_by`` / ``purged_by``)由 ``current_user.uid`` 填充。Router 内不
|
||
try/except 领域异常,由全局异常处理器统一处理。
|
||
|
||
路径顺序(关键,来自 FastAPI 路由匹配规则):静态路径端点(``GET ""`` /
|
||
``POST "/purge"``)必须在动态路径端点(``POST "/{resource_type}/{resource_id}/restore"``
|
||
/ ``DELETE "/{resource_type}/{resource_id}"``)之前声明。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
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.trash import (
|
||
HardDeleteInput,
|
||
ListTrashInput,
|
||
PurgeTrashInput,
|
||
RestoreInput,
|
||
)
|
||
from yuxi.storage.postgres.models_business import User
|
||
|
||
from server.utils.auth_middleware import get_admin_user, get_db
|
||
|
||
trash_router = APIRouter(
|
||
prefix="/trash",
|
||
tags=["external-systems-trash"],
|
||
)
|
||
|
||
|
||
# ---------------- Request Schemas ----------------
|
||
|
||
|
||
class PurgeTrashRequest(BaseModel):
|
||
"""清理过期回收站请求体。字段对齐 ``PurgeTrashInput``。"""
|
||
|
||
model_config = ConfigDict(frozen=True)
|
||
|
||
older_than_days: int = Field(default=30, ge=1)
|
||
|
||
|
||
# ---------------- Endpoints ----------------
|
||
|
||
|
||
@trash_router.get("", response_model=dict)
|
||
async def list_trash(
|
||
resource_type: str | None = Query(None),
|
||
limit: int = Query(50, ge=1, le=200),
|
||
offset: int = Query(0, ge=0),
|
||
start_time: datetime | None = Query(None),
|
||
end_time: datetime | None = Query(None),
|
||
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 = ListTrashInput(
|
||
resource_type=resource_type,
|
||
limit=limit,
|
||
offset=offset,
|
||
start_time=start_time,
|
||
end_time=end_time,
|
||
)
|
||
output = await use_cases.trash_service.list_trash(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@trash_router.post("/purge", response_model=dict)
|
||
async def purge_trash(
|
||
payload: PurgeTrashRequest,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""清理过期回收站(物理删除超过保留期的软删除资源)。
|
||
|
||
``purged_by`` 由当前管理员填充。
|
||
"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = PurgeTrashInput(
|
||
older_than_days=payload.older_than_days,
|
||
purged_by=current_user.uid,
|
||
)
|
||
output = await use_cases.trash_service.purge(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@trash_router.post("/{resource_type}/{resource_id}/restore", response_model=dict)
|
||
async def restore_resource(
|
||
resource_type: str,
|
||
resource_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""恢复软删除资源。
|
||
|
||
``restored_by`` 由当前管理员填充。
|
||
"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = RestoreInput(
|
||
resource_type=resource_type,
|
||
resource_id=resource_id,
|
||
restored_by=current_user.uid,
|
||
)
|
||
output = await use_cases.trash_service.restore(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|
||
|
||
|
||
@trash_router.delete("/{resource_type}/{resource_id}", response_model=dict)
|
||
async def hard_delete_resource(
|
||
resource_type: str,
|
||
resource_id: int,
|
||
db: AsyncSession = Depends(get_db),
|
||
current_user: User = Depends(get_admin_user),
|
||
) -> dict[str, Any]:
|
||
"""物理删除回收站中的资源(不可恢复)。
|
||
|
||
``deleted_by`` 由当前管理员填充。
|
||
"""
|
||
use_cases = create_use_cases_from_db(db)
|
||
input_dto = HardDeleteInput(
|
||
resource_type=resource_type,
|
||
resource_id=resource_id,
|
||
deleted_by=current_user.uid,
|
||
)
|
||
output = await use_cases.trash_service.hard_delete(input_dto)
|
||
return {"success": True, "data": output.model_dump()}
|