views.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. from django.utils import timezone
  2. from .models import OperationLog
  3. from rest_framework import viewsets
  4. from rest_framework.filters import OrderingFilter
  5. from django_filters.rest_framework import DjangoFilterBackend
  6. from utils.page import MyPageNumberPagination
  7. from .serializers import OperationLogSerializer
  8. from .filter import OperationLogFilter
  9. class OperationLogger:
  10. @staticmethod
  11. def log_operation(request, operation_content, operation_level, operator=None,
  12. operation_result='success', module_name=None, object_id=None):
  13. """
  14. 记录操作日志
  15. Args:
  16. request: HttpRequest对象
  17. operation_content: 操作内容描述
  18. operation_level: 操作级别
  19. operator: 操作者标识(用户名等),如果为None则尝试从request中获取
  20. operation_result: 操作结果,'success'或'failure'
  21. module_name: 模块名称,用于分类
  22. object_id: 操作对象ID
  23. """
  24. try:
  25. # 验证必需参数
  26. if not request or not operation_content or not operation_level:
  27. raise ValueError("缺少必需的参数: request, operation_content, operation_level")
  28. # 获取操作者标识
  29. operator_identifier = OperationLogger._get_operator_identifier(request, operator)
  30. # 获取模块名称(如果没有提供则从请求路径推断)
  31. if not module_name:
  32. module_name = OperationLogger._infer_module_name(request.path)
  33. # 获取客户端信息
  34. ip_address = OperationLogger._get_client_ip(request)
  35. user_agent = request.META.get('HTTP_USER_AGENT', '')[:500] # 限制长度
  36. # 创建日志记录
  37. OperationLog.objects.create(
  38. operator=operator_identifier,
  39. operation_content=operation_content,
  40. operation_level=operation_level,
  41. operation_result=operation_result,
  42. ip_address=ip_address,
  43. user_agent=user_agent,
  44. request_method=request.method,
  45. request_path=request.path,
  46. module_name=module_name,
  47. object_id=object_id
  48. )
  49. except Exception as e:
  50. # 日志记录失败不应影响主要业务逻辑,但需要记录错误
  51. import logging
  52. logger = logging.getLogger(__name__)
  53. logger.error(f"操作日志记录失败: {str(e)}", exc_info=True)
  54. @staticmethod
  55. def _get_operator_identifier(request, operator):
  56. """获取操作者标识"""
  57. # 如果提供了operator参数,直接使用
  58. if operator is not None:
  59. return str(operator)
  60. # 优先尝试从request.auth获取(系统的自定义认证方式)
  61. if hasattr(request, 'auth') and request.auth:
  62. # 如果auth对象有name属性,优先使用
  63. if hasattr(request.auth, 'name') and request.auth.name:
  64. return str(request.auth.name)
  65. # 如果有openid属性
  66. elif hasattr(request.auth, 'openid') and request.auth.openid:
  67. return f"用户OpenID:{request.auth.openid}"
  68. # 如果auth本身是字符串
  69. elif isinstance(request.auth, str):
  70. return request.auth
  71. # 尝试从request.user获取(标准Django认证)
  72. if hasattr(request, 'user') and request.user.is_authenticated:
  73. # 优先使用username,如果没有则使用其他标识
  74. if hasattr(request.user, 'username') and request.user.username:
  75. return request.user.username
  76. elif hasattr(request.user, 'get_username'):
  77. username = request.user.get_username()
  78. if username:
  79. return username
  80. if hasattr(request.user, 'id'):
  81. return f"用户ID:{request.user.id}"
  82. # 如果都没有,返回未知用户
  83. return "未知用户"
  84. @staticmethod
  85. def _infer_module_name(path):
  86. """从路径推断模块名称"""
  87. if not path:
  88. return 'unknown'
  89. # 移除首尾斜杠并分割路径
  90. path_parts = path.strip('/').split('/')
  91. if path_parts:
  92. # 使用第一个非空路径部分作为模块名
  93. for part in path_parts:
  94. if part and part not in ('api', 'v1', 'v2'): # 跳过常见的API前缀
  95. return part
  96. return 'unknown'
  97. @staticmethod
  98. def _get_client_ip(request):
  99. """获取客户端IP地址"""
  100. x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
  101. if x_forwarded_for:
  102. ip = x_forwarded_for.split(',')[0].strip()
  103. else:
  104. ip = request.META.get('REMOTE_ADDR')
  105. return ip
  106. # 快捷函数
  107. def log_operation(request, operation_content, operation_level, operator=None,
  108. operation_result='success', module_name=None, object_id=None):
  109. """快捷记录操作日志函数"""
  110. OperationLogger.log_operation(
  111. request=request,
  112. operation_content=operation_content,
  113. operation_level=operation_level,
  114. operator=operator,
  115. operation_result=operation_result,
  116. module_name=module_name,
  117. object_id=object_id
  118. )
  119. # 专门用于成功操作的快捷函数
  120. def log_success_operation(request, operation_content, operation_level, operator=None,
  121. module_name=None, object_id=None):
  122. """记录成功操作的快捷函数"""
  123. log_operation(
  124. request=request,
  125. operation_content=operation_content,
  126. operation_level=operation_level,
  127. operator=operator,
  128. operation_result='success',
  129. module_name=module_name,
  130. object_id=object_id
  131. )
  132. # 专门用于失败操作的快捷函数
  133. def log_failure_operation(request, operation_content, operation_level, operator=None,
  134. module_name=None, object_id=None):
  135. """记录失败操作的快捷函数"""
  136. log_operation(
  137. request=request,
  138. operation_content=operation_content,
  139. operation_level=operation_level,
  140. operator=operator,
  141. operation_result='failure',
  142. module_name=module_name,
  143. object_id=object_id
  144. )
  145. class OperationLogViewSet(viewsets.ReadOnlyModelViewSet):
  146. """
  147. 操作日志 ViewSet
  148. 只读,不支持创建、更新、删除操作
  149. """
  150. queryset = OperationLog.objects.all()
  151. serializer_class = OperationLogSerializer
  152. pagination_class = MyPageNumberPagination
  153. filter_backends = [DjangoFilterBackend, OrderingFilter]
  154. filter_class = OperationLogFilter
  155. ordering_fields = ['operation_time', 'id']
  156. ordering = ['-operation_time'] # 默认按操作时间倒序排列