100 lines
3.2 KiB
Python
100 lines
3.2 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
|
from sqlalchemy import or_, select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.db.session import get_db
|
|
from app.models import Document, KnowledgeItem, Source
|
|
from app.schemas.catalog import AuditRead, DocumentCreate, DocumentRead, DocumentUpdate, Page
|
|
from app.services.catalog import (
|
|
ensure_version,
|
|
get_item,
|
|
history,
|
|
invalidate_knowledge,
|
|
page_result,
|
|
record_event,
|
|
update_values,
|
|
)
|
|
|
|
router = APIRouter(prefix="/documents", tags=["documents"])
|
|
|
|
|
|
@router.get("", response_model=Page[DocumentRead])
|
|
def list_documents(
|
|
q: str = Query("", max_length=200),
|
|
source_id: int | None = Query(None, gt=0),
|
|
category: str | None = Query(None, max_length=80),
|
|
page: int = Query(1, ge=1),
|
|
page_size: int = Query(20, ge=1, le=100),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
statement = select(Document).where(Document.is_deleted.is_(False))
|
|
if source_id is not None:
|
|
statement = statement.where(Document.source_id == source_id)
|
|
if category is not None:
|
|
statement = statement.where(Document.category == category)
|
|
if q:
|
|
statement = statement.where(
|
|
or_(
|
|
Document.title.icontains(q, autoescape=True),
|
|
Document.content.icontains(q, autoescape=True),
|
|
)
|
|
)
|
|
return page_result(db, statement.order_by(Document.id.desc()), page, page_size)
|
|
|
|
|
|
@router.post("", response_model=DocumentRead, status_code=201)
|
|
def create_document(payload: DocumentCreate, db: Session = Depends(get_db)):
|
|
get_item(db, Source, payload.source_id, lock=True)
|
|
item = Document(**payload.model_dump())
|
|
db.add(item)
|
|
record_event(db, item, "create")
|
|
db.commit()
|
|
return item
|
|
|
|
|
|
@router.get("/{item_id}", response_model=DocumentRead)
|
|
def read_document(item_id: int, db: Session = Depends(get_db)):
|
|
return get_item(db, Document, item_id)
|
|
|
|
|
|
@router.patch("/{item_id}", response_model=DocumentRead)
|
|
def update_document(item_id: int, payload: DocumentUpdate, db: Session = Depends(get_db)):
|
|
item = get_item(db, Document, item_id, lock=True)
|
|
ensure_version(item, payload.expected_version)
|
|
update_values(item, payload)
|
|
record_event(db, item, "update")
|
|
invalidate_knowledge(db, KnowledgeItem.document_id == item.id)
|
|
db.commit()
|
|
return item
|
|
|
|
|
|
@router.delete("/{item_id}", status_code=204)
|
|
def delete_document(
|
|
item_id: int, expected_version: int = Query(..., ge=1), db: Session = Depends(get_db)
|
|
):
|
|
item = get_item(db, Document, item_id, lock=True)
|
|
ensure_version(item, expected_version)
|
|
if (
|
|
db.scalar(
|
|
select(KnowledgeItem.id)
|
|
.where(KnowledgeItem.document_id == item_id, KnowledgeItem.is_deleted.is_(False))
|
|
.limit(1)
|
|
)
|
|
is not None
|
|
):
|
|
raise HTTPException(409, "文档仍被知识条目引用,不能删除")
|
|
item.is_deleted = True
|
|
record_event(db, item, "delete")
|
|
db.commit()
|
|
return Response(status_code=204)
|
|
|
|
|
|
@router.get("/{item_id}/history", response_model=Page[AuditRead])
|
|
def document_history(
|
|
item_id: int,
|
|
page: int = Query(1, ge=1),
|
|
page_size: int = Query(20, ge=1, le=100),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
return history(db, Document, item_id, page, page_size)
|