| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- 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'] # 默认按操作时间倒序排列
|