feat: 信标设备绑定 + 蓝牙模式管理 + 系统管理增强 + 数据导出
- 新增 DeviceBeaconBinding 模型,信标-设备多对多绑定 CRUD - 蓝牙打卡模式批量配置/恢复正常模式 API - 反向同步: 查询设备 BTMACSET 配置更新数据库绑定 (独立 session 解决事务隔离) - 设备列表快捷操作弹窗修复 (fire-and-forget IIFE 替代阻塞轮询) - 保存按钮防抖: 围栏/信标绑定保存点击后 disabled + 转圈防重复提交 - 审计日志中间件 + 系统配置/备份/固件 API - 设备分组管理 + 告警规则配置 - 5个数据导出 API (CSV UTF-8 BOM) - 位置热力图 + 告警条件删除 + 位置清理 via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -6,9 +6,13 @@ API endpoints for device CRUD operations and statistics.
|
||||
import math
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
from fastapi.responses import Response
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
from app.models import Device
|
||||
from app.services.export_utils import build_csv_content, csv_filename
|
||||
from app.schemas import (
|
||||
APIResponse,
|
||||
BatchDeviceCreateRequest,
|
||||
@@ -74,6 +78,40 @@ async def device_stats(db: AsyncSession = Depends(get_db)):
|
||||
return APIResponse(data=stats)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/export",
|
||||
summary="导出设备列表 CSV / Export devices CSV",
|
||||
)
|
||||
async def export_devices(
|
||||
status: str | None = Query(default=None, description="状态过滤 (online/offline)"),
|
||||
search: str | None = Query(default=None, description="搜索IMEI或名称"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""导出设备列表为 CSV 文件,支持状态/搜索筛选。"""
|
||||
from sqlalchemy import or_
|
||||
|
||||
query = select(Device)
|
||||
if status:
|
||||
query = query.where(Device.status == status)
|
||||
if search:
|
||||
pattern = f"%{search}%"
|
||||
query = query.where(or_(Device.imei.ilike(pattern), Device.name.ilike(pattern)))
|
||||
query = query.order_by(Device.id)
|
||||
|
||||
result = await db.execute(query)
|
||||
devices = list(result.scalars().all())
|
||||
|
||||
headers = ["ID", "IMEI", "名称", "类型", "状态", "电量%", "信号", "ICCID", "IMSI", "最后心跳", "最后登录", "创建时间"]
|
||||
fields = ["id", "imei", "name", "device_type", "status", "battery_level", "gsm_signal", "iccid", "imsi", "last_heartbeat", "last_login", "created_at"]
|
||||
|
||||
content = build_csv_content(headers, devices, fields)
|
||||
return Response(
|
||||
content=content,
|
||||
media_type="text/csv; charset=utf-8",
|
||||
headers={"Content-Disposition": f"attachment; filename={csv_filename('devices')}"},
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/imei/{imei}",
|
||||
response_model=APIResponse[DeviceResponse],
|
||||
|
||||
Reference in New Issue
Block a user