feat: simplify growth price command panel
This commit is contained in:
@@ -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 # 用於模板顯示
|
||||
|
||||
|
||||
@@ -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 價格作戰可用度」:只呈現可直接決策、同款覆蓋、價格需刷新、待補/待確認四個訊號,並依資料狀態給下一步建議與今日作戰入口;測試禁止回到「比價資料品質、高信心門檻、未知新鮮度、人工否決」這類工程化列表。 |
|
||||
|
||||
@@ -137,43 +137,52 @@
|
||||
|
||||
<article class="ga-chart-card">
|
||||
<header class="ga-chart-card__head">
|
||||
<span class="ga-chart-card__title"><i class="fas fa-bullseye"></i> 比價資料品質</span>
|
||||
<span class="ga-chart-card__title"><i class="fas fa-bullseye"></i> PChome 價格作戰可用度</span>
|
||||
</header>
|
||||
<div class="ga-chart-card__body ga-chart-card__body--md">
|
||||
<div class="ga-chart-card__body ga-chart-card__body--md ga-chart-card__body--command">
|
||||
{% set coverage = chart_data.competitor_coverage | default({}) %}
|
||||
<div class="ga-competitor-quality">
|
||||
<span>高信心門檻</span>
|
||||
<strong class="momo-mono">{{ coverage.match_score_floor | default(0.76) }}</strong>
|
||||
<span>決策支援覆蓋率</span>
|
||||
<strong class="momo-mono">{{ coverage.decision_support_rate | default(coverage.decision_ready_rate | default(0)) }}%</strong>
|
||||
<span>精準可告警覆蓋</span>
|
||||
<strong class="momo-mono">{{ coverage.decision_ready_rate | default(0) }}%</strong>
|
||||
<span>身份配對</span>
|
||||
<strong class="momo-mono">{{ coverage.valid_matches | default(0) | number_format }}</strong>
|
||||
<span>身份覆蓋率</span>
|
||||
<strong class="momo-mono">{{ coverage.match_rate | default(0) }}%</strong>
|
||||
<span>價格新鮮</span>
|
||||
<strong class="momo-mono">{{ coverage.fresh_matches | default(0) | number_format }}</strong>
|
||||
<span>新鮮率</span>
|
||||
<strong class="momo-mono">{{ coverage.fresh_match_rate | default(0) }}%</strong>
|
||||
<span>價格過期</span>
|
||||
<strong class="momo-mono">{{ coverage.stale_matches | default(0) | number_format }}</strong>
|
||||
<span>未知新鮮度</span>
|
||||
<strong class="momo-mono">{{ coverage.unknown_freshness_matches | default(0) | number_format }}</strong>
|
||||
<span>未形成有效身份配對</span>
|
||||
<strong class="momo-mono">{{ coverage.pending | default(0) | number_format }}</strong>
|
||||
<span>需單位價覆核</span>
|
||||
<strong class="momo-mono">{{ coverage.unit_comparable_count | default(0) | number_format }}</strong>
|
||||
<span>型錄/任選可比</span>
|
||||
<strong class="momo-mono">{{ coverage.catalog_comparable_count | default(0) | number_format }}</strong>
|
||||
<span>重算待人工覆核</span>
|
||||
<strong class="momo-mono">{{ coverage.rescore_accepted_count | default(0) | number_format }}</strong>
|
||||
<span>人工採用</span>
|
||||
<strong class="momo-mono">{{ coverage.manual_accept_count | default(0) | number_format }}</strong>
|
||||
<span>人工否決</span>
|
||||
<strong class="momo-mono">{{ coverage.manual_reject_count | default(0) | number_format }}</strong>
|
||||
<span>人工單位價</span>
|
||||
<strong class="momo-mono">{{ coverage.manual_unit_price_count | default(0) | number_format }}</strong>
|
||||
{% 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 %}
|
||||
<div class="ga-competitor-command">
|
||||
<div class="ga-competitor-signal">
|
||||
<span>可直接決策</span>
|
||||
<strong class="momo-mono">{{ "{:.1f}%".format(decision_rate) }}</strong>
|
||||
</div>
|
||||
<div class="ga-competitor-signal">
|
||||
<span>同款覆蓋</span>
|
||||
<strong class="momo-mono">{{ "{:.1f}%".format(match_rate) }}</strong>
|
||||
</div>
|
||||
<div class="ga-competitor-signal">
|
||||
<span>價格需刷新</span>
|
||||
<strong class="momo-mono">{{ stale_count | number_format }}</strong>
|
||||
</div>
|
||||
<div class="ga-competitor-signal">
|
||||
<span>待補 / 待確認</span>
|
||||
<strong class="momo-mono">{{ action_count | number_format }}</strong>
|
||||
</div>
|
||||
<div class="ga-competitor-next">
|
||||
<span class="ga-competitor-next__label">下一步</span>
|
||||
{% if decision_rate < 30 %}
|
||||
<strong>先補齊高業績商品的 MOMO 對應</strong>
|
||||
<p>可決策覆蓋偏低,先提高同款覆蓋再判斷價格策略。</p>
|
||||
{% elif stale_count > 0 %}
|
||||
<strong>先刷新過期價格</strong>
|
||||
<p>避免用舊價格誤判,刷新後再看 MOMO 低價壓力。</p>
|
||||
{% elif action_count > 0 %}
|
||||
<strong>先處理待補與候選確認</strong>
|
||||
<p>把候選轉成可用比價,再進入售價、券與組合檢查。</p>
|
||||
{% else %}
|
||||
<strong>可進入價格策略檢查</strong>
|
||||
<p>先防守 MOMO 低價壓力,再放大 PChome 價格優勢。</p>
|
||||
{% endif %}
|
||||
<a href="/" class="ga-competitor-next__link">
|
||||
<i class="fas fa-arrow-right" aria-hidden="true"></i>前往今日作戰
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user