order-webserver/app/routes.py
2025-06-17 21:04:43 +08:00

266 lines
9.1 KiB
Python
Raw 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, 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': '已删除'})