230 lines
8.0 KiB
Python
Executable File
230 lines
8.0 KiB
Python
Executable File
# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/documents.py
|
|
import uuid
|
|
from typing import Any, Dict
|
|
from fastapi import APIRouter, Depends, UploadFile, File, Form, BackgroundTasks, HTTPException, status
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.db.session import get_db
|
|
from app.api.deps import get_current_user
|
|
from app.services.document_service import DocumentService
|
|
from app.models.identity import User
|
|
|
|
router = APIRouter()
|
|
|
|
@router.post("/upload/{parent_type}/{parent_id}")
|
|
async def upload_document(
|
|
parent_type: str,
|
|
parent_id: str,
|
|
background_tasks: BackgroundTasks,
|
|
doc_type: str = Form(..., description="A dokumentum típusa: 'invoice', 'registration_card', 'sale_contract'"),
|
|
file: UploadFile = File(...),
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""
|
|
MB 2.0 Dokumentum Pipeline.
|
|
1. Ellenőrzi a felhasználó havi OCR kvótáját (Admin 2.0).
|
|
2. Optimalizálja és NAS-ra menti a képet (WebP konverzió).
|
|
3. Automatikusan elindítja a Robot 1-et (OCR), ha a típus engedélyezett.
|
|
"""
|
|
|
|
# 1. Bemeneti validáció
|
|
valid_parents = ["organizations", "assets", "transfers"]
|
|
if parent_type not in valid_parents:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Érvénytelen cél-típus! Megengedett: {', '.join(valid_parents)}"
|
|
)
|
|
|
|
try:
|
|
# 2. Feldolgozás a szolgáltatás rétegben
|
|
# Itt történik a kvóta-ellenőrzés és a Robot 1 triggerelése is
|
|
doc = await DocumentService.process_upload(
|
|
db=db,
|
|
user_id=current_user.id,
|
|
file=file,
|
|
parent_type=parent_type,
|
|
parent_id=parent_id,
|
|
doc_type=doc_type,
|
|
background_tasks=background_tasks
|
|
)
|
|
|
|
# 3. Válasz összeállítása az állapot alapján
|
|
response_data = {
|
|
"document_id": doc.id,
|
|
"original_name": doc.original_name,
|
|
"status": doc.status,
|
|
"thumbnail": doc.thumbnail_path,
|
|
}
|
|
|
|
if doc.status == "processing":
|
|
response_data["message"] = "🤖 Robot 1 megkezdte a dokumentum elemzését. Értesítjük, ha kész!"
|
|
else:
|
|
response_data["message"] = "Dokumentum sikeresen archiválva a széfben."
|
|
|
|
return response_data
|
|
|
|
except HTTPException as he:
|
|
# Közvetlenül átengedjük a service-ből jövő (pl. kvóta) hibákat
|
|
raise he
|
|
except ValueError as ve:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(ve))
|
|
except Exception as e:
|
|
# Sentinel naplózás és általános hiba
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="Kritikus hiba a dokumentum feldolgozása során."
|
|
)
|
|
|
|
@router.get("/{document_id}/status")
|
|
async def get_document_status(
|
|
document_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Lekérdezhető, hogy a robot végzett-e már a feldolgozással."""
|
|
# (Itt egy egyszerű lekérdezés a Document táblából a státuszra)
|
|
pass
|
|
|
|
|
|
# RBAC helper function
|
|
def _check_premium_or_admin(user: User) -> bool:
|
|
"""Check if user has premium subscription or admin role."""
|
|
premium_plans = ['PREMIUM', 'PREMIUM_PLUS', 'VIP', 'VIP_PLUS']
|
|
if user.role == 'admin':
|
|
return True
|
|
if hasattr(user, 'subscription_plan') and user.subscription_plan in premium_plans:
|
|
return True
|
|
return False
|
|
|
|
|
|
@router.post("/scan-instant")
|
|
async def scan_instant(
|
|
file: UploadFile = File(...),
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""
|
|
Szinkron végpont (Villámszkenner) - forgalmi/ID dokumentumokhoz.
|
|
Azonnali OCR feldolgozás és válasz.
|
|
RBAC: Csak prémium előfizetés vagy admin.
|
|
"""
|
|
# RBAC ellenőrzés
|
|
if not _check_premium_or_admin(current_user):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Prémium előfizetés szükséges a funkcióhoz"
|
|
)
|
|
|
|
try:
|
|
# 1. Fájl feltöltése MinIO-ba (StorageService segítségével)
|
|
# Jelenleg mock: feltételezzük, hogy a StorageService.upload_file létezik
|
|
from app.services.storage_service import StorageService
|
|
file_url = await StorageService.upload_file(file, prefix="instant_scan")
|
|
|
|
# 2. Mock OCR hívás (valós implementációban AiOcrService-t hívnánk)
|
|
mock_ocr_result = {
|
|
"plate": "TEST-123",
|
|
"vin": "TRX12345",
|
|
"make": "Toyota",
|
|
"model": "Corolla",
|
|
"year": 2022,
|
|
"fuel_type": "petrol",
|
|
"engine_capacity": 1600
|
|
}
|
|
|
|
# 3. Dokumentum rekord létrehozása system.documents táblában
|
|
from app.models import Document
|
|
from datetime import datetime, timezone
|
|
import uuid
|
|
|
|
doc = Document(
|
|
id=uuid.uuid4(),
|
|
user_id=current_user.id,
|
|
original_name=file.filename,
|
|
file_path=file_url,
|
|
file_size=file.size,
|
|
mime_type=file.content_type,
|
|
status='processed',
|
|
ocr_data=mock_ocr_result,
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc)
|
|
)
|
|
db.add(doc)
|
|
await db.commit()
|
|
await db.refresh(doc)
|
|
|
|
# 4. Válasz
|
|
return {
|
|
"document_id": str(doc.id),
|
|
"status": "processed",
|
|
"ocr_result": mock_ocr_result,
|
|
"file_url": file_url,
|
|
"message": "Dokumentum sikeresen feldolgozva"
|
|
}
|
|
|
|
except Exception as e:
|
|
await db.rollback()
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Hiba a dokumentum feldolgozása során: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.post("/upload-async")
|
|
async def upload_async(
|
|
file: UploadFile = File(...),
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""
|
|
Aszinkron végpont (Költség/Számla nyelő) - háttérben futó OCR-nek.
|
|
Azonnali 202 Accepted válasz, pending_ocr státusszal.
|
|
RBAC: Csak prémium előfizetés vagy admin.
|
|
"""
|
|
# RBAC ellenőrzés
|
|
if not _check_premium_or_admin(current_user):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Prémium előfizetés szükséges a funkcióhoz"
|
|
)
|
|
|
|
try:
|
|
# 1. Fájl feltöltése MinIO-ba
|
|
from app.services.storage_service import StorageService
|
|
file_url = await StorageService.upload_file(file, prefix="async_upload")
|
|
|
|
# 2. Dokumentum rekord létrehozása pending_ocr státusszal
|
|
from app.models import Document
|
|
from datetime import datetime, timezone
|
|
import uuid
|
|
|
|
doc = Document(
|
|
id=uuid.uuid4(),
|
|
user_id=current_user.id,
|
|
original_name=file.filename,
|
|
file_path=file_url,
|
|
file_size=file.size,
|
|
mime_type=file.content_type,
|
|
status='pending_ocr', # Fontos: a háttérrobot ezt fogja felvenni
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc)
|
|
)
|
|
db.add(doc)
|
|
await db.commit()
|
|
await db.refresh(doc)
|
|
|
|
# 3. 202 Accepted válasz
|
|
return {
|
|
"document_id": str(doc.id),
|
|
"status": "pending_ocr",
|
|
"message": "A dokumentum feltöltve, háttérben történő elemzése megkezdődött.",
|
|
"file_url": file_url
|
|
}
|
|
|
|
except Exception as e:
|
|
await db.rollback()
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Hiba a dokumentum feltöltése során: {str(e)}"
|
|
) |