211 lines
6.8 KiB
Python
211 lines
6.8 KiB
Python
# app/routers/app_downloads.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 app.database import get_db
|
|
from app.models.app_download import AppDownload
|
|
from app.models.admin import Admin
|
|
from app.auth import get_current_admin
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
# Schemas
|
|
class AppDownloadCreate(BaseModel):
|
|
app_name: str
|
|
platform: str
|
|
download_url: str
|
|
description: str = ""
|
|
icon: str = ""
|
|
sort_order: int = 0
|
|
is_enabled: bool = True
|
|
|
|
|
|
class AppDownloadUpdate(BaseModel):
|
|
app_name: str = None
|
|
platform: str = None
|
|
download_url: str = None
|
|
description: str = None
|
|
icon: str = None
|
|
sort_order: int = None
|
|
is_enabled: bool = None
|
|
|
|
|
|
class AppDownloadResponse(BaseModel):
|
|
code: int = 200
|
|
data: Union[dict, list] = None
|
|
message: str = "success"
|
|
|
|
|
|
# Helper
|
|
|
|
def get_qrcode_dir():
|
|
routers_dir = os.path.dirname(os.path.abspath(__file__))
|
|
app_dir = os.path.dirname(routers_dir)
|
|
backend_dir = os.path.dirname(app_dir)
|
|
project_root = os.path.dirname(backend_dir)
|
|
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 delete_qrcode_image(image_path: str):
|
|
"""Delete QR code image file if exists"""
|
|
if not image_path:
|
|
return
|
|
filename = os.path.basename(image_path)
|
|
filepath = os.path.join(get_qrcode_dir(), filename)
|
|
if os.path.exists(filepath):
|
|
os.remove(filepath)
|
|
|
|
|
|
def app_download_to_dict(ad: AppDownload) -> dict:
|
|
return {
|
|
"id": ad.id,
|
|
"app_name": ad.app_name,
|
|
"platform": ad.platform,
|
|
"download_url": ad.download_url,
|
|
"description": ad.description,
|
|
"icon": ad.icon,
|
|
"qr_image_path": ad.qr_image_path,
|
|
"sort_order": ad.sort_order,
|
|
"is_enabled": ad.is_enabled,
|
|
}
|
|
|
|
|
|
# Public routes
|
|
@router.get("/app-downloads", response_model=AppDownloadResponse)
|
|
def get_public_app_downloads(db: Session = Depends(get_db)):
|
|
"""Get enabled app downloads for public display"""
|
|
items = db.query(AppDownload).filter(
|
|
AppDownload.is_enabled == True
|
|
).order_by(AppDownload.sort_order.asc()).all()
|
|
return AppDownloadResponse(data=[app_download_to_dict(i) for i in items])
|
|
|
|
|
|
# Admin routes
|
|
@router.get("/admin/app-downloads", response_model=AppDownloadResponse)
|
|
def get_admin_app_downloads(
|
|
db: Session = Depends(get_db),
|
|
current_admin: Admin = Depends(get_current_admin)
|
|
):
|
|
"""Get all app downloads for admin"""
|
|
items = db.query(AppDownload).order_by(AppDownload.sort_order.asc()).all()
|
|
return AppDownloadResponse(data=[app_download_to_dict(i) for i in items])
|
|
|
|
|
|
@router.post("/admin/app-downloads", response_model=AppDownloadResponse)
|
|
def create_app_download(
|
|
request: AppDownloadCreate,
|
|
db: Session = Depends(get_db),
|
|
current_admin: Admin = Depends(get_current_admin)
|
|
):
|
|
"""Create new app download, auto-generate QR code"""
|
|
if not request.app_name.strip():
|
|
raise HTTPException(status_code=400, detail="应用名称不能为空")
|
|
if not request.download_url.strip():
|
|
raise HTTPException(status_code=400, detail="下载链接不能为空")
|
|
if not request.platform.strip():
|
|
raise HTTPException(status_code=400, detail="平台不能为空")
|
|
|
|
# Generate QR code
|
|
filename = f"app_download_{uuid.uuid4().hex[:8]}.png"
|
|
qr_image_path = generate_qrcode_image(request.download_url.strip(), filename)
|
|
|
|
item = AppDownload(
|
|
app_name=request.app_name.strip(),
|
|
platform=request.platform.strip(),
|
|
download_url=request.download_url.strip(),
|
|
description=request.description.strip() if request.description else "",
|
|
icon=request.icon.strip() if request.icon else "",
|
|
qr_image_path=qr_image_path,
|
|
sort_order=request.sort_order,
|
|
is_enabled=request.is_enabled,
|
|
)
|
|
db.add(item)
|
|
db.commit()
|
|
db.refresh(item)
|
|
return AppDownloadResponse(message="创建成功", data=app_download_to_dict(item))
|
|
|
|
|
|
@router.put("/admin/app-downloads/{item_id}", response_model=AppDownloadResponse)
|
|
def update_app_download(
|
|
item_id: int,
|
|
request: AppDownloadUpdate,
|
|
db: Session = Depends(get_db),
|
|
current_admin: Admin = Depends(get_current_admin)
|
|
):
|
|
"""Update app download, regenerate QR code if URL changed"""
|
|
item = db.query(AppDownload).filter(AppDownload.id == item_id).first()
|
|
if not item:
|
|
raise HTTPException(status_code=404, detail="下载链接不存在")
|
|
|
|
update_data = request.dict(exclude_unset=True)
|
|
|
|
# If download_url changed, regenerate QR code
|
|
new_url = update_data.get("download_url", "").strip()
|
|
if new_url and new_url != item.download_url:
|
|
delete_qrcode_image(item.qr_image_path)
|
|
filename = f"app_download_{uuid.uuid4().hex[:8]}.png"
|
|
item.qr_image_path = generate_qrcode_image(new_url, filename)
|
|
item.download_url = new_url
|
|
update_data.pop("download_url", None)
|
|
|
|
for field, value in update_data.items():
|
|
if field == "app_name" and value:
|
|
value = value.strip()
|
|
elif field == "platform" and value:
|
|
value = value.strip()
|
|
elif field == "description" and value is not None:
|
|
value = value.strip()
|
|
elif field == "icon" and value is not None:
|
|
value = value.strip()
|
|
setattr(item, field, value)
|
|
|
|
db.commit()
|
|
db.refresh(item)
|
|
return AppDownloadResponse(message="更新成功", data=app_download_to_dict(item))
|
|
|
|
|
|
@router.delete("/admin/app-downloads/{item_id}", response_model=AppDownloadResponse)
|
|
def delete_app_download(
|
|
item_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_admin: Admin = Depends(get_current_admin)
|
|
):
|
|
"""Delete app download and its QR code image"""
|
|
item = db.query(AppDownload).filter(AppDownload.id == item_id).first()
|
|
if not item:
|
|
raise HTTPException(status_code=404, detail="下载链接不存在")
|
|
|
|
delete_qrcode_image(item.qr_image_path)
|
|
|
|
db.delete(item)
|
|
db.commit()
|
|
return AppDownloadResponse(message="删除成功")
|