doc-forge-reds/backend/app/api/templates.py

127 lines
4.5 KiB
Python

import uuid
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Query
from fastapi.responses import Response
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.models.template import Template
from app.schemas.template import TemplateResponse, TemplateListItem, HTMLContentResponse, HTMLUpdateRequest
from app.services.file_storage import save_upload, get_file_content, delete_file, TEMPLATES_DIR
from app.services.document_processor import docx_to_html, html_to_docx_bytes, docx_to_pdf_bytes
router = APIRouter(prefix="/templates", tags=["模板管理"])
@router.post("", response_model=TemplateResponse)
async def upload_template(
file: UploadFile = File(...),
name: str | None = Form(None),
db: AsyncSession = Depends(get_db),
):
if not file.filename or not file.filename.endswith(".docx"):
raise HTTPException(status_code=400, detail="仅支持 .docx 文件")
file_path = await save_upload(file, TEMPLATES_DIR)
file_content = await get_file_content(file_path)
html_content = await docx_to_html(file_content)
template = Template(
name=name or file.filename.replace(".docx", ""),
file_path=file_path,
html_content=html_content,
)
db.add(template)
await db.flush()
await db.refresh(template)
return template
@router.get("", response_model=list[TemplateListItem])
async def list_templates(
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
):
query = select(Template).offset(skip).limit(limit).order_by(Template.created_at.desc())
result = await db.execute(query)
return result.scalars().all()
@router.get("/{template_id}", response_model=TemplateResponse)
async def get_template(template_id: str, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Template).where(Template.id == template_id))
template = result.scalar_one_or_none()
if not template:
raise HTTPException(status_code=404, detail="模板不存在")
return template
@router.get("/{template_id}/html", response_model=HTMLContentResponse)
async def get_template_html(template_id: str, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Template).where(Template.id == template_id))
template = result.scalar_one_or_none()
if not template:
raise HTTPException(status_code=404, detail="模板不存在")
return HTMLContentResponse(html_content=template.html_content or "")
@router.put("/{template_id}/html")
async def update_template_html(
template_id: str,
data: HTMLUpdateRequest,
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(Template).where(Template.id == template_id))
template = result.scalar_one_or_none()
if not template:
raise HTTPException(status_code=404, detail="模板不存在")
template.html_content = data.html_content
docx_bytes = html_to_docx_bytes(data.html_content)
with open(template.file_path, "wb") as f:
f.write(docx_bytes)
await db.flush()
return {"detail": "保存成功"}
@router.get("/{template_id}/download")
async def download_template(
template_id: str,
format: str = Query("docx", pattern="^(docx|pdf)$"),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(Template).where(Template.id == template_id))
template = result.scalar_one_or_none()
if not template:
raise HTTPException(status_code=404, detail="模板不存在")
file_content = await get_file_content(template.file_path)
if format == "pdf":
file_content = docx_to_pdf_bytes(file_content)
media_type = "application/pdf"
filename = f"{template.name}.pdf"
else:
media_type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
filename = f"{template.name}.docx"
return Response(
content=file_content,
media_type=media_type,
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
@router.delete("/{template_id}")
async def delete_template(template_id: str, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Template).where(Template.id == template_id))
template = result.scalar_one_or_none()
if not template:
raise HTTPException(status_code=404, detail="模板不存在")
delete_file(template.file_path)
await db.delete(template)
return {"detail": "删除成功"}