fix:优化数据
This commit is contained in:
93
backend/app/routers/settings.py
Normal file
93
backend/app/routers/settings.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, File, HTTPException, UploadFile, status
|
||||
|
||||
router = APIRouter(prefix="/settings", tags=["settings"])
|
||||
|
||||
TEMPLATES_DIR = Path("data/templates")
|
||||
ALLOWED_EXCEL = {".xlsx", ".xltx"}
|
||||
ALLOWED_WORD = {".docx", ".dotx"}
|
||||
ALLOWED_EXTENSIONS = ALLOWED_EXCEL | ALLOWED_WORD
|
||||
|
||||
# Allowed MIME types when client sends Content-Type (validate if present)
|
||||
ALLOWED_MIME_TYPES = frozenset({
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", # .xlsx
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.template", # .xltx
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", # .docx
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.template", # .dotx
|
||||
})
|
||||
|
||||
|
||||
def _ensure_templates_dir() -> Path:
|
||||
TEMPLATES_DIR.mkdir(parents=True, exist_ok=True)
|
||||
return TEMPLATES_DIR
|
||||
|
||||
|
||||
@router.get("/templates", response_model=List[dict])
|
||||
async def list_templates():
|
||||
"""List uploaded template files (name, type, size, mtime)."""
|
||||
_ensure_templates_dir()
|
||||
out: List[dict] = []
|
||||
for f in sorted(TEMPLATES_DIR.iterdir(), key=lambda p: p.stat().st_mtime, reverse=True):
|
||||
if not f.is_file():
|
||||
continue
|
||||
suf = f.suffix.lower()
|
||||
if suf not in ALLOWED_EXTENSIONS:
|
||||
continue
|
||||
st = f.stat()
|
||||
out.append({
|
||||
"name": f.name,
|
||||
"type": "excel" if suf in ALLOWED_EXCEL else "word",
|
||||
"size": st.st_size,
|
||||
"uploaded_at": st.st_mtime,
|
||||
})
|
||||
return out
|
||||
|
||||
|
||||
@router.post("/templates/upload", status_code=status.HTTP_201_CREATED)
|
||||
async def upload_template(file: UploadFile = File(...)):
|
||||
"""Upload a .xlsx, .xltx, .docx or .dotx template to data/templates/."""
|
||||
suf = Path(file.filename or "").suffix.lower()
|
||||
if suf not in ALLOWED_EXTENSIONS:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Only .xlsx, .xltx, .docx and .dotx files are allowed.",
|
||||
)
|
||||
content_type = (file.content_type or "").strip().split(";")[0].strip().lower()
|
||||
if content_type and content_type not in ALLOWED_MIME_TYPES:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid content type. Allowed: .xlsx, .xltx, .docx, .dotx Office formats.",
|
||||
)
|
||||
dir_path = _ensure_templates_dir()
|
||||
dest = dir_path / (file.filename or "template" + suf)
|
||||
content = await file.read()
|
||||
dest.write_bytes(content)
|
||||
return {"name": dest.name, "path": str(dest)}
|
||||
|
||||
|
||||
def get_latest_excel_template() -> Path | None:
|
||||
"""Return path to the most recently modified .xlsx or .xltx in data/templates, or None."""
|
||||
if not TEMPLATES_DIR.exists():
|
||||
return None
|
||||
excel_files = [
|
||||
f for f in TEMPLATES_DIR.iterdir()
|
||||
if f.is_file() and f.suffix.lower() in ALLOWED_EXCEL
|
||||
]
|
||||
if not excel_files:
|
||||
return None
|
||||
return max(excel_files, key=lambda p: p.stat().st_mtime)
|
||||
|
||||
|
||||
def get_quote_template_path(template_filename: str | None) -> Path:
|
||||
"""Resolve quote template path: optional filename in data/templates or latest excel template or default."""
|
||||
if template_filename:
|
||||
candidate = TEMPLATES_DIR / template_filename
|
||||
if candidate.is_file() and candidate.suffix.lower() in ALLOWED_EXCEL:
|
||||
return candidate
|
||||
latest = get_latest_excel_template()
|
||||
if latest:
|
||||
return latest
|
||||
default = Path("templates/quote_template.xlsx")
|
||||
return default
|
||||
Reference in New Issue
Block a user