This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
================================================================================
|
||||
|
||||
【已完成】
|
||||
- V10.329 優化 `/growth_analysis` 冷快取策略:source fingerprint 未變時,成長分析共享快取由 30 分鐘延長為 6 小時有效;每日匯入仍會主動清除快取,避免資料更新後沿用舊圖表,同時降低正式端重啟或冷 worker 重新掃 `realtime_sales_monthly` 的 14 秒級等待。
|
||||
- V10.328 強化 MOMO/PChome 核心比價準確性第一波:補高頻品牌 alias、中文商品線 bigram 訊號、保健/包裝同義單位與買送件數解析,搜尋詞改為品牌/核心/主規格三層;PChome 比對嘗試與正式快照補存 URL、圖片、庫存與結構化 diagnostics,商品列表用 tone 分流顯示尚未搜尋、低信心、身份否決、單位價與過期狀態,不再把不同問題全部壓成灰色待比對;同步持久化首頁 / PChome coverage 熱路徑索引,避免重開機後慢查詢回歸。
|
||||
- V10.327 補 OpenClaw fallback 可觀測性:週報、月報、Meta、日報洞察、每日報告的 Gemini/NIM 備援 caller 納入 caller registry、AI 觀測台 agent group 與 Telegram 狀態統計,並補 MCP collector Ollama-first regression test,避免 fallback 真實使用量在觀測層被歸類成未知或漏算。
|
||||
- V10.326 補市場情報 candidate queue review AI summary Telegram dispatch report run readiness:新增 read-only report run readiness builder、POST endpoint、UI 按鈕與 deployment readiness smoke target,在 report run package 後整理 report generation readiness manifest、manual report command boundary、artifact path gate 與後續 report run receipt gate;API/UI 不讀 approval/Telegram token、不呼叫 LLM、不派送 Telegram、不開 DB、不寫檔、不產報表、不更新 review_state、不掛 scheduler。
|
||||
|
||||
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.328"
|
||||
SYSTEM_VERSION = "V10.329"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
### 2026-05-20:重開機後首頁熱路徑索引持久化
|
||||
- **Dashboard / PChome 慢查詢修復**: 主機重開機後 `https://mo.wooo.work/` 首頁可用但多次逾時,實際瓶頸集中在首頁與 PChome coverage 查詢掃描 `products`、`price_records`、`competitor_match_attempts`。線上先補三個索引讓首頁恢復 200,並新增 `migrations/040_dashboard_hot_path_indexes.sql` 將修復持久化到 fresh restore / DB rebuild 流程。
|
||||
- **Growth Analysis 冷快取修復**: `/growth_analysis` 在 `monthly_summary_analysis` 落後時會改掃 `realtime_sales_monthly` 聚合,冷計算約 14 秒;修正為 source fingerprint 未變時延長共享快取有效期,匯入流程仍主動清除快取,避免資料未變卻反覆掃大表。
|
||||
|
||||
### 2026-04-29:ADR-017 Phase 3f 模組化收尾啟動
|
||||
- **DB metadata 救急**: `database/manager.py` 改為顯式載入 permission / AI / autoheal / import / vendor / realtime_sales ORM,PostgreSQL 初始化透過 process-local guard + advisory lock 執行 `Base.metadata.create_all()`,避免新環境漏表與一般流量重複碰 DDL。
|
||||
|
||||
@@ -41,6 +41,7 @@ _GROWTH_ANALYSIS_CACHE = {
|
||||
'source_fingerprint': None,
|
||||
}
|
||||
_GROWTH_CACHE_TTL = 1800 # 成長分析快取: 30 分鐘
|
||||
_GROWTH_STABLE_SOURCE_CACHE_TTL = 21600 # source fingerprint 未變時延長到 6 小時,避免冷 worker 反覆掃明細表
|
||||
_BASE_DIR = Path(__file__).resolve().parents[1]
|
||||
_GROWTH_SHARED_CACHE_FILE = _BASE_DIR / "data" / "growth_analysis_cache.pkl"
|
||||
_GROWTH_SOURCE_FINGERPRINT_CACHE = {}
|
||||
@@ -166,10 +167,12 @@ def _is_growth_payload_valid(payload, source_fingerprint=None):
|
||||
return False
|
||||
if not payload.get('chart_data') or not payload.get('kpi'):
|
||||
return False
|
||||
if source_fingerprint is not None:
|
||||
if payload.get('source_fingerprint') != source_fingerprint:
|
||||
return False
|
||||
return is_cache_valid(payload.get('timestamp'), _GROWTH_STABLE_SOURCE_CACHE_TTL)
|
||||
if not is_cache_valid(payload.get('timestamp'), _GROWTH_CACHE_TTL):
|
||||
return False
|
||||
if source_fingerprint is not None:
|
||||
return payload.get('source_fingerprint') == source_fingerprint
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -147,6 +147,34 @@ def test_growth_cache_shared_file_roundtrip(tmp_path, monkeypatch):
|
||||
assert shared_cache.exists()
|
||||
|
||||
|
||||
def test_growth_cache_reuses_expired_ttl_when_source_fingerprint_matches(tmp_path, monkeypatch):
|
||||
import pickle
|
||||
from datetime import timedelta
|
||||
from services import cache_service
|
||||
|
||||
shared_cache = tmp_path / "growth_analysis_cache.pkl"
|
||||
monkeypatch.setattr(cache_service, "_GROWTH_SHARED_CACHE_FILE", shared_cache)
|
||||
cache_service.clear_growth_cache()
|
||||
cache_service.set_growth_cache(
|
||||
{"labels": ["2026-05"], "revenue": [1000]},
|
||||
{"ytd_revenue": 1000},
|
||||
source_fingerprint=("2026-05-17", 10),
|
||||
)
|
||||
|
||||
stale_timestamp = (
|
||||
cache_service.datetime.now(cache_service.TAIPEI_TZ)
|
||||
- timedelta(seconds=cache_service._GROWTH_CACHE_TTL + 60)
|
||||
)
|
||||
cache_service._GROWTH_ANALYSIS_CACHE["timestamp"] = stale_timestamp
|
||||
payload = pickle.loads(shared_cache.read_bytes())
|
||||
payload["timestamp"] = stale_timestamp
|
||||
shared_cache.write_bytes(pickle.dumps(payload, protocol=pickle.HIGHEST_PROTOCOL))
|
||||
|
||||
assert cache_service.is_growth_cache_valid(("2026-05-17", 10))
|
||||
assert not cache_service.is_growth_cache_valid(("2026-05-18", 10))
|
||||
assert not cache_service.is_growth_cache_valid()
|
||||
|
||||
|
||||
def test_clear_growth_cache_removes_shared_file(tmp_path, monkeypatch):
|
||||
from services import cache_service
|
||||
|
||||
|
||||
Reference in New Issue
Block a user