增加充值审计日志
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"""审计日志查询接口,仅超级管理员可访问。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db
|
||||
from ..deps import get_current_user
|
||||
from ..models import Account, AuditLog, DouyuTask, User
|
||||
|
||||
|
||||
router = APIRouter(prefix="/api/audit-logs", tags=["审计日志"])
|
||||
|
||||
|
||||
def require_super_admin(current: User = Depends(get_current_user)) -> User:
|
||||
"""审计日志属于高敏感运维数据,只允许超级管理员查看。"""
|
||||
if current.role != "super_admin":
|
||||
raise HTTPException(status_code=403, detail="仅超级管理员可查看审计日志")
|
||||
return current
|
||||
|
||||
|
||||
def _audit_log_out(db: Session, row: AuditLog) -> dict:
|
||||
"""序列化审计记录,并为旧斗鱼直充批次补齐可安全展示的账号身份。"""
|
||||
detail_text = row.detail or ""
|
||||
if row.action == "recharge:douyu:create" and row.target.startswith("douyu_batch:"):
|
||||
try:
|
||||
detail = json.loads(detail_text) if detail_text else {}
|
||||
except json.JSONDecodeError:
|
||||
detail = None
|
||||
if isinstance(detail, dict) and not detail.get("recharge_accounts"):
|
||||
batch_id = row.target.removeprefix("douyu_batch:")
|
||||
accounts = (
|
||||
db.query(DouyuTask, Account)
|
||||
.join(Account, Account.id == DouyuTask.account_id)
|
||||
.filter(DouyuTask.batch_id == batch_id, DouyuTask.task_type == "create_gold_qr")
|
||||
.order_by(DouyuTask.id.asc())
|
||||
.limit(100)
|
||||
.all()
|
||||
)
|
||||
if accounts:
|
||||
detail["recharge_accounts"] = [
|
||||
{
|
||||
"task_id": task.id,
|
||||
"username": account.username,
|
||||
"douyu_uid": account.uid or "",
|
||||
"douyu_nickname": account.nickname or "",
|
||||
}
|
||||
for task, account in accounts
|
||||
]
|
||||
detail["recharge_accounts_truncated"] = len(accounts) == 100
|
||||
detail_text = json.dumps(detail, ensure_ascii=False, separators=(",", ":"))
|
||||
return {
|
||||
"id": row.id,
|
||||
"user_id": row.user_id,
|
||||
"username": row.username or "",
|
||||
"action": row.action,
|
||||
"target": row.target or "",
|
||||
"detail": detail_text,
|
||||
"success": row.success,
|
||||
"created_at": row.created_at,
|
||||
}
|
||||
|
||||
|
||||
@router.get("")
|
||||
def list_audit_logs(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(50, ge=1, le=200),
|
||||
username: str | None = Query(None, max_length=64),
|
||||
action: str | None = Query(None, max_length=128),
|
||||
keyword: str | None = Query(None, max_length=128),
|
||||
success: bool | None = Query(None),
|
||||
start_time: datetime | None = Query(None),
|
||||
end_time: datetime | None = Query(None),
|
||||
db: Session = Depends(get_db),
|
||||
_: User = Depends(require_super_admin),
|
||||
):
|
||||
"""分页查询审计记录,不提供修改和删除能力。"""
|
||||
query = db.query(AuditLog)
|
||||
if username:
|
||||
query = query.filter(AuditLog.username.ilike(f"%{username.strip()}%"))
|
||||
if action:
|
||||
query = query.filter(AuditLog.action == action.strip())
|
||||
if keyword:
|
||||
pattern = f"%{keyword.strip()}%"
|
||||
query = query.filter((AuditLog.target.ilike(pattern)) | (AuditLog.detail.ilike(pattern)))
|
||||
if success is not None:
|
||||
query = query.filter(AuditLog.success == success)
|
||||
if start_time:
|
||||
query = query.filter(AuditLog.created_at >= start_time)
|
||||
if end_time:
|
||||
query = query.filter(AuditLog.created_at <= end_time)
|
||||
total = query.count()
|
||||
rows = query.order_by(AuditLog.id.desc()).offset((page - 1) * page_size).limit(page_size).all()
|
||||
return {
|
||||
"items": [_audit_log_out(db, row) for row in rows],
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
}
|
||||
Reference in New Issue
Block a user