diff --git a/config.py b/config.py index 93f6b6b..92666d0 100644 --- a/config.py +++ b/config.py @@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.692" +SYSTEM_VERSION = "V10.693" 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 783eff2..d711507 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -769,3 +769,4 @@ POSTGRES_HOST=momo-db | 2026-06-25 | Google Drive 背景匯入不得尋找本機瀏覽器 | V10.690 起 `GoogleDriveService.authenticate()` 預設拒絕背景 OAuth;即使人工明確開啟互動授權,也使用 `open_browser=False` 並輸出授權網址,不在 scheduler/app 容器內尋找 runnable browser。 | | 2026-06-25 | 全頁價格方向統一為 PChome 成長視角 | V10.691 起 AI Intelligence、Daily Sales、Growth Analysis、Dashboard、Telegram 與 AI 報告 prompt 不再使用「PChome 價格壓力 / MOMO 價格優勢 / MOMO 更便宜 / PChome 有優勢」等易混淆詞;統一為「PChome 價格優勢」與「MOMO 低價壓力」。 | | 2026-06-25 | 候選比較卡與價格語意必須有測試防線 | V10.692 起 `tests/test_pchome_revenue_growth_service.py` 鎖定 `/ai_intelligence` 模板必須提供 PChome/MOMO 雙賣場連結、雙開賣場操作與白話候選理由,且不得再出現 `variant_selection_review`、`focused_exact_identity`、`source_code`、`momo_reference` 或反向價格詞。 | +| 2026-06-25 | 成長報表不得把比價內部指標整排丟給使用者 | V10.693 起 `/growth_analysis` 的比價品質區改為「PChome 價格作戰可用度」:只呈現可直接決策、同款覆蓋、價格需刷新、待補/待確認四個訊號,並依資料狀態給下一步建議與今日作戰入口;測試禁止回到「比價資料品質、高信心門檻、未知新鮮度、人工否決」這類工程化列表。 | diff --git a/templates/growth_analysis.html b/templates/growth_analysis.html index fa69542..e658be9 100644 --- a/templates/growth_analysis.html +++ b/templates/growth_analysis.html @@ -137,43 +137,52 @@
- 比價資料品質 + PChome 價格作戰可用度
-
+
{% set coverage = chart_data.competitor_coverage | default({}) %} -
- 高信心門檻 - {{ coverage.match_score_floor | default(0.76) }} - 決策支援覆蓋率 - {{ coverage.decision_support_rate | default(coverage.decision_ready_rate | default(0)) }}% - 精準可告警覆蓋 - {{ coverage.decision_ready_rate | default(0) }}% - 身份配對 - {{ coverage.valid_matches | default(0) | number_format }} - 身份覆蓋率 - {{ coverage.match_rate | default(0) }}% - 價格新鮮 - {{ coverage.fresh_matches | default(0) | number_format }} - 新鮮率 - {{ coverage.fresh_match_rate | default(0) }}% - 價格過期 - {{ coverage.stale_matches | default(0) | number_format }} - 未知新鮮度 - {{ coverage.unknown_freshness_matches | default(0) | number_format }} - 未形成有效身份配對 - {{ coverage.pending | default(0) | number_format }} - 需單位價覆核 - {{ coverage.unit_comparable_count | default(0) | number_format }} - 型錄/任選可比 - {{ coverage.catalog_comparable_count | default(0) | number_format }} - 重算待人工覆核 - {{ coverage.rescore_accepted_count | default(0) | number_format }} - 人工採用 - {{ coverage.manual_accept_count | default(0) | number_format }} - 人工否決 - {{ coverage.manual_reject_count | default(0) | number_format }} - 人工單位價 - {{ coverage.manual_unit_price_count | default(0) | number_format }} + {% set decision_rate = coverage.decision_support_rate | default(coverage.decision_ready_rate | default(0)) | float %} + {% set match_rate = coverage.match_rate | default(0) | float %} + {% set stale_count = coverage.stale_matches | default(0) | int %} + {% set pending_count = coverage.pending | default(0) | int %} + {% set review_count = coverage.actionable_review_count | default(coverage.rescore_accepted_count | default(0)) | int %} + {% set action_count = pending_count + review_count %} +
+
+ 可直接決策 + {{ "{:.1f}%".format(decision_rate) }} +
+
+ 同款覆蓋 + {{ "{:.1f}%".format(match_rate) }} +
+
+ 價格需刷新 + {{ stale_count | number_format }} +
+
+ 待補 / 待確認 + {{ action_count | number_format }} +
+
+ 下一步 + {% if decision_rate < 30 %} + 先補齊高業績商品的 MOMO 對應 +

可決策覆蓋偏低,先提高同款覆蓋再判斷價格策略。

+ {% elif stale_count > 0 %} + 先刷新過期價格 +

避免用舊價格誤判,刷新後再看 MOMO 低價壓力。

+ {% elif action_count > 0 %} + 先處理待補與候選確認 +

把候選轉成可用比價,再進入售價、券與組合檢查。

+ {% else %} + 可進入價格策略檢查 +

先防守 MOMO 低價壓力,再放大 PChome 價格優勢。

+ {% endif %} + + 前往今日作戰 + +
diff --git a/tests/test_pchome_revenue_growth_service.py b/tests/test_pchome_revenue_growth_service.py index ae688d5..5e1dd77 100644 --- a/tests/test_pchome_revenue_growth_service.py +++ b/tests/test_pchome_revenue_growth_service.py @@ -521,6 +521,28 @@ def test_ai_intelligence_template_uses_pchome_growth_name_and_endpoint(): assert "今日重點總覽" in template assert "nextActionTitle" in template assert "商品處理進度" in template + + +def test_growth_analysis_uses_actionable_price_command_panel(): + from pathlib import Path + + template = Path("templates/growth_analysis.html").read_text(encoding="utf-8") + css = Path("web/static/css/page-growth-bem.css").read_text(encoding="utf-8") + + assert "PChome 價格作戰可用度" in template + assert "ga-chart-card__body--command" in template + assert "ga-competitor-command" in template + assert "可直接決策" in template + assert "待補 / 待確認" in template + assert "下一步" in template + assert "前往今日作戰" in template + assert "ga-competitor-signal" in css + assert "ga-competitor-next" in css + assert ".growth-analysis-page .ga-chart-card__body--command" in css + assert "比價資料品質" not in template + assert "高信心門檻" not in template + assert "未知新鮮度" not in template + assert "人工否決" not in template assert "價格風險分佈" in template assert "growthActionHint" in template assert "growthDataSourceSummary" in template diff --git a/web/static/css/page-growth-bem.css b/web/static/css/page-growth-bem.css index 27a6828..5b6168c 100644 --- a/web/static/css/page-growth-bem.css +++ b/web/static/css/page-growth-bem.css @@ -241,6 +241,10 @@ .growth-analysis-page .ga-chart-card__body--md { height: 300px; } +.growth-analysis-page .ga-chart-card__body--command { + height: auto; + min-height: 300px; +} .growth-analysis-page .ga-chart-card__body canvas { display: block; width: 100% !important; @@ -383,6 +387,77 @@ overflow-wrap: anywhere; } +.growth-analysis-page .ga-competitor-command { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + width: 100%; +} + +.growth-analysis-page .ga-competitor-signal { + min-width: 0; + border: 1px solid var(--momo-border-muted, rgba(31, 41, 55, 0.12)); + border-radius: 8px; + padding: 10px 12px; + background: var(--momo-surface-soft, rgba(255, 255, 255, 0.72)); +} + +.growth-analysis-page .ga-competitor-signal span, +.growth-analysis-page .ga-competitor-next__label { + display: block; + color: var(--momo-text-secondary); + font-size: var(--momo-text-caption, 12px); + font-weight: 800; +} + +.growth-analysis-page .ga-competitor-signal strong { + display: block; + min-width: 0; + margin-top: 4px; + color: var(--momo-text-primary); + font-size: clamp(1.1rem, 1.8vw, 1.55rem); + line-height: 1.1; + overflow-wrap: anywhere; +} + +.growth-analysis-page .ga-competitor-next { + grid-column: 1 / -1; + min-width: 0; + border-left: 4px solid var(--momo-page-accent); + border-radius: 8px; + padding: 12px 14px; + background: var(--momo-surface-muted, rgba(248, 250, 252, 0.88)); +} + +.growth-analysis-page .ga-competitor-next strong { + display: block; + margin-top: 4px; + color: var(--momo-text-primary); + font-size: var(--momo-text-body, 14px); +} + +.growth-analysis-page .ga-competitor-next p { + margin: 4px 0 0; + color: var(--momo-text-secondary); + font-size: var(--momo-text-body-sm, 13px); + line-height: 1.45; +} + +.growth-analysis-page .ga-competitor-next__link { + display: inline-flex; + align-items: center; + gap: 6px; + margin-top: 10px; + color: var(--momo-page-accent); + font-size: var(--momo-text-body-sm, 13px); + font-weight: 800; + text-decoration: none; +} + +.growth-analysis-page .ga-competitor-next__link:hover { + text-decoration: underline; +} + @media (max-width: 640px) { .growth-analysis-page .ga-page-head { align-items: flex-start; @@ -419,4 +494,13 @@ height: 260px !important; padding: var(--momo-space-3, 12px); } + + .growth-analysis-page .ga-chart-card__body--command { + height: auto !important; + min-height: 0; + } + + .growth-analysis-page .ga-competitor-command { + grid-template-columns: 1fr; + } }