fix(ai): supersede old product picks
All checks were successful
CD Pipeline / deploy (push) Successful in 2m48s
All checks were successful
CD Pipeline / deploy (push) Successful in 2m48s
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> 本文件定義專案開發的核心準則與不可違反的規範
|
||||
> **建立日期**: 2026-01-12
|
||||
> **當前版本**: V10.63 (Warm dashboard cache after AI pick regeneration)
|
||||
> **當前版本**: V10.64 (Keep only latest 50 AI product picks pending)
|
||||
> **最後更新**: 2026-05-01
|
||||
|
||||
---
|
||||
|
||||
4
app.py
4
app.py
@@ -95,8 +95,8 @@ except Exception as e:
|
||||
sys_log.error(f"無法檢測磁碟空間: {e}")
|
||||
|
||||
# 🚩 系統版本定義 (備份與顯示用)
|
||||
# 🚩 2026-05-01 V10.63: Warm dashboard cache after AI pick regeneration
|
||||
SYSTEM_VERSION = "V10.63"
|
||||
# 🚩 2026-05-01 V10.64: Keep only latest 50 AI product picks pending
|
||||
SYSTEM_VERSION = "V10.64"
|
||||
|
||||
# ==========================================
|
||||
# 🔒 SQL Injection 防護函數
|
||||
|
||||
@@ -254,7 +254,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.63"
|
||||
SYSTEM_VERSION = "V10.64"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ SQL漏斗(~300筆)
|
||||
`services/ai_product_pick_agent.py` 新增 PChome 銷售用挑品 Agent:
|
||||
|
||||
- 只讀真實資料表:`products`、`price_records`、`competitor_prices`、`competitor_price_history`,若 `daily_sales_snapshot` 可用則納入近 7 天銷售額、數量、毛利或成本推算毛利率。
|
||||
- 將 PChome 比 MOMO 有價格優勢、比對信心足夠、且有歷史快照或銷售動能的品項寫入 `ai_price_recommendations`。信心度不以固定倍率灌高,而是由商機分數與證據完整度共同決定,證據包含 PChome match score、歷史快照、銷售/毛利、PChome 商品 ID/名稱、抓取時間與促銷/評價/庫存標籤。
|
||||
- 將 PChome 比 MOMO 有價格優勢、比對信心足夠、且有歷史快照或銷售動能的品項寫入 `ai_price_recommendations`。信心度不以固定倍率灌高,而是由商機分數與證據完整度共同決定,證據包含 PChome match score、歷史快照、銷售/毛利、PChome 商品 ID/名稱、抓取時間與促銷/評價/庫存標籤。每次重算只保留最新 50 品為 `status='pending'`,未進榜舊品標為 `superseded`,避免統計與清單超量。
|
||||
- 寫入策略使用 `strategy='product_pick'`,保留在既有 AI 決策表,不新增假頁面或暫存 JSON。
|
||||
- 後台入口:`POST /api/ai/product-picks/generate`,`/ai_intelligence` 可手動產生清單。
|
||||
- 配對來源仍以 PChome crawler 真實搜尋結果為準;無競品資料時不生成挑品。
|
||||
|
||||
@@ -423,6 +423,30 @@ def _write_pick(conn, pick: Dict[str, Any]) -> None:
|
||||
})
|
||||
|
||||
|
||||
def _supersede_old_picks(conn, current_skus: List[str]) -> None:
|
||||
from sqlalchemy import bindparam, text
|
||||
|
||||
if not current_skus:
|
||||
conn.execute(text("""
|
||||
UPDATE ai_price_recommendations
|
||||
SET status = 'superseded',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE strategy = 'product_pick'
|
||||
AND status = 'pending'
|
||||
"""))
|
||||
return
|
||||
|
||||
stmt = text("""
|
||||
UPDATE ai_price_recommendations
|
||||
SET status = 'superseded',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE strategy = 'product_pick'
|
||||
AND status = 'pending'
|
||||
AND sku NOT IN :current_skus
|
||||
""").bindparams(bindparam("current_skus", expanding=True))
|
||||
conn.execute(stmt, {"current_skus": [str(sku) for sku in current_skus]})
|
||||
|
||||
|
||||
def generate_product_pick_list(engine, limit: int = 50) -> ProductPickResult:
|
||||
"""產生並保存 AI 建議挑品清單。"""
|
||||
generated_at = datetime.now().isoformat(timespec="seconds")
|
||||
@@ -439,6 +463,7 @@ def generate_product_pick_list(engine, limit: int = 50) -> ProductPickResult:
|
||||
picks = picks[:limit]
|
||||
for pick in picks:
|
||||
_write_pick(conn, pick)
|
||||
_supersede_old_picks(conn, [pick["sku"] for pick in picks])
|
||||
|
||||
return ProductPickResult(
|
||||
candidates=len(rows),
|
||||
|
||||
@@ -197,6 +197,8 @@ def test_ai_product_pick_agent_uses_real_competitor_data_and_dashboard_action():
|
||||
assert "evidence_quality" in agent_source
|
||||
assert "opportunity_score" in agent_source
|
||||
assert "margin_rate" in agent_source
|
||||
assert "_supersede_old_picks" in agent_source
|
||||
assert "status = 'superseded'" in agent_source
|
||||
assert "{date_col}::date" in agent_source
|
||||
assert "conn.rollback()" in agent_source
|
||||
|
||||
|
||||
Reference in New Issue
Block a user