deliveryman-api/app/api/endpoints/merchant_order.py
2025-01-13 22:03:58 +08:00

377 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from app.models.merchant_order import (
MerchantOrderDB,
MerchantOrderCreate,
MerchantOrderInfo,
generate_order_id,
generate_verify_code,
MerchantOrderStatus
)
from app.models.merchant_product import MerchantProductDB
from app.models.database import get_db
from app.api.deps import get_current_user, get_admin_user
from app.models.user import UserDB
from app.core.response import success_response, error_response, ResponseModel
from datetime import datetime, timezone
from app.models.merchant import MerchantDB
from app.models.user_point_log import UserPointLogDB, PointChangeType
router = APIRouter()
@router.post("", response_model=ResponseModel)
async def create_order(
order: MerchantOrderCreate,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""创建商家订单"""
# 检查商品是否存在
product = db.query(MerchantProductDB).filter(
MerchantProductDB.id == order.merchant_product_id
).first()
if not product:
return error_response(code=404, message="商品不存在")
# 计算积分抵扣
available_points = current_user.points or 0
deduct_amount = min(
float(product.max_deduct_points),
available_points
) if available_points > 0 else 0
# 计算实际支付金额
original_amount = float(product.sale_price)
pay_amount = max(0, original_amount - deduct_amount)
# 生成订单号和核销码
while True:
order_id = generate_order_id()
verify_code = generate_verify_code()
exists = db.query(MerchantOrderDB).filter(
(MerchantOrderDB.order_id == order_id) |
(MerchantOrderDB.order_verify_code == verify_code)
).first()
if not exists:
break
# 创建订单
db_order = MerchantOrderDB(
order_id=order_id,
user_id=current_user.userid,
merchant_product_id=order.merchant_product_id,
order_amount=original_amount,
deduct_amount=deduct_amount,
pay_amount=pay_amount,
status=MerchantOrderStatus.CREATED,
order_verify_code=verify_code
)
# 扣除用户积分并记录日志
if deduct_amount > 0:
current_user.points -= deduct_amount
# 记录积分变动日志
point_log = UserPointLogDB(
user_id=current_user.userid,
change_amount=-deduct_amount, # 负数表示扣减
type=PointChangeType.CONSUME_DEDUCT,
remark=f"订单{order_id}消费抵扣"
)
db.add(point_log)
db.add(db_order)
try:
db.commit()
db.refresh(db_order)
return success_response(data=MerchantOrderInfo.model_validate(db_order))
except Exception as e:
db.rollback()
return error_response(code=500, message=f"创建订单失败: {str(e)}")
@router.get("/user", response_model=ResponseModel)
async def get_user_orders(
skip: int = 0,
limit: int = 20,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""获取用户的订单列表"""
orders = db.query(
MerchantOrderDB,
MerchantProductDB.name.label('product_name'),
MerchantProductDB.image_url.label('product_image'),
MerchantDB.name.label('merchant_name'),
MerchantDB.latitude.label('merchant_latitude'),
MerchantDB.longitude.label('merchant_longitude'),
MerchantDB.phone.label('merchant_phone')
).join(
MerchantProductDB,
MerchantOrderDB.merchant_product_id == MerchantProductDB.id
).join(
MerchantDB,
MerchantProductDB.merchant_id == MerchantDB.id
).filter(
MerchantOrderDB.user_id == current_user.userid
).order_by(
MerchantOrderDB.create_time.desc()
).offset(skip).limit(limit).all()
# 构建返回数据
order_list = [{
"id": order.MerchantOrderDB.id,
"order_id": order.MerchantOrderDB.order_id,
"user_id": order.MerchantOrderDB.user_id,
"merchant_product_id": order.MerchantOrderDB.merchant_product_id,
"order_amount": order.MerchantOrderDB.order_amount,
"status": order.MerchantOrderDB.status,
"order_verify_code": order.MerchantOrderDB.order_verify_code,
"verify_time": order.MerchantOrderDB.verify_time,
"verify_user_id": order.MerchantOrderDB.verify_user_id,
"create_time": order.MerchantOrderDB.create_time,
"update_time": order.MerchantOrderDB.update_time,
# 商品信息
"product_name": order.product_name,
"product_image": order.product_image,
# 商家信息
"merchant_name": order.merchant_name,
"merchant_latitude": order.merchant_latitude,
"merchant_longitude": order.merchant_longitude,
"merchant_phone": order.merchant_phone
} for order in orders]
return success_response(data=order_list)
@router.post("/{order_id}/verify", response_model=ResponseModel)
async def verify_order(
order_id: str,
verify_code: str,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""核销订单"""
order = db.query(MerchantOrderDB).filter(
MerchantOrderDB.order_id == order_id,
MerchantOrderDB.order_verify_code == verify_code,
MerchantOrderDB.verify_time.is_(None) # 未核销
).first()
if not order:
return error_response(code=404, message="订单不存在或已核销")
# 更新核销时间和核销用户
order.verify_time = datetime.now(timezone.utc)
order.verify_user_id = current_user.userid
order.status = MerchantOrderStatus.VERIFIED # 更新为已核销状态
try:
db.commit()
return success_response(
message="核销成功",
data=MerchantOrderInfo.model_validate(order)
)
except Exception as e:
db.rollback()
return error_response(code=500, message=f"核销失败: {str(e)}")
@router.post("/{order_id}/unverify", response_model=ResponseModel)
async def set_order_unverified(
order_id: str,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""设置订单为未核销状态"""
order = db.query(MerchantOrderDB).filter(
MerchantOrderDB.order_id == order_id,
MerchantOrderDB.status == MerchantOrderStatus.CREATED # 只有已下单状态可以设为未核销
).first()
if not order:
return error_response(code=404, message="订单不存在或状态不正确")
# 更新状态为未核销
order.status = MerchantOrderStatus.UNVERIFIED
try:
db.commit()
return success_response(
message="状态更新成功",
data=MerchantOrderInfo.model_validate(order)
)
except Exception as e:
db.rollback()
return error_response(code=500, message=f"状态更新失败: {str(e)}")
@router.post("/{order_id}/refund/apply", response_model=ResponseModel)
async def apply_refund(
order_id: str,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""申请退款"""
order = db.query(MerchantOrderDB).filter(
MerchantOrderDB.order_id == order_id,
MerchantOrderDB.user_id == current_user.userid, # 只能申请自己的订单
MerchantOrderDB.status.in_([MerchantOrderStatus.CREATED, MerchantOrderStatus.UNVERIFIED]) # 只有未核销的订单可以退款
).first()
if not order:
return error_response(code=404, message="订单不存在或状态不允许退款")
# 更新状态为退款中
order.status = MerchantOrderStatus.REFUNDING
try:
db.commit()
return success_response(
message="退款申请成功",
data=MerchantOrderInfo.model_validate(order)
)
except Exception as e:
db.rollback()
return error_response(code=500, message=f"申请退款失败: {str(e)}")
@router.post("/{order_id}/refund/confirm", response_model=ResponseModel)
async def confirm_refund(
order_id: str,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_admin_user)
):
"""确认退款(管理员)"""
# 查询订单和用户信息
order = db.query(MerchantOrderDB).join(
UserDB,
MerchantOrderDB.user_id == UserDB.userid
).filter(
MerchantOrderDB.order_id == order_id,
MerchantOrderDB.status == MerchantOrderStatus.REFUNDING # 只能确认退款中的订单
).first()
if not order:
return error_response(code=404, message="订单不存在或状态不正确")
try:
# 更新状态为已退款
order.status = MerchantOrderStatus.REFUNDED
# 如果有积分抵扣,返还积分
if order.deduct_amount > 0:
# 返还用户积分
order.user.points += float(order.deduct_amount)
# 记录积分变动日志
point_log = UserPointLogDB(
user_id=order.user_id,
change_amount=float(order.deduct_amount), # 正数表示返还
type=PointChangeType.CONSUME_RETURN,
remark=f"订单{order_id}退款返还"
)
db.add(point_log)
db.commit()
return success_response(
message="退款确认成功",
data=MerchantOrderInfo.model_validate(order)
)
except Exception as e:
db.rollback()
return error_response(code=500, message=f"确认退款失败: {str(e)}")
@router.get("/{order_id}", response_model=ResponseModel)
async def get_order_detail(
order_id: str,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""获取订单详情"""
order = db.query(
MerchantOrderDB,
MerchantProductDB.name.label('product_name'),
MerchantProductDB.image_url.label('product_image'),
MerchantDB.name.label('merchant_name'),
MerchantDB.latitude.label('merchant_latitude'),
MerchantDB.longitude.label('merchant_longitude'),
MerchantDB.phone.label('merchant_phone')
).join(
MerchantProductDB,
MerchantOrderDB.merchant_product_id == MerchantProductDB.id
).join(
MerchantDB,
MerchantProductDB.merchant_id == MerchantDB.id
).filter(
MerchantOrderDB.order_id == order_id
).first()
if not order:
return error_response(code=404, message="订单不存在")
# 检查权限
if order.MerchantOrderDB.user_id != current_user.userid:
return error_response(code=403, message="无权查看此订单")
# 构建返回数据
order_data = {
"id": order.MerchantOrderDB.id,
"order_id": order.MerchantOrderDB.order_id,
"user_id": order.MerchantOrderDB.user_id,
"merchant_product_id": order.MerchantOrderDB.merchant_product_id,
"order_amount": order.MerchantOrderDB.order_amount,
"status": order.MerchantOrderDB.status,
"order_verify_code": order.MerchantOrderDB.order_verify_code,
"verify_time": order.MerchantOrderDB.verify_time,
"verify_user_id": order.MerchantOrderDB.verify_user_id,
"create_time": order.MerchantOrderDB.create_time,
"update_time": order.MerchantOrderDB.update_time,
# 商品信息
"product_name": order.product_name,
"product_image": order.product_image,
# 商家信息
"merchant_name": order.merchant_name,
"merchant_latitude": order.merchant_latitude,
"merchant_longitude": order.merchant_longitude,
"merchant_phone": order.merchant_phone
}
return success_response(data=order_data)
@router.post("/calculate-price", response_model=ResponseModel)
async def calculate_order_price(
order: MerchantOrderCreate,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""计算订单价格
1. 获取商品信息和用户积分
2. 计算最高可抵扣金额
3. 返回最终支付金额
"""
# 查询商品信息
product = db.query(MerchantProductDB).filter(
MerchantProductDB.id == order.merchant_product_id
).first()
if not product:
return error_response(code=404, message="商品不存在")
# 获取用户积分
available_points = current_user.points or 0
# 计算最高可抵扣金额1积分 = 1分钱
max_deduct_amount = min(
float(product.max_deduct_points), # 商品最高可抵扣金额
available_points # 用户可用积分
) if available_points > 0 else 0
# 计算最终支付金额
final_amount = max(0, float(product.sale_price) - max_deduct_amount)
return success_response(data={
"original_price": float(product.sale_price),
"available_points": available_points,
"max_deduct_points": float(product.max_deduct_points),
"max_deduct_amount": max_deduct_amount,
"final_amount": final_amount
})