""" REST API 数据接入模块 支持 IoT 设备数据、手动录入和 API 批量导入 """ from fastapi import FastAPI, HTTPException, Depends from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from typing import List, Optional, Dict, Any import uvicorn import asyncio import json from datetime import datetime # 导入BI模块 from ..bi.controllers import router as bi_router from .bi_api import router as bi_api_router # 将BI路由添加到主应用 app.include_router(bi_router) app.include_router(bi_api_router) # 创建FastAPI应用 app = FastAPI(title="Water Management System Data API", version="1.0.0") # CORS配置 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 数据模型 class IoTData(BaseModel): device_id: str data_type: str # "LL", "YL", "SW", "ZD" 等 value: float timestamp: datetime location: str class ManualInputData(BaseModel): source: str data_type: str value: float timestamp: datetime operator: str notes: Optional[str] = None class BatchImportRequest(BaseModel): batch_id: str data_source: str records: List[Dict[str, Any]] # 数据存储(示例,实际应该用数据库) data_store = [] @app.get("/") async def root(): """API根路径""" return {"message": "Water Management System API", "version": "1.0.0"} @app.post("/api/iot/data") async def receive_iot_data(data: IoTData): """接收IoT设备数据""" try: data_dict = data.dict() data_store.append({ **data_dict, "id": len(data_store) + 1, "type": "iot" }) return {"status": "success", "id": len(data_store), "message": "IoT data received"} except Exception as e: raise HTTPException(status_code=400, detail=str(e)) @app.post("/api/manual/data") async def receive_manual_data(data: ManualInputData): """接收手动录入数据""" try: data_dict = data.dict() data_store.append({ **data_dict, "id": len(data_store) + 1, "type": "manual" }) return {"status": "success", "id": len(data_store), "message": "Manual data received"} except Exception as e: raise HTTPException(status_code=400, detail=str(e)) @app.post("/api/batch/import") async def batch_import(request: BatchImportRequest): """批量导入数据""" try: imported_count = 0 failed_count = 0 for record in request.records: # 验证记录 if not all(k in record for k in ['device_id', 'data_type', 'value']): failed_count += 1 continue # 创建数据对象 data_record = { "device_id": record['device_id'], "data_type": record['data_type'], "value": float(record['value']), "timestamp": record.get('timestamp', datetime.now()), "location": record.get('location', 'unknown'), "batch_id": request.batch_id, "source": request.data_source, "id": len(data_store) + 1, "type": "batch" } data_store.append(data_record) imported_count += 1 return { "status": "success", "imported_count": imported_count, "failed_count": failed_count, "message": f"Batch import completed: {imported_count} records imported, {failed_count} failed" } except Exception as e: raise HTTPException(status_code=400, detail=str(e)) @app.get("/api/data/{data_type}") async def get_data_by_type(data_type: str, limit: int = 100, offset: int = 0): """根据数据类型获取数据""" filtered_data = [ item for item in data_store if item.get('data_type') == data_type ] return { "data": filtered_data[offset:offset+limit], "total": len(filtered_data), "limit": limit, "offset": offset } @app.get("/api/data/recent") async def get_recent_data(hours: int = 24, limit: int = 100): """获取最近的数据""" from datetime import timedelta cutoff_time = datetime.now() - timedelta(hours=hours) recent_data = [ item for item in data_store if item.get('timestamp', datetime.now()) > cutoff_time ] return { "data": recent_data[-limit:], "total": len(recent_data), "hours": hours, "limit": limit } @app.get("/api/stats") async def get_statistics(): """获取数据统计信息""" stats = { "total_records": len(data_store), "by_type": {}, "by_device": {}, "by_hour": {} } for item in data_store: data_type = item.get('data_type', 'unknown') device_id = item.get('device_id', 'unknown') hour = item.get('timestamp', datetime.now()).strftime('%Y-%m-%d %H:00:00') stats['by_type'][data_type] = stats['by_type'].get(data_type, 0) + 1 stats['by_device'][device_id] = stats['by_device'].get(device_id, 0) + 1 stats['by_hour'][hour] = stats['by_hour'].get(hour, 0) + 1 return stats @app.get("/health") async def health_check(): """健康检查""" return {"status": "healthy", "timestamp": datetime.now()} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)