添加积分商品和积分商品订单。
This commit is contained in:
parent
553f8b2e0c
commit
ecdc84822f
@ -627,6 +627,10 @@ async def get_deliveryman_orders(
|
||||
if status:
|
||||
statuses = status.split(",")
|
||||
query = query.filter(ShippingOrderDB.status.in_(statuses))
|
||||
|
||||
# 如果订单状态不是待接单,则需要过滤快递员
|
||||
if OrderStatus.CREATED not in statuses:
|
||||
query = query.filter(ShippingOrderDB.deliveryman_user_id == deliveryman.userid)
|
||||
|
||||
# 楼栋筛选
|
||||
if building_id:
|
||||
|
||||
100
app/api/endpoints/point_product.py
Normal file
100
app/api/endpoints/point_product.py
Normal file
@ -0,0 +1,100 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from app.models.point_product import (
|
||||
PointProductDB,
|
||||
PointProductCreate,
|
||||
PointProductUpdate,
|
||||
PointProductInfo
|
||||
)
|
||||
from app.models.database import get_db
|
||||
from app.api.deps import get_admin_user
|
||||
from app.models.user import UserDB
|
||||
from app.core.response import success_response, error_response, ResponseModel
|
||||
from typing import Optional
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("", response_model=ResponseModel)
|
||||
async def create_point_product(
|
||||
product: PointProductCreate,
|
||||
db: Session = Depends(get_db),
|
||||
admin: UserDB = Depends(get_admin_user)
|
||||
):
|
||||
"""创建积分商品(管理员)"""
|
||||
db_product = PointProductDB(**product.model_dump())
|
||||
db.add(db_product)
|
||||
|
||||
try:
|
||||
db.commit()
|
||||
db.refresh(db_product)
|
||||
return success_response(data=PointProductInfo.model_validate(db_product))
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return error_response(code=500, message=f"创建失败: {str(e)}")
|
||||
|
||||
@router.get("", response_model=ResponseModel)
|
||||
async def get_point_products(
|
||||
is_active: Optional[bool] = None,
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取积分商品列表"""
|
||||
query = db.query(PointProductDB)
|
||||
|
||||
if is_active is not None:
|
||||
query = query.filter(PointProductDB.is_active == is_active)
|
||||
|
||||
total = query.count()
|
||||
|
||||
products = query.order_by(PointProductDB.create_time.desc())\
|
||||
.offset(skip)\
|
||||
.limit(limit)\
|
||||
.all()
|
||||
|
||||
return success_response(data={
|
||||
"total": total,
|
||||
"items": [PointProductInfo.model_validate(p) for p in products]
|
||||
})
|
||||
|
||||
@router.get("/{product_id}", response_model=ResponseModel)
|
||||
async def get_point_product(
|
||||
product_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取积分商品详情"""
|
||||
product = db.query(PointProductDB).filter(
|
||||
PointProductDB.id == product_id
|
||||
).first()
|
||||
|
||||
if not product:
|
||||
return error_response(code=404, message="商品不存在")
|
||||
|
||||
return success_response(data=PointProductInfo.model_validate(product))
|
||||
|
||||
@router.put("/{product_id}", response_model=ResponseModel)
|
||||
async def update_point_product(
|
||||
product_id: int,
|
||||
product: PointProductUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
admin: UserDB = Depends(get_admin_user)
|
||||
):
|
||||
"""更新积分商品(管理员)"""
|
||||
db_product = db.query(PointProductDB).filter(
|
||||
PointProductDB.id == product_id
|
||||
).first()
|
||||
|
||||
if not db_product:
|
||||
return error_response(code=404, message="商品不存在")
|
||||
|
||||
update_data = product.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(db_product, key, value)
|
||||
|
||||
try:
|
||||
db.commit()
|
||||
db.refresh(db_product)
|
||||
return success_response(data=PointProductInfo.model_validate(db_product))
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return error_response(code=500, message=f"更新失败: {str(e)}")
|
||||
172
app/api/endpoints/point_product_order.py
Normal file
172
app/api/endpoints/point_product_order.py
Normal file
@ -0,0 +1,172 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from app.models.point_product_order import (
|
||||
PointProductOrderDB,
|
||||
PointProductOrderCreate,
|
||||
PointProductOrderUpdate,
|
||||
PointProductOrderInfo,
|
||||
PointProductOrderStatus
|
||||
)
|
||||
from app.models.point_product import PointProductDB
|
||||
from app.models.database import get_db
|
||||
from app.api.deps import get_current_user, get_admin_user, get_deliveryman_user
|
||||
from app.models.user import UserDB
|
||||
from app.core.response import success_response, error_response, ResponseModel
|
||||
from app.core.point_manager import PointManager
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("", response_model=ResponseModel)
|
||||
async def create_point_product_order(
|
||||
order: PointProductOrderCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: UserDB = Depends(get_current_user)
|
||||
):
|
||||
"""创建积分商品订单"""
|
||||
# 查询商品信息
|
||||
product = db.query(PointProductDB).filter(
|
||||
PointProductDB.id == order.product_id,
|
||||
PointProductDB.is_active == True
|
||||
).first()
|
||||
|
||||
if not product:
|
||||
return error_response(code=404, message="商品不存在或已下架")
|
||||
|
||||
# 计算所需总积分
|
||||
total_points = product.point_amount * order.quantity
|
||||
|
||||
# 检查用户积分是否足够
|
||||
if current_user.points < total_points:
|
||||
return error_response(code=400, message="积分不足")
|
||||
|
||||
# 创建订单
|
||||
db_order = PointProductOrderDB(
|
||||
orderid=PointProductOrderDB.generate_order_id(),
|
||||
user_id=current_user.userid,
|
||||
product_id=product.id,
|
||||
product_name=product.name,
|
||||
product_image=product.product_image,
|
||||
product_description=product.description,
|
||||
product_point_amount=product.point_amount,
|
||||
quantity=order.quantity,
|
||||
order_point_amount=total_points
|
||||
)
|
||||
|
||||
try:
|
||||
# 扣减用户积分
|
||||
point_manager = PointManager(db)
|
||||
point_manager.deduct_points(
|
||||
user_id=current_user.userid,
|
||||
points=total_points,
|
||||
description=f"兑换商品: {product.name}",
|
||||
order_id=db_order.orderid
|
||||
)
|
||||
|
||||
# 保存订单
|
||||
db.add(db_order)
|
||||
db.commit()
|
||||
db.refresh(db_order)
|
||||
|
||||
return success_response(data=PointProductOrderInfo.model_validate(db_order))
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return error_response(code=500, message=f"创建订单失败: {str(e)}")
|
||||
|
||||
@router.get("", response_model=ResponseModel)
|
||||
async def get_point_product_orders(
|
||||
status: Optional[PointProductOrderStatus] = None,
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: UserDB = Depends(get_current_user)
|
||||
):
|
||||
"""获取积分商品订单列表"""
|
||||
query = db.query(PointProductOrderDB).filter(
|
||||
PointProductOrderDB.user_id == current_user.userid
|
||||
)
|
||||
|
||||
if status:
|
||||
query = query.filter(PointProductOrderDB.status == status)
|
||||
|
||||
total = query.count()
|
||||
|
||||
orders = query.order_by(PointProductOrderDB.create_time.desc())\
|
||||
.offset(skip)\
|
||||
.limit(limit)\
|
||||
.all()
|
||||
|
||||
return success_response(data={
|
||||
"total": total,
|
||||
"items": [PointProductOrderInfo.model_validate(o) for o in orders]
|
||||
})
|
||||
|
||||
@router.get("/{orderid}", response_model=ResponseModel)
|
||||
async def get_point_product_order(
|
||||
orderid: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: UserDB = Depends(get_current_user)
|
||||
):
|
||||
"""获取积分商品订单详情"""
|
||||
order = db.query(PointProductOrderDB).filter(
|
||||
PointProductOrderDB.orderid == orderid,
|
||||
PointProductOrderDB.user_id == current_user.userid
|
||||
).first()
|
||||
|
||||
if not order:
|
||||
return error_response(code=404, message="订单不存在")
|
||||
|
||||
return success_response(data=PointProductOrderInfo.model_validate(order))
|
||||
|
||||
|
||||
# 接受订单
|
||||
@router.post("/{orderid}/accept", response_model=ResponseModel)
|
||||
async def accept_point_product_order(
|
||||
orderid: str,
|
||||
db: Session = Depends(get_db),
|
||||
deliveryman: UserDB = Depends(get_deliveryman_user)
|
||||
):
|
||||
order = db.query(PointProductOrderDB).filter(
|
||||
PointProductOrderDB.orderid == orderid,
|
||||
PointProductOrderDB.status == PointProductOrderStatus.PENDING
|
||||
).first()
|
||||
|
||||
if not order:
|
||||
return error_response(code=404, message="订单不存在")
|
||||
|
||||
if order.status != PointProductOrderStatus.PENDING:
|
||||
return error_response(code=400, message="订单状态不正确")
|
||||
|
||||
# 更新订单状态
|
||||
order.status = PointProductOrderStatus.CONFIRMED
|
||||
db.commit()
|
||||
db.refresh(order)
|
||||
|
||||
return success_response(data=PointProductOrderInfo.model_validate(order))
|
||||
|
||||
# 拒绝订单
|
||||
@router.post("/{orderid}/reject", response_model=ResponseModel)
|
||||
async def reject_point_product_order(
|
||||
orderid: str,
|
||||
db: Session = Depends(get_db),
|
||||
deliveryman: UserDB = Depends(get_deliveryman_user)
|
||||
):
|
||||
order = db.query(PointProductOrderDB).filter(
|
||||
PointProductOrderDB.orderid == orderid,
|
||||
PointProductOrderDB.status == PointProductOrderStatus.PENDING
|
||||
).first()
|
||||
|
||||
if not order:
|
||||
return error_response(code=404, message="订单不存在")
|
||||
|
||||
if order.status != PointProductOrderStatus.PENDING:
|
||||
return error_response(code=400, message="订单状态不正确")
|
||||
|
||||
# 更新订单状态
|
||||
order.status = PointProductOrderStatus.CANCELLED
|
||||
db.commit()
|
||||
db.refresh(order)
|
||||
|
||||
return success_response(data=PointProductOrderInfo.model_validate(order))
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from app.api.endpoints import wechat,user, address, community, station, order, coupon, community_building, upload, merchant, merchant_product, merchant_order, point, config, merchant_category, log, account,merchant_pay_order, message, bank_card, withdraw, mp
|
||||
from app.api.endpoints import wechat,user, address, community, station, order, coupon, community_building, upload, merchant, merchant_product, merchant_order, point, config, merchant_category, log, account,merchant_pay_order, message, bank_card, withdraw, mp, point_product, point_product_order
|
||||
from app.models.database import Base, engine
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import JSONResponse
|
||||
@ -39,6 +39,8 @@ app.include_router(user.router, prefix="/api/user", tags=["用户"])
|
||||
app.include_router(bank_card.router, prefix="/api/bank-cards", tags=["用户银行卡"])
|
||||
app.include_router(withdraw.router, prefix="/api/withdraw", tags=["提现"])
|
||||
app.include_router(point.router, prefix="/api/point", tags=["用户积分"])
|
||||
app.include_router(point_product.router, prefix="/api/point-products", tags=["积分商品"])
|
||||
app.include_router(point_product_order.router, prefix="/api/point-product-orders", tags=["积分商品订单"])
|
||||
app.include_router(account.router, prefix="/api/account", tags=["账户"])
|
||||
app.include_router(address.router, prefix="/api/address", tags=["配送地址"])
|
||||
app.include_router(community.router, prefix="/api/community", tags=["社区"])
|
||||
|
||||
58
app/models/point_product.py
Normal file
58
app/models/point_product.py
Normal file
@ -0,0 +1,58 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Boolean, JSON
|
||||
from sqlalchemy.sql import func
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
from .database import Base
|
||||
from app.core.imageprocessor import process_image, ImageFormat
|
||||
|
||||
class PointProductDB(Base):
|
||||
__tablename__ = "point_products"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String(100), nullable=False)
|
||||
product_image = Column(String(200), nullable=False)
|
||||
tags = Column(String(100), nullable=True) # 商品标签,JSON数组
|
||||
description = Column(String(500), nullable=False)
|
||||
point_amount = Column(Integer, nullable=False) # 所需积分
|
||||
is_active = Column(Boolean, nullable=False, default=True) # 是否上架
|
||||
create_time = Column(DateTime(timezone=True), server_default=func.now())
|
||||
update_time = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
@property
|
||||
def optimized_product_image(self):
|
||||
"""获取优化后的商品图片"""
|
||||
if self.product_image:
|
||||
return process_image(self.product_image).thumbnail(400, 400).format(ImageFormat.WEBP).build()
|
||||
return None
|
||||
|
||||
# Pydantic 模型
|
||||
class PointProductCreate(BaseModel):
|
||||
name: str = Field(..., max_length=100)
|
||||
product_image: str = Field(..., max_length=200)
|
||||
tags: Optional[str] = None
|
||||
description: str = Field(..., max_length=500)
|
||||
point_amount: int = Field(..., gt=0)
|
||||
is_active: bool = Field(default=True)
|
||||
|
||||
class PointProductUpdate(BaseModel):
|
||||
name: Optional[str] = Field(None, max_length=100)
|
||||
product_image: Optional[str] = Field(None, max_length=200)
|
||||
tags: Optional[List[str]] = None
|
||||
description: Optional[str] = Field(None, max_length=500)
|
||||
point_amount: Optional[int] = Field(None, gt=0)
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
class PointProductInfo(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
product_image: str
|
||||
optimized_product_image: Optional[str]
|
||||
tags: Optional[str]
|
||||
description: str
|
||||
point_amount: int
|
||||
is_active: bool
|
||||
create_time: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
73
app/models/point_product_order.py
Normal file
73
app/models/point_product_order.py
Normal file
@ -0,0 +1,73 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Enum, Float
|
||||
from sqlalchemy.sql import func
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
from .database import Base
|
||||
import enum
|
||||
from app.core.utils import CommonUtils
|
||||
|
||||
class PointProductOrderStatus(str, enum.Enum):
|
||||
PENDING = "PENDING" # 待确认
|
||||
CONFIRMED = "CONFIRMED" # 已确认
|
||||
CANCELLED = "CANCELLED" # 已取消
|
||||
DELIVERED = "DELIVERED" # 已送达
|
||||
|
||||
@property
|
||||
def status_text(self) -> str:
|
||||
status_map = {
|
||||
PointProductOrderStatus.PENDING: "待确认",
|
||||
PointProductOrderStatus.CONFIRMED: "已确认",
|
||||
PointProductOrderStatus.CANCELLED: "已取消",
|
||||
PointProductOrderStatus.DELIVERED: "已送达"
|
||||
}
|
||||
return status_map.get(self, "未知状态")
|
||||
|
||||
class PointProductOrderDB(Base):
|
||||
__tablename__ = "point_product_orders"
|
||||
|
||||
orderid = Column(String(32), primary_key=True)
|
||||
user_id = Column(Integer, ForeignKey("users.userid"), index=True)
|
||||
delivery_order_id = Column(String(32), ForeignKey("shipping_orders.orderid"), nullable=True)
|
||||
product_id = Column(Integer, ForeignKey("point_products.id"))
|
||||
product_name = Column(String(100), nullable=False)
|
||||
product_image = Column(String(200), nullable=False)
|
||||
product_description = Column(String(500), nullable=False)
|
||||
product_point_amount = Column(Integer, nullable=False) # 单件商品所需积分
|
||||
quantity = Column(Integer, nullable=False, default=1)
|
||||
order_point_amount = Column(Integer, nullable=False) # 订单总积分
|
||||
status = Column(Enum(PointProductOrderStatus), nullable=False, default=PointProductOrderStatus.PENDING)
|
||||
create_time = Column(DateTime(timezone=True), server_default=func.now())
|
||||
update_time = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
@staticmethod
|
||||
def generate_order_id() -> str:
|
||||
"""生成订单号"""
|
||||
return CommonUtils.generate_order_id("P") # P代表积分商品订单
|
||||
|
||||
# Pydantic 模型
|
||||
class PointProductOrderCreate(BaseModel):
|
||||
product_id: int
|
||||
quantity: int = Field(default=1, gt=0)
|
||||
|
||||
class PointProductOrderUpdate(BaseModel):
|
||||
status: PointProductOrderStatus
|
||||
delivery_order_id: Optional[str] = None
|
||||
|
||||
class PointProductOrderInfo(BaseModel):
|
||||
orderid: str
|
||||
user_id: int
|
||||
delivery_order_id: Optional[str]
|
||||
product_id: int
|
||||
product_name: str
|
||||
product_image: str
|
||||
product_description: str
|
||||
product_point_amount: int
|
||||
quantity: int
|
||||
order_point_amount: int
|
||||
status: PointProductOrderStatus
|
||||
create_time: datetime
|
||||
update_time: Optional[datetime]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
Loading…
Reference in New Issue
Block a user