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:
55
app/routers/geocoding.py
Normal file
55
app/routers/geocoding.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""Geocoding proxy endpoints — keeps AMAP_KEY server-side."""
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
import httpx
|
||||
|
||||
from app.config import settings
|
||||
from app.geocoding import reverse_geocode, _amap_sign, AMAP_KEY
|
||||
|
||||
router = APIRouter(prefix="/api/geocode", tags=["geocoding"])
|
||||
|
||||
|
||||
@router.get("/search")
|
||||
async def search_location(
|
||||
keyword: str = Query(..., min_length=1, max_length=100),
|
||||
city: str = Query(default="", max_length=50),
|
||||
):
|
||||
"""Proxy for Amap POI text search. Returns GCJ-02 coordinates."""
|
||||
if not AMAP_KEY:
|
||||
return {"results": []}
|
||||
params = {
|
||||
"key": AMAP_KEY,
|
||||
"keywords": keyword,
|
||||
"output": "json",
|
||||
"offset": "10",
|
||||
"page": "1",
|
||||
}
|
||||
if city:
|
||||
params["city"] = city
|
||||
sig = _amap_sign(params)
|
||||
if sig:
|
||||
params["sig"] = sig
|
||||
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||
resp = await client.get("https://restapi.amap.com/v3/place/text", params=params)
|
||||
data = resp.json()
|
||||
if data.get("status") != "1":
|
||||
return {"results": []}
|
||||
results = []
|
||||
for poi in data.get("pois", [])[:10]:
|
||||
if poi.get("location"):
|
||||
results.append({
|
||||
"name": poi.get("name", ""),
|
||||
"address": poi.get("address", ""),
|
||||
"location": poi["location"], # "lng,lat" in GCJ-02
|
||||
})
|
||||
return {"results": results}
|
||||
|
||||
|
||||
@router.get("/reverse")
|
||||
async def reverse_geocode_endpoint(
|
||||
lat: float = Query(..., ge=-90, le=90),
|
||||
lon: float = Query(..., ge=-180, le=180),
|
||||
):
|
||||
"""Reverse geocode WGS-84 coords to address via Amap."""
|
||||
address = await reverse_geocode(lat, lon)
|
||||
return {"address": address or ""}
|
||||
Reference in New Issue
Block a user