265 lines
8.9 KiB
Python
265 lines
8.9 KiB
Python
"""
|
||
评价管理视图
|
||
"""
|
||
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)}'})
|