Files
zpark-ems/backend/app/api/v1/audit.py
Du Wenbo 026c837b91 Squashed 'core/' content from commit 92ec910
git-subtree-dir: core
git-subtree-split: 92ec910a132e379a3a6e442a75bcb07cac0f0010
2026-04-04 18:17:10 +08:00

77 lines
2.4 KiB
Python

from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from datetime import datetime
from app.core.database import get_db
from app.core.deps import get_current_user, require_roles
from app.models.user import User, AuditLog
router = APIRouter(prefix="/audit", tags=["审计日志"])
@router.get("/logs")
async def list_audit_logs(
user_id: int | None = None,
action: str | None = None,
resource: str | None = None,
start_time: str | None = None,
end_time: str | None = None,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_roles("admin", "energy_manager")),
):
"""Return paginated audit logs with optional filters."""
query = select(
AuditLog.id,
AuditLog.user_id,
User.username,
AuditLog.action,
AuditLog.resource,
AuditLog.detail,
AuditLog.ip_address,
AuditLog.created_at,
).outerjoin(User, AuditLog.user_id == User.id)
if user_id is not None:
query = query.where(AuditLog.user_id == user_id)
if action:
query = query.where(AuditLog.action == action)
if resource:
query = query.where(AuditLog.resource == resource)
if start_time:
try:
st = datetime.fromisoformat(start_time)
query = query.where(AuditLog.created_at >= st)
except ValueError:
pass
if end_time:
try:
et = datetime.fromisoformat(end_time)
query = query.where(AuditLog.created_at <= et)
except ValueError:
pass
# Count
count_q = select(func.count()).select_from(query.subquery())
total = (await db.execute(count_q)).scalar()
# Paginate
query = query.order_by(AuditLog.created_at.desc()).offset((page - 1) * page_size).limit(page_size)
result = await db.execute(query)
items = []
for row in result.all():
items.append({
"id": row.id,
"user_id": row.user_id,
"username": row.username or "-",
"action": row.action,
"resource": row.resource,
"detail": row.detail,
"ip_address": row.ip_address,
"created_at": str(row.created_at) if row.created_at else None,
})
return {"total": total, "items": items}