from pathlib import Path from fastapi import FastAPI from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from contextlib import asynccontextmanager from app.database import init_db, async_session from app.tcp_server import tcp_manager from app.config import settings from app.routers import devices, locations, alarms, attendance, commands, bluetooth, beacons import asyncio import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): # Startup logger.info("Initializing database...") await init_db() # Reset all devices to offline on startup (stale state from previous run) try: from sqlalchemy import update from app.models import Device async with async_session() as session: async with session.begin(): await session.execute(update(Device).values(status="offline")) logger.info("All devices reset to offline on startup") except Exception: logger.exception("Failed to reset device statuses on startup") logger.info("Starting TCP server on %s:%d", settings.TCP_HOST, settings.TCP_PORT) tcp_task = asyncio.create_task(tcp_manager.start(settings.TCP_HOST, settings.TCP_PORT)) yield # Shutdown logger.info("Shutting down TCP server...") await tcp_manager.stop() tcp_task.cancel() app = FastAPI( title="KKS Badge Management System / KKS工牌管理系统", description=""" ## KKS P240 & P241 蓝牙工牌管理后台 ### 功能模块 / Features: - **设备管理 / Device Management** - 设备注册、状态监控 - **位置数据 / Location Data** - GPS/LBS/WIFI定位数据查询与轨迹回放 - **报警管理 / Alarm Management** - SOS、围栏、低电等报警处理 - **考勤管理 / Attendance** - 打卡记录查询与统计 - **指令管理 / Commands** - 远程指令下发与留言 - **蓝牙数据 / Bluetooth** - 蓝牙打卡与定位数据 - **信标管理 / Beacons** - 蓝牙信标注册与位置配置 ### 通讯协议 / Protocol: - TCP端口: {tcp_port} (设备连接) - 支持协议: KKS P240/P241 通讯协议 """.format(tcp_port=settings.TCP_PORT), version="1.0.0", docs_url="/docs", redoc_url="/redoc", lifespan=lifespan, ) # Include routers app.include_router(devices.router) app.include_router(locations.router) app.include_router(alarms.router) app.include_router(attendance.router) app.include_router(commands.router) app.include_router(bluetooth.router) app.include_router(beacons.router) _STATIC_DIR = Path(__file__).parent / "static" app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static") @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")) @app.get("/", tags=["Root"]) async def root(): return { "name": settings.APP_NAME, "version": "1.0.0", "docs": "/docs", "redoc": "/redoc", "admin": "/admin", "tcp_port": settings.TCP_PORT, } @app.get("/health", tags=["Root"]) async def health(): return { "status": "healthy", "connected_devices": len(tcp_manager.connections), }