Add WebSocket, multi API key, geocoding proxy, beacon map picker, and comprehensive bug fixes
- Multi API Key + permission system (read/write/admin) with SHA-256 hash - WebSocket real-time push (location, alarm, device_status, attendance, bluetooth) - Geocoding proxy endpoints for Amap POI search and reverse geocode - Beacon modal map-based location picker with search and click-to-select - GCJ-02 ↔ WGS-84 bidirectional coordinate conversion - Data cleanup scheduler (configurable retention days) - Fix GPS longitude sign inversion (course_status bit 11: 0=East, 1=West) - Fix 2G CellID 2→3 bytes across all protocols (0x28, 0x2C, parser.py) - Fix parser loop guards, alarm_source field length, CommandLog.sent_at - Fix geocoding IMEI parameterization, require_admin import - Improve API error messages for 422 validation errors - Remove beacon floor/area fields (consolidated into name) via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -5,6 +5,7 @@ API endpoints for sending commands / messages to devices and viewing command his
|
||||
|
||||
import logging
|
||||
import math
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
from pydantic import BaseModel, Field
|
||||
@@ -21,6 +22,7 @@ from app.schemas import (
|
||||
CommandResponse,
|
||||
PaginatedList,
|
||||
)
|
||||
from app.dependencies import require_write
|
||||
from app.services import command_service, device_service
|
||||
from app.services import tcp_command_service
|
||||
|
||||
@@ -127,6 +129,7 @@ async def _send_to_device(
|
||||
)
|
||||
|
||||
command_log.status = "sent"
|
||||
command_log.sent_at = datetime.now(timezone.utc)
|
||||
await db.flush()
|
||||
await db.refresh(command_log)
|
||||
|
||||
@@ -148,17 +151,18 @@ async def _send_to_device(
|
||||
)
|
||||
async def list_commands(
|
||||
device_id: int | None = Query(default=None, description="设备ID / Device ID"),
|
||||
command_type: str | None = Query(default=None, description="指令类型 / Command type (online_cmd/message/tts)"),
|
||||
status: str | None = Query(default=None, description="指令状态 / Command status (pending/sent/success/failed)"),
|
||||
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 command history with optional device and status filters.
|
||||
获取指令历史记录,支持按设备、指令类型和状态过滤。
|
||||
List command history with optional device, command type and status filters.
|
||||
"""
|
||||
commands, total = await command_service.get_commands(
|
||||
db, device_id=device_id, status=status, page=page, page_size=page_size
|
||||
db, device_id=device_id, command_type=command_type, status=status, page=page, page_size=page_size
|
||||
)
|
||||
return APIResponse(
|
||||
data=PaginatedList(
|
||||
@@ -176,6 +180,7 @@ async def list_commands(
|
||||
response_model=APIResponse[CommandResponse],
|
||||
status_code=201,
|
||||
summary="发送指令 / Send command to device",
|
||||
dependencies=[Depends(require_write)],
|
||||
)
|
||||
async def send_command(body: SendCommandRequest, db: AsyncSession = Depends(get_db)):
|
||||
"""
|
||||
@@ -201,6 +206,7 @@ async def send_command(body: SendCommandRequest, db: AsyncSession = Depends(get_
|
||||
response_model=APIResponse[CommandResponse],
|
||||
status_code=201,
|
||||
summary="发送留言 / Send message to device (0x82)",
|
||||
dependencies=[Depends(require_write)],
|
||||
)
|
||||
async def send_message(body: SendMessageRequest, db: AsyncSession = Depends(get_db)):
|
||||
"""
|
||||
@@ -223,6 +229,7 @@ async def send_message(body: SendMessageRequest, db: AsyncSession = Depends(get_
|
||||
response_model=APIResponse[CommandResponse],
|
||||
status_code=201,
|
||||
summary="语音下发 / Send TTS voice broadcast to device",
|
||||
dependencies=[Depends(require_write)],
|
||||
)
|
||||
async def send_tts(body: SendTTSRequest, db: AsyncSession = Depends(get_db)):
|
||||
"""
|
||||
@@ -249,6 +256,7 @@ async def send_tts(body: SendTTSRequest, db: AsyncSession = Depends(get_db)):
|
||||
response_model=APIResponse[BatchCommandResponse],
|
||||
status_code=201,
|
||||
summary="批量发送指令 / Batch send command to multiple devices",
|
||||
dependencies=[Depends(require_write)],
|
||||
)
|
||||
@limiter.limit(settings.RATE_LIMIT_WRITE)
|
||||
async def batch_send_command(request: Request, body: BatchCommandRequest, db: AsyncSession = Depends(get_db)):
|
||||
@@ -282,6 +290,7 @@ async def batch_send_command(request: Request, body: BatchCommandRequest, db: As
|
||||
device.imei, body.command_type, body.command_content
|
||||
)
|
||||
cmd_log.status = "sent"
|
||||
cmd_log.sent_at = datetime.now(timezone.utc)
|
||||
await db.flush()
|
||||
await db.refresh(cmd_log)
|
||||
results.append(BatchCommandResult(
|
||||
|
||||
Reference in New Issue
Block a user