Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

191 lines
6.2 KiB
Python

# app/routers/contact_qrcodes.py
import os
import uuid
from typing import List, Union
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.orm import Session
import qrcode
from qrcode.image.pil import PilImage
from app.database import get_db
from app.models.contact_qrcode import ContactQRCode
from app.models.admin import Admin
from app.auth import get_current_admin
from app.config import get_settings
router = APIRouter()
settings = get_settings()
# Schemas
class ContactQRCodeCreate(BaseModel):
content: str
description: str = ""
sort_order: int = 0
is_enabled: bool = True
class ContactQRCodeUpdate(BaseModel):
content: str = None
description: str = None
sort_order: int = None
is_enabled: bool = None
class ContactQRCodeResponse(BaseModel):
code: int = 200
data: Union[dict, list] = None
message: str = "success"
# Helper
def get_qrcode_dir():
# __file__ = backend/app/routers/contact_qrcodes.py
# 需要向上3级才能到达项目根目录
routers_dir = os.path.dirname(os.path.abspath(__file__)) # backend/app/routers
app_dir = os.path.dirname(routers_dir) # backend/app
backend_dir = os.path.dirname(app_dir) # backend
project_root = os.path.dirname(backend_dir) # project root
qrcode_dir = os.path.join(project_root, "storage", "admin", "images", "qrcode")
os.makedirs(qrcode_dir, exist_ok=True)
return qrcode_dir
def generate_qrcode_image(content: str, filename: str) -> str:
"""Generate QR code image and save to storage, return URL path"""
qrcode_dir = get_qrcode_dir()
filepath = os.path.join(qrcode_dir, filename)
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(content)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save(filepath)
return f"/storage/admin/images/qrcode/{filename}"
def qrcode_to_dict(qr: ContactQRCode) -> dict:
return {
"id": qr.id,
"content": qr.content,
"description": qr.description,
"image_path": qr.image_path,
"sort_order": qr.sort_order,
"is_enabled": qr.is_enabled,
}
# Public routes
@router.get("/contact-qrcodes", response_model=ContactQRCodeResponse)
def get_public_qrcodes(db: Session = Depends(get_db)):
"""Get enabled QR codes for public display"""
qrcodes = db.query(ContactQRCode).filter(
ContactQRCode.is_enabled == True
).order_by(ContactQRCode.sort_order.asc()).all()
return ContactQRCodeResponse(data=[qrcode_to_dict(q) for q in qrcodes])
# Admin routes
@router.get("/admin/contact-qrcodes", response_model=ContactQRCodeResponse)
def get_admin_qrcodes(
db: Session = Depends(get_db),
current_admin: Admin = Depends(get_current_admin)
):
"""Get all QR codes for admin"""
qrcodes = db.query(ContactQRCode).order_by(ContactQRCode.sort_order.asc()).all()
return ContactQRCodeResponse(data=[qrcode_to_dict(q) for q in qrcodes])
@router.post("/admin/contact-qrcodes", response_model=ContactQRCodeResponse)
def create_qrcode(
request: ContactQRCodeCreate,
db: Session = Depends(get_db),
current_admin: Admin = Depends(get_current_admin)
):
"""Create new QR code, auto-generate image"""
if not request.content.strip():
raise HTTPException(status_code=400, detail="内容不能为空")
filename = f"qrcode_{uuid.uuid4().hex[:8]}.png"
image_path = generate_qrcode_image(request.content.strip(), filename)
qr = ContactQRCode(
content=request.content.strip(),
description=request.description.strip() if request.description else "",
image_path=image_path,
sort_order=request.sort_order,
is_enabled=request.is_enabled,
)
db.add(qr)
db.commit()
db.refresh(qr)
return ContactQRCodeResponse(message="创建成功", data=qrcode_to_dict(qr))
@router.put("/admin/contact-qrcodes/{qr_id}", response_model=ContactQRCodeResponse)
def update_qrcode(
qr_id: int,
request: ContactQRCodeUpdate,
db: Session = Depends(get_db),
current_admin: Admin = Depends(get_current_admin)
):
"""Update QR code, regenerate image if content changed"""
qr = db.query(ContactQRCode).filter(ContactQRCode.id == qr_id).first()
if not qr:
raise HTTPException(status_code=404, detail="QR code not found")
update_data = request.dict(exclude_unset=True)
# If content changed, regenerate QR code image
new_content = update_data.get("content", "").strip()
if new_content and new_content != qr.content:
# Delete old image
if qr.image_path:
old_filename = os.path.basename(qr.image_path)
old_filepath = os.path.join(get_qrcode_dir(), old_filename)
if os.path.exists(old_filepath):
os.remove(old_filepath)
# Generate new image
filename = f"qrcode_{uuid.uuid4().hex[:8]}.png"
qr.image_path = generate_qrcode_image(new_content, filename)
qr.content = new_content
update_data.pop("content", None)
for field, value in update_data.items():
setattr(qr, field, value)
db.commit()
db.refresh(qr)
return ContactQRCodeResponse(message="更新成功", data=qrcode_to_dict(qr))
@router.delete("/admin/contact-qrcodes/{qr_id}", response_model=ContactQRCodeResponse)
def delete_qrcode(
qr_id: int,
db: Session = Depends(get_db),
current_admin: Admin = Depends(get_current_admin)
):
"""Delete QR code and its image file"""
qr = db.query(ContactQRCode).filter(ContactQRCode.id == qr_id).first()
if not qr:
raise HTTPException(status_code=404, detail="QR code not found")
# Delete image file
if qr.image_path:
filename = os.path.basename(qr.image_path)
filepath = os.path.join(get_qrcode_dir(), filename)
if os.path.exists(filepath):
os.remove(filepath)
db.delete(qr)
db.commit()
return ContactQRCodeResponse(message="删除成功")