441 lines
15 KiB
Python
441 lines
15 KiB
Python
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import func
|
|
from typing import List, Optional
|
|
from app.models.merchant_order import (
|
|
MerchantOrderDB,
|
|
MerchantOrderCreate,
|
|
MerchantOrderInfo,
|
|
MerchantOrderStatus
|
|
)
|
|
from app.models.address import AddressDB, AddressType
|
|
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, get_merchant_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
|
|
from app.core.config import settings
|
|
from app.core.utils import CommonUtils
|
|
from sqlalchemy.sql import text
|
|
import qrcode
|
|
import io
|
|
from fastapi import UploadFile
|
|
from app.core.qcloud import qcloud_manager
|
|
from app.core.imageprocessor import process_image, ImageFormat
|
|
from starlette.datastructures import Headers
|
|
from app.models.merchant_order import MerchantOrderVerify
|
|
from app.core.point_manager import PointManager, PointRecordType
|
|
from datetime import timedelta
|
|
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 > 0 and order.qty > product.purchase_limit:
|
|
return error_response(
|
|
code=400,
|
|
message=f"该商品限购 {product.purchase_limit} 个"
|
|
)
|
|
|
|
order_id = CommonUtils.generate_order_id('M')
|
|
verify_code = CommonUtils.generate_verify_code()
|
|
|
|
|
|
# 创建订单
|
|
pay_amount = float(product.sale_price) * order.qty
|
|
db_order = MerchantOrderDB(
|
|
order_id=order_id,
|
|
user_id=current_user.userid,
|
|
merchant_product_id=order.merchant_product_id,
|
|
unit_price=product.sale_price,
|
|
qty=order.qty,
|
|
address_id=order.address_id,
|
|
order_amount=pay_amount,
|
|
pay_amount=pay_amount,
|
|
gift_points=int(float(product.sale_price) * (float(product.gift_points_rate) / 100) * settings.POINT_RATIO),
|
|
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'),
|
|
MerchantProductDB.tags.label('product_tags'),
|
|
MerchantDB.name.label('merchant_name'),
|
|
MerchantDB.latitude.label('merchant_latitude'),
|
|
MerchantDB.longitude.label('merchant_longitude'),
|
|
MerchantDB.phone.label('merchant_phone'),
|
|
MerchantDB.id.label('merchant_id')
|
|
).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": process_image(order.product_image).thumbnail(width=800, height=800).format(ImageFormat.WEBP).build(),
|
|
"product_tags": order.product_tags,
|
|
# 商家信息
|
|
"merchant_id": order.merchant_id,
|
|
"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.get("/merchant/verify/{verify_code}", response_model=ResponseModel)
|
|
async def query_verify(
|
|
verify_code: str,
|
|
db: Session = Depends(get_db),
|
|
merchant_user: UserDB = Depends(get_merchant_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_verify_code == verify_code,
|
|
MerchantOrderDB.status == MerchantOrderStatus.UNVERIFIED,
|
|
MerchantDB.user_id == merchant_user.userid
|
|
).first()
|
|
|
|
return success_response(data=MerchantOrderInfo.model_validate(order))
|
|
|
|
@router.post("/merchant/verify", response_model=ResponseModel)
|
|
async def verify_order(
|
|
request: MerchantOrderVerify,
|
|
db: Session = Depends(get_db),
|
|
merchant_user: UserDB = Depends(get_merchant_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_verify_code == request.verify_code,
|
|
MerchantOrderDB.status == MerchantOrderStatus.UNVERIFIED,
|
|
MerchantDB.user_id == merchant_user.userid
|
|
).first()
|
|
|
|
if not order:
|
|
return error_response(code=404, message="订单不存在或已核销")
|
|
|
|
try:
|
|
# 更新核销时间和核销用户
|
|
order.MerchantOrderDB.verify_time = datetime.now(timezone.utc)
|
|
order.MerchantOrderDB.verify_user_id = merchant_user.userid
|
|
order.MerchantOrderDB.status = MerchantOrderStatus.VERIFIED
|
|
|
|
|
|
# 如果有积分奖励,赠送积分
|
|
if order.MerchantProductDB.gift_points > 0:
|
|
point_manager = PointManager(db)
|
|
point_manager.add_points(
|
|
user_id=order.MerchantOrderDB.user_id,
|
|
points=order.MerchantProductDB.gift_points,
|
|
description=f"团购券核销奖励",
|
|
order_id=order.MerchantOrderDB.order_id
|
|
)
|
|
|
|
# 对商家进行结算
|
|
account_manager = AccountManager(db)
|
|
settlement_amount = float(order.MerchantProductDB.settlement_amount)
|
|
if settlement_amount > 0:
|
|
account_manager.change_balance(
|
|
user_id=order.MerchantDB.user_id,
|
|
amount=settlement_amount,
|
|
description=f"团购券核销",
|
|
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}/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 == MerchantOrderStatus.UNVERIFIED # 只有未核销的订单可以退款
|
|
).first()
|
|
|
|
if not order:
|
|
return error_response(code=404, message="订单不存在或状态不允许退款")
|
|
|
|
# 更新状态为退款中
|
|
order.status = MerchantOrderStatus.REFUNDING
|
|
|
|
try:
|
|
# 调用微信支付退款
|
|
wechat = WeChatClient()
|
|
await wechat.apply_refund(
|
|
order_id=order.order_id,
|
|
total_amount=int(float(order.pay_amount) * 100) if not settings.DEBUG else 1, # 转换为分
|
|
reason="用户申请退款"
|
|
)
|
|
|
|
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,
|
|
longitude: Optional[float] = None,
|
|
latitude: Optional[float] = None,
|
|
db: Session = Depends(get_db),
|
|
current_user: UserDB = Depends(get_current_user)
|
|
):
|
|
"""获取订单详情"""
|
|
order = db.query(MerchantOrderDB).filter(
|
|
MerchantOrderDB.order_id == order_id
|
|
).first()
|
|
|
|
if not order:
|
|
return error_response(code=404, message="订单不存在")
|
|
|
|
product = db.query(MerchantProductDB).filter(
|
|
MerchantProductDB.id == order.merchant_product_id
|
|
).first()
|
|
|
|
if not product:
|
|
return error_response(code=404, message="商品不存在")
|
|
|
|
|
|
merchant = db.query(MerchantDB).filter(
|
|
MerchantDB.id == product.merchant_id
|
|
).first()
|
|
|
|
if not merchant:
|
|
return error_response(code=404, message="商家不存在")
|
|
|
|
|
|
order_data = {
|
|
**order.model_dump(),
|
|
**product.model_dump(),
|
|
**merchant.model_dump()
|
|
}
|
|
|
|
if order.address_id:
|
|
address = db.query(AddressDB).filter(
|
|
AddressDB.id == order.address_id
|
|
).first()
|
|
order_data["address"] = address.model_dump()
|
|
|
|
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={
|
|
"gift_points": int(float(product.sale_price) * (float(product.gift_points_rate) / 100) * settings.POINT_RATIO),
|
|
"amount": product.sale_price
|
|
})
|
|
|
|
|
|
@router.get("/{order_id}/verify-qrcode", response_model=ResponseModel)
|
|
async def get_order_qrcode(
|
|
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
|
|
).first()
|
|
|
|
if not order:
|
|
return error_response(code=404, message="订单不存在")
|
|
|
|
# 如果已经有二维码,直接返回
|
|
if order.qrcode_url:
|
|
url = process_image(order.qrcode_url).thumbnail(800, 800).format(ImageFormat.WEBP).build()
|
|
return success_response(data={"qrcode_url":url})
|
|
|
|
try:
|
|
# 生成二维码
|
|
qr = qrcode.QRCode(
|
|
version=1,
|
|
error_correction=qrcode.constants.ERROR_CORRECT_L,
|
|
box_size=10,
|
|
border=1,
|
|
)
|
|
qr.add_data(order.order_verify_code)
|
|
qr.make(fit=True)
|
|
|
|
# 创建图片
|
|
img = qr.make_image(fill_color="black", back_color="white")
|
|
|
|
# 将图片转换为字节流
|
|
img_byte_arr = io.BytesIO()
|
|
img.save(img_byte_arr, format='PNG')
|
|
img_byte_arr.seek(0)
|
|
|
|
# 创建 UploadFile 对象
|
|
file = UploadFile(
|
|
file=img_byte_arr,
|
|
filename=f"qrcode_{order_id}.png",
|
|
headers=Headers({"content-type": "image/png"})
|
|
)
|
|
|
|
# 上传到腾讯云
|
|
url = await qcloud_manager.upload_file(file)
|
|
|
|
# 更新订单的二维码URL
|
|
order.qrcode_url = url
|
|
db.commit()
|
|
|
|
process_url = process_image(url).thumbnail(800, 800).format(ImageFormat.WEBP).build()
|
|
return success_response(data={"qrcode_url": process_url})
|
|
except Exception as e:
|
|
db.rollback()
|
|
return error_response(code=500, message=f"生成二维码失败: {str(e)}")
|
|
|
|
|
|
# 获取商家订单数据汇总
|
|
@router.get("/merchant/summary", response_model=ResponseModel)
|
|
async def get_merchant_order_summary(
|
|
db: Session = Depends(get_db),
|
|
merchant_user: UserDB = Depends(get_merchant_user)
|
|
):
|
|
"""获取商家订单数据汇总"""
|
|
# 查询商家订单数量
|
|
total = db.query(MerchantOrderDB).filter(
|
|
MerchantOrderDB.user_id == merchant_user.userid
|
|
).count()
|
|
|
|
today = datetime.now().date()
|
|
yesterday = today - timedelta(days=1)
|
|
today_start = datetime.combine(today, datetime.min.time())
|
|
today_end = datetime.combine(today, datetime.max.time())
|
|
yesterday_start = datetime.combine(yesterday, datetime.min.time())
|
|
yesterday_end = datetime.combine(yesterday, datetime.max.time())
|
|
|
|
# 查询商家昨日、今日订单数量
|
|
yesterday_total = db.query(MerchantOrderDB).filter(
|
|
MerchantOrderDB.user_id == merchant_user.userid,
|
|
MerchantOrderDB.status == MerchantOrderStatus.VERIFIED,
|
|
MerchantOrderDB.create_time.between(yesterday_start, yesterday_end)
|
|
).count()
|
|
|
|
today_total = db.query(MerchantOrderDB).filter(
|
|
MerchantOrderDB.user_id == merchant_user.userid,
|
|
MerchantOrderDB.status == MerchantOrderStatus.VERIFIED,
|
|
MerchantOrderDB.create_time.between(today_start, today_end)
|
|
).count()
|
|
|
|
return success_response(data={
|
|
"total": total,
|
|
"yesterday_total": yesterday_total,
|
|
"today_total": today_total
|
|
})
|