from django.utils import timezone from .models import OperationLog from rest_framework import viewsets from rest_framework.filters import OrderingFilter from django_filters.rest_framework import DjangoFilterBackend from utils.page import MyPageNumberPagination from .serializers import OperationLogSerializer from .filter import OperationLogFilter class OperationLogger: @staticmethod def log_operation(request, operation_content, operation_level, operator=None, operation_result='success', module_name=None, object_id=None): """ 记录操作日志 Args: request: HttpRequest对象 operation_content: 操作内容描述 operation_level: 操作级别 operator: 操作者标识(用户名等),如果为None则尝试从request中获取 operation_result: 操作结果,'success'或'failure' module_name: 模块名称,用于分类 object_id: 操作对象ID """ try: # 验证必需参数 if not request or not operation_content or not operation_level: raise ValueError("缺少必需的参数: request, operation_content, operation_level") # 获取操作者标识 operator_identifier = OperationLogger._get_operator_identifier(request, operator) # 获取模块名称(如果没有提供则从请求路径推断) if not module_name: module_name = OperationLogger._infer_module_name(request.path) # 获取客户端信息 ip_address = OperationLogger._get_client_ip(request) user_agent = request.META.get('HTTP_USER_AGENT', '')[:500] # 限制长度 # 创建日志记录 OperationLog.objects.create( operator=operator_identifier, operation_content=operation_content, operation_level=operation_level, operation_result=operation_result, ip_address=ip_address, user_agent=user_agent, request_method=request.method, request_path=request.path, module_name=module_name, object_id=object_id ) except Exception as e: # 日志记录失败不应影响主要业务逻辑,但需要记录错误 import logging logger = logging.getLogger(__name__) logger.error(f"操作日志记录失败: {str(e)}", exc_info=True) @staticmethod def _get_operator_identifier(request, operator): """获取操作者标识""" # 如果提供了operator参数,直接使用 if operator is not None: return str(operator) # 优先尝试从request.auth获取(系统的自定义认证方式) if hasattr(request, 'auth') and request.auth: # 如果auth对象有name属性,优先使用 if hasattr(request.auth, 'name') and request.auth.name: return str(request.auth.name) # 如果有openid属性 elif hasattr(request.auth, 'openid') and request.auth.openid: return f"用户OpenID:{request.auth.openid}" # 如果auth本身是字符串 elif isinstance(request.auth, str): return request.auth # 尝试从request.user获取(标准Django认证) if hasattr(request, 'user') and request.user.is_authenticated: # 优先使用username,如果没有则使用其他标识 if hasattr(request.user, 'username') and request.user.username: return request.user.username elif hasattr(request.user, 'get_username'): username = request.user.get_username() if username: return username if hasattr(request.user, 'id'): return f"用户ID:{request.user.id}" # 如果都没有,返回未知用户 return "未知用户" @staticmethod def _infer_module_name(path): """从路径推断模块名称""" if not path: return 'unknown' # 移除首尾斜杠并分割路径 path_parts = path.strip('/').split('/') if path_parts: # 使用第一个非空路径部分作为模块名 for part in path_parts: if part and part not in ('api', 'v1', 'v2'): # 跳过常见的API前缀 return part return 'unknown' @staticmethod def _get_client_ip(request): """获取客户端IP地址""" x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: ip = x_forwarded_for.split(',')[0].strip() else: ip = request.META.get('REMOTE_ADDR') return ip # 快捷函数 def log_operation(request, operation_content, operation_level, operator=None, operation_result='success', module_name=None, object_id=None): """快捷记录操作日志函数""" OperationLogger.log_operation( request=request, operation_content=operation_content, operation_level=operation_level, operator=operator, operation_result=operation_result, module_name=module_name, object_id=object_id ) # 专门用于成功操作的快捷函数 def log_success_operation(request, operation_content, operation_level, operator=None, module_name=None, object_id=None): """记录成功操作的快捷函数""" log_operation( request=request, operation_content=operation_content, operation_level=operation_level, operator=operator, operation_result='success', module_name=module_name, object_id=object_id ) # 专门用于失败操作的快捷函数 def log_failure_operation(request, operation_content, operation_level, operator=None, module_name=None, object_id=None): """记录失败操作的快捷函数""" log_operation( request=request, operation_content=operation_content, operation_level=operation_level, operator=operator, operation_result='failure', module_name=module_name, object_id=object_id ) class OperationLogViewSet(viewsets.ReadOnlyModelViewSet): """ 操作日志 ViewSet 只读,不支持创建、更新、删除操作 """ queryset = OperationLog.objects.all() serializer_class = OperationLogSerializer pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter] filter_class = OperationLogFilter ordering_fields = ['operation_time', 'id'] ordering = ['-operation_time'] # 默认按操作时间倒序排列