diff --git a/config.py b/config.py index f266657..9ed6302 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.610" +SYSTEM_VERSION = "V10.611" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index 2e0d54e..cd1b11b 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -1,8 +1,8 @@ # PChome 業績成長自動化作戰系統 — AI 競價情報模組 Single Source of Truth -> **最後更新**: 2026-06-15 (台北時間) -> **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯;PChome 後台業績匯入韌性已補強;產品定位正名為「PChome 業績成長自動化作戰系統」;外部市場來源正規化層、自動同步、作戰清單優先讀取與 CSV 備援預檢已建立 -> **適用版本**: V10.610 +> **最後更新**: 2026-06-16 (台北時間) +> **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯;PChome 後台業績匯入韌性已補強;產品定位正名為「PChome 業績成長自動化作戰系統」;外部市場來源正規化層、自動同步、作戰清單優先讀取、CSV 備援預檢與前台操作入口已建立 +> **適用版本**: V10.611 --- @@ -57,6 +57,7 @@ - V10.608 新增 `/api/ai/pchome-growth/external-offers/csv-dry-run` 與 AI 情報頁「外部報價預檢」。CSV 預檢只讀、不寫 DB;逐列回報「可使用」「需人工確認」「不能使用」,並支援中文表頭,避免格式小錯造成整批匯入失敗。 - V10.609 明確把外部報價主路徑改為自動化:`run_external_offer_sync_task` 每 4 小時將已確認同款的既有比價快取同步進 `external_offers`。CSV 只保留為 API / crawler / provider 失敗時的備援預檢入口,不是日常營運主流程。 - V10.610 起 `/api/ai/pchome-growth/opportunities` 優先讀取 `external_offers` 的自動同步資料;只有新資料層缺資料時才 fallback 舊 `competitor_prices`。API stats 會回傳資料來源計數,方便確認作戰清單是否已走新資料層。 +- V10.611 起 `/ai_intelligence` 是營運使用者主入口:頁首提供「今日作戰入口」,依序連到 PChome 成長作戰、補商品對應、MOMO 外部價格參考與外部報價預檢;作戰清單左側會直接顯示今日優先動作與資料來源摘要。 ## 零之一、12 Agent 決策信封(2026-05-24) diff --git a/docs/memory/current_execution_queue_20260524.md b/docs/memory/current_execution_queue_20260524.md index 6510a0a..88702d6 100644 --- a/docs/memory/current_execution_queue_20260524.md +++ b/docs/memory/current_execution_queue_20260524.md @@ -212,3 +212,10 @@ - `external_offers.raw_payload_json` 會保留舊比價快取中的 PChome 公開價,讓新資料層仍可算出 MOMO / PChome 價差。 - API stats 新增 `external_data_source_counts`,可看到「自動同步資料層」與「舊比價快取」各有多少筆。 - 下一步:把其他比價報表與 AI 告警逐步改讀 `external_offers`,讓 `competitor_prices` 降為 bridge/cache。 + +## 14. 2026-06-16 V10.611 作戰入口與資料來源可見化 + +- `/ai_intelligence` 是 PChome 業績成長自動化作戰系統的營運主入口,頁首新增「今日作戰入口」:看作戰清單、補商品對應、看可比價商品、預檢備援資料。 +- PChome 成長作戰區塊會依 API stats 顯示今日優先動作,例如先補商品對應、先處理可直接比價商品,避免使用者只看到數字不知道下一步。 +- 同區塊新增資料來源摘要,直接顯示「自動同步資料層」或「舊比價快取」各有多少筆,方便確認 V10.610 新資料層是否真的被作戰清單採用。 +- CSV 預檢在 UI 文案上維持備援定位;日常主流程仍是自動同步外部報價。 diff --git a/templates/ai_intelligence.html b/templates/ai_intelligence.html index a70ce82..fd97e08 100644 --- a/templates/ai_intelligence.html +++ b/templates/ai_intelligence.html @@ -239,6 +239,92 @@ gap: 14px; } + .ops-flow { + border: 1px solid var(--momo-border-subtle); + border-radius: 8px; + background: rgba(255, 255, 255, 0.82); + box-shadow: var(--momo-shadow-soft); + padding: 14px; + } + + .ops-flow-head { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 8px; + align-items: center; + margin-bottom: 10px; + } + + .ops-flow-note { + color: var(--momo-text-muted); + font-size: 0.78rem; + font-weight: 800; + } + + .ops-flow-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; + } + + .ops-flow-item { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + gap: 10px; + align-items: start; + width: 100%; + border: 1px solid rgba(42, 37, 32, 0.1); + border-radius: 8px; + background: rgba(250, 247, 240, 0.52); + color: var(--momo-text-strong); + padding: 11px; + text-align: left; + transition: transform 0.16s ease, border-color 0.16s ease, background-color 0.16s ease; + } + + .ops-flow-item:hover, + .ops-flow-item:focus { + border-color: rgba(172, 92, 58, 0.34); + background: rgba(255, 255, 255, 0.86); + transform: translateY(-1px); + } + + .ops-flow-step { + display: inline-flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + border-radius: 999px; + background: rgba(172, 92, 58, 0.12); + color: var(--momo-warm-rust); + font-family: var(--momo-font-mono); + font-size: 0.76rem; + font-weight: 900; + } + + .ops-flow-title { + margin: 0; + color: var(--momo-text-strong); + font-size: 0.86rem; + font-weight: 900; + line-height: 1.25; + } + + .ops-flow-copy, + .ops-flow-target { + margin: 4px 0 0; + color: var(--momo-text-muted); + font-size: 0.74rem; + line-height: 1.4; + } + + .ops-flow-target { + color: var(--momo-warm-rust); + font-weight: 900; + } + .growth-metric-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); @@ -273,6 +359,29 @@ line-height: 1.55; } + .growth-action-hint { + margin: 10px 0 0; + border: 1px solid rgba(172, 92, 58, 0.16); + border-radius: 8px; + background: rgba(242, 178, 90, 0.14); + color: var(--momo-text-strong); + font-size: 0.82rem; + font-weight: 800; + line-height: 1.5; + padding: 9px 10px; + } + + .growth-data-summary { + margin: 8px 0 0; + border: 1px solid rgba(42, 37, 32, 0.08); + border-radius: 8px; + background: rgba(255, 255, 255, 0.68); + color: var(--momo-text-muted); + font-size: 0.78rem; + line-height: 1.45; + padding: 8px 10px; + } + .growth-source-list { display: grid; gap: 8px; @@ -574,7 +683,8 @@ .growth-ops-grid, .offer-dryrun-grid, - .growth-metric-row { + .growth-metric-row, + .ops-flow-grid { grid-template-columns: 1fr; } @@ -606,18 +716,62 @@ - - - + +
+
+ + 今日作戰入口 + + 依清單、對應、比價、備援資料的順序處理 +
+
+ + + + +
+
+
@@ -646,6 +800,8 @@ 待補對應
+

正在判斷今天優先處理順序...

+

資料來源整理中...

來源整理中...

@@ -764,7 +920,7 @@
-
+
MOMO 外部價格參考 @@ -915,6 +1071,12 @@ function escapeHtml(value) { }[ch])); } +function scrollToPanel(panelId) { + const panel = document.getElementById(panelId); + if (!panel) return; + panel.scrollIntoView({ behavior: 'smooth', block: 'start' }); +} + async function loadGrowthOps(forceRefresh = false) { const list = document.getElementById('growthOpsList'); if (forceRefresh) { @@ -933,6 +1095,8 @@ async function loadGrowthOps(forceRefresh = false) { document.getElementById('growthCandidateCount').textContent = (stats.candidate_count || 0).toLocaleString(); document.getElementById('growthMappedCount').textContent = (stats.mapped_count || 0).toLocaleString(); document.getElementById('growthNeedsMapping').textContent = (stats.needs_mapping_count || 0).toLocaleString(); + renderGrowthActionHint(stats); + renderGrowthDataSourceSummary(stats); const scope = data.source_scope || {}; const active = (scope.active_external_sources || []).join('、') || '尚未接入'; @@ -944,6 +1108,8 @@ async function loadGrowthOps(forceRefresh = false) { renderGrowthOps(data.opportunities || []); } catch (error) { console.error(error); + renderGrowthActionHint({ candidate_count: 0, mapped_count: 0, needs_mapping_count: 0 }); + renderGrowthDataSourceSummary({}); list.innerHTML = `
PChome 成長作戰清單暫時無法讀取,請稍後再試。 @@ -951,6 +1117,49 @@ async function loadGrowthOps(forceRefresh = false) { } } +function renderGrowthActionHint(stats) { + const hint = document.getElementById('growthActionHint'); + if (!hint) return; + + const candidateCount = Number(stats.candidate_count || 0); + const mappedCount = Number(stats.mapped_count || 0); + const needsMapping = Number(stats.needs_mapping_count || 0); + + if (!candidateCount) { + hint.textContent = '目前沒有可處理的作戰商品,請先確認 PChome 業績資料已更新。'; + return; + } + + if (needsMapping > 0 && mappedCount === 0) { + hint.textContent = `今天先補商品對應:目前 ${needsMapping.toLocaleString()} 件高業績商品還不能直接比價。`; + return; + } + + if (needsMapping > 0) { + hint.textContent = `先處理 ${mappedCount.toLocaleString()} 件可直接比價商品,再補 ${needsMapping.toLocaleString()} 件商品對應。`; + return; + } + + hint.textContent = '目前商品都可直接比價,先檢查售價偏高或可以放大價格優勢的項目。'; +} + +function renderGrowthDataSourceSummary(stats) { + const summary = document.getElementById('growthDataSourceSummary'); + if (!summary) return; + + const counts = stats.external_data_source_counts || {}; + const entries = Object.entries(counts) + .filter(([, count]) => Number(count || 0) > 0) + .map(([label, count]) => `${label} ${Number(count).toLocaleString()} 筆`); + + if (!entries.length) { + summary.textContent = '資料來源:目前作戰清單尚未接到外部參考價,請先補商品對應。'; + return; + } + + summary.textContent = `資料來源:${entries.join('、')}。`; +} + function renderGrowthSourceReadiness(sources) { const box = document.getElementById('growthSourceReadiness'); if (!box) return; diff --git a/tests/test_pchome_revenue_growth_service.py b/tests/test_pchome_revenue_growth_service.py index 41b2610..e3ddd95 100644 --- a/tests/test_pchome_revenue_growth_service.py +++ b/tests/test_pchome_revenue_growth_service.py @@ -155,5 +155,10 @@ def test_ai_intelligence_template_uses_pchome_growth_name_and_endpoint(): assert "/api/ai/pchome-growth/opportunities" in template assert "/api/ai/pchome-growth/external-offers/csv-dry-run" in template assert "growthSourceReadiness" in template + assert "今日作戰入口" in template + assert "growthActionHint" in template + assert "growthDataSourceSummary" in template + assert "external_data_source_counts" in template + assert "scrollToPanel('externalPricePanel')" in template assert "外部報價預檢" in template assert "待補對應" in template