deliveryman-api/app/api/endpoints/merchant_order.py
2025-01-27 23:57:58 +08:00

414 lines
14 KiB
Python

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from sqlalchemy import func
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.point import PointRecordDB
from app.core.account import AccountManager
from app.core.wechat import WeChatClient
from pydantic import BaseModel
router = APIRouter()
@router.post("", response_model=ResponseModel)
async def create_merchant_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="商品不存在")
# 检查限购
if product.purchase_limit:
# 查询用户已购买数量
purchased_count = db.query(func.count(MerchantOrderDB.id)).filter(
MerchantOrderDB.user_id == current_user.userid,
MerchantOrderDB.merchant_product_id == product.id,
MerchantOrderDB.status.in_([
MerchantOrderStatus.CREATED,
MerchantOrderStatus.VERIFIED,
MerchantOrderStatus.UNVERIFIED
])
).scalar()
if purchased_count >= product.purchase_limit:
return error_response(
code=400,
message=f"该商品限购{product.purchase_limit}次,您已达到限购次数"
)
original_amount = float(product.sale_price)
pay_amount = original_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,
pay_amount=pay_amount,
status=MerchantOrderStatus.CREATED,
order_verify_code=verify_code
)
try:
db.add(db_order)
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,
MerchantProductDB,
MerchantDB
).join(
MerchantProductDB,
MerchantOrderDB.merchant_product_id == MerchantProductDB.id
).join(
MerchantDB,
MerchantProductDB.merchant_id == MerchantDB.id
).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="订单不存在或已核销")
try:
# 更新核销时间和核销用户
order.MerchantOrderDB.verify_time = datetime.now(timezone.utc)
order.MerchantOrderDB.verify_user_id = current_user.userid
order.MerchantOrderDB.status = MerchantOrderStatus.VERIFIED
# 使用账户管理器处理余额变更
account_manager = AccountManager(db)
settlement_amount = float(order.MerchantProductDB.settlement_amount)
account_manager.change_balance(
user_id=order.MerchantDB.user_id,
amount=settlement_amount,
description=f"{order.MerchantProductDB.name} 订单核销",
transaction_id=order.MerchantOrderDB.order_id
)
db.commit()
return success_response(
message="核销成功",
data=MerchantOrderInfo.model_validate(order.MerchantOrderDB)
)
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
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="商品不存在")
return success_response(data={
"original_price": float(product.sale_price),
"final_amount": product.sale_price
})
class RefundRequest(BaseModel):
"""退款请求"""
order_id: str
@router.post("/refund/approve", response_model=ResponseModel)
async def refund_merchant_order(
request: RefundRequest,
db: Session = Depends(get_db),
admin_user: UserDB = Depends(get_admin_user)
):
"""
审核通过退款申请(管理员接口)
- 检查订单是否处于退款中状态
- 调用微信支付退款接口
- 退款状态由微信支付回调更新
"""
try:
# 查询订单
order = db.query(MerchantOrderDB).filter(
MerchantOrderDB.order_id == request.order_id,
MerchantOrderDB.status == MerchantOrderStatus.REFUNDING, # 只能退款申请中的订单
MerchantOrderDB.pay_status == True # 已支付的订单
).first()
if not order:
return error_response(code=404, message="订单不存在或状态不正确")
# 调用微信支付退款
wechat = WeChatClient()
try:
await wechat.apply_refund(
order_id=order.order_id,
total_amount=int(float(order.pay_amount) * 100), # 转换为分
reason="用户申请退款"
)
except Exception as e:
return error_response(code=500, message=f"申请退款失败: {str(e)}")
db.commit()
return success_response(message="退款申请成功")
except Exception as e:
db.rollback()
return error_response(code=500, message=f"处理退款失败: {str(e)}")