from __future__ import annotations from pathlib import Path from fastapi import FastAPI, File, HTTPException, Request, UploadFile from fastapi.responses import FileResponse, HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from pydantic import BaseModel, Field from .config import ROOT_DIR from .job_service import analyze_attachment_urls, job_store, process_uploads app = FastAPI(title="PCF 焊接数据导入生成工具") templates = Jinja2Templates(directory=str(ROOT_DIR / "templates")) app.mount("/static", StaticFiles(directory=str(ROOT_DIR / "static")), name="static") class ParseUrlRequest(BaseModel): attachment_url: str | None = Field(default=None, description="单个附件 URL,支持 .pcf 或 .zip") attachment_urls: list[str] | None = Field(default=None, description="多个附件 URL,支持 .pcf 或 .zip") def urls(self) -> list[str]: urls: list[str] = [] if self.attachment_url: urls.append(self.attachment_url) if self.attachment_urls: urls.extend(self.attachment_urls) return [url for url in urls if url] @app.get("/", response_class=HTMLResponse) async def index(request: Request): return templates.TemplateResponse("index.html", {"request": request}) @app.post("/api/jobs") async def create_job(files: list[UploadFile] = File(...)): if not files: raise HTTPException(status_code=400, detail="请至少上传一个 PCF 文件") result = await process_uploads(files) return result.to_public_dict() @app.post("/api/AnalysisPcf") async def analysis_pcf(request: ParseUrlRequest): urls = request.urls() if not urls: raise HTTPException(status_code=400, detail="请提供 attachment_url 或 attachment_urls") return await analyze_attachment_urls(urls) @app.get("/api/jobs/{job_id}") async def get_job(job_id: str): result = job_store.get(job_id) if not result: raise HTTPException(status_code=404, detail="任务不存在或服务已重启") return result.to_public_dict() @app.get("/api/jobs/{job_id}/download/{kind}") async def download(job_id: str, kind: str): result = job_store.get(job_id) if not result: raise HTTPException(status_code=404, detail="任务不存在或服务已重启") files = { "xls": (result.output_xls, "application/vnd.ms-excel"), "issues": (result.issues_csv, "text/csv; charset=utf-8"), "report": (result.report_json, "application/json"), } if kind not in files: raise HTTPException(status_code=404, detail="下载类型不存在") path, media_type = files[kind] if not Path(path).exists(): raise HTTPException(status_code=404, detail="文件不存在") return FileResponse(path, media_type=media_type, filename=Path(path).name)