fix: harden uploads downloads and deployment config
This commit is contained in:
@@ -12,6 +12,26 @@ from app.schemas.order import OrderCreate, OrderResponse, PaymentResponse
|
||||
|
||||
router = APIRouter(prefix="/orders", tags=["订单"])
|
||||
|
||||
|
||||
def _generate_order_no(db: Session) -> str:
|
||||
"""生成带随机后缀的唯一订单号,避免同秒冲突。"""
|
||||
now = datetime.now()
|
||||
six_months_ago = now - timedelta(days=180)
|
||||
date_part = six_months_ago.strftime('%Y%m%d')
|
||||
time_part = now.strftime('%H%M%S')
|
||||
|
||||
for _ in range(5):
|
||||
suffix = secrets.token_hex(3).upper()
|
||||
order_no = f"ORD{date_part}{time_part}{suffix}"
|
||||
exists = db.query(Order.id).filter(Order.order_no == order_no).first()
|
||||
if not exists:
|
||||
return order_no
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="生成订单号失败,请稍后重试",
|
||||
)
|
||||
|
||||
def get_current_user(authorization: str = Header(None), db: Session = Depends(get_db)):
|
||||
"""获取当前登录用户"""
|
||||
if not authorization or not authorization.startswith("Bearer "):
|
||||
@@ -66,12 +86,8 @@ def create_order(
|
||||
detail="您已购买过此作品"
|
||||
)
|
||||
|
||||
# 生成订单号:前缀 + (当前时间-6个月)的年月日 + 当前时间的时分秒
|
||||
now = datetime.now()
|
||||
six_months_ago = now - timedelta(days=180) # 半年前
|
||||
date_part = six_months_ago.strftime('%Y%m%d') # 半年前的年月日
|
||||
time_part = now.strftime('%H%M%S') # 当前时间的时分秒
|
||||
order_no = f"ORD{date_part}{time_part}"
|
||||
# 生成唯一订单号:半年前日期 + 当前时分秒 + 随机后缀
|
||||
order_no = _generate_order_no(db)
|
||||
|
||||
# 创建订单
|
||||
new_order = Order(
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form, Header
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form, Header, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import desc, or_
|
||||
from typing import Optional
|
||||
import os
|
||||
import uuid
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from PIL import Image
|
||||
import json
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.models.work import Work
|
||||
from app.models.user import User
|
||||
@@ -51,6 +50,15 @@ def get_current_user(authorization: str = Header(None), db: Session = Depends(ge
|
||||
return user
|
||||
|
||||
|
||||
def _user_designer_aliases(user: User) -> list[str]:
|
||||
aliases = []
|
||||
for value in (getattr(user, "nickname", None), getattr(user, "phone", None)):
|
||||
cleaned = str(value or "").strip()
|
||||
if cleaned and cleaned not in aliases:
|
||||
aliases.append(cleaned)
|
||||
return aliases
|
||||
|
||||
|
||||
def generate_thumbnail(image_path: str, thumb_path: str, size=(400, 400)):
|
||||
"""生成缩略图 - 修复透明 PNG 问题"""
|
||||
with Image.open(image_path) as img:
|
||||
@@ -177,7 +185,7 @@ async def upload_work(
|
||||
|
||||
# 解析标签 - 修复 Bug #3: tags 转字符串
|
||||
if tags:
|
||||
tags_list = [tag.strip() for tag in tags.split(",")]
|
||||
tags_list = [tag.strip() for tag in tags.split(",") if tag.strip()]
|
||||
tags_str = ",".join(tags_list) # 转成逗号分隔的字符串
|
||||
else:
|
||||
tags_str = None
|
||||
@@ -227,20 +235,22 @@ async def upload_work(
|
||||
|
||||
@router.get("/my", summary="我的上传")
|
||||
def get_my_uploads(
|
||||
page: int = Form(1, ge=1),
|
||||
page_size: int = Form(20, ge=1, le=100),
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取当前用户的上传记录"""
|
||||
from sqlalchemy import desc
|
||||
|
||||
aliases = _user_designer_aliases(current_user)
|
||||
offset = (page - 1) * page_size
|
||||
works = db.query(Work).filter(
|
||||
Work.designer == current_user.phone
|
||||
).order_by(desc(Work.created_at)).offset(offset).limit(page_size).all()
|
||||
|
||||
total = db.query(Work).filter(Work.designer == current_user.phone).count()
|
||||
query = db.query(Work)
|
||||
if aliases:
|
||||
query = query.filter(or_(*[Work.designer == alias for alias in aliases]))
|
||||
else:
|
||||
query = query.filter(Work.id == -1)
|
||||
|
||||
works = query.order_by(desc(Work.created_at)).offset(offset).limit(page_size).all()
|
||||
total = query.count()
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
|
||||
@@ -4,11 +4,13 @@ from sqlalchemy.orm import Session
|
||||
from sqlalchemy import desc
|
||||
from typing import List
|
||||
import os
|
||||
import mimetypes
|
||||
from app.core.database import get_db
|
||||
from app.models.work import Work
|
||||
from app.models.order import Order, OrderStatus
|
||||
from app.models.user import User
|
||||
from app.core.security import decode_access_token
|
||||
from app.core.config import settings
|
||||
from app.schemas.work import WorkResponse, WorkListResponse
|
||||
|
||||
router = APIRouter(prefix="/works", tags=["作品"])
|
||||
@@ -137,9 +139,10 @@ def download_work(
|
||||
)
|
||||
|
||||
# 构建原图文件路径
|
||||
# 假设原图存储在 uploads/original/ 目录下
|
||||
file_path = work.original_image.lstrip('/')
|
||||
full_path = os.path.join(os.getcwd(), file_path)
|
||||
relative_path = work.original_image.lstrip("/")
|
||||
if relative_path.startswith("uploads/"):
|
||||
relative_path = relative_path[len("uploads/"):]
|
||||
full_path = os.path.join(settings.UPLOAD_DIR, relative_path)
|
||||
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(full_path):
|
||||
@@ -150,8 +153,9 @@ def download_work(
|
||||
|
||||
# 返回文件
|
||||
filename = os.path.basename(full_path)
|
||||
media_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
||||
return FileResponse(
|
||||
path=full_path,
|
||||
filename=filename,
|
||||
media_type='application/octet-stream'
|
||||
media_type=media_type
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user