feat: 性能优化 + 设备总览轨迹展示 + 广播指令API
性能: SQLite WAL模式、aiohttp Session复用、TCP连接锁+空闲超时、 device_id缓存、WebSocket并发广播、API Key认证缓存、围栏N+1查询 批量化、逆地理编码并行化、新增5个DB索引、日志降级DEBUG 功能: 广播指令API(broadcast)、exclude_type低精度后端过滤、 前端设备总览Tab+多设备轨迹叠加+高亮联动+搜索+专属颜色 via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
14
app/main.py
14
app/main.py
@@ -89,9 +89,14 @@ async def lifespan(app: FastAPI):
|
||||
for stmt in [
|
||||
"CREATE INDEX IF NOT EXISTS ix_alarm_type ON alarm_records(alarm_type)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_alarm_ack ON alarm_records(acknowledged)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_alarm_source ON alarm_records(alarm_source)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_bt_beacon_mac ON bluetooth_records(beacon_mac)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_loc_type ON location_records(location_type)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_att_type ON attendance_records(attendance_type)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_att_source ON attendance_records(attendance_source)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_att_device_type_time ON attendance_records(device_id, attendance_type, recorded_at)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_device_status ON devices(status)",
|
||||
"CREATE INDEX IF NOT EXISTS ix_fence_active ON fence_configs(is_active)",
|
||||
]:
|
||||
await conn.execute(sa_text(stmt))
|
||||
logger.info("Database indexes verified/created")
|
||||
@@ -107,6 +112,9 @@ async def lifespan(app: FastAPI):
|
||||
logger.info("Shutting down TCP server...")
|
||||
await tcp_manager.stop()
|
||||
tcp_task.cancel()
|
||||
# Close shared HTTP session
|
||||
from app.geocoding import close_http_session
|
||||
await close_http_session()
|
||||
|
||||
app = FastAPI(
|
||||
title="KKS Badge Management System / KKS工牌管理系统",
|
||||
@@ -186,12 +194,14 @@ app.include_router(geocoding.router, dependencies=[*_api_deps])
|
||||
_STATIC_DIR = Path(__file__).parent / "static"
|
||||
app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static")
|
||||
|
||||
# Cache admin.html in memory at startup (avoid disk read per request)
|
||||
_admin_html_cache: str = (_STATIC_DIR / "admin.html").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@app.get("/admin", response_class=HTMLResponse, tags=["Admin"])
|
||||
async def admin_page():
|
||||
"""管理后台页面 / Admin Dashboard"""
|
||||
html_path = _STATIC_DIR / "admin.html"
|
||||
return HTMLResponse(content=html_path.read_text(encoding="utf-8"))
|
||||
return HTMLResponse(content=_admin_html_cache)
|
||||
|
||||
|
||||
@app.get("/", tags=["Root"])
|
||||
|
||||
Reference in New Issue
Block a user