Files
ewoooc/services/cache_service.py
ogt 1b4f3a7bbe
Some checks failed
CD Pipeline / deploy (push) Failing after 59s
feat: EwoooC 初始化 — 完整專案推版至 Gitea
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml)
- 部署模式: rsync Python 檔案至 188 → docker restart (volume mount)
- Dockerfile/requirements 變動時自動重建 Docker image
- 部署通知: Telegram (開始/成功/失敗)
- 健康檢查: https://mo.wooo.work/health (最多 5 次重試)
- 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 01:21:13 +08:00

159 lines
4.4 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
快取服務模組
集中管理所有快取變數和相關操作
"""
from datetime import datetime, timezone, timedelta
# 台北時區
TAIPEI_TZ = timezone(timedelta(hours=8))
# ==========================================
# 業績分析快取
# ==========================================
_SALES_DF_CACHE = {}
_SALES_PROCESSED_CACHE = {} # 處理後資料快取 (二級快取)
_SALES_OPTIONS_CACHE = {} # 下拉選單選項 (類別、品牌、廠商等)
_SALES_ANALYSIS_RESULT_CACHE = {} # 過濾後的分析結果集
# 快取 TTL 設定
_SALES_CACHE_TTL = 3600 # 業績分析快取: 60 分鐘
_SALES_OPTIONS_TTL = 21600 # 選項快取: 6 小時
_SALES_RESULT_TTL = 3600 # 結果快取: 60 分鐘
# ==========================================
# 商品看板快取
# ==========================================
_DASHBOARD_DATA_CACHE = {
'consolidated_data': None,
'consolidated_timestamp': None,
'full_data': None,
'full_timestamp': None
}
_DASHBOARD_CACHE_TTL = 1800 # 商品看板快取: 30 分鐘
# ==========================================
# 成長分析快取
# ==========================================
_GROWTH_ANALYSIS_CACHE = {
'chart_data': None,
'kpi': None,
'timestamp': None
}
_GROWTH_CACHE_TTL = 1800 # 成長分析快取: 30 分鐘
# ==========================================
# 慢查詢監控
# ==========================================
_SLOW_QUERY_STATS = {
'total_queries': 0,
'slow_queries': 0,
'very_slow_queries': 0,
'total_query_time_ms': 0,
'last_slow_query': None,
'last_slow_query_time': None,
}
_SLOW_QUERY_THRESHOLD_MS = 1000 # 慢查詢閾值: 1秒
_VERY_SLOW_QUERY_THRESHOLD_MS = 5000 # 極慢查詢閾值: 5秒
def track_query_time(query_name, duration_ms):
"""追蹤查詢時間,更新慢查詢統計"""
global _SLOW_QUERY_STATS
_SLOW_QUERY_STATS['total_queries'] += 1
_SLOW_QUERY_STATS['total_query_time_ms'] += duration_ms
if duration_ms >= _VERY_SLOW_QUERY_THRESHOLD_MS:
_SLOW_QUERY_STATS['very_slow_queries'] += 1
_SLOW_QUERY_STATS['slow_queries'] += 1
_SLOW_QUERY_STATS['last_slow_query'] = query_name
_SLOW_QUERY_STATS['last_slow_query_time'] = datetime.now(TAIPEI_TZ).isoformat()
elif duration_ms >= _SLOW_QUERY_THRESHOLD_MS:
_SLOW_QUERY_STATS['slow_queries'] += 1
_SLOW_QUERY_STATS['last_slow_query'] = query_name
_SLOW_QUERY_STATS['last_slow_query_time'] = datetime.now(TAIPEI_TZ).isoformat()
def get_slow_query_stats():
"""取得慢查詢統計資料"""
return _SLOW_QUERY_STATS.copy()
def clear_sales_cache():
"""清除業績分析快取"""
global _SALES_DF_CACHE, _SALES_PROCESSED_CACHE, _SALES_OPTIONS_CACHE, _SALES_ANALYSIS_RESULT_CACHE
_SALES_DF_CACHE.clear()
_SALES_PROCESSED_CACHE.clear()
_SALES_OPTIONS_CACHE.clear()
_SALES_ANALYSIS_RESULT_CACHE.clear()
def clear_dashboard_cache():
"""清除商品看板快取"""
global _DASHBOARD_DATA_CACHE
_DASHBOARD_DATA_CACHE = {
'consolidated_data': None,
'consolidated_timestamp': None,
'full_data': None,
'full_timestamp': None
}
def clear_growth_cache():
"""清除成長分析快取"""
global _GROWTH_ANALYSIS_CACHE
_GROWTH_ANALYSIS_CACHE = {
'chart_data': None,
'kpi': None,
'timestamp': None
}
def clear_all_cache():
"""清除所有快取"""
clear_sales_cache()
clear_dashboard_cache()
clear_growth_cache()
def is_cache_valid(timestamp, ttl_seconds):
"""
檢查快取是否有效
Args:
timestamp: 快取時間戳記
ttl_seconds: 快取有效期(秒)
Returns:
bool: True 表示快取有效
"""
if timestamp is None:
return False
now = datetime.now(TAIPEI_TZ)
if timestamp.tzinfo is None:
timestamp = timestamp.replace(tzinfo=TAIPEI_TZ)
age = (now - timestamp).total_seconds()
return age < ttl_seconds
def get_growth_cache():
"""取得成長分析快取"""
return _GROWTH_ANALYSIS_CACHE
def set_growth_cache(chart_data, kpi):
"""設定成長分析快取"""
global _GROWTH_ANALYSIS_CACHE
_GROWTH_ANALYSIS_CACHE = {
'chart_data': chart_data,
'kpi': kpi,
'timestamp': datetime.now(TAIPEI_TZ)
}
def is_growth_cache_valid():
"""檢查成長分析快取是否有效"""
return is_cache_valid(_GROWTH_ANALYSIS_CACHE.get('timestamp'), _GROWTH_CACHE_TTL)