Add cached dashboard prewarm hints for priority cities

This commit is contained in:
2569718930@qq.com
2026-04-16 15:05:14 +08:00
parent b635070106
commit c0e5307893
5 changed files with 672 additions and 123 deletions
+203
View File
@@ -182,6 +182,64 @@ class DBManager:
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS city_summary_cache (
city TEXT PRIMARY KEY,
payload_json TEXT NOT NULL,
updated_at TEXT NOT NULL,
updated_at_ts REAL NOT NULL,
version TEXT,
source_fingerprint TEXT
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS city_panel_cache (
city TEXT PRIMARY KEY,
payload_json TEXT NOT NULL,
updated_at TEXT NOT NULL,
updated_at_ts REAL NOT NULL,
version TEXT,
source_fingerprint TEXT
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS city_nearby_cache (
city TEXT PRIMARY KEY,
payload_json TEXT NOT NULL,
updated_at TEXT NOT NULL,
updated_at_ts REAL NOT NULL,
version TEXT,
source_fingerprint TEXT
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS city_market_cache (
city TEXT PRIMARY KEY,
payload_json TEXT NOT NULL,
updated_at TEXT NOT NULL,
updated_at_ts REAL NOT NULL,
version TEXT,
source_fingerprint TEXT
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS city_history_preview_cache (
city TEXT PRIMARY KEY,
payload_json TEXT NOT NULL,
updated_at TEXT NOT NULL,
updated_at_ts REAL NOT NULL,
version TEXT,
source_fingerprint TEXT
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS cache_refresh_locks (
cache_key TEXT PRIMARY KEY,
locked_until_ts REAL NOT NULL,
owner TEXT NOT NULL,
updated_at TEXT NOT NULL
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS payment_audit_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -247,6 +305,151 @@ class DBManager:
conn.commit()
logger.info(f"Database initialized successfully path={self.db_path}")
def _cache_table_name(self, kind: str) -> Optional[str]:
normalized = str(kind or "").strip().lower()
if normalized == "summary":
return "city_summary_cache"
if normalized == "panel":
return "city_panel_cache"
if normalized == "nearby":
return "city_nearby_cache"
if normalized == "market":
return "city_market_cache"
if normalized == "history_preview":
return "city_history_preview_cache"
return None
def get_city_cache(self, kind: str, city: str) -> Optional[Dict[str, Any]]:
table = self._cache_table_name(kind)
normalized_city = str(city or "").strip().lower()
if not table or not normalized_city:
return None
with self._get_connection() as conn:
conn.row_factory = sqlite3.Row
row = conn.execute(
f"""
SELECT city, payload_json, updated_at, updated_at_ts, version, source_fingerprint
FROM {table}
WHERE city = ?
LIMIT 1
""",
(normalized_city,),
).fetchone()
if not row:
return None
try:
payload = json.loads(str(row["payload_json"] or "{}"))
except Exception:
return None
if not isinstance(payload, dict):
return None
return {
"city": str(row["city"] or normalized_city),
"payload": payload,
"updated_at": str(row["updated_at"] or ""),
"updated_at_ts": float(row["updated_at_ts"] or 0.0),
"version": str(row["version"] or ""),
"source_fingerprint": str(row["source_fingerprint"] or ""),
}
def set_city_cache(
self,
kind: str,
city: str,
payload: Dict[str, Any],
*,
version: str = "v1",
source_fingerprint: Optional[str] = None,
) -> None:
table = self._cache_table_name(kind)
normalized_city = str(city or "").strip().lower()
if not table or not normalized_city or not isinstance(payload, dict):
return
now = datetime.now().isoformat()
now_ts = datetime.now().timestamp()
with self._get_connection() as conn:
conn.execute(
f"""
INSERT INTO {table} (
city,
payload_json,
updated_at,
updated_at_ts,
version,
source_fingerprint
)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(city) DO UPDATE SET
payload_json = excluded.payload_json,
updated_at = excluded.updated_at,
updated_at_ts = excluded.updated_at_ts,
version = excluded.version,
source_fingerprint = excluded.source_fingerprint
""",
(
normalized_city,
json.dumps(payload, ensure_ascii=False),
now,
now_ts,
str(version or "v1"),
str(source_fingerprint or ""),
),
)
conn.commit()
def acquire_cache_refresh_lock(
self,
cache_key: str,
*,
ttl_sec: int = 120,
owner: Optional[str] = None,
) -> Optional[str]:
normalized_key = str(cache_key or "").strip().lower()
if not normalized_key:
return None
lock_owner = str(owner or "").strip() or hashlib.sha1(
f"{normalized_key}:{datetime.now().timestamp()}".encode("utf-8")
).hexdigest()[:12]
now_ts = datetime.now().timestamp()
locked_until_ts = now_ts + max(15, int(ttl_sec or 120))
updated_at = datetime.now().isoformat()
with self._get_connection() as conn:
cursor = conn.execute(
"""
INSERT INTO cache_refresh_locks (cache_key, locked_until_ts, owner, updated_at)
VALUES (?, ?, ?, ?)
ON CONFLICT(cache_key) DO UPDATE SET
locked_until_ts = excluded.locked_until_ts,
owner = excluded.owner,
updated_at = excluded.updated_at
WHERE cache_refresh_locks.locked_until_ts < ?
""",
(
normalized_key,
locked_until_ts,
lock_owner,
updated_at,
now_ts,
),
)
conn.commit()
return lock_owner if int(cursor.rowcount or 0) > 0 else None
def release_cache_refresh_lock(self, cache_key: str, owner: str) -> None:
normalized_key = str(cache_key or "").strip().lower()
normalized_owner = str(owner or "").strip()
if not normalized_key or not normalized_owner:
return
with self._get_connection() as conn:
conn.execute(
"""
DELETE FROM cache_refresh_locks
WHERE cache_key = ? AND owner = ?
""",
(normalized_key, normalized_owner),
)
conn.commit()
def get_payment_runtime_state(self, state_key: str) -> Optional[Dict[str, Any]]:
key = str(state_key or "").strip()
if not key: