from django.shortcuts import render, get_object_or_404 from django.http import JsonResponse from django.db.models import Count, Case, When, Q, Subquery, OuterRef from django.views.decorators.csrf import csrf_exempt from django.utils import timezone import json from .models import ServiceGroup, Service, ServiceCheckRecord from .utils import get_or_create_service, get_status_summary, get_service_latest_status, get_service_status_timeline, get_service_response_time_chart_data # 引入loguru库用于日志记录 try: from loguru import logger except ImportError: import logging logger = logging.getLogger(__name__) # 主页视图 def home(request): """首页Dashboard""" summary = get_status_summary() # 获取最近异常的服务 recent_issues = ServiceCheckRecord.objects.filter( status__in=['DOWN', 'UNKNOWN'] ).order_by('-checked_at')[:10] context = { 'summary': summary, 'recent_issues': recent_issues } return render(request, 'status/index.html', context) # 服务列表页 def service_list(request): """服务列表页""" group_filter = request.GET.get('group') status_filter = request.GET.get('status') search_query = request.GET.get('search', '') services = Service.objects.all() if group_filter: services = services.filter(group__name=group_filter) if status_filter: # 根据最新状态筛选 latest_records = ServiceCheckRecord.objects.filter( service=OuterRef('pk') ).order_by('-checked_at') services = services.annotate( latest_status=Subquery(latest_records.values('status')[:1]) ).filter(latest_status=status_filter) if search_query: services = services.filter( Q(name__icontains=search_query) | Q(host__icontains=search_query) | Q(description__icontains=search_query) ) # 获取每个服务的最新状态 latest_records = ServiceCheckRecord.objects.filter( service=OuterRef('pk') ).order_by('-checked_at') services = services.annotate( latest_status=Subquery(latest_records.values('status')[:1]), latest_check_time=Subquery(latest_records.values('checked_at')[:1]), latest_response_time=Subquery(latest_records.values('response_time')[:1]) ) groups = ServiceGroup.objects.all() context = { 'services': services, 'groups': groups, 'current_group': group_filter, 'current_status': status_filter, 'search_query': search_query } return render(request, 'status/service_list.html', context) # 服务详情页 def service_detail(request, service_id): """服务详情页""" service = get_object_or_404(Service, pk=service_id) # 获取最近10条检测记录 recent_records = service.records.all()[:10] # 获取状态变化时间轴数据 status_changes = get_service_status_timeline(service, limit=20) # 获取响应时间趋势图数据 chart_data = get_service_response_time_chart_data(service, hours=24) # 获取服务的最新状态信息 latest_status = get_service_latest_status(service) service.latest_status = latest_status['status'] service.latest_check_time = latest_status['check_time'] service.latest_response_time = latest_status['response_time'] service.latest_message = latest_status['message'] context = { 'service': service, 'recent_records': recent_records, 'status_changes': status_changes, 'chart_labels': chart_data['labels'], 'chart_data': chart_data['data'] } return render(request, 'status/service_detail.html', context) # API视图 - 客户端上报接口 @csrf_exempt def checkin(request): """客户端定期调用此接口上报服务状态""" if request.method != 'POST': return JsonResponse({'code': 405, 'message': '只支持POST请求'}, status=405) try: data = json.loads(request.body) logger.info(f"收到服务状态上报: {data.get('service_name')} - {data.get('status')}") # 验证必要字段 required_fields = ['service_name', 'host', 'check_type', 'status'] for field in required_fields: if field not in data: return JsonResponse({'code': 400, 'message': f'缺少必要字段: {field}'}, status=400) # 获取或创建服务 service = get_or_create_service(data) # 创建检测记录 record = ServiceCheckRecord.objects.create( service=service, status=data['status'], response_time=data.get('response_time'), message=data.get('message', '') ) logger.info(f"服务状态已记录: {service.name} - {record.status}") return JsonResponse({ 'code': 200, 'message': '上报成功', 'service_id': service.id }) except json.JSONDecodeError: return JsonResponse({'code': 400, 'message': '无效的JSON数据'}, status=400) except Exception as e: logger.error(f"处理上报数据时出错: {str(e)}") return JsonResponse({'code': 500, 'message': f'服务器内部错误: {str(e)}'}, status=500) # API视图 - 获取所有服务列表(含最新状态) def api_services(request): """获取所有服务列表(含最新状态)""" # 获取每个服务的最新状态 latest_records = ServiceCheckRecord.objects.filter( service=OuterRef('pk') ).order_by('-checked_at') services = Service.objects.annotate( latest_status=Subquery(latest_records.values('status')[:1]), latest_check_time=Subquery(latest_records.values('checked_at')[:1]), latest_response_time=Subquery(latest_records.values('response_time')[:1]) ) data = [] for service in services: data.append({ 'id': service.id, 'name': service.name, 'group': service.group.name, 'host': service.host, 'port': service.port, 'check_type': service.check_type, 'is_active': service.is_active, 'latest_status': service.latest_status, 'latest_check_time': service.latest_check_time.isoformat() if service.latest_check_time else None, 'latest_response_time': service.latest_response_time }) return JsonResponse(data, safe=False) # API视图 - 获取某服务历史记录(分页) def api_service_history(request, service_id): """获取某服务历史记录(分页)""" service = get_object_or_404(Service, pk=service_id) # 分页参数 page = int(request.GET.get('page', 1)) page_size = int(request.GET.get('page_size', 20)) # 计算分页范围 start = (page - 1) * page_size end = start + page_size records = service.records.all()[start:end] total = service.records.count() data = [] for record in records: data.append({ 'id': record.id, 'status': record.status, 'response_time': record.response_time, 'message': record.message, 'checked_at': record.checked_at.isoformat() }) return JsonResponse({ 'total': total, 'page': page, 'page_size': page_size, 'records': data }) # API视图 - 获取全局状态摘要 def api_status_summary(request): """获取全局状态摘要(如:总共服务数、正常数、异常数)""" summary = get_status_summary() return JsonResponse(summary) # API文档页面 def api_docs(request): """API文档页面""" return render(request, 'status/api_docs.html')