diff --git a/config.py b/config.py index 2cd3e68..3918f1a 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.656" +SYSTEM_VERSION = "V10.657" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/templates/ai_intelligence.html b/templates/ai_intelligence.html index 4151eb8..6fc1f49 100644 --- a/templates/ai_intelligence.html +++ b/templates/ai_intelligence.html @@ -3414,11 +3414,12 @@ function renderGrowthCommandCenter(stats = {}, rows = []) { const salesDeltaPct = Number(stats.overall_sales_delta_pct || 0); const matchRate = Number(stats.mapping_rate || (candidateCount ? (mappedCount / candidateCount) * 100 : 0)); const latestSalesDate = String(stats.overall_latest_sales_date || stats.latest_sales_date || '').slice(0, 10); - const pressureRows = safeRows.filter((row) => row?.recommended_action?.code === 'review_price_or_promo' || Number(row?.external_price?.gap_pct || 0) > 0); - const advantageRows = safeRows.filter((row) => row?.recommended_action?.code === 'amplify_price_advantage' || Number(row?.external_price?.gap_pct || 0) < 0); + const actionCounts = stats.action_code_counts || {}; + const pressureCount = Number(actionCounts.review_price_or_promo || 0); + const advantageCount = Number(actionCounts.amplify_price_advantage || 0); const pendingCount = Math.max(0, needsMapping + reviewCandidateCount); - const readyShare = candidateCount ? (advantageRows.length / candidateCount) * 100 : 0; - const pressureShare = candidateCount ? (pressureRows.length / candidateCount) * 100 : 0; + const readyShare = candidateCount ? (advantageCount / candidateCount) * 100 : 0; + const pressureShare = candidateCount ? (pressureCount / candidateCount) * 100 : 0; setText('commandSales7d', sales7d ? formatMoney(sales7d) : '—'); setText('commandSalesDelta', prevSales7d @@ -3436,8 +3437,8 @@ function renderGrowthCommandCenter(stats = {}, rows = []) { setText('commandStatusReview', formatCount(reviewCandidateCount)); setText('commandStatusNeeds', formatCount(needsMapping)); setText('commandLatestSalesDate', latestSalesDate || '—'); - setText('commandPriceAdvantage', `${formatCount(advantageRows.length)} 件`); - setText('commandPricePressure', `${formatCount(pressureRows.length)} 件`); + setText('commandPriceAdvantage', `${formatCount(advantageCount)} 件`); + setText('commandPricePressure', `${formatCount(pressureCount)} 件`); setText('commandPricePending', `${formatCount(pendingCount)} 件`); setText('commandDonutText', `${Math.round(matchRate)}%`); @@ -3491,7 +3492,8 @@ function renderGrowthCommandTopDecliners(rows) { const tbody = document.getElementById('commandTopDecliners'); if (!tbody) return; const sortedRows = [...rows] - .sort((a, b) => Math.abs(Number(b.sales_delta_pct || 0)) - Math.abs(Number(a.sales_delta_pct || 0))) + .filter((row) => Number(row.sales_7d || 0) > 0) + .sort((a, b) => Number(b.sales_7d || 0) - Number(a.sales_7d || 0)) .slice(0, 5); if (!sortedRows.length) { @@ -3502,6 +3504,9 @@ function renderGrowthCommandTopDecliners(rows) { tbody.innerHTML = sortedRows.map((row, index) => { const delta = Number(row.sales_delta_pct || 0); const action = row.recommended_action?.label || '查看商品'; + const trendText = delta + ? `${delta > 0 ? '+' : ''}${delta.toFixed(1)}%` + : '趨勢待補'; return `