Merge commit 'd8e4449f1009bc03b167c0e5667413585b2b3e53' as 'core'

This commit is contained in:
Du Wenbo
2026-04-04 18:16:49 +08:00
227 changed files with 39179 additions and 0 deletions

135
core/backend/app/main.py Normal file
View File

@@ -0,0 +1,135 @@
import logging
import uuid
from contextlib import asynccontextmanager
from typing import Optional
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.api.router import api_router
from app.api.v1.websocket import start_broadcast_task, stop_broadcast_task
from app.core.config import get_settings
from app.core.cache import get_redis, close_redis
from app.services.simulator import DataSimulator
from app.services.report_scheduler import start_scheduler, stop_scheduler
from app.services.aggregation import start_aggregation_scheduler, stop_aggregation_scheduler
from app.collectors.manager import CollectorManager
from app.collectors.queue import IngestionWorker
settings = get_settings()
customer_config = settings.load_customer_config()
simulator = DataSimulator()
collector_manager: Optional[CollectorManager] = None
ingestion_worker: Optional[IngestionWorker] = None
logger = logging.getLogger("app")
@asynccontextmanager
async def lifespan(app: FastAPI):
global collector_manager, ingestion_worker
logger.info("Loading customer: %s (%s)", settings.CUSTOMER,
customer_config.get("customer_name", settings.CUSTOMER))
# Initialize Redis cache
if settings.REDIS_ENABLED:
redis = await get_redis()
if redis:
logger.info("Redis cache initialized")
# Start aggregation scheduler
if settings.AGGREGATION_ENABLED:
await start_aggregation_scheduler()
logger.info("Aggregation scheduler started")
# Start ingestion worker
if settings.INGESTION_QUEUE_ENABLED:
ingestion_worker = IngestionWorker()
await ingestion_worker.start()
logger.info("Ingestion worker started")
if settings.USE_SIMULATOR:
logger.info("Starting in SIMULATOR mode")
await simulator.start()
else:
logger.info("Starting in COLLECTOR mode (real IoT devices)")
collector_manager = CollectorManager()
await collector_manager.start()
start_broadcast_task()
await start_scheduler()
yield
await stop_scheduler()
stop_broadcast_task()
if settings.USE_SIMULATOR:
await simulator.stop()
else:
if collector_manager:
await collector_manager.stop()
collector_manager = None
# Stop ingestion worker
if ingestion_worker:
await ingestion_worker.stop()
ingestion_worker = None
# Stop aggregation scheduler
if settings.AGGREGATION_ENABLED:
await stop_aggregation_scheduler()
# Close Redis
if settings.REDIS_ENABLED:
await close_redis()
logger.info("Redis cache closed")
app = FastAPI(
title=customer_config.get("platform_name", "天普零碳园区智慧能源管理平台"),
description=customer_config.get("platform_name_en", "Tianpu Zero-Carbon Park Smart Energy Management System"),
version="1.0.0",
lifespan=lifespan,
)
_default_origins = ["http://localhost:3000", "http://localhost:5173", "http://127.0.0.1:3000", "http://127.0.0.1:5173"]
_customer_origins = customer_config.get("cors_origins", [])
_cors_origins = list(set(_default_origins + _customer_origins))
app.add_middleware(
CORSMiddleware,
allow_origins=_cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def request_id_middleware(request: Request, call_next):
"""Add a unique X-Request-ID header to every response."""
request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
request.state.request_id = request_id
response = await call_next(request)
response.headers["X-Request-ID"] = request_id
return response
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Global exception handler for consistent error responses."""
request_id = getattr(request.state, "request_id", "unknown")
logger.error("Unhandled exception [request_id=%s]: %s", request_id, exc, exc_info=True)
return JSONResponse(
status_code=500,
content={
"detail": "Internal server error",
"request_id": request_id,
},
)
app.include_router(api_router)
@app.get("/health")
async def health():
return {"status": "ok", "app": settings.APP_NAME}