feat: add template upload endpoint

This commit is contained in:
zwt13703
2026-07-01 20:27:15 +08:00
parent beec8cf5bb
commit 95c507536a
6 changed files with 82 additions and 15 deletions
+35
View File
@@ -0,0 +1,35 @@
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from sqlalchemy.orm import Session
from ..database import get_db
from ..services.template_service import create_template_record, store_template_file
router = APIRouter(prefix="/api/templates", tags=["templates"])
@router.post("/upload")
def upload_template(
file: UploadFile = File(...),
name: str = Form(...),
type: str = Form("report"),
db: Session = Depends(get_db),
):
"""上传 Word 模板并创建模板记录。"""
try:
stored_path, _ = store_template_file(file)
template = create_template_record(db, name=name, file_path=stored_path, type=type)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
except Exception as exc:
db.rollback()
raise HTTPException(status_code=500, detail="模板上传失败") from exc
return {
"data": {
"template_id": template.id,
"name": template.name,
"status": "uploaded",
},
"message": "ok",
}
+4
View File
@@ -1,6 +1,8 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .api.templates import router as templates_router
app = FastAPI(title="AI 文档模板生成系统", version="0.1.0")
app.add_middleware(
@@ -11,6 +13,8 @@ app.add_middleware(
allow_headers=["*"],
)
app.include_router(templates_router)
@app.get("/health")
def health():
+4 -11
View File
@@ -1,9 +1,6 @@
import os
from io import BytesIO
from minio import Minio
from minio.error import S3Error
from typing import Any
STORAGE_BASE = os.getenv("LOCAL_STORAGE_PATH", "./data/files")
MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT", "localhost:9000")
@@ -29,7 +26,9 @@ def _read_local(bucket: str, file_path: str) -> bytes:
return f.read()
def get_minio_client() -> Minio:
def get_minio_client() -> Any:
from minio import Minio
return Minio(
MINIO_ENDPOINT,
access_key=MINIO_ACCESS_KEY,
@@ -45,8 +44,6 @@ def ensure_bucket(bucket_name: str) -> bool:
if not client.bucket_exists(bucket_name):
client.make_bucket(bucket_name)
return True
except S3Error:
return False
except Exception:
return False
@@ -64,8 +61,6 @@ def upload_file(bucket: str, file_path: str, content: bytes) -> str:
content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)
return f"minio://{bucket}/{file_path}"
except S3Error:
pass
except Exception:
pass
@@ -83,8 +78,6 @@ def download_file(bucket: str, file_path: str) -> bytes:
finally:
response.close()
response.release_conn()
except S3Error:
pass
except Exception:
pass
+24
View File
@@ -1,15 +1,24 @@
"""模板相关业务逻辑"""
from datetime import datetime
from pathlib import Path
from uuid import uuid4
from fastapi import UploadFile
from sqlalchemy.orm import Session
from ..models.template import Template
from .storage import upload_file
ALLOWED_EXTENSIONS = {".docx"}
MAX_FILE_SIZE = 50 * 1024 * 1024 # 50MB
TEMPLATE_BUCKET = "templates"
def validate_file(file: UploadFile):
"""校验文件格式和大小"""
if not file.filename:
raise ValueError("文件名不能为空")
ext = "." + file.filename.rsplit(".", 1)[-1].lower() if "." in file.filename else ""
if ext not in ALLOWED_EXTENSIONS:
raise ValueError(f"不支持的文件格式: {ext},仅支持 .docx")
@@ -21,6 +30,21 @@ def validate_file(file: UploadFile):
return content
def build_template_file_path(filename: str) -> str:
"""生成模板文件存储路径。"""
suffix = Path(filename).suffix.lower()
today = datetime.now().strftime("%Y%m%d")
return f"{today}/{uuid4().hex}{suffix}"
def store_template_file(file: UploadFile) -> tuple[str, bytes]:
"""校验并保存上传的模板文件,返回存储路径和文件内容。"""
content = validate_file(file)
file_path = build_template_file_path(file.filename or "template.docx")
stored_path = upload_file(TEMPLATE_BUCKET, file_path, content)
return stored_path, content
def create_template_record(db: Session, name: str, file_path: str, type: str = "report") -> Template:
"""创建模板记录"""
tmpl = Template(name=name, type=type, original_file_path=file_path)