第一次提交
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""图片上传与 OCR 提取触发。"""
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
BackgroundTasks,
|
||||
Depends,
|
||||
HTTPException,
|
||||
Request,
|
||||
UploadFile,
|
||||
status,
|
||||
)
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db import get_db
|
||||
from app.deps import get_current_user
|
||||
from app.models.image import Image
|
||||
from app.models.job import ProcessingJob
|
||||
from app.models.user import User
|
||||
from app.ratelimit import AI_LIMIT, UPLOAD_LIMIT, limiter, user_key
|
||||
from app.schemas.job import ImageOut, JobOut
|
||||
from app.services import storage
|
||||
from app.services.extract_pipeline import run_extract_job
|
||||
|
||||
router = APIRouter(prefix="/api/images", tags=["images"])
|
||||
|
||||
|
||||
@router.post("", response_model=ImageOut, status_code=status.HTTP_201_CREATED)
|
||||
@limiter.limit(UPLOAD_LIMIT, key_func=user_key)
|
||||
async def upload(
|
||||
request: Request,
|
||||
file: UploadFile,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current: User = Depends(get_current_user),
|
||||
):
|
||||
data = await file.read()
|
||||
try:
|
||||
mime, ext = storage.validate_image(data)
|
||||
except storage.ImageValidationError as e:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(e))
|
||||
|
||||
sha = storage.sha256_hex(data)
|
||||
# 去重:同用户同图直接复用
|
||||
existing = await db.scalar(
|
||||
select(Image).where(Image.user_id == current.id, Image.sha256 == sha)
|
||||
)
|
||||
if existing:
|
||||
return existing
|
||||
|
||||
try:
|
||||
key = await storage.upload_image_async(current.id, data, ext, mime)
|
||||
except storage.StorageError as e:
|
||||
raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, detail=str(e))
|
||||
|
||||
image = Image(
|
||||
user_id=current.id,
|
||||
object_key=key,
|
||||
mime=mime,
|
||||
size_bytes=len(data),
|
||||
sha256=sha,
|
||||
)
|
||||
db.add(image)
|
||||
await db.commit()
|
||||
await db.refresh(image)
|
||||
return image
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{image_id}/extract",
|
||||
response_model=JobOut,
|
||||
status_code=status.HTTP_202_ACCEPTED,
|
||||
)
|
||||
@limiter.limit(AI_LIMIT, key_func=user_key)
|
||||
async def extract(
|
||||
request: Request,
|
||||
image_id: str,
|
||||
background: BackgroundTasks,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current: User = Depends(get_current_user),
|
||||
):
|
||||
image = await db.scalar(
|
||||
select(Image).where(Image.id == image_id, Image.user_id == current.id)
|
||||
)
|
||||
if image is None:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="图片不存在")
|
||||
|
||||
job = ProcessingJob(user_id=current.id, image_id=image.id, kind="ocr_extract")
|
||||
db.add(job)
|
||||
await db.commit()
|
||||
await db.refresh(job)
|
||||
|
||||
background.add_task(
|
||||
run_extract_job, job.id, current.id, image.object_key, image.mime
|
||||
)
|
||||
return job
|
||||
Reference in New Issue
Block a user