""" Bluetooth Router - 蓝牙数据接口 API endpoints for querying Bluetooth punch and location 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 BluetoothRecord from app.schemas import ( APIResponse, BluetoothRecordResponse, PaginatedList, ) from app.services import device_service router = APIRouter(prefix="/api/bluetooth", tags=["Bluetooth / 蓝牙数据"]) @router.get( "", response_model=APIResponse[PaginatedList[BluetoothRecordResponse]], summary="获取蓝牙记录列表 / List bluetooth records", ) async def list_bluetooth_records( device_id: int | None = Query(default=None, description="设备ID / Device ID"), record_type: str | None = Query(default=None, description="记录类型 / Record type (punch/location)"), 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 Bluetooth records with filters for device, record type, and time range. """ query = select(BluetoothRecord) count_query = select(func.count(BluetoothRecord.id)) if device_id is not None: query = query.where(BluetoothRecord.device_id == device_id) count_query = count_query.where(BluetoothRecord.device_id == device_id) if record_type: query = query.where(BluetoothRecord.record_type == record_type) count_query = count_query.where(BluetoothRecord.record_type == record_type) if start_time: query = query.where(BluetoothRecord.recorded_at >= start_time) count_query = count_query.where(BluetoothRecord.recorded_at >= start_time) if end_time: query = query.where(BluetoothRecord.recorded_at <= end_time) count_query = count_query.where(BluetoothRecord.recorded_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(BluetoothRecord.recorded_at.desc()).offset(offset).limit(page_size) result = await db.execute(query) records = list(result.scalars().all()) return APIResponse( data=PaginatedList( items=[BluetoothRecordResponse.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( "/device/{device_id}", response_model=APIResponse[PaginatedList[BluetoothRecordResponse]], summary="获取设备蓝牙记录 / Get bluetooth records for device", ) async def device_bluetooth_records( device_id: int, record_type: str | None = Query(default=None, description="记录类型 / Record type (punch/location)"), start_time: datetime | None = Query(default=None, description="开始时间 / Start time"), end_time: datetime | None = Query(default=None, description="结束时间 / End time"), page: int = Query(default=1, ge=1, description="页码"), page_size: int = Query(default=20, ge=1, le=100, description="每页数量"), db: AsyncSession = Depends(get_db), ): """ 获取指定设备的蓝牙数据记录。 Get Bluetooth records for a specific device. """ device = await device_service.get_device(db, device_id) if device is None: raise HTTPException(status_code=404, detail=f"Device {device_id} not found / 未找到设备{device_id}") query = select(BluetoothRecord).where(BluetoothRecord.device_id == device_id) count_query = select(func.count(BluetoothRecord.id)).where(BluetoothRecord.device_id == device_id) if record_type: query = query.where(BluetoothRecord.record_type == record_type) count_query = count_query.where(BluetoothRecord.record_type == record_type) if start_time: query = query.where(BluetoothRecord.recorded_at >= start_time) count_query = count_query.where(BluetoothRecord.recorded_at >= start_time) if end_time: query = query.where(BluetoothRecord.recorded_at <= end_time) count_query = count_query.where(BluetoothRecord.recorded_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(BluetoothRecord.recorded_at.desc()).offset(offset).limit(page_size) result = await db.execute(query) records = list(result.scalars().all()) return APIResponse( data=PaginatedList( items=[BluetoothRecordResponse.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, ) ) # NOTE: /{record_id} must be after /device/{device_id} to avoid route conflicts @router.get( "/{record_id}", response_model=APIResponse[BluetoothRecordResponse], summary="获取蓝牙记录详情 / Get bluetooth record", ) async def get_bluetooth_record(record_id: int, db: AsyncSession = Depends(get_db)): """按ID获取蓝牙记录详情 / Get bluetooth record details by ID.""" result = await db.execute( select(BluetoothRecord).where(BluetoothRecord.id == record_id) ) record = result.scalar_one_or_none() if record is None: raise HTTPException(status_code=404, detail=f"Bluetooth record {record_id} not found") return APIResponse(data=BluetoothRecordResponse.model_validate(record))