This commit is contained in:
@@ -202,6 +202,43 @@ def _get_available_daily_dates(engine, table_name='daily_sales_snapshot'):
|
||||
return dates
|
||||
|
||||
|
||||
def _get_daily_sales_metadata(engine, table_name='daily_sales_snapshot'):
|
||||
"""一次取得日期選單與資料指紋,避免首屏每次掃兩輪 daily snapshot。"""
|
||||
validate_table_name(table_name)
|
||||
if engine.dialect.name != 'postgresql':
|
||||
return (
|
||||
_get_available_daily_dates(engine, table_name),
|
||||
_get_data_fingerprint(engine, table_name),
|
||||
)
|
||||
|
||||
query = text(
|
||||
f'SELECT MAX(snapshot_date)::text AS max_snapshot_date, '
|
||||
f'COUNT(*) AS row_count, '
|
||||
f'ARRAY_AGG(DISTINCT snapshot_date::date ORDER BY snapshot_date::date DESC) '
|
||||
f'FILTER (WHERE snapshot_date IS NOT NULL) AS available_dates '
|
||||
f'FROM "{table_name}"'
|
||||
)
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
row = conn.execute(query).fetchone()
|
||||
if not row:
|
||||
return [], (None, 0)
|
||||
raw_dates = row[2] or []
|
||||
dates = []
|
||||
for raw_date in raw_dates:
|
||||
try:
|
||||
dates.append(pd.to_datetime(raw_date).normalize())
|
||||
except Exception:
|
||||
continue
|
||||
return dates, (row[0], row[1] or 0)
|
||||
except Exception as exc:
|
||||
sys_log.warning(f"[DailySales] metadata 查詢失敗,改用相容查詢: {exc}")
|
||||
return (
|
||||
_get_available_daily_dates(engine, table_name),
|
||||
_get_data_fingerprint(engine, table_name),
|
||||
)
|
||||
|
||||
|
||||
def _read_daily_sales_window(engine, table_name, start_date, end_date):
|
||||
"""只讀取畫面需要的日期窗口,降低冷 worker 首頁等待時間。"""
|
||||
return safe_read_sql(
|
||||
@@ -501,7 +538,7 @@ def daily_sales():
|
||||
chart_data=None, categories=None, calendar_data=None, selected_month=None,
|
||||
datetime_now=datetime_now_str, active_page='daily_sales')
|
||||
|
||||
available_dates = _get_available_daily_dates(engine, table_name)
|
||||
available_dates, current_fingerprint = _get_daily_sales_metadata(engine, table_name)
|
||||
if not available_dates:
|
||||
return render_template('daily_sales.html',
|
||||
error="資料表為空,請先匯入當日業績資料。",
|
||||
@@ -537,7 +574,6 @@ def daily_sales():
|
||||
)
|
||||
data_end = max(selected_date, month_end)
|
||||
|
||||
current_fingerprint = _get_data_fingerprint(engine, table_name)
|
||||
view_cache_key = "|".join([
|
||||
table_name,
|
||||
'view',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
import pickle
|
||||
from flask import Flask, session
|
||||
|
||||
@@ -183,6 +184,54 @@ def test_clear_daily_sales_cache_removes_shared_view_cache_files(tmp_path, monke
|
||||
assert not cache_file.exists()
|
||||
|
||||
|
||||
def test_daily_sales_metadata_uses_single_postgres_query():
|
||||
from routes import daily_sales_routes
|
||||
|
||||
class FakeDialect:
|
||||
name = "postgresql"
|
||||
|
||||
class FakeResult:
|
||||
def fetchone(self):
|
||||
return ("2026-05-17", 85118, [date(2026, 5, 17), date(2026, 5, 16)])
|
||||
|
||||
class FakeConnection:
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
def execute(self, query):
|
||||
self.query = str(query)
|
||||
return FakeResult()
|
||||
|
||||
class FakeEngine:
|
||||
dialect = FakeDialect()
|
||||
|
||||
def connect(self):
|
||||
return FakeConnection()
|
||||
|
||||
dates, fingerprint = daily_sales_routes._get_daily_sales_metadata(FakeEngine())
|
||||
|
||||
assert [d.strftime("%Y-%m-%d") for d in dates] == ["2026-05-17", "2026-05-16"]
|
||||
assert fingerprint == ("2026-05-17", 85118)
|
||||
|
||||
|
||||
def test_daily_sales_metadata_falls_back_for_sqlite(monkeypatch):
|
||||
from routes import daily_sales_routes
|
||||
|
||||
class FakeDialect:
|
||||
name = "sqlite"
|
||||
|
||||
class FakeEngine:
|
||||
dialect = FakeDialect()
|
||||
|
||||
monkeypatch.setattr(daily_sales_routes, "_get_available_daily_dates", lambda engine, table_name: ["date-a"])
|
||||
monkeypatch.setattr(daily_sales_routes, "_get_data_fingerprint", lambda engine, table_name: ("date-a", 1))
|
||||
|
||||
assert daily_sales_routes._get_daily_sales_metadata(FakeEngine()) == (["date-a"], ("date-a", 1))
|
||||
|
||||
|
||||
def test_promo_dashboard_shared_cache_roundtrip(tmp_path, monkeypatch):
|
||||
from routes import edm_routes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user