diff --git a/config.py b/config.py
index 0dbdc1b..6034712 100644
--- a/config.py
+++ b/config.py
@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
-SYSTEM_VERSION = "V10.718"
+SYSTEM_VERSION = "V10.719"
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 88a96b6..999ab19 100644
--- a/docs/AI_INTELLIGENCE_MODULE_SOT.md
+++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md
@@ -2,7 +2,7 @@
> **最後更新**: 2026-06-26 (台北時間)
> **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯;PChome 後台業績匯入韌性已補強;產品定位正名為「PChome 業績成長自動化作戰系統」;外部市場來源正規化層、自動同步、作戰清單與價格參考表優先讀取、CSV 備援預檢、前台操作入口、高可見頁面繁中化守門、比價/作戰 UI 工作台化、跨平台來源治理與商品身份 UI 契約已建立,GCP embedding 熔斷延後處理、110 proxy rescue 與 direct host health skip 已建立
-> **適用版本**: V10.718
+> **適用版本**: V10.719
---
@@ -803,3 +803,4 @@ POSTGRES_HOST=momo-db
| 2026-06-26 | 首頁覆核候選必須標示為待確認商品 | V10.716 起首頁 PChome 覆核區不再使用「PChome 候選」作為可見主語,統一改為「PChome 待確認商品」與「開 PChome 待確認商品」,避免使用者把未確認同款誤認為正式比價結果。 |
| 2026-06-26 | 供貨風險頁不得使用資料表或英文模組名作為主語 | V10.717 起缺貨清單與補貨通知頁統一使用「供貨風險、缺貨處理清單、補貨通知紀錄」等營運語言,不再顯示「缺貨資料表、缺貨資料、Vendor Stockout」等資料庫或英文模組感文案。 |
| 2026-06-26 | AI 觀測頁不得外露 caller key | V10.718 起 AI 品質診斷與知識召回頁使用「使用情境」作為可見主語,並透過 `obs_label.caller()` 顯示營運名稱;前台不得直接顯示 `{{ caller }}`、`top_k` 或「全部呼叫端」等工程語言。 |
+| 2026-06-26 | 商品來源頁不得提供 raw JSON 匯出 | V10.719 起 `/pchome_crawler` 改為「PChome 商品監控」營運清單,只提供表格與賣場清單 CSV;前台不得出現 `exportJson`、`JSON.stringify(currentProducts)`、`圖片URL`、`商品URL` 或 raw JSON 檔名。 |
diff --git a/templates/pchome_crawler.html b/templates/pchome_crawler.html
index e1eb3e9..26dfaea 100644
--- a/templates/pchome_crawler.html
+++ b/templates/pchome_crawler.html
@@ -78,6 +78,31 @@
color: var(--momo-tag-muted-text);
}
+.pchome-product-title {
+ color: var(--momo-text-primary);
+ font-weight: 800;
+ line-height: 1.45;
+}
+
+.pchome-product-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ margin-top: 6px;
+ color: var(--momo-text-secondary);
+ font-size: var(--momo-text-small);
+}
+
+.pchome-product-meta span {
+ display: inline-flex;
+ align-items: center;
+ min-height: 22px;
+ padding: 2px 7px;
+ border: 1px solid var(--momo-border-light);
+ border-radius: var(--momo-radius-sm);
+ background: var(--momo-bg-paper);
+}
+
.pchome-toast {
top: 20px;
right: 20px;
@@ -127,7 +152,7 @@
@@ -237,8 +262,8 @@
-
@@ -343,7 +368,7 @@
// 匯出
document.getElementById('exportExcelBtn').addEventListener('click', exportExcel);
- document.getElementById('exportJsonBtn').addEventListener('click', exportJson);
+ document.getElementById('exportStoreLinksBtn').addEventListener('click', exportStoreLinks);
}
async function crawlRegion() {
@@ -465,33 +490,47 @@
tbody.innerHTML = '';
for (const p of products) {
- const discount = p.discount ? `-${p.discount}%` : '-';
- const stockBadge = p.stock > 0
- ? `${p.stock}`
+ const productName = String(p.name || 'PChome 商品');
+ const productId = String(p.product_id || '').trim();
+ const productUrl = String(p.product_url || '').trim();
+ const imageUrl = String(p.image_url || '').trim();
+ const price = toNumber(p.price);
+ const originalPrice = toNumber(p.original_price);
+ const discountValue = toNumber(p.discount);
+ const stock = toNumber(p.stock);
+ const discount = discountValue > 0 ? `-${discountValue}%` : '-';
+ const stockBadge = stock > 0
+ ? `${stock}`
: `缺貨`;
+ const imageSrc = imageUrl ? `${escapeHtml(imageUrl)}?width=80` : '/static/images/no-image.png';
+ const productTitle = `${escapeHtml(productName.substring(0, 50))}${productName.length > 50 ? '...' : ''}`;
+ const productLink = productUrl
+ ? `${productTitle}`
+ : `${productTitle}`;
+ const storeAction = productUrl
+ ? `賣場`
+ : `待補`;
const row = document.createElement('tr');
row.innerHTML = `
-
|
-
- ${escapeHtml(p.name.substring(0, 50))}${p.name.length > 50 ? '...' : ''}
-
-
- ${p.product_id}
+ ${productLink}
+
+ 商品編號 ${escapeHtml(productId || '待補')}
+ PChome 24h 官方賣場
+
|
- $${p.price.toLocaleString()} |
- $${p.original_price.toLocaleString()} |
+ ${formatMoney(price)} |
+ ${originalPrice ? `${formatMoney(originalPrice)}` : '-'} |
${discount} |
${stockBadge} |
-
-
-
+ ${storeAction}
|
`;
tbody.appendChild(row);
@@ -504,31 +543,36 @@
return;
}
- // 建立 CSV
- const headers = ['商品ID', '名稱', '售價', '原價', '折扣%', '庫存', '圖片URL', '商品URL'];
+ const headers = ['商品編號', '商品名稱', 'PChome 售價', 'PChome 原價', '折扣', '庫存狀態', '商品圖片', 'PChome 賣場'];
const rows = currentProducts.map(p => [
- p.product_id,
- `"${p.name.replace(/"/g, '""')}"`,
- p.price,
- p.original_price,
+ csvCell(p.product_id || ''),
+ csvCell(p.name || ''),
+ toNumber(p.price),
+ toNumber(p.original_price),
p.discount || '',
- p.stock,
- p.image_url,
- p.product_url
+ toNumber(p.stock),
+ csvCell(p.image_url || ''),
+ csvCell(p.product_url || '')
]);
const csv = '\uFEFF' + [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
downloadFile(csv, 'pchome_products.csv', 'text/csv;charset=utf-8');
}
- function exportJson() {
+ function exportStoreLinks() {
if (!currentProducts.length) {
showToast('沒有資料可匯出', 'warning');
return;
}
- const json = JSON.stringify(currentProducts, null, 2);
- downloadFile(json, 'pchome_products.json', 'application/json');
+ const headers = ['商品編號', '商品名稱', 'PChome 賣場'];
+ const rows = currentProducts.map(p => [
+ csvCell(p.product_id || ''),
+ csvCell(p.name || ''),
+ csvCell(p.product_url || '')
+ ]);
+ const csv = '\uFEFF' + [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
+ downloadFile(csv, 'pchome_store_links.csv', 'text/csv;charset=utf-8');
}
function downloadFile(content, filename, type) {
@@ -547,6 +591,19 @@
return div.innerHTML;
}
+ function toNumber(value) {
+ const number = Number(value);
+ return Number.isFinite(number) ? number : 0;
+ }
+
+ function formatMoney(value) {
+ return `$${toNumber(value).toLocaleString()}`;
+ }
+
+ function csvCell(value) {
+ return `"${String(value).replace(/"/g, '""')}"`;
+ }
+
function showToast(message, type = 'info') {
// 簡易 Toast
const toast = document.createElement('div');
diff --git a/tests/test_frontend_v2_assets.py b/tests/test_frontend_v2_assets.py
index 9dbf740..35eed09 100644
--- a/tests/test_frontend_v2_assets.py
+++ b/tests/test_frontend_v2_assets.py
@@ -197,8 +197,16 @@ def test_growth_workflow_pages_hide_raw_export_and_fallback_content():
assert "PChome 商品監控" in pchome_crawler
assert "商品清單" in pchome_crawler
- assert "下載完整清單" in pchome_crawler
+ assert "下載賣場清單" in pchome_crawler
+ assert "商品編號" in pchome_crawler
+ assert "PChome 24h 官方賣場" in pchome_crawler
assert "匯出 JSON" not in pchome_crawler
+ assert "下載完整清單" not in pchome_crawler
+ assert "exportJson" not in pchome_crawler
+ assert "JSON.stringify(currentProducts" not in pchome_crawler
+ assert "pchome_products.json" not in pchome_crawler
+ assert "圖片URL" not in pchome_crawler
+ assert "商品URL" not in pchome_crawler
assert "PChome 爬蟲" not in pchome_crawler
assert "爬蟲" not in pchome_crawler