feat:Redis缓存升级
This commit is contained in:
parent
e166ec615b
commit
fbcb3953b7
Binary file not shown.
Binary file not shown.
Binary file not shown.
29
app/db.py
29
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():
|
||||
|
@ -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条记录
|
||||
|
43
app/utils.py
43
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)
|
||||
|
BIN
order_ms.db
BIN
order_ms.db
Binary file not shown.
@ -5,4 +5,5 @@ pyjwt
|
||||
werkzeug
|
||||
flask-cors
|
||||
pymysql
|
||||
pyecharts
|
||||
pyecharts
|
||||
redis
|
Loading…
x
Reference in New Issue
Block a user