82 lines
2.7 KiB
Python
82 lines
2.7 KiB
Python
# PRD 上传与解析接口
|
|
|
|
import logging
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
from fastapi import APIRouter, UploadFile, File, HTTPException
|
|
|
|
from server.services.prd_manager.service import prd_service
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
ALLOWED_EXTENSIONS = {".md", ".txt", ".docx", ".pdf"}
|
|
_parse_executor = ThreadPoolExecutor(max_workers=2)
|
|
|
|
|
|
@router.post("/upload")
|
|
async def upload_prd(file: UploadFile = File(...)):
|
|
"""上传 PRD 文件,立即返回 PRD ID,后台异步解析"""
|
|
ext = "." + file.filename.rsplit(".", 1)[-1].lower() if "." in file.filename else ""
|
|
if ext not in ALLOWED_EXTENSIONS:
|
|
raise HTTPException(400, f"不支持的文件格式: {ext},支持: {', '.join(ALLOWED_EXTENSIONS)}")
|
|
|
|
content = await file.read()
|
|
if len(content) == 0:
|
|
raise HTTPException(400, "文件为空")
|
|
|
|
try:
|
|
# Quick: save and return immediately
|
|
prd_version = await prd_service.upload_quick(file.filename, content)
|
|
|
|
# Background: parse async (image analysis may take 60s+)
|
|
_parse_executor.submit(_parse_in_background, prd_version["prd_id"], file.filename, content)
|
|
|
|
return {
|
|
"prd_id": prd_version["prd_id"],
|
|
"filename": prd_version["filename"],
|
|
"status": "parsing",
|
|
"uploaded_at": prd_version["uploaded_at"],
|
|
}
|
|
except Exception as e:
|
|
logger.error("PRD upload failed: %s", e)
|
|
raise HTTPException(500, f"上传失败: {e}")
|
|
|
|
|
|
def _parse_in_background(prd_id: str, filename: str, content: bytes):
|
|
"""Run full parsing (text + images + vision analysis) in background thread."""
|
|
import asyncio
|
|
try:
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
loop.run_until_complete(prd_service.parse_async(prd_id, filename, content))
|
|
except Exception as e:
|
|
logger.error("Background parse failed for %s: %s", prd_id, e)
|
|
prd_service.set_status(prd_id, "error", str(e))
|
|
|
|
|
|
@router.get("/{prd_id}/status")
|
|
async def get_prd_status(prd_id: str):
|
|
"""获取 PRD 解析状态"""
|
|
status = await prd_service.get_status(prd_id)
|
|
if not status:
|
|
raise HTTPException(404, "PRD not found")
|
|
return status
|
|
|
|
|
|
@router.get("/{prd_id}")
|
|
async def get_prd(prd_id: str):
|
|
"""获取 PRD 详情与版本"""
|
|
prd = await prd_service.get_prd(prd_id)
|
|
if not prd:
|
|
raise HTTPException(404, "PRD not found")
|
|
return {
|
|
"prd_id": prd["prd_id"],
|
|
"filename": prd["filename"],
|
|
"status": prd.get("status", "ready"),
|
|
"full_text": prd.get("full_text", ""),
|
|
"sections": prd.get("sections", []),
|
|
"images": prd.get("images", []),
|
|
"uploaded_at": prd["uploaded_at"],
|
|
}
|