增加预下单接口,增加积分抵扣。

This commit is contained in:
aaron 2025-01-25 21:35:25 +08:00
parent 6e37ea3bfe
commit a31d161699
3 changed files with 155 additions and 88 deletions

View File

@ -13,7 +13,8 @@ from app.models.order import (
OrderPriceInfo,
OrderStatus,
OrderCancel,
OrderComplete
OrderComplete,
OrderPriceResult
)
from app.models.database import get_db
from app.api.deps import get_current_user, get_deliveryman_user
@ -27,9 +28,91 @@ from sqlalchemy.orm import joinedload
from app.models.community import CommunityDB
from app.models.community_building import CommunityBuildingDB
from app.models.station import StationDB
from app.models.point import PointRecordDB, PointRecordType
router = APIRouter()
def calculate_price(price_request: OrderPriceCalculateRequest,user: UserDB,db: Session) -> OrderPriceResult:
"""
计算订单价格自动使用优惠券或积分抵扣
Args:
price_request: 价格计算请求
user: 用户信息
Returns:
OrderPriceResult: 包含价格信息和使用的优惠券/积分信息
"""
# 计算所有包裹中的取件码总数
package_count = sum(
len(package.pickup_codes.split(','))
for package in price_request.packages
if package.pickup_codes
)
result = OrderPriceResult(
price_info=OrderPriceInfo(
package_count=package_count,
original_amount=settings.ORDER_BASE_PRICE,
coupon_discount_amount=0,
points_discount_amount=0,
final_amount=settings.ORDER_BASE_PRICE
)
)
original_amount = settings.ORDER_BASE_PRICE
# 超过阈值的包裹额外收费
if package_count > settings.ORDER_EXTRA_PACKAGE_THRESHOLD:
extra_count = package_count - settings.ORDER_EXTRA_PACKAGE_THRESHOLD
original_amount += extra_count * settings.ORDER_EXTRA_PACKAGE_PRICE
result.price_info.package_count = package_count
result.price_info.original_amount = original_amount
result.price_info.final_amount = original_amount
remaining_amount = original_amount
# 1. 查找用户可用的优惠券(按金额从大到小排序)
available_coupon = db.query(UserCouponDB).filter(
UserCouponDB.user_id == user.userid,
UserCouponDB.status == CouponStatus.UNUSED,
UserCouponDB.expire_time > datetime.now()
).order_by(UserCouponDB.coupon_amount.desc()).first()
# 2. 如果有可用优惠券,优先使用优惠券
if available_coupon:
coupon_discount = min(remaining_amount, available_coupon.coupon_amount)
result.price_info.coupon_discount_amount = coupon_discount
result.price_info.coupon_id = available_coupon.id
result.used_coupon_id = available_coupon.id
remaining_amount -= coupon_discount
# 3. 如果没有优惠券,且用户有积分,则使用积分抵扣
elif user.points > 0:
# 计算最大可抵扣金额1元=10积分
max_points_discount = user.points / 10
points_discount = min(remaining_amount, max_points_discount)
result.price_info.points_discount_amount = points_discount
result.price_info.points_used = int(points_discount * 10)
result.used_points = int(points_discount * 10) # 记录使用的积分数
remaining_amount -= points_discount
# 计算最终金额
result.price_info.final_amount = remaining_amount
return result
@router.post("/pre-order", response_model=ResponseModel)
async def pre_order(
request: OrderPriceCalculateRequest,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""预下单 - 计算价格"""
price_info = calculate_price(request, current_user, db)
return success_response(data=price_info)
@router.post("/", response_model=ResponseModel)
async def create_shipping_order(
order: OrderCreate,
@ -38,51 +121,44 @@ async def create_shipping_order(
):
"""创建配送订单"""
# 计算订单价格
price_result = calculate_price(order.price_request, current_user, db)
price_info = price_result.price_info
# 生成订单号
orderid = generate_order_id()
# 计算订单价格
package_count = sum(
len(package.pickup_codes.split(','))
for package in order.price_request.packages
if package.pickup_codes
)
# 计算原始金额
original_amount = settings.ORDER_BASE_PRICE
if package_count > settings.ORDER_EXTRA_PACKAGE_THRESHOLD:
extra_packages = package_count - settings.ORDER_EXTRA_PACKAGE_THRESHOLD
original_amount += extra_packages * settings.ORDER_EXTRA_PACKAGE_PRICE
original_amount = price_info.original_amount
# 计算优惠券折扣
coupon_discount = 0
coupon_id = None
coupon_discount = price_info.coupon_discount_amount
coupon_id = price_info.coupon_id
# 查询用户优惠券
user_coupon = db.query(UserCouponDB).filter(
UserCouponDB.user_id == current_user.userid,
UserCouponDB.status == CouponStatus.UNUSED,
UserCouponDB.expire_time > datetime.now(timezone.utc)
).first()
if user_coupon:
coupon_discount = user_coupon.coupon_amount
coupon_id = user_coupon.id
# 更新优惠券状态
user_coupon.status = CouponStatus.USED
# 计算最终金额
final_amount = max(0, original_amount - coupon_discount)
if coupon_id:
user_coupon = db.query(UserCouponDB).filter(
UserCouponDB.id == coupon_id
).first()
if user_coupon:
coupon_discount = user_coupon.coupon_amount
coupon_id = user_coupon.id
# 更新优惠券状态
user_coupon.status = CouponStatus.USED
# 创建订单
db_order = ShippingOrderDB(
orderid=orderid,
userid=current_user.userid,
addressid=order.addressid,
package_count=package_count,
package_count=price_info.package_count,
original_amount=original_amount,
coupon_discount_amount=coupon_discount,
point_discount_amount=price_info.points_discount_amount,
coupon_id=coupon_id,
final_amount=final_amount,
final_amount=price_info.final_amount,
status=OrderStatus.CREATED,
delivery_method=order.delivery_method
)
db.add(db_order)
@ -97,6 +173,29 @@ async def create_shipping_order(
db.add(db_package)
try:
# 如果使用了优惠券,更新优惠券状态
if price_result.used_coupon_id:
coupon = db.query(UserCouponDB).filter(
UserCouponDB.id == price_result.used_coupon_id,
).first()
if coupon:
coupon.status = CouponStatus.USED
# 如果使用了积分,扣减用户积分并记录
if price_result.used_points:
# 扣减用户积分
current_user.points -= price_result.used_points
# 记录积分变动
point_record = PointRecordDB(
user_id=current_user.userid,
points=-price_result.used_points, # 负数表示扣减
type=PointRecordType.CONSUME_DEDUCT,
order_id=db_order.orderid,
description=f"订单{db_order.orderid}抵扣{price_result.used_points}积分"
)
db.add(point_record)
db.commit()
db.refresh(db_order)
@ -309,58 +408,6 @@ async def get_deliveryman_orders(
"items": orders
})
@router.post("/calculate-price", response_model=ResponseModel)
async def calculate_order_price(
request: OrderPriceCalculateRequest,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""计算订单价格"""
# 计算所有包裹中的取件码总数
package_count = sum(
len(package.pickup_codes.split(','))
for package in request.packages
if package.pickup_codes
)
# 计算原始金额
original_amount = settings.ORDER_BASE_PRICE
if package_count > settings.ORDER_EXTRA_PACKAGE_THRESHOLD:
extra_packages = package_count - settings.ORDER_EXTRA_PACKAGE_THRESHOLD
original_amount += extra_packages * settings.ORDER_EXTRA_PACKAGE_PRICE
# 计算优惠券折扣
coupon_discount = 0
used_coupon_id = None
# 查询用户所有可用的优惠券
available_coupons = db.query(UserCouponDB).filter(
UserCouponDB.user_id == current_user.userid,
UserCouponDB.status == CouponStatus.UNUSED
).order_by(
UserCouponDB.coupon_amount.desc() # 优先使用面额大的优惠券
).all()
# 选择最优惠的券
if available_coupons:
user_coupon = available_coupons[0] # 取面额最大的优惠券
coupon_discount = user_coupon.coupon_amount
used_coupon_id = user_coupon.id
# 计算最终金额
final_amount = max(0, original_amount - coupon_discount)
price_info = OrderPriceInfo(
package_count=package_count,
original_amount=original_amount,
coupon_discount_amount=coupon_discount,
coupon_id=used_coupon_id,
final_amount=final_amount
)
return success_response(data=price_info)
@router.post("/{orderid}/cancel", response_model=ResponseModel)
async def cancel_order(
orderid: str,

View File

@ -28,6 +28,7 @@ class ShippingOrderDB(Base):
package_count = Column(Integer, nullable=False)
original_amount = Column(Float, nullable=False)
coupon_discount_amount = Column(Float, default=0)
point_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.CREATED)
@ -80,6 +81,7 @@ class OrderInfo(BaseModel):
package_count: int
original_amount: float
coupon_discount_amount: float
point_discount_amount: float = 0
coupon_id: Optional[int]
final_amount: float
status: OrderStatus
@ -121,9 +123,11 @@ def generate_order_id() -> str:
class OrderPriceInfo(BaseModel):
package_count: int
original_amount: float
coupon_discount_amount: float
coupon_discount_amount: float = 0
points_discount_amount: float = 0
points_used: Optional[int] = None
coupon_id: Optional[int] = None
final_amount: float
final_amount: float
# 添加取消订单请求模型
class OrderCancel(BaseModel):
@ -131,4 +135,10 @@ class OrderCancel(BaseModel):
# 完成订单请求模型
class OrderComplete(BaseModel):
images: Optional[List[str]] = Field(None, max_items=5) # 最多5张图片可选
images: Optional[List[str]] = Field(None, max_items=5) # 最多5张图片可选
class OrderPriceResult(BaseModel):
"""订单价格计算结果"""
price_info: OrderPriceInfo
used_coupon_id: Optional[int] = None # 使用的优惠券ID
used_points: Optional[int] = None # 使用的积分数

View File

@ -1,16 +1,24 @@
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, DECIMAL
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Enum
from sqlalchemy.sql import func
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime
from .database import Base
import enum
class PointRecordType(str, enum.Enum):
CONSUME_DEDUCT = "CONSUME_DEDUCT" # 消费抵扣
CONSUME_RETURN = "CONSUME_RETURN" # 消费返还
SYSTEM_SEND = "SYSTEM_SEND" # 系统发放
class PointRecordDB(Base):
__tablename__ = "point_records"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.userid"), nullable=False)
points = Column(DECIMAL(10,1), nullable=False)
points = Column(Integer, nullable=False) # 正数表示增加,负数表示减少
type = Column(Enum(PointRecordType), nullable=False)
order_id = Column(String(32), nullable=True, index=True) # 添加索引但不设置外键
description = Column(String(200), nullable=False)
create_time = Column(DateTime(timezone=True), server_default=func.now())
@ -22,7 +30,9 @@ class PointRecordCreate(BaseModel):
class PointRecordInfo(BaseModel):
id: int
user_id: int
points: float
points: int
type: PointRecordType
order_id: Optional[str]
description: str
create_time: datetime