2025-07-09 05:22:28 +08:00

265 lines
8.9 KiB
Python
Raw Permalink 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 flask import Blueprint, render_template, request, jsonify, redirect, url_for, session, flash, g
from sqlalchemy import func, desc
from config.database import db
from app.models.review import Review
from app.models.order import Order, OrderItem
from app.models.product import Product
from app.models.user import User
from app.utils.decorators import login_required, log_operation
from app.utils.file_upload import file_upload_handler
import json
review_bp = Blueprint('review', __name__, url_prefix='/review')
@review_bp.route('/product/<int:product_id>')
def product_reviews(product_id):
"""商品评价列表AJAX接口"""
try:
page = request.args.get('page', 1, type=int)
rating_filter = request.args.get('rating', type=int)
# 基础查询
query = Review.query.filter_by(product_id=product_id, status=1)
# 评分筛选
if rating_filter:
query = query.filter_by(rating=rating_filter)
# 分页查询
reviews = query.order_by(desc(Review.created_at)).paginate(
page=page, per_page=10, error_out=False
)
# 评价统计
stats = db.session.query(
Review.rating,
func.count(Review.id).label('count')
).filter_by(product_id=product_id, status=1).group_by(Review.rating).all()
rating_stats = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
total_reviews = 0
for stat in stats:
rating_stats[stat.rating] = stat.count
total_reviews += stat.count
# 好评率计算
good_rate = 0
if total_reviews > 0:
good_reviews = rating_stats[4] + rating_stats[5]
good_rate = round(good_reviews / total_reviews * 100, 1)
# 转换为字典
reviews_data = []
for review in reviews.items:
review_dict = review.to_dict()
# 添加用户头像
if review.user:
review_dict['user_avatar'] = review.user.avatar_url
reviews_data.append(review_dict)
return jsonify({
'success': True,
'reviews': reviews_data,
'pagination': {
'page': reviews.page,
'pages': reviews.pages,
'per_page': reviews.per_page,
'total': reviews.total,
'has_next': reviews.has_next,
'has_prev': reviews.has_prev
},
'stats': {
'total_reviews': total_reviews,
'good_rate': good_rate,
'rating_stats': rating_stats
}
})
except Exception as e:
return jsonify({'success': False, 'message': str(e)})
@review_bp.route('/write/<int:order_id>/<int:product_id>')
@login_required
def write_review(order_id, product_id):
"""写评价页面"""
# 验证订单和商品
order = Order.query.filter_by(id=order_id, user_id=session["user_id"]).first()
if not order:
flash('订单不存在', 'error')
return redirect(url_for('order.list'))
# 检查订单状态
if order.status not in [4, 5]: # 待评价或已完成
flash('该订单暂时无法评价', 'error')
return redirect(url_for('order.detail', order_id=order_id))
# 检查商品是否在订单中
order_item = OrderItem.query.filter_by(order_id=order_id, product_id=product_id).first()
if not order_item:
flash('商品不在此订单中', 'error')
return redirect(url_for('order.detail', order_id=order_id))
# 检查是否已经评价过
existing_review = Review.query.filter_by(
user_id=session["user_id"],
product_id=product_id,
order_id=order_id
).first()
if existing_review:
flash('您已经评价过该商品', 'info')
return redirect(url_for('order.detail', order_id=order_id))
return render_template('review/write.html',
order=order,
order_item=order_item,
product=order_item.product)
@review_bp.route('/submit', methods=['POST'])
@login_required
@log_operation('提交商品评价')
def submit_review():
"""提交评价"""
try:
data = request.get_json()
order_id = data.get('order_id')
product_id = data.get('product_id')
rating = data.get('rating')
content = data.get('content', '').strip()
is_anonymous = data.get('is_anonymous', False)
images = data.get('images', [])
# 参数验证
if not all([order_id, product_id, rating]):
return jsonify({'success': False, 'message': '参数不完整'})
if not (1 <= rating <= 5):
return jsonify({'success': False, 'message': '评分必须在1-5星之间'})
# 验证订单
order = Order.query.filter_by(id=order_id, user_id=session["user_id"]).first()
if not order:
return jsonify({'success': False, 'message': '订单不存在'})
if order.status not in [4, 5]:
return jsonify({'success': False, 'message': '该订单暂时无法评价'})
# 验证商品在订单中
order_item = OrderItem.query.filter_by(order_id=order_id, product_id=product_id).first()
if not order_item:
return jsonify({'success': False, 'message': '商品不在此订单中'})
# 检查是否已评价
existing_review = Review.query.filter_by(
user_id=session["user_id"],
product_id=product_id,
order_id=order_id
).first()
if existing_review:
return jsonify({'success': False, 'message': '您已经评价过该商品'})
# 创建评价
review = Review(
user_id=session["user_id"],
product_id=product_id,
order_id=order_id,
rating=rating,
content=content if content else None,
is_anonymous=1 if is_anonymous else 0
)
# 设置图片
if images:
review.set_images(images)
db.session.add(review)
# 检查订单中所有商品是否都已评价
total_items = OrderItem.query.filter_by(order_id=order_id).count()
reviewed_items = Review.query.filter_by(order_id=order_id).count() + 1 # +1 是当前这个评价
# 如果所有商品都已评价,更新订单状态为已完成
if reviewed_items >= total_items and order.status == 4:
order.status = 5 # 已完成
db.session.commit()
return jsonify({
'success': True,
'message': '评价提交成功',
'review_id': review.id
})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'message': f'提交失败: {str(e)}'})
@review_bp.route('/upload_image', methods=['POST'])
@login_required
def upload_review_image():
"""上传评价图片"""
try:
if 'file' not in request.files:
return jsonify({'success': False, 'message': '没有选择文件'})
file = request.files['file']
if file.filename == '':
return jsonify({'success': False, 'message': '没有选择文件'})
# 使用现有的文件上传处理器
result = file_upload_handler.upload_image(file, 'reviews', process_image=True)
if result['success']:
return jsonify({
'success': True,
'message': '图片上传成功',
'url': result['url']
})
else:
return jsonify({'success': False, 'message': result['error']})
except Exception as e:
return jsonify({'success': False, 'message': f'上传失败: {str(e)}'})
@review_bp.route('/my_reviews')
@login_required
def my_reviews():
"""我的评价列表"""
page = request.args.get('page', 1, type=int)
reviews = Review.query.filter_by(user_id=session["user_id"]).order_by(
desc(Review.created_at)
).paginate(page=page, per_page=10, error_out=False)
return render_template('review/my_reviews.html', reviews=reviews)
@review_bp.route('/delete/<int:review_id>', methods=['POST'])
@login_required
@log_operation('删除商品评价')
def delete_review(review_id):
"""删除评价(仅限自己的评价)"""
try:
review = Review.query.filter_by(id=review_id, user_id=session["user_id"]).first()
if not review:
return jsonify({'success': False, 'message': '评价不存在'})
db.session.delete(review)
db.session.commit()
return jsonify({'success': True, 'message': '评价删除成功'})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'message': f'删除失败: {str(e)}'})