增加预下单接口,增加积分抵扣。
This commit is contained in:
parent
6e37ea3bfe
commit
a31d161699
@ -13,7 +13,8 @@ from app.models.order import (
|
|||||||
OrderPriceInfo,
|
OrderPriceInfo,
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
OrderCancel,
|
OrderCancel,
|
||||||
OrderComplete
|
OrderComplete,
|
||||||
|
OrderPriceResult
|
||||||
)
|
)
|
||||||
from app.models.database import get_db
|
from app.models.database import get_db
|
||||||
from app.api.deps import get_current_user, get_deliveryman_user
|
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 import CommunityDB
|
||||||
from app.models.community_building import CommunityBuildingDB
|
from app.models.community_building import CommunityBuildingDB
|
||||||
from app.models.station import StationDB
|
from app.models.station import StationDB
|
||||||
|
from app.models.point import PointRecordDB, PointRecordType
|
||||||
|
|
||||||
router = APIRouter()
|
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)
|
@router.post("/", response_model=ResponseModel)
|
||||||
async def create_shipping_order(
|
async def create_shipping_order(
|
||||||
order: OrderCreate,
|
order: OrderCreate,
|
||||||
@ -38,30 +121,24 @@ 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()
|
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
|
original_amount = price_info.original_amount
|
||||||
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
|
coupon_discount = price_info.coupon_discount_amount
|
||||||
coupon_id = None
|
coupon_id = price_info.coupon_id
|
||||||
|
|
||||||
# 查询用户优惠券
|
# 查询用户优惠券
|
||||||
|
if coupon_id:
|
||||||
user_coupon = db.query(UserCouponDB).filter(
|
user_coupon = db.query(UserCouponDB).filter(
|
||||||
UserCouponDB.user_id == current_user.userid,
|
UserCouponDB.id == coupon_id
|
||||||
UserCouponDB.status == CouponStatus.UNUSED,
|
|
||||||
UserCouponDB.expire_time > datetime.now(timezone.utc)
|
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if user_coupon:
|
if user_coupon:
|
||||||
@ -70,19 +147,18 @@ async def create_shipping_order(
|
|||||||
# 更新优惠券状态
|
# 更新优惠券状态
|
||||||
user_coupon.status = CouponStatus.USED
|
user_coupon.status = CouponStatus.USED
|
||||||
|
|
||||||
# 计算最终金额
|
|
||||||
final_amount = max(0, original_amount - coupon_discount)
|
|
||||||
|
|
||||||
# 创建订单
|
# 创建订单
|
||||||
db_order = ShippingOrderDB(
|
db_order = ShippingOrderDB(
|
||||||
orderid=orderid,
|
orderid=orderid,
|
||||||
userid=current_user.userid,
|
userid=current_user.userid,
|
||||||
addressid=order.addressid,
|
addressid=order.addressid,
|
||||||
package_count=package_count,
|
package_count=price_info.package_count,
|
||||||
original_amount=original_amount,
|
original_amount=original_amount,
|
||||||
coupon_discount_amount=coupon_discount,
|
coupon_discount_amount=coupon_discount,
|
||||||
|
point_discount_amount=price_info.points_discount_amount,
|
||||||
coupon_id=coupon_id,
|
coupon_id=coupon_id,
|
||||||
final_amount=final_amount,
|
final_amount=price_info.final_amount,
|
||||||
|
status=OrderStatus.CREATED,
|
||||||
delivery_method=order.delivery_method
|
delivery_method=order.delivery_method
|
||||||
)
|
)
|
||||||
db.add(db_order)
|
db.add(db_order)
|
||||||
@ -97,6 +173,29 @@ async def create_shipping_order(
|
|||||||
db.add(db_package)
|
db.add(db_package)
|
||||||
|
|
||||||
try:
|
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.commit()
|
||||||
db.refresh(db_order)
|
db.refresh(db_order)
|
||||||
|
|
||||||
@ -309,58 +408,6 @@ async def get_deliveryman_orders(
|
|||||||
"items": 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)
|
@router.post("/{orderid}/cancel", response_model=ResponseModel)
|
||||||
async def cancel_order(
|
async def cancel_order(
|
||||||
orderid: str,
|
orderid: str,
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class ShippingOrderDB(Base):
|
|||||||
package_count = Column(Integer, nullable=False)
|
package_count = Column(Integer, nullable=False)
|
||||||
original_amount = Column(Float, nullable=False)
|
original_amount = Column(Float, nullable=False)
|
||||||
coupon_discount_amount = Column(Float, default=0)
|
coupon_discount_amount = Column(Float, default=0)
|
||||||
|
point_discount_amount = Column(Float, default=0)
|
||||||
coupon_id = Column(Integer, ForeignKey("user_coupons.id"), nullable=True)
|
coupon_id = Column(Integer, ForeignKey("user_coupons.id"), nullable=True)
|
||||||
final_amount = Column(Float, nullable=False)
|
final_amount = Column(Float, nullable=False)
|
||||||
status = Column(Enum(OrderStatus), nullable=False, default=OrderStatus.CREATED)
|
status = Column(Enum(OrderStatus), nullable=False, default=OrderStatus.CREATED)
|
||||||
@ -80,6 +81,7 @@ class OrderInfo(BaseModel):
|
|||||||
package_count: int
|
package_count: int
|
||||||
original_amount: float
|
original_amount: float
|
||||||
coupon_discount_amount: float
|
coupon_discount_amount: float
|
||||||
|
point_discount_amount: float = 0
|
||||||
coupon_id: Optional[int]
|
coupon_id: Optional[int]
|
||||||
final_amount: float
|
final_amount: float
|
||||||
status: OrderStatus
|
status: OrderStatus
|
||||||
@ -121,7 +123,9 @@ def generate_order_id() -> str:
|
|||||||
class OrderPriceInfo(BaseModel):
|
class OrderPriceInfo(BaseModel):
|
||||||
package_count: int
|
package_count: int
|
||||||
original_amount: float
|
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
|
coupon_id: Optional[int] = None
|
||||||
final_amount: float
|
final_amount: float
|
||||||
|
|
||||||
@ -132,3 +136,9 @@ class OrderCancel(BaseModel):
|
|||||||
# 完成订单请求模型
|
# 完成订单请求模型
|
||||||
class OrderComplete(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 # 使用的积分数
|
||||||
@ -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 sqlalchemy.sql import func
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from .database import Base
|
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):
|
class PointRecordDB(Base):
|
||||||
__tablename__ = "point_records"
|
__tablename__ = "point_records"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
user_id = Column(Integer, ForeignKey("users.userid"), nullable=False)
|
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)
|
description = Column(String(200), nullable=False)
|
||||||
create_time = Column(DateTime(timezone=True), server_default=func.now())
|
create_time = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
|
||||||
@ -22,7 +30,9 @@ class PointRecordCreate(BaseModel):
|
|||||||
class PointRecordInfo(BaseModel):
|
class PointRecordInfo(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
user_id: int
|
user_id: int
|
||||||
points: float
|
points: int
|
||||||
|
type: PointRecordType
|
||||||
|
order_id: Optional[str]
|
||||||
description: str
|
description: str
|
||||||
create_time: datetime
|
create_time: datetime
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user