立项配置页初步优化

This commit is contained in:
Cheng Zhou
2026-02-24 16:53:22 +08:00
parent 0693f09b1d
commit 8f3f717e48
124 changed files with 12519 additions and 2954 deletions
+81
View File
@@ -1,5 +1,9 @@
import asyncio
import logging
import re
import time
from contextlib import asynccontextmanager
from collections import defaultdict
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
@@ -14,6 +18,12 @@ from app.db.session import SessionLocal, engine
from app.services.fee_seed import seed_demo_fees
from app.services.visit_scheduler import run_daily_lost_visit_job
logger = logging.getLogger("ctms.setup_config")
UUID_RE = re.compile(
r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
)
setup_config_stats: dict[str, int] = defaultdict(int)
@asynccontextmanager
async def lifespan(_: FastAPI):
@@ -100,6 +110,62 @@ def create_app() -> FastAPI:
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def setup_config_monitoring_middleware(request, call_next):
path = request.url.path
is_setup_config_path = "/api/v1/studies/" in path and "/setup-config" in path
started_at = time.perf_counter()
try:
response = await call_next(request)
except Exception:
if is_setup_config_path:
normalized_path = UUID_RE.sub("{study_id}", path)
duration_ms = int((time.perf_counter() - started_at) * 1000)
key = f"{request.method} {normalized_path} 5xx"
setup_config_stats[key] += 1
logger.exception(
"setup_config_request_error method=%s path=%s status=%s duration_ms=%s",
request.method,
normalized_path,
500,
duration_ms,
)
raise
if is_setup_config_path:
normalized_path = UUID_RE.sub("{study_id}", path)
duration_ms = int((time.perf_counter() - started_at) * 1000)
status_code = int(response.status_code)
status_bucket = f"{status_code // 100}xx"
key = f"{request.method} {normalized_path} {status_bucket}"
setup_config_stats[key] += 1
if status_code >= 500:
logger.error(
"setup_config_request method=%s path=%s status=%s duration_ms=%s",
request.method,
normalized_path,
status_code,
duration_ms,
)
elif status_code >= 400:
logger.warning(
"setup_config_request method=%s path=%s status=%s duration_ms=%s",
request.method,
normalized_path,
status_code,
duration_ms,
)
else:
logger.info(
"setup_config_request method=%s path=%s status=%s duration_ms=%s",
request.method,
normalized_path,
status_code,
duration_ms,
)
return response
register_exception_handlers(app)
@app.get(
@@ -111,6 +177,21 @@ def create_app() -> FastAPI:
async def health() -> dict[str, str]:
return {"status": "ok"}
@app.get(
"/health/setup-config-stats",
tags=["health"],
summary="立项配置接口监控统计",
description="返回当前进程内立项配置接口访问计数(按接口+状态段汇总)。",
)
async def setup_config_health_stats() -> dict[str, dict[str, int]]:
summary: dict[str, int] = dict(setup_config_stats)
totals = {
"2xx": sum(v for k, v in summary.items() if k.endswith(" 2xx")),
"4xx": sum(v for k, v in summary.items() if k.endswith(" 4xx")),
"5xx": sum(v for k, v in summary.items() if k.endswith(" 5xx")),
}
return {"totals": totals, "by_endpoint": summary}
app.include_router(api_router, prefix="/api/v1")
return app