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

138 lines
4.6 KiB
Python
Raw Normal View History

"""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()}