189 lines
4.9 KiB
Python
189 lines
4.9 KiB
Python
"""
|
||
意图编制 API 路由
|
||
"""
|
||
|
||
from typing import List, Optional
|
||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from sqlalchemy import select
|
||
from pydantic import BaseModel
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
from app.database import get_db
|
||
from app.models.intent import Intent, AICheckRecord
|
||
from planner.planning_agent.input_pipeline import parse_intent_file
|
||
import tempfile
|
||
|
||
router = APIRouter(prefix="/intent", tags=["意图编制"])
|
||
|
||
|
||
# ============ Pydantic Schemas ============
|
||
|
||
class IntentCreate(BaseModel):
|
||
"""创建意图请求"""
|
||
title: str
|
||
content: Optional[str] = None
|
||
status: str = "draft"
|
||
|
||
|
||
class IntentUpdate(BaseModel):
|
||
"""更新意图请求"""
|
||
title: Optional[str] = None
|
||
content: Optional[str] = None
|
||
status: Optional[str] = None
|
||
|
||
|
||
class IntentResponse(BaseModel):
|
||
"""意图响应"""
|
||
id: int
|
||
title: str
|
||
content: Optional[str]
|
||
status: str
|
||
created_at: datetime
|
||
updated_at: datetime
|
||
|
||
class Config:
|
||
from_attributes = True
|
||
|
||
|
||
class IntentListResponse(BaseModel):
|
||
"""意图列表响应"""
|
||
total: int
|
||
items: List[IntentResponse]
|
||
|
||
|
||
# ============ API Endpoints ============
|
||
|
||
@router.post("", response_model=IntentResponse, status_code=status.HTTP_201_CREATED)
|
||
async def create_intent(
|
||
intent_data: IntentCreate,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""创建新意图"""
|
||
intent = Intent(
|
||
title=intent_data.title,
|
||
content=intent_data.content,
|
||
status=intent_data.status
|
||
)
|
||
db.add(intent)
|
||
await db.commit()
|
||
await db.refresh(intent)
|
||
return intent
|
||
|
||
|
||
@router.get("", response_model=IntentListResponse)
|
||
async def list_intents(
|
||
skip: int = 0,
|
||
limit: int = 20,
|
||
status_filter: Optional[str] = None,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""获取意图列表"""
|
||
query = select(Intent)
|
||
|
||
if status_filter:
|
||
query = query.where(Intent.status == status_filter)
|
||
|
||
query = query.order_by(Intent.updated_at.desc())
|
||
|
||
# 获取总数
|
||
count_query = select(Intent)
|
||
if status_filter:
|
||
count_query = count_query.where(Intent.status == status_filter)
|
||
result = await db.execute(count_query)
|
||
total = len(result.scalars().all())
|
||
|
||
# 获取分页数据
|
||
query = query.offset(skip).limit(limit)
|
||
result = await db.execute(query)
|
||
items = result.scalars().all()
|
||
|
||
return IntentListResponse(total=total, items=items)
|
||
|
||
|
||
@router.get("/{intent_id}", response_model=IntentResponse)
|
||
async def get_intent(
|
||
intent_id: int,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""获取单个意图"""
|
||
result = await db.execute(select(Intent).where(Intent.id == intent_id))
|
||
intent = result.scalar_one_or_none()
|
||
|
||
if not intent:
|
||
raise HTTPException(status_code=404, detail="意图不存在")
|
||
|
||
return intent
|
||
|
||
|
||
@router.put("/{intent_id}", response_model=IntentResponse)
|
||
async def update_intent(
|
||
intent_id: int,
|
||
intent_data: IntentUpdate,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""更新意图"""
|
||
result = await db.execute(select(Intent).where(Intent.id == intent_id))
|
||
intent = result.scalar_one_or_none()
|
||
|
||
if not intent:
|
||
raise HTTPException(status_code=404, detail="意图不存在")
|
||
|
||
if intent_data.title is not None:
|
||
intent.title = intent_data.title
|
||
if intent_data.content is not None:
|
||
intent.content = intent_data.content
|
||
if intent_data.status is not None:
|
||
intent.status = intent_data.status
|
||
|
||
intent.updated_at = datetime.utcnow()
|
||
|
||
await db.commit()
|
||
await db.refresh(intent)
|
||
return intent
|
||
|
||
|
||
@router.delete("/{intent_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||
async def delete_intent(
|
||
intent_id: int,
|
||
db: AsyncSession = Depends(get_db)
|
||
):
|
||
"""删除意图"""
|
||
result = await db.execute(select(Intent).where(Intent.id == intent_id))
|
||
intent = result.scalar_one_or_none()
|
||
|
||
if not intent:
|
||
raise HTTPException(status_code=404, detail="意图不存在")
|
||
|
||
await db.delete(intent)
|
||
await db.commit()
|
||
return None
|
||
|
||
|
||
@router.post("/parse-file")
|
||
async def parse_intent_file_endpoint(file: UploadFile = File(...)):
|
||
"""解析意图编制导入文件(pdf/图片走 MinerU,文本直接读取)"""
|
||
if not file.filename:
|
||
raise HTTPException(status_code=400, detail="未提供文件")
|
||
|
||
suffix = Path(file.filename).suffix.lower()
|
||
|
||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||
tmp_path = Path(tmp_dir) / file.filename
|
||
content = await file.read()
|
||
tmp_path.write_bytes(content)
|
||
|
||
try:
|
||
result = parse_intent_file(str(tmp_path))
|
||
except Exception as e:
|
||
raise HTTPException(status_code=400, detail=str(e))
|
||
|
||
return {
|
||
"filename": file.filename,
|
||
"suffix": suffix,
|
||
"title": result.get("title", ""),
|
||
"content": result.get("content", ""),
|
||
"raw_result": result.get("raw_result"),
|
||
}
|