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.point import PointRecordDB from app.core.account import AccountManager 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_record = PointRecordDB( user_id=current_user.userid, points=-float(deduct_amount), # 负数表示扣减 description=f"订单消费抵扣" ) db.add(point_record) 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, 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 # 如果有积分抵扣,返还积分 if order.deduct_amount > 0: # 返还用户积分 order.user.points += float(order.deduct_amount) point_record = PointRecordDB( user_id=order.user_id, points=float(order.deduct_amount), # 正数表示返还 description=f"订单退款返还" ) db.add(point_record) 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 })