正在整理處理建議
@@ -3606,6 +3645,40 @@ function growthActionPlanForRow(row) {
};
}
+function growthActionEvidence(row) {
+ const price = row?.external_price || null;
+ const candidate = row?.review_candidate || null;
+ const gap = price && price.gap_pct !== null && price.gap_pct !== undefined ? Number(price.gap_pct) : null;
+ const quality = Math.round(rowQualityScore(row));
+ return [
+ {
+ label: 'PChome',
+ value: price ? formatGrowthDetailPrice(price, 'pchome') : candidate ? formatPriceAmount(candidate.pchome_price) : '待補',
+ },
+ {
+ label: 'MOMO',
+ value: price ? formatGrowthDetailPrice(price, 'momo') : candidate ? formatPriceAmount(candidate.momo_price) : '待補',
+ },
+ {
+ label: '差距',
+ value: candidate ? '候選待確認' : gap === null ? '待判斷' : formatGapDisplay(gap),
+ },
+ {
+ label: '可信度',
+ value: quality ? `${quality}%` : '待補',
+ },
+ ];
+}
+
+function renderGrowthActionEvidence(row) {
+ return `
+ ${growthActionEvidence(row).map((item) => `
+ ${escapeHtml(item.label)}
+ ${escapeHtml(item.value)}
+ `).join('')}
+
`;
+}
+
function renderGrowthActionBoard() {
const board = document.getElementById('growthActionBoard');
if (!board) return;
@@ -3639,6 +3712,7 @@ function renderGrowthActionBoard() {
${escapeHtml(plan.label)}
近 7 天 ${escapeHtml(formatMoney(row.sales_7d))}
+ ${renderGrowthActionEvidence(row)}
diff --git a/tests/test_pchome_revenue_growth_service.py b/tests/test_pchome_revenue_growth_service.py
index d64ffdb..dda9834 100644
--- a/tests/test_pchome_revenue_growth_service.py
+++ b/tests/test_pchome_revenue_growth_service.py
@@ -57,6 +57,7 @@ def _seed_growth_external_offers(engine):
source_product_id TEXT,
source_offer_key TEXT,
title TEXT,
+ product_url TEXT,
price REAL,
observed_at TEXT,
expires_at TEXT,
@@ -73,13 +74,13 @@ def _seed_growth_external_offers(engine):
conn.execute(text("""
INSERT INTO external_offers (
id, source_code, platform_code, source_product_id, source_offer_key,
- title, price, observed_at, expires_at, ingestion_method,
+ title, product_url, price, observed_at, expires_at, ingestion_method,
pchome_product_id, momo_sku, match_status, quality_score,
data_quality_status, quality_notes_json, raw_payload_json
)
VALUES (
1, 'momo_reference', 'momo', 'MOMO-NEW', 'momo_reference:MOMO-NEW:PCH-1',
- 'MOMO 新資料層商品', 870, '2026-06-14 12:00:00', NULL, 'legacy_competitor_cache',
+ 'MOMO 新資料層商品', 'https://www.momoshop.com.tw/goods/MOMO-NEW', 870, '2026-06-14 12:00:00', NULL, 'legacy_competitor_cache',
'PCH-1', 'MOMO-NEW', 'verified', 92,
'verified', '["自動同步"]',
'{"pchome_public_price": 1000, "pchome_public_name": "PChome 公開商品"}'
@@ -97,6 +98,7 @@ def _seed_growth_unit_price_external_offer(engine):
source_product_id TEXT,
source_offer_key TEXT,
title TEXT,
+ product_url TEXT,
price REAL,
observed_at TEXT,
expires_at TEXT,
@@ -113,13 +115,13 @@ def _seed_growth_unit_price_external_offer(engine):
conn.execute(text("""
INSERT INTO external_offers (
id, source_code, platform_code, source_product_id, source_offer_key,
- title, price, observed_at, expires_at, ingestion_method,
+ title, product_url, price, observed_at, expires_at, ingestion_method,
pchome_product_id, momo_sku, match_status, quality_score,
data_quality_status, quality_notes_json, raw_payload_json
)
VALUES (
1, 'momo_reference', 'momo', 'MOMO-UNIT', 'momo_reference:MOMO-UNIT:PCH-1:unit_price',
- 'MOMO 單位價商品', 468, '2026-06-14 12:00:00', NULL, 'targeted_momo_search',
+ 'MOMO 單位價商品', 'https://www.momoshop.com.tw/goods/MOMO-UNIT', 468, '2026-06-14 12:00:00', NULL, 'targeted_momo_search',
'PCH-1', 'MOMO-UNIT', 'verified', 82,
'verified', '["自動單位價比較"]',
'{"price_basis": "unit_price", "pchome_public_price": 920, "pchome_public_name": "PChome 公開商品", "tags": ["identity_v2", "price_basis_unit_price"], "unit_price_comparison": {"unit_label": "ml", "momo_unit_price": 11.7, "competitor_unit_price": 23.0, "momo_total_quantity": 40, "competitor_total_quantity": 40, "unit_gap_pct": -49.13}}'
@@ -487,6 +489,8 @@ def test_ai_intelligence_template_uses_pchome_growth_name_and_endpoint():
assert "今日策略動作" in template
assert "renderGrowthActionBoard" in template
assert "growthActionPlanForRow" in template
+ assert "growthActionEvidence" in template
+ assert "growth-action-evidence-chip" in template
assert "document.querySelectorAll('.growth-detail-row')" in template
assert "scrollToPanel('externalPricePanel')" in template
assert "備援資料檢查" in template
@@ -495,3 +499,14 @@ def test_ai_intelligence_template_uses_pchome_growth_name_and_endpoint():
assert "鎖定商品" in template
assert "無法比價" in template
assert "補齊比價資料" in template
+
+
+def test_formal_homepage_routes_to_growth_command_center():
+ from pathlib import Path
+
+ route_source = Path("routes/dashboard_routes.py").read_text(encoding="utf-8")
+
+ assert "@dashboard_bp.route('/')" in route_source
+ assert "url_for('ai.ai_intelligence')" in route_source
+ assert "@dashboard_bp.route('/dashboard')" in route_source
+ assert "@dashboard_bp.route('/product-dashboard')" in route_source