78f60c6857
P0 修复: - /api/ready import 错误 (_get_engine + settings.create_redis_client) - 删 agent.otp_secret/otp_enabled 双字段 (migration 026) - 重建 021_rbac migration (IF NOT EXISTS 兼容) P1 新增: - 企微 SSO (auth_wecom_sso.py, useWeChatWorkSSO composable, PortalSelect UA 检测) - RBAC 5 角色 × 4 资源 × 4 操作 × 3 范围 (rbac_service + seed_rbac + require_permission) - audit_log 模型 + migration 027 + 服务 + API - 管理后台 RBAC 权限矩阵 UI (PermissionsMatrix.vue) 质量: - pytest 405 passed / 33 pre-existing failed / 4 xfailed (v0.7.1 引入失败 = 0) - conftest GBK patch 强制 UTF-8 读 .env - .gitignore 排除 *.b64 (含 admin token 凭据) - DEPLOY-v0.7.1.md 7 步 runbook + 4 坑 + 回滚预案
76 lines
2.8 KiB
Python
76 lines
2.8 KiB
Python
# =============================================================================
|
|
# 企微IT智能服务台 — 审计日志 API (v0.7.1 task #89)
|
|
# =============================================================================
|
|
# 说明: 审计日志只读端点,给 auditor / admin 用
|
|
# 权限要求: audit_log:read:all (由 RBAC 装饰器校验)
|
|
# =============================================================================
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.dependencies import require_permission, UserInfo
|
|
from app.database import get_db
|
|
from app.services.audit_log_service import list_audit_logs
|
|
from app.utils.response import success_response
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/admin/audit-logs", tags=["审计日志"])
|
|
|
|
|
|
@router.get("")
|
|
@require_permission("audit_log", "read", "all")
|
|
async def get_audit_logs(
|
|
employee_id: Optional[str] = Query(None, description="按操作人过滤"),
|
|
action: Optional[str] = Query(None, description="按操作类型过滤"),
|
|
resource: Optional[str] = Query(None, description="按资源类型过滤"),
|
|
from_time: Optional[datetime] = Query(None, alias="from", description="起始时间(ISO8601)"),
|
|
to_time: Optional[datetime] = Query(None, alias="to", description="结束时间(ISO8601)"),
|
|
page: int = Query(1, ge=1, description="页码"),
|
|
page_size: int = Query(50, ge=1, le=500, description="每页条数"),
|
|
admin: UserInfo = None, # 由 require_permission 注入(签名合并)
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""查询审计日志(分页)。
|
|
|
|
权限: 需要 audit_log:read:all (admin / auditor 角色拥有)
|
|
|
|
Returns:
|
|
Dict: 统一响应格式,包含 items/total/page/page_size
|
|
"""
|
|
result = await list_audit_logs(
|
|
db,
|
|
employee_id=employee_id,
|
|
action=action,
|
|
resource=resource,
|
|
from_time=from_time,
|
|
to_time=to_time,
|
|
page=page,
|
|
page_size=page_size,
|
|
)
|
|
|
|
return success_response(data={
|
|
"items": [
|
|
{
|
|
"id": log.id,
|
|
"employee_id": log.employee_id,
|
|
"action": log.action,
|
|
"resource": log.resource,
|
|
"resource_id": log.resource_id,
|
|
"details": log.details,
|
|
"result": log.result,
|
|
"ip_address": log.ip_address,
|
|
"user_agent": log.user_agent,
|
|
"created_at": log.created_at.isoformat() if log.created_at else None,
|
|
}
|
|
for log in result["items"]
|
|
],
|
|
"total": result["total"],
|
|
"page": result["page"],
|
|
"page_size": result["page_size"],
|
|
})
|