266 lines
9.1 KiB
Python
266 lines
9.1 KiB
Python
from flask import Blueprint, request, jsonify, g, render_template, redirect, url_for
|
||
from sqlmodel import Session, select
|
||
from sqlalchemy import text
|
||
from .models import User, Order
|
||
from .schemas import UserLogin, OrderCreate, OrderUpdate, UserUpdate
|
||
from .utils import hash_password, verify_password, create_jwt_token, decode_jwt_token, admin_required, cache_response
|
||
from .db import engine
|
||
from .schemas import OrderStatus
|
||
|
||
auth_bp = Blueprint('auth', __name__)
|
||
order_bp = Blueprint('order', __name__)
|
||
|
||
def jwt_required(f):
|
||
def wrapper(*args, **kwargs):
|
||
auth = request.headers.get('Authorization', None)
|
||
if not auth or not auth.startswith('Bearer '):
|
||
return jsonify({'msg': 'Missing or invalid token'}), 401
|
||
token = auth.split(' ')[1]
|
||
user_data = decode_jwt_token(token)
|
||
if not user_data:
|
||
return jsonify({'msg': 'Token invalid or expired'}), 401
|
||
g.user_id = user_data['user_id']
|
||
g.role = user_data['role']
|
||
return f(*args, **kwargs)
|
||
wrapper.__name__ = f.__name__
|
||
return wrapper
|
||
|
||
@auth_bp.route('/login', methods=['POST'])
|
||
def login():
|
||
data = request.get_json()
|
||
login_data = UserLogin(**data)
|
||
with Session(engine) as session:
|
||
user = session.exec(select(User).where(User.username == login_data.username)).first()
|
||
if not user or not verify_password(login_data.password, user.password_hash):
|
||
return jsonify({'msg': 'Invalid credentials'}), 401
|
||
token = create_jwt_token(user)
|
||
return jsonify({'token': token})
|
||
|
||
@auth_bp.route('/')
|
||
def index():
|
||
return redirect(url_for('auth.login_page'))
|
||
|
||
@auth_bp.route('/login_page')
|
||
def login_page():
|
||
return render_template('login.html')
|
||
|
||
# 订单管理接口
|
||
@order_bp.route('/', methods=['GET'])
|
||
@jwt_required
|
||
@cache_response(ttl=30, key_prefix="orders")
|
||
def list_orders():
|
||
with Session(engine) as session:
|
||
orders = session.exec(select(Order)).all()
|
||
return jsonify([order.dict() for order in orders])
|
||
|
||
@order_bp.route('/', methods=['POST'])
|
||
@jwt_required
|
||
def create_order():
|
||
data = request.get_json()
|
||
order_data = OrderCreate(**data)
|
||
order = Order(title=order_data.title, description=order_data.description)
|
||
with Session(engine) as session:
|
||
session.add(order)
|
||
session.commit()
|
||
session.refresh(order)
|
||
from app.utils import invalidate_cache
|
||
invalidate_cache("orders")
|
||
return jsonify(order.dict()), 201
|
||
|
||
@order_bp.route('/<int:order_id>', methods=['PUT'])
|
||
@jwt_required
|
||
def update_order(order_id):
|
||
data = request.get_json()
|
||
with Session(engine) as session:
|
||
order = session.get(Order, order_id)
|
||
if not order:
|
||
return jsonify({'msg': 'Order not found'}), 404
|
||
update_data = OrderUpdate(**data)
|
||
for field, value in update_data.dict(exclude_unset=True).items():
|
||
if field == "status" and value is not None:
|
||
order.status = value
|
||
elif field != "status":
|
||
setattr(order, field, value)
|
||
session.add(order)
|
||
session.commit()
|
||
from app.utils import invalidate_cache
|
||
invalidate_cache("orders")
|
||
invalidate_cache("summary")
|
||
invalidate_cache("rating")
|
||
return jsonify(order.dict())
|
||
|
||
@order_bp.route('/<int:order_id>', methods=['DELETE'])
|
||
@jwt_required
|
||
def delete_order(order_id):
|
||
with Session(engine) as session:
|
||
order = session.get(Order, order_id)
|
||
if not order:
|
||
return jsonify({'msg': 'Order not found'}), 404
|
||
session.delete(order)
|
||
session.commit()
|
||
from app.utils import invalidate_cache
|
||
invalidate_cache("orders")
|
||
invalidate_cache("summary")
|
||
invalidate_cache("rating")
|
||
return jsonify({'msg': 'Deleted'})
|
||
|
||
@auth_bp.route('/users/<int:user_id>', methods=['PUT'])
|
||
@jwt_required
|
||
def update_user(user_id):
|
||
data = request.get_json()
|
||
update_data = UserUpdate(**data)
|
||
with Session(engine) as session:
|
||
user = session.get(User, user_id)
|
||
if not user:
|
||
return jsonify({'msg': 'User not found'}), 404
|
||
for field, value in update_data.dict(exclude_unset=True).items():
|
||
setattr(user, field, value)
|
||
session.add(user)
|
||
session.commit()
|
||
return jsonify(user.dict())
|
||
|
||
@order_bp.route('/panel')
|
||
def order_panel():
|
||
return render_template('orders.html')
|
||
|
||
@order_bp.route('/summary')
|
||
@jwt_required
|
||
@cache_response(ttl=60, key_prefix="summary")
|
||
def get_order_summary():
|
||
with Session(engine) as session:
|
||
# 查询all_orders_summary表,按status分组统计count总和
|
||
result = session.execute(
|
||
text("SELECT status, SUM(count) as total_count "
|
||
"FROM all_orders_summary "
|
||
"GROUP BY status")
|
||
).fetchall()
|
||
|
||
# 转换为字典列表格式
|
||
status_mapping = {
|
||
"0": "差评",
|
||
"50": "中评",
|
||
"100": "好评"
|
||
}
|
||
summary = [{"status": status_mapping.get(str(row[0]), str(row[0])), "count": row[1]} for row in result]
|
||
return jsonify(summary)
|
||
|
||
@order_bp.route('/rating_summary')
|
||
@jwt_required
|
||
@cache_response(ttl=60, key_prefix="rating")
|
||
def get_rating_summary():
|
||
with Session(engine) as session:
|
||
# 查询each_order_summary表,按order_id和status分组统计
|
||
result = session.execute(
|
||
text("SELECT order_id, status, SUM(count) as total_count "
|
||
"FROM each_order_summary "
|
||
"GROUP BY order_id, status "
|
||
"ORDER BY order_id, status")
|
||
).fetchall()
|
||
|
||
# 获取所有种类
|
||
categories = sorted({row[0] for row in result})
|
||
status_mapping = {
|
||
"0": "差评",
|
||
"50": "中评",
|
||
"100": "好评"
|
||
}
|
||
|
||
# 初始化数据结构,确保每个种类都有三种评分
|
||
series_data = {
|
||
"差评": [0] * len(categories),
|
||
"中评": [0] * len(categories),
|
||
"好评": [0] * len(categories)
|
||
}
|
||
|
||
# 填充数据
|
||
for row in result:
|
||
category_idx = categories.index(row[0])
|
||
status = status_mapping[row[1]]
|
||
series_data[status][category_idx] = row[2]
|
||
|
||
return jsonify({
|
||
"categories": categories,
|
||
"series": [
|
||
{"name": "差评", "data": series_data["差评"]},
|
||
{"name": "中评", "data": series_data["中评"]},
|
||
{"name": "好评", "data": series_data["好评"]}
|
||
]
|
||
})
|
||
@order_bp.route('/type_summary')
|
||
@jwt_required
|
||
@cache_response(ttl=60, key_prefix="type")
|
||
def get_type_summary():
|
||
with Session(engine) as session:
|
||
# 查询order_type_summary表,按order_type分组统计count总和
|
||
result = session.execute(
|
||
text("SELECT order_type, SUM(count) as total_count "
|
||
"FROM order_type_summary "
|
||
"GROUP BY order_type")
|
||
).fetchall()
|
||
|
||
# 转换为字典列表格式
|
||
summary = [{"order_type": row[0], "count": row[1]} for row in result]
|
||
return jsonify(summary)
|
||
|
||
@order_bp.route('/top_products')
|
||
@jwt_required
|
||
@cache_response(ttl=60, key_prefix="top")
|
||
def get_top_products():
|
||
with Session(engine) as session:
|
||
# 查询top_products_summary表,按sales_count降序获取前5条记录
|
||
result = session.execute(
|
||
text("SELECT order_type, SUM(count) as total_count "
|
||
"FROM order_type_summary "
|
||
"GROUP BY order_type"
|
||
" ORDER BY total_count DESC "
|
||
"LIMIT 5")
|
||
).fetchall()
|
||
|
||
# 转换为字典列表格式
|
||
top_products = [{"product_name": row[0], "sales_count": row[1]} for row in result]
|
||
return jsonify(top_products)
|
||
|
||
|
||
@auth_bp.route('/users/', methods=['GET'])
|
||
@jwt_required
|
||
@admin_required
|
||
def list_users():
|
||
with Session(engine) as session:
|
||
users = session.exec(select(User)).all()
|
||
return jsonify([user.dict() for user in users])
|
||
|
||
@auth_bp.route('/users/panel')
|
||
def users_panel():
|
||
return render_template('users.html')
|
||
|
||
@auth_bp.route('/users/', methods=['POST'])
|
||
@jwt_required
|
||
@admin_required
|
||
def create_user():
|
||
data = request.get_json()
|
||
username = data.get('username')
|
||
password = data.get('password')
|
||
role = data.get('role', 'user')
|
||
if not username or not password:
|
||
return jsonify({'msg': '用户名和密码必填'}), 400
|
||
with Session(engine) as session:
|
||
if session.exec(select(User).where(User.username == username)).first():
|
||
return jsonify({'msg': '用户名已存在'}), 400
|
||
from .utils import hash_password
|
||
user = User(username=username, password_hash=hash_password(password), role=role)
|
||
session.add(user)
|
||
session.commit()
|
||
return jsonify(user.dict()), 201
|
||
|
||
@auth_bp.route('/users/<int:user_id>', methods=['DELETE'])
|
||
@jwt_required
|
||
@admin_required
|
||
def delete_user(user_id):
|
||
with Session(engine) as session:
|
||
user = session.get(User, user_id)
|
||
if not user:
|
||
return jsonify({'msg': '用户不存在'}), 404
|
||
session.delete(user)
|
||
session.commit()
|
||
return jsonify({'msg': '已删除'})
|