From 7fd1dc34f4ca3d1af48c042d4444d732e7e9ade1 Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Fri, 19 Dec 2025 15:53:46 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0Company=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E7=9A=84UI=E4=B8=BAFUI=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index c8494ba9..1bcb92fb 100755 --- a/app.py +++ b/app.py @@ -217,6 +217,30 @@ def get_target_and_prev_trading_day(event_datetime): # 应用启动时加载交易日数据 load_trading_days() + +def is_trading_hours(): + """ + 判断当前是否在交易时间段内 + 交易时间:交易日的 9:00-15:00(含午休时间,因为事件可能在午休发布) + + Returns: + bool: True 表示在交易时间段,False 表示非交易时间 + """ + now = datetime.now() + today = now.date() + current_time = now.time() + + # 判断今天是否为交易日 + if today not in trading_days_set: + return False + + # 判断是否在 9:00-15:00 之间 + market_open = dt_time(9, 0) + market_close = dt_time(15, 0) + + return market_open <= current_time <= market_close + + engine = create_engine( "mysql+pymysql://root:Zzl33818!@127.0.0.1:3306/stock?charset=utf8mb4", echo=False, @@ -300,6 +324,105 @@ def delete_verification_code(key): print(f"📦 验证码存储: Redis, 过期时间: {VERIFICATION_CODE_EXPIRE}秒") +# ============ 事件列表 Redis 缓存(智能 TTL 策略) ============ +EVENTS_CACHE_PREFIX = "events:cache:" +EVENTS_CACHE_TTL_TRADING = 20 # 交易时间缓存 TTL(秒) +EVENTS_CACHE_TTL_NON_TRADING = 600 # 非交易时间缓存 TTL(秒,10分钟) + + +def generate_events_cache_key(args_dict): + """ + 根据请求参数生成缓存 Key + 使用 MD5 哈希保证 key 长度固定且唯一 + + Args: + args_dict: 请求参数字典 + + Returns: + str: 缓存 key,格式为 events:cache:{md5_hash} + """ + import hashlib + + # 过滤掉空值,并排序保证顺序一致 + filtered_params = {k: v for k, v in sorted(args_dict.items()) + if v is not None and v != '' and v != 'all'} + + # 生成参数字符串并计算 MD5 + params_str = json.dumps(filtered_params, sort_keys=True) + params_hash = hashlib.md5(params_str.encode()).hexdigest() + + return f"{EVENTS_CACHE_PREFIX}{params_hash}" + + +def get_events_cache(cache_key): + """ + 从 Redis 获取事件列表缓存 + + Args: + cache_key: 缓存 key + + Returns: + dict or None: 缓存的响应数据,如果不存在或出错返回 None + """ + try: + cached = redis_client.get(cache_key) + if cached: + return json.loads(cached) + return None + except Exception as e: + print(f"❌ Redis 获取事件缓存失败: {e}") + return None + + +def set_events_cache(cache_key, data): + """ + 将事件列表数据存入 Redis 缓存 + 根据是否在交易时间自动选择 TTL + + Args: + cache_key: 缓存 key + data: 要缓存的响应数据 + + Returns: + bool: 是否成功 + """ + try: + # 根据交易时间选择 TTL + ttl = EVENTS_CACHE_TTL_TRADING if is_trading_hours() else EVENTS_CACHE_TTL_NON_TRADING + + redis_client.setex(cache_key, ttl, json.dumps(data, ensure_ascii=False)) + return True + except Exception as e: + print(f"❌ Redis 存储事件缓存失败: {e}") + return False + + +def clear_events_cache(): + """ + 清除所有事件列表缓存 + 用于事件数据更新后主动刷新缓存 + """ + try: + # 使用 SCAN 命令迭代删除,避免 KEYS 命令阻塞 + cursor = 0 + deleted_count = 0 + while True: + cursor, keys = redis_client.scan(cursor, match=f"{EVENTS_CACHE_PREFIX}*", count=100) + if keys: + redis_client.delete(*keys) + deleted_count += len(keys) + if cursor == 0: + break + if deleted_count > 0: + print(f"🗑️ 已清除 {deleted_count} 个事件缓存") + return deleted_count + except Exception as e: + print(f"❌ 清除事件缓存失败: {e}") + return 0 + + +print(f"📦 事件列表缓存: 交易时间 {EVENTS_CACHE_TTL_TRADING}s / 非交易时间 {EVENTS_CACHE_TTL_NON_TRADING}s") + # ============ 微信登录 Session 管理(Redis 存储,支持多进程) ============ WECHAT_SESSION_EXPIRE = 300 # Session 过期时间(5分钟) WECHAT_SESSION_PREFIX = "wechat_session:" @@ -10517,8 +10640,26 @@ def get_stock_list(): def api_get_events(): """ 获取事件列表API - 支持筛选、排序、分页,兼容前端调用 + + Redis 缓存策略: + - 交易时间(交易日 9:00-15:00):缓存 20 秒 + - 非交易时间:缓存 10 分钟 """ try: + # ==================== Redis 缓存检查 ==================== + # 获取所有请求参数用于生成缓存 key + cache_params = dict(request.args) + cache_key = generate_events_cache_key(cache_params) + + # 尝试从缓存获取 + cached_response = get_events_cache(cache_key) + if cached_response: + # 添加缓存命中标记(可选,用于调试) + cached_response['_cached'] = True + cached_response['_cache_ttl'] = EVENTS_CACHE_TTL_TRADING if is_trading_hours() else EVENTS_CACHE_TTL_NON_TRADING + return jsonify(cached_response) + + # ==================== 缓存未命中,执行数据库查询 ==================== # 分页参数 page = max(1, request.args.get('page', 1, type=int)) per_page = min(100, max(1, request.args.get('per_page', 10, type=int))) @@ -10776,7 +10917,9 @@ def api_get_events(): if search_query: applied_filters['search_query'] = search_query applied_filters['search_type'] = search_type - return jsonify({ + + # 构建响应数据 + response_data = { 'success': True, 'data': { 'events': events_data, @@ -10793,7 +10936,12 @@ def api_get_events(): 'total_count': paginated.total } } - }) + } + + # ==================== 存入 Redis 缓存 ==================== + set_events_cache(cache_key, response_data) + + return jsonify(response_data) except Exception as e: app.logger.error(f"获取事件列表出错: {str(e)}", exc_info=True) return jsonify({