feat:Redis缓存升级

This commit is contained in:
kakune55 2025-06-17 21:04:43 +08:00
parent e166ec615b
commit fbcb3953b7
8 changed files with 88 additions and 4 deletions

Binary file not shown.

View File

@ -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():

View File

@ -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('/<int:order_id>', 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('/<int:order_id>', 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/<int:user_id>', 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条记录

View File

@ -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)

Binary file not shown.

View File

@ -6,3 +6,4 @@ werkzeug
flask-cors
pymysql
pyecharts
redis