快取活動看板資料指紋
All checks were successful
CD Pipeline / deploy (push) Successful in 56s

This commit is contained in:
OoO
2026-05-13 11:05:52 +08:00
parent 5b52af9e2f
commit 20ab5a7b90
2 changed files with 47 additions and 2 deletions

View File

@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.102"
SYSTEM_VERSION = "V10.103"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -27,6 +27,9 @@ sys_log = SystemLogger("EDMRoutes").get_logger()
# Blueprint 定義
edm_bp = Blueprint('edm', __name__)
_PROMO_DASHBOARD_CACHE = {}
_PROMO_DASHBOARD_CACHE_MAX = 32
# ==========================================
# 輔助函數
@@ -62,11 +65,51 @@ def load_scheduler_stats():
return {}
def _get_promo_dashboard_fingerprint(session, page_type):
"""取得促銷資料指紋,用於判斷快取是否仍可復用。"""
max_id, max_crawled_at, row_count = session.query(
func.max(PromoProduct.id),
func.max(PromoProduct.crawled_at),
func.count(PromoProduct.id)
).filter(PromoProduct.page_type == page_type).one()
return (
max_id or 0,
max_crawled_at.isoformat() if max_crawled_at else '',
row_count or 0
)
def _get_promo_dashboard_cache_key(session, page_type, sort_by, order, requested_slot):
"""建立 dashboard data 快取鍵;日期/小時避免自動時段跨時段後仍命中舊首屏。"""
now_taipei = datetime.now(TAIPEI_TZ)
auto_slot_bucket = now_taipei.strftime('%Y-%m-%d-%H') if not requested_slot else ''
return (
page_type,
sort_by,
order,
requested_slot or '',
auto_slot_bucket,
_get_promo_dashboard_fingerprint(session, page_type)
)
def _remember_promo_dashboard_data(cache_key, data):
"""保留少量活動 dashboard data避免常用時段每次重算。"""
if len(_PROMO_DASHBOARD_CACHE) >= _PROMO_DASHBOARD_CACHE_MAX:
_PROMO_DASHBOARD_CACHE.pop(next(iter(_PROMO_DASHBOARD_CACHE)))
_PROMO_DASHBOARD_CACHE[cache_key] = data
def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order, requested_slot=None):
"""
通用的促銷儀表板數據建構函數
用於 edm 和 festival 兩種頁面類型
"""
cache_key = _get_promo_dashboard_cache_key(session, page_type, sort_by, order, requested_slot)
cached_data = _PROMO_DASHBOARD_CACHE.get(cache_key)
if cached_data is not None:
return cached_data
# 1. 基礎統計
last_update = session.query(PromoProduct.crawled_at).filter(
PromoProduct.page_type == page_type
@@ -288,7 +331,7 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order, r
slot_stats[slot]['on_shelf'] = on_shelf_count
slot_stats[slot]['delisted_total'] = delisted_total_count
return {
data = {
'sorted_grouped_items': sorted_grouped_items,
'slot_stats': slot_stats,
'items_in_batch': items_in_batch,
@@ -297,6 +340,8 @@ def _build_promo_dashboard_data(session, page_type, page_name, sort_by, order, r
'active_tab': active_tab,
'current_batch_id': current_batch_id
}
_remember_promo_dashboard_data(cache_key, data)
return data
# ==========================================