"""admin_audit_log 写入 + 查询。 审计日志只增不改不删——任何写操作经 app.admin.audit.write_audit 落一条。 """ from __future__ import annotations from sqlalchemy import func, select from sqlalchemy.orm import Session from app.models.admin import AdminAuditLog def add_audit_log( db: Session, *, admin_id: int, admin_username: str, action: str, target_type: str, target_id: str | None = None, detail: dict | None = None, ip: str | None = None, commit: bool = True, ) -> AdminAuditLog: """插一条审计。commit=False 时只 flush,让调用方把审计和业务写操作放同一事务。""" log = AdminAuditLog( admin_id=admin_id, admin_username=admin_username, action=action, target_type=target_type, target_id=target_id, detail=detail, ip=ip, ) db.add(log) if commit: db.commit() db.refresh(log) else: db.flush() return log def list_audit_logs( db: Session, *, action: str | None = None, target_type: str | None = None, admin_id: int | None = None, limit: int = 50, cursor: int | None = None, ) -> tuple[list[AdminAuditLog], int | None, int]: """offset 分页(id 倒序)+ total。cursor 即 offset((page-1)*pageSize),支持页码跳页。 返回 (rows, next_cursor, total)。""" stmt = select(AdminAuditLog) if action: stmt = stmt.where(AdminAuditLog.action == action) if target_type: stmt = stmt.where(AdminAuditLog.target_type == target_type) if admin_id is not None: stmt = stmt.where(AdminAuditLog.admin_id == admin_id) total = int(db.execute(select(func.count()).select_from(stmt.subquery())).scalar_one()) offset = max(cursor or 0, 0) rows = list( db.execute( stmt.order_by(AdminAuditLog.id.desc()).offset(offset).limit(limit + 1) ).scalars().all() ) has_more = len(rows) > limit items = rows[:limit] next_cursor = offset + limit if has_more else None return items, next_cursor, total