""" Heartbeats Router - 心跳数据接口 API endpoints for querying device heartbeat records. """ import math from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.models import HeartbeatRecord from app.schemas import ( APIResponse, HeartbeatRecordResponse, PaginatedList, ) from app.services import device_service router = APIRouter(prefix="/api/heartbeats", tags=["Heartbeats / 心跳数据"]) @router.get( "", response_model=APIResponse[PaginatedList[HeartbeatRecordResponse]], summary="获取心跳记录列表 / List heartbeat records", ) async def list_heartbeats( device_id: int | None = Query(default=None, description="设备ID / Device ID"), start_time: datetime | None = Query(default=None, description="开始时间 / Start time (ISO 8601)"), end_time: datetime | None = Query(default=None, description="结束时间 / End time (ISO 8601)"), page: int = Query(default=1, ge=1, description="页码 / Page number"), page_size: int = Query(default=20, ge=1, le=100, description="每页数量 / Items per page"), db: AsyncSession = Depends(get_db), ): """ 获取心跳记录列表,支持按设备和时间范围过滤。 List heartbeat records with optional device and time range filters. """ query = select(HeartbeatRecord) count_query = select(func.count(HeartbeatRecord.id)) if device_id is not None: query = query.where(HeartbeatRecord.device_id == device_id) count_query = count_query.where(HeartbeatRecord.device_id == device_id) if start_time: query = query.where(HeartbeatRecord.created_at >= start_time) count_query = count_query.where(HeartbeatRecord.created_at >= start_time) if end_time: query = query.where(HeartbeatRecord.created_at <= end_time) count_query = count_query.where(HeartbeatRecord.created_at <= end_time) total_result = await db.execute(count_query) total = total_result.scalar() or 0 offset = (page - 1) * page_size query = query.order_by(HeartbeatRecord.created_at.desc()).offset(offset).limit(page_size) result = await db.execute(query) records = list(result.scalars().all()) return APIResponse( data=PaginatedList( items=[HeartbeatRecordResponse.model_validate(r) for r in records], total=total, page=page, page_size=page_size, total_pages=math.ceil(total / page_size) if total else 0, ) ) @router.get( "/{heartbeat_id}", response_model=APIResponse[HeartbeatRecordResponse], summary="获取心跳详情 / Get heartbeat details", ) async def get_heartbeat(heartbeat_id: int, db: AsyncSession = Depends(get_db)): """ 按ID获取心跳记录详情。 Get heartbeat record details by ID. """ result = await db.execute( select(HeartbeatRecord).where(HeartbeatRecord.id == heartbeat_id) ) record = result.scalar_one_or_none() if record is None: raise HTTPException(status_code=404, detail=f"Heartbeat {heartbeat_id} not found") return APIResponse(data=HeartbeatRecordResponse.model_validate(record))