from pathlib import Path from urllib.parse import urlencode from urllib.request import Request, urlopen import json from fastapi import FastAPI, HTTPException from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles app = FastAPI(title="格士立青岛出口本体接口") ROOT = Path(__file__).resolve().parents[1] QUERIES = ROOT / "queries" STATIC = ROOT / "app" / "static" FUSEKI_ENDPOINT = "http://localhost:3030/gesli/sparql" app.mount("/static", StaticFiles(directory=STATIC), name="static") @app.get("/health") def health(): return {"status": "ok"} @app.get("/") def index(): return FileResponse(STATIC / "index.html") @app.get("/api/orders") def orders(): return query_file("order-status.rq") @app.get("/api/risks") def risks(): return query_file("risk-orders.rq") @app.get("/api/flow") def flow(): return query_file("operation-flow.rq") @app.get("/api/node-times") def node_times(): return query_file("node-time-status.rq") @app.get("/api/order-cutoffs") def order_cutoffs(): return query_file("order-cutoff-times.rq") @app.get("/api/exception-map") def exception_map(): return query_file("exception-risk-map.rq") def query_file(filename: str): path = QUERIES / filename if not path.exists(): raise HTTPException(status_code=404, detail=f"Query file not found: {filename}") return run_sparql(path.read_text(encoding="utf-8")) def run_sparql(query: str): data = urlencode({"query": query}).encode("utf-8") request = Request( FUSEKI_ENDPOINT, data=data, headers={ "Accept": "application/sparql-results+json", "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", }, method="POST", ) try: with urlopen(request, timeout=10) as response: payload = json.loads(response.read().decode("utf-8")) except Exception as exc: raise HTTPException( status_code=502, detail="无法连接 Fuseki。请确认 Docker 和 http://localhost:3030/gesli/sparql 正常运行。", ) from exc variables = payload.get("head", {}).get("vars", []) rows = [] for binding in payload.get("results", {}).get("bindings", []): rows.append({ variable: binding.get(variable, {}).get("value", "") for variable in variables }) return {"rows": rows}