2025-07-04 19:07:35 +08:00

341 lines
11 KiB
Python

"""
订单视图
"""
from flask import Blueprint, render_template, request, jsonify, session, redirect, url_for, flash, g
from app.models.order import Order, OrderItem
from app.models.cart import Cart
from app.models.address import UserAddress
from app.models.product import ProductInventory
from app.models.payment import Payment
from app.forms import CheckoutForm
from app.utils.decorators import login_required
from config.database import db
import json
order_bp = Blueprint('order', __name__, url_prefix='/order')
@order_bp.route('/checkout')
@login_required
def checkout():
"""订单结算页面"""
user_id = session['user_id']
selected_items = request.args.getlist('items')
if not selected_items:
flash('请选择要购买的商品', 'error')
return redirect(url_for('cart.index'))
# 获取选中的购物车项目
cart_items = Cart.query.filter(
Cart.id.in_(selected_items),
Cart.user_id == user_id
).all()
if not cart_items:
flash('选中的商品不存在', 'error')
return redirect(url_for('cart.index'))
# 检查商品可用性和库存
unavailable_items = []
total_amount = 0
for item in cart_items:
if not item.is_available():
unavailable_items.append(item.product.name)
else:
total_amount += item.get_total_price()
if unavailable_items:
flash(f'以下商品库存不足或已下架:{", ".join(unavailable_items)}', 'error')
return redirect(url_for('cart.index'))
# 获取用户地址
addresses = UserAddress.get_user_addresses(user_id)
if not addresses:
flash('请先添加收货地址', 'warning')
return redirect(url_for('address.add'))
# 计算运费
shipping_fee = 0 # 默认免运费
# 创建表单并设置地址选项
form = CheckoutForm()
form.address_id.choices = [(addr.id, f"{addr.receiver_name} - {addr.get_full_address()}")
for addr in addresses]
# 设置默认地址
default_address = UserAddress.get_default_address(user_id)
if default_address:
form.address_id.data = default_address.id
return render_template('order/checkout.html',
cart_items=cart_items,
addresses=addresses,
form=form,
total_amount=total_amount,
shipping_fee=shipping_fee,
final_amount=total_amount + shipping_fee)
@order_bp.route('/create', methods=['POST'])
@login_required
def create():
"""创建订单"""
try:
user_id = session['user_id']
data = request.get_json()
selected_items = data.get('selected_items', [])
address_id = data.get('address_id')
shipping_method = data.get('shipping_method', 'standard')
payment_method = data.get('payment_method', 'wechat')
remark = data.get('remark', '')
if not selected_items or not address_id:
return jsonify({'success': False, 'message': '参数错误'})
# 获取购物车商品
cart_items = Cart.query.filter(
Cart.id.in_(selected_items),
Cart.user_id == user_id
).all()
if not cart_items:
return jsonify({'success': False, 'message': '购物车商品不存在'})
# 验证地址
address = UserAddress.query.filter_by(id=address_id, user_id=user_id).first()
if not address:
return jsonify({'success': False, 'message': '收货地址不存在'})
# 再次检查库存和计算总价
total_amount = 0
order_items_data = []
for cart_item in cart_items:
if not cart_item.is_available():
return jsonify({
'success': False,
'message': f'商品"{cart_item.product.name}"库存不足或已下架'
})
# 检查库存是否足够
current_stock = cart_item.get_stock()
if current_stock < cart_item.quantity:
return jsonify({
'success': False,
'message': f'商品"{cart_item.product.name}"库存不足,仅剩{current_stock}'
})
item_total = cart_item.get_total_price()
total_amount += item_total
order_items_data.append({
'product_id': cart_item.product_id,
'sku_code': cart_item.sku_code,
'product_name': cart_item.product.name,
'product_image': cart_item.product.main_image,
'spec_combination': cart_item.spec_combination,
'price': cart_item.get_price(),
'quantity': cart_item.quantity,
'total_price': item_total
})
# 计算运费
shipping_fee_map = {
'standard': 0,
'express': 10,
'same_day': 20
}
shipping_fee = shipping_fee_map.get(shipping_method, 0)
actual_amount = total_amount + shipping_fee
# 创建订单
order = Order(
user_id=user_id,
order_sn=Order.generate_order_sn(),
total_amount=total_amount,
actual_amount=actual_amount,
shipping_fee=shipping_fee,
payment_method=payment_method,
shipping_method=shipping_method,
remark=remark
)
# 设置收货人信息
order.set_receiver_info({
'receiver_name': address.receiver_name,
'receiver_phone': address.receiver_phone,
'province': address.province,
'city': address.city,
'district': address.district,
'detail_address': address.detail_address,
'postal_code': address.postal_code,
'full_address': address.get_full_address()
})
db.session.add(order)
db.session.flush() # 获取订单ID
# 创建订单商品明细
for item_data in order_items_data:
order_item = OrderItem(
order_id=order.id,
**item_data
)
db.session.add(order_item)
# 扣减库存
for cart_item in cart_items:
if cart_item.sku_code:
sku_info = ProductInventory.query.filter_by(sku_code=cart_item.sku_code).first()
if sku_info:
sku_info.stock -= cart_item.quantity
# 增加销量
cart_item.product.sales_count += cart_item.quantity
# 删除购物车商品
for cart_item in cart_items:
db.session.delete(cart_item)
# 创建支付记录
payment = Payment(
order_id=order.id,
payment_sn=Payment.generate_payment_sn(),
payment_method=payment_method,
amount=actual_amount
)
db.session.add(payment)
db.session.commit()
return jsonify({
'success': True,
'message': '订单创建成功',
'order_id': order.id,
'order_sn': order.order_sn,
'payment_sn': payment.payment_sn
})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'message': f'创建订单失败: {str(e)}'})
@order_bp.route('/list')
@login_required
def list():
"""订单列表"""
user_id = session['user_id']
status = request.args.get('status', type=int)
page = request.args.get('page', 1, type=int)
per_page = 10
query = Order.query.filter_by(user_id=user_id)
if status:
query = query.filter_by(status=status)
orders = query.order_by(Order.created_at.desc()).paginate(
page=page, per_page=per_page, error_out=False
)
return render_template('user/orders.html', orders=orders, current_status=status)
@order_bp.route('/detail/<int:order_id>')
@login_required
def detail(order_id):
"""订单详情"""
user_id = session['user_id']
order = Order.query.filter_by(id=order_id, user_id=user_id).first_or_404()
return render_template('order/detail.html', order=order)
@order_bp.route('/cancel/<int:order_id>', methods=['POST'])
@login_required
def cancel(order_id):
"""取消订单"""
try:
user_id = session['user_id']
order = Order.query.filter_by(id=order_id, user_id=user_id).first()
if not order:
return jsonify({'success': False, 'message': '订单不存在'})
if not order.can_cancel():
return jsonify({'success': False, 'message': '订单状态不允许取消'})
# 更新订单状态
order.status = Order.STATUS_CANCELLED
# 恢复库存
for item in order.order_items:
if item.sku_code:
sku_info = ProductInventory.query.filter_by(sku_code=item.sku_code).first()
if sku_info:
sku_info.stock += item.quantity
# 减少销量
if item.product:
item.product.sales_count = max(0, item.product.sales_count - item.quantity)
db.session.commit()
return jsonify({'success': True, 'message': '订单已取消'})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'message': f'取消失败: {str(e)}'})
@order_bp.route('/confirm_receipt/<int:order_id>', methods=['POST'])
@login_required
def confirm_receipt(order_id):
"""确认收货"""
try:
user_id = session['user_id']
order = Order.query.filter_by(id=order_id, user_id=user_id).first()
if not order:
return jsonify({'success': False, 'message': '订单不存在'})
if not order.can_confirm_receipt():
return jsonify({'success': False, 'message': '订单状态不允许确认收货'})
# 更新订单状态
order.status = Order.STATUS_PENDING_REVIEW
order.received_at = db.func.now()
db.session.commit()
return jsonify({'success': True, 'message': '确认收货成功'})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'message': f'确认收货失败: {str(e)}'})
@order_bp.route('/pay/<payment_sn>')
@login_required
def pay(payment_sn):
"""支付页面"""
user_id = session['user_id']
payment = Payment.query.filter_by(payment_sn=payment_sn).first_or_404()
order = payment.order
# 验证订单所有权
if order.user_id != user_id:
flash('订单不存在', 'error')
return redirect(url_for('order.list'))
# 检查是否可以支付
if not order.can_pay():
flash('订单不可支付', 'error')
return redirect(url_for('order.detail', order_id=order.id))
return render_template('order/pay.html', order=order, payment=payment)