diff --git a/app/__pycache__/db.cpython-311.pyc b/app/__pycache__/db.cpython-311.pyc index ef9b976..85a434e 100644 Binary files a/app/__pycache__/db.cpython-311.pyc and b/app/__pycache__/db.cpython-311.pyc differ diff --git a/app/__pycache__/routes.cpython-311.pyc b/app/__pycache__/routes.cpython-311.pyc index fe0dbb2..95c185e 100644 Binary files a/app/__pycache__/routes.cpython-311.pyc and b/app/__pycache__/routes.cpython-311.pyc differ diff --git a/app/__pycache__/utils.cpython-311.pyc b/app/__pycache__/utils.cpython-311.pyc index c49b51a..ea95b58 100644 Binary files a/app/__pycache__/utils.cpython-311.pyc and b/app/__pycache__/utils.cpython-311.pyc differ diff --git a/app/db.py b/app/db.py index 736345b..b66c10b 100644 --- a/app/db.py +++ b/app/db.py @@ -3,6 +3,8 @@ from sqlalchemy.pool import QueuePool from sqlalchemy import text from app.models import UserRole, OrderStatus import time +import redis +from redis import RedisError def wait_for_db(max_retries=5, delay=5): """等待数据库连接可用""" @@ -27,8 +29,35 @@ def wait_for_db(max_retries=5, delay=5): time.sleep(delay) return None +def init_redis(max_retries=3, delay=1): + """初始化Redis连接池""" + redis_pool = redis.ConnectionPool( + host='localhost', + port=6379, + db=0, + max_connections=10, + decode_responses=True + ) + + for i in range(max_retries): + try: + r = redis.Redis(connection_pool=redis_pool) + if r.ping(): + print(f"Redis连接成功 (尝试 {i+1}/{max_retries})") + return redis_pool + except RedisError as e: + if i == max_retries - 1: + raise + time.sleep(delay) + return None + # 使用连接池的数据库引擎 engine = wait_for_db() +redis_pool = init_redis() + +def get_redis(): + """获取Redis连接""" + return redis.Redis(connection_pool=redis_pool) # 创建数据库表 def init_db(): diff --git a/app/routes.py b/app/routes.py index c8bee57..88ad844 100644 --- a/app/routes.py +++ b/app/routes.py @@ -3,7 +3,7 @@ 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 +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 @@ -47,6 +47,7 @@ def login_page(): # 订单管理接口 @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() @@ -62,6 +63,8 @@ def create_order(): 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('/', methods=['PUT']) @@ -80,6 +83,10 @@ def update_order(order_id): 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('/', methods=['DELETE']) @@ -91,6 +98,10 @@ def delete_order(order_id): 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/', methods=['PUT']) @@ -114,6 +125,7 @@ def order_panel(): @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总和 @@ -134,6 +146,7 @@ def get_order_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分组统计 @@ -175,6 +188,7 @@ def get_rating_summary(): }) @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总和 @@ -190,6 +204,7 @@ def get_type_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条记录 diff --git a/app/utils.py b/app/utils.py index fe570e3..6e1c1f4 100644 --- a/app/utils.py +++ b/app/utils.py @@ -2,6 +2,9 @@ import jwt from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime, timedelta from flask import current_app, request, jsonify +from functools import wraps +from app.db import get_redis +import json def hash_password(password): return generate_password_hash(password) @@ -13,13 +16,21 @@ def create_jwt_token(user): payload = { 'user_id': user.id, 'role': user.role, - 'exp': datetime.utcnow() + timedelta(days=1) + 'exp': datetime.utcnow() + timedelta(days=7) } token = jwt.encode(payload, current_app.config['SECRET_KEY'], algorithm='HS256') + # 缓存token + r = get_redis() + r.setex(f"token:{token}", int(timedelta(days=1).total_seconds()), "1") return token def decode_jwt_token(token): try: + # 先检查Redis缓存 + r = get_redis() + if not r.exists(f"token:{token}"): + return None + payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256']) return {'user_id': payload['user_id'], 'role': payload['role']} except jwt.ExpiredSignatureError: @@ -28,6 +39,7 @@ def decode_jwt_token(token): return None def admin_required(f): + @wraps(f) def wrapper(*args, **kwargs): auth = request.headers.get('Authorization', None) if not auth or not auth.startswith('Bearer '): @@ -37,5 +49,32 @@ def admin_required(f): if not user_data or user_data.get('role') != 'admin': return jsonify({'msg': 'Admin access required'}), 403 return f(*args, **kwargs) - wrapper.__name__ = f.__name__ return wrapper + +def cache_response(ttl=30, key_prefix="cache"): + """缓存响应装饰器""" + def decorator(f): + @wraps(f) + def wrapper(*args, **kwargs): + r = get_redis() + cache_key = f"{key_prefix}:{request.path}" + + # 尝试从缓存获取 + cached_data = r.get(cache_key) + if cached_data: + return jsonify(json.loads(cached_data)) + + # 执行函数并缓存结果 + response = f(*args, **kwargs) + if response.status_code == 200: + r.setex(cache_key, ttl, json.dumps(response.json)) + return response + return wrapper + return decorator + +def invalidate_cache(key_prefix): + """使缓存失效""" + r = get_redis() + keys = r.keys(f"{key_prefix}:*") + if keys: + r.delete(*keys) diff --git a/order_ms.db b/order_ms.db deleted file mode 100644 index 0578412..0000000 Binary files a/order_ms.db and /dev/null differ diff --git a/requirements.txt b/requirements.txt index b92beb8..9b2a4a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ pyjwt werkzeug flask-cors pymysql -pyecharts \ No newline at end of file +pyecharts +redis \ No newline at end of file