From fbcb3953b78df4086e3e8f9d900e8456fee7585c Mon Sep 17 00:00:00 2001 From: kakune55 Date: Tue, 17 Jun 2025 21:04:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:Redis=E7=BC=93=E5=AD=98=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/__pycache__/db.cpython-311.pyc | Bin 2055 -> 3396 bytes app/__pycache__/routes.cpython-311.pyc | Bin 18966 -> 19896 bytes app/__pycache__/utils.cpython-311.pyc | Bin 2961 -> 5147 bytes app/db.py | 29 +++++++++++++++++ app/routes.py | 17 +++++++++- app/utils.py | 43 +++++++++++++++++++++++-- order_ms.db | Bin 16384 -> 0 bytes requirements.txt | 3 +- 8 files changed, 88 insertions(+), 4 deletions(-) delete mode 100644 order_ms.db diff --git a/app/__pycache__/db.cpython-311.pyc b/app/__pycache__/db.cpython-311.pyc index ef9b97624b9c29ceff706bfce54c0748692e8b88..85a434ea3075a10221b132bd9024a89b0f85daa2 100644 GIT binary patch delta 1497 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 delta 1027 zcmZuw&1(};5Pxs8UrD-2O}1YuZAb+hp?cM}iY6CZf419M(_l%VNsBd9gl-H zO#zysf)avf`x6n*rH|pN1XXCGzo=-DhxJU@mvW4E2uD6Mk_q{8*>$+DE!azilFfX@ zE6q9cF20{?Ufw#*9ouuPe8sZ@H$MSxEZMywcI4~YX0)|-g~!F3(v|)O^kWhx-l3B6 z`+Ajlve=N!aYnl!a95Z#OW%ZEz@4x0-auApl`{Dt1}x>kV(K&as9xeV!{N-!qOP`K zx0;eyfHALD$nDSssZi(v#}xc1K!*AeyD(egIWJpuc{}nQ$V-QGm3IT>tIU}$F^6jy zloEZ~X;b38Xrux5=Oh-i_O?^Nv>|&Jphg;!s+e2iyS6-m?7g#tWIaCI6WTChh}{(M zui@d)u?gFl=stAv0hytUuIelFA;|-`D^+SWRr3Cw{)MV0*f3QuGCqK>W>jCc`K;@! zbCuwf?Xs5*s`kS;a*NrX?b8_-*9AtMO4Ya_q-ej<1BCk(;CGE|iJ|EF9b7SJm~L6~ zcG0mcXwo^L_y9&?#Z!B08nan3H*az0RyoHV#)rU%Ep+<(O%U z@=w{^j3#QMJh(F+3_uhT=Lb3dI>Fip+sE-EgZ!^Z5F7j_JNi(u#L5Nd0>21;Ts!wI g5y|9Dv6f5?X!J;f>_bAMCYpPYh|*+({0l+$KLD`RPXGV_ 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 057841225ed460876bd2afff0509cbe4c3e40186..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI&%TC)s6b4|MAksuC?gn|$%?Pnb)Pj7!WYeZ#l{6uNG_<7~H1#BJFBk|_4(veK25?(*lm{qe)@mgL>Ukj4mSKa)q^FE9wMsKjYrg^RxS&kEG%yA=c z%v{UWY|CK2Zu!1O8FRQ#HQhEsllrFac-~kSSCQ%ZR-_pYwoQhYP)QW81XXP^M0$EnBCFNTAde6METnIn_0uX=z z1Rwwb2tWV=5P$##ZWE}Lfd>_}R__0Ae*eFcl&jkkFb)9-KmY;|fB*y_009U<00Izz rz?~Fmia#PKO8c>{8=7ISYtFjv5>0OzUQ2T~98>dbbIKDICm+58o$=L! 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