新增上传图片的接口

This commit is contained in:
aaron 2025-01-05 17:50:19 +08:00
parent 93fb0fe4e9
commit 0afdca5805
8 changed files with 211 additions and 6 deletions

View File

@ -9,7 +9,10 @@ from app.models.order import (
OrderPackageInfo,
generate_order_id,
OrderPriceCalculateRequest,
OrderPriceInfo
OrderPriceInfo,
OrderStatus,
OrderCancel,
OrderComplete
)
from app.models.database import get_db
from app.api.deps import get_current_user
@ -182,3 +185,82 @@ async def calculate_order_price(
)
return success_response(data=price_info)
@router.post("/{orderid}/cancel", response_model=ResponseModel)
async def cancel_order(
orderid: str,
cancel_data: OrderCancel,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""取消订单"""
# 查询订单
order = db.query(ShippingOrderDB).filter(
ShippingOrderDB.orderid == orderid,
ShippingOrderDB.userid == current_user.userid
).first()
if not order:
return error_response(code=404, message="订单不存在")
# 检查订单状态是否可取消
if order.status not in [OrderStatus.PENDING, OrderStatus.ACCEPTED]:
return error_response(code=400, message="当前订单状态不可取消")
try:
# 更新订单状态和取消原因
order.status = OrderStatus.CANCELLED
order.cancel_reason = cancel_data.reason
# 如果使用了优惠券,返还优惠券
if order.coupon_id:
coupon = db.query(UserCouponDB).filter(
UserCouponDB.id == order.coupon_id
).first()
if coupon:
coupon.status = CouponStatus.UNUSED
db.commit()
return success_response(
message="订单取消成功",
data=OrderInfo.model_validate(order)
)
except Exception as e:
db.rollback()
return error_response(code=500, message=f"取消订单失败: {str(e)}")
@router.post("/{orderid}/complete", response_model=ResponseModel)
async def complete_order(
orderid: str,
complete_data: OrderComplete,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""完成订单"""
# 查询订单
order = db.query(ShippingOrderDB).filter(
ShippingOrderDB.orderid == orderid,
ShippingOrderDB.userid == current_user.userid
).first()
if not order:
return error_response(code=404, message="订单不存在")
# 检查订单状态
if order.status != OrderStatus.ACCEPTED:
return error_response(code=400, message="只有已接单的订单才能标记为完成")
try:
# 更新订单状态和完成图片
order.status = OrderStatus.COMPLETED
if complete_data.images:
order.complete_images = ",".join(complete_data.images)
db.commit()
return success_response(
message="订单已完成",
data=OrderInfo.model_validate(order)
)
except Exception as e:
db.rollback()
return error_response(code=500, message=f"完成订单失败: {str(e)}")

View File

@ -0,0 +1,64 @@
from fastapi import APIRouter, UploadFile, File, Depends
from typing import List
import uuid
from datetime import datetime
from app.core.cos import cos_client
from app.core.config import settings
from app.models.upload import UploadResponse, MultiUploadResponse
from app.core.response import success_response, error_response, ResponseModel
from app.api.deps import get_current_user
from app.models.user import UserDB
router = APIRouter()
async def upload_to_cos(file: UploadFile) -> str:
"""上传文件到腾讯云COS"""
# 生成唯一文件名
ext = file.filename.split('.')[-1] if '.' in file.filename else ''
filename = f"{datetime.now().strftime('%Y%m%d')}/{uuid.uuid4()}.{ext}"
# 上传文件
cos_client.put_object(
Bucket=settings.COS_BUCKET,
Body=await file.read(),
Key=filename,
ContentType=file.content_type
)
# 返回文件URL
return f"{settings.COS_BASE_URL}/{filename}"
@router.post("/image", response_model=ResponseModel)
async def upload_image(
file: UploadFile = File(...),
current_user: UserDB = Depends(get_current_user)
):
"""上传单张图片"""
if not file.content_type.startswith('image/'):
return error_response(code=400, message="只能上传图片文件")
try:
url = await upload_to_cos(file)
return success_response(data=UploadResponse(url=url))
except Exception as e:
return error_response(code=500, message=f"上传失败: {str(e)}")
@router.post("/images", response_model=ResponseModel)
async def upload_images(
files: List[UploadFile] = File(...),
current_user: UserDB = Depends(get_current_user)
):
"""上传多张图片"""
if len(files) > 5:
return error_response(code=400, message="最多同时上传5张图片")
urls = []
try:
for file in files:
if not file.content_type.startswith('image/'):
return error_response(code=400, message="只能上传图片文件")
url = await upload_to_cos(file)
urls.append(url)
return success_response(data=MultiUploadResponse(urls=urls))
except Exception as e:
return error_response(code=500, message=f"上传失败: {str(e)}")

View File

@ -37,6 +37,13 @@ class Settings(BaseSettings):
def SQLALCHEMY_DATABASE_URL(self) -> str:
return f"mysql+pymysql://{self.MYSQL_USER}:{self.MYSQL_PASSWORD}@{self.MYSQL_HOST}:{self.MYSQL_PORT}/{self.MYSQL_DB}?charset=utf8mb4"
# 腾讯云 COS 配置
COS_SECRET_ID: str = "AKIDxnbGj281iHtKallqqzvlV5YxBCrPltnS"
COS_SECRET_KEY: str = "ta6PXTMBsX7dzA7IN6uYUFn8F9uTovoU"
COS_REGION: str = "ap-chengdu"
COS_BUCKET: str = "dman-1311994147"
COS_BASE_URL: str = "dman-1311994147.cos.ap-chengdu.myqcloud.com"
class Config:
case_sensitive = True

15
app/core/cos.py Normal file
View File

@ -0,0 +1,15 @@
from qcloud_cos import CosConfig, CosS3Client
from app.core.config import settings
import sys
import logging
# 正常情况日志级别使用INFO需要定位时可以修改为DEBUG此时SDK会打印和服务端的通信信息
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
cos_config = CosConfig(
Region=settings.COS_REGION,
SecretId=settings.COS_SECRET_ID,
SecretKey=settings.COS_SECRET_KEY
)
cos_client = CosS3Client(cos_config)

View File

@ -1,6 +1,6 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.endpoints import user, address, community, station, order, coupon, community_building
from app.api.endpoints import user, address, community, station, order, coupon, community_building, upload
from app.models.database import Base, engine
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
@ -33,6 +33,7 @@ app.include_router(community_building.router, prefix="/api/community/building",
app.include_router(station.router, prefix="/api/station", tags=["驿站"])
app.include_router(order.router, prefix="/api/order", tags=["订单"])
app.include_router(coupon.router, prefix="/api/coupon", tags=["优惠券"])
app.include_router(upload.router, prefix="/api/upload", tags=["文件上传"])
@app.get("/")
async def root():

View File

@ -1,9 +1,17 @@
from datetime import datetime
from typing import Optional, List
from sqlalchemy import Column, String, Integer, Float, DateTime, ForeignKey
from sqlalchemy import Column, String, Integer, Float, DateTime, ForeignKey, Enum
from sqlalchemy.sql import func
from pydantic import BaseModel, Field
from .database import Base
import enum
class OrderStatus(str, enum.Enum):
PENDING = "pending"
ACCEPTED = "accepted"
UNPAID = "unpaid"
COMPLETED = "completed"
CANCELLED = "cancelled"
# 数据库模型
class ShippingOrderDB(Base):
@ -17,6 +25,9 @@ class ShippingOrderDB(Base):
coupon_discount_amount = Column(Float, default=0)
coupon_id = Column(Integer, ForeignKey("user_coupons.id"), nullable=True)
final_amount = Column(Float, nullable=False)
status = Column(Enum(OrderStatus), nullable=False, default=OrderStatus.PENDING)
cancel_reason = Column(String(200), nullable=True) # 取消原因
complete_images = Column(String(1000), nullable=True) # 完成订单的图片URL多个URL用逗号分隔
create_time = Column(DateTime(timezone=True), server_default=func.now())
class ShippingOrderPackageDB(Base):
@ -49,8 +60,16 @@ class OrderInfo(BaseModel):
coupon_discount_amount: float
coupon_id: Optional[int]
final_amount: float
status: OrderStatus
complete_images: Optional[List[str]] = None
create_time: datetime
def __init__(self, **data):
super().__init__(**data)
# 将逗号分隔的图片URL字符串转换为列表
if self.complete_images and isinstance(self.complete_images, str):
self.complete_images = self.complete_images.split(",")
class Config:
from_attributes = True
@ -82,3 +101,11 @@ class OrderPriceInfo(BaseModel):
coupon_discount_amount: float
coupon_id: Optional[int] = None
final_amount: float
# 添加取消订单请求模型
class OrderCancel(BaseModel):
reason: str = Field(..., max_length=200, description="取消原因")
# 完成订单请求模型
class OrderComplete(BaseModel):
images: Optional[List[str]] = Field(None, max_items=5) # 最多5张图片可选

8
app/models/upload.py Normal file
View File

@ -0,0 +1,8 @@
from pydantic import BaseModel
from typing import List
class UploadResponse(BaseModel):
url: str
class MultiUploadResponse(BaseModel):
urls: List[str]

View File

@ -10,3 +10,4 @@ redis==5.0.1
pymysql==1.1.0
SQLAlchemy==2.0.27
unisms
cos-python-sdk-v5==1.9.25