PMFlow/backend/app/api/audit.py

69 lines
2.4 KiB
Python

"""审计日志查询 API。"""
from __future__ import annotations
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from ..core.response import success_response
from ..core.database import get_db
from ..dependencies.auth import get_optional_current_user
from ..dependencies.tenant import get_tenant_id
from ..models import User
from ..services.audit_query_service import AuditQueryService
from ..services.auth import has_any_role
router = APIRouter(prefix="/audit", tags=["Audit"])
def _effective_tenant_id(current_user: User, header_tenant_id: int) -> int:
try:
return int(getattr(current_user, "tenant_id") or header_tenant_id or 1)
except Exception:
return int(header_tenant_id or 1)
def _user_role(user: User) -> str:
role = getattr(user, "role", "")
return str(getattr(role, "value", role) or "").strip()
@router.get("/operations")
async def list_audit_operations(
tenant_id: Optional[int] = Query(default=None, ge=1),
entity_type: Optional[str] = Query(default=None),
start_time: Optional[datetime] = Query(default=None),
end_time: Optional[datetime] = Query(default=None),
limit: int = Query(default=100, ge=1, le=100),
current_user: Optional[User] = Depends(get_optional_current_user),
db: AsyncSession = Depends(get_db),
header_tenant_id: int = Depends(get_tenant_id),
):
if current_user is not None and not has_any_role(_user_role(current_user), ["BOSS", "ADMIN"]):
raise HTTPException(status_code=403, detail="Access denied. Required roles: BOSS, ADMIN")
if current_user is None:
effective_tenant_id = int(header_tenant_id or 1)
else:
effective_tenant_id = _effective_tenant_id(current_user, header_tenant_id)
if tenant_id is not None and int(tenant_id) != effective_tenant_id:
raise HTTPException(status_code=403, detail="cross-tenant query is forbidden")
scoped_tenant_id = int(tenant_id or effective_tenant_id)
items = await AuditQueryService(db).list_operations(
tenant_id=scoped_tenant_id,
entity_type=entity_type,
start_time=start_time,
end_time=end_time,
limit=limit,
)
return success_response(
{
"items": items,
"count": len(items),
"tenant_id": scoped_tenant_id,
}
)