From 70de91f1f6a4268d2d0ce47191d9c7ae5907fa8d Mon Sep 17 00:00:00 2001 From: OoO Date: Fri, 1 May 2026 10:12:32 +0800 Subject: [PATCH] =?UTF-8?q?fix(ai):=20=E4=BF=AE=E6=AD=A3=E6=8C=91=E5=93=81?= =?UTF-8?q?=E6=B8=85=E5=96=AE=E9=8A=B7=E5=94=AE=E6=97=A5=E6=9C=9F=E6=9F=A5?= =?UTF-8?q?=E8=A9=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONSTITUTION.md | 2 +- app.py | 4 ++-- services/ai_product_pick_agent.py | 16 +++++++++++----- tests/test_frontend_v2_assets.py | 2 ++ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CONSTITUTION.md b/CONSTITUTION.md index cd73677..9b96d73 100644 --- a/CONSTITUTION.md +++ b/CONSTITUTION.md @@ -2,7 +2,7 @@ > 本文件定義專案開發的核心準則與不可違反的規範 > **建立日期**: 2026-01-12 -> **當前版本**: V10.45 (AI product pick list and improved PChome matching) +> **當前版本**: V10.46 (Fix product pick sales date casting) > **最後更新**: 2026-05-01 --- diff --git a/app.py b/app.py index 746c99f..c1ece93 100644 --- a/app.py +++ b/app.py @@ -95,8 +95,8 @@ except Exception as e: sys_log.error(f"無法檢測磁碟空間: {e}") # 🚩 系統版本定義 (備份與顯示用) -# 🚩 2026-05-01 V10.45: AI product pick list and improved PChome matching -SYSTEM_VERSION = "V10.45" +# 🚩 2026-05-01 V10.46: Fix product pick sales date casting +SYSTEM_VERSION = "V10.46" # ========================================== # 🔒 SQL Injection 防護函數 diff --git a/services/ai_product_pick_agent.py b/services/ai_product_pick_agent.py index dc97dd3..8391a4f 100644 --- a/services/ai_product_pick_agent.py +++ b/services/ai_product_pick_agent.py @@ -76,12 +76,12 @@ def _fetch_candidates(conn, limit: int) -> List[Dict[str, Any]]: LEFT JOIN ( SELECT "商品ID" AS sku, - SUM(CASE WHEN snapshot_date >= CURRENT_DATE - 7 + SUM(CASE WHEN snapshot_date::date >= CURRENT_DATE - 7 THEN COALESCE("銷售金額"::numeric, 0) ELSE 0 END) AS sales_7d, - SUM(CASE WHEN snapshot_date >= CURRENT_DATE - 14 - AND snapshot_date < CURRENT_DATE - 7 + SUM(CASE WHEN snapshot_date::date >= CURRENT_DATE - 14 + AND snapshot_date::date < CURRENT_DATE - 7 THEN COALESCE("銷售金額"::numeric, 0) ELSE 0 END) AS sales_prev_7d, - SUM(CASE WHEN snapshot_date >= CURRENT_DATE - 7 + SUM(CASE WHEN snapshot_date::date >= CURRENT_DATE - 7 THEN COALESCE("數量"::numeric, 0) ELSE 0 END) AS qty_7d FROM daily_sales_snapshot GROUP BY "商品ID" @@ -157,6 +157,10 @@ def _fetch_candidates(conn, limit: int) -> List[Dict[str, Any]]: return [dict(row) for row in conn.execute(sql, {"limit": max(limit * 6, 100)}).mappings().all()] except Exception as exc: logger.warning("[ProductPickAgent] sales-aware query failed, fallback without sales: %s", exc) + try: + conn.rollback() + except Exception: + pass fallback = text(""" WITH latest_momo AS ( SELECT @@ -315,8 +319,10 @@ def _write_pick(conn, pick: Dict[str, Any]) -> None: def generate_product_pick_list(engine, limit: int = 30) -> ProductPickResult: """產生並保存 AI 建議挑品清單。""" generated_at = datetime.now().isoformat(timespec="seconds") - with engine.begin() as conn: + with engine.connect() as conn: rows = _fetch_candidates(conn, limit) + + with engine.begin() as conn: scored = [_score_candidate(row) for row in rows if _to_float(row.get("pchome_price")) > 0] picks = [ pick for pick in scored diff --git a/tests/test_frontend_v2_assets.py b/tests/test_frontend_v2_assets.py index 35f7c6a..69dca02 100644 --- a/tests/test_frontend_v2_assets.py +++ b/tests/test_frontend_v2_assets.py @@ -148,6 +148,8 @@ def test_ai_product_pick_agent_uses_real_competitor_data_and_dashboard_action(): assert "'product_pick'" in agent_source assert "PChomeProductPickAgent" in agent_source assert "PChome 價格優勢" in agent_source + assert "snapshot_date::date" in agent_source + assert "conn.rollback()" in agent_source assert "@ai_bp.route('/api/ai/product-picks/generate', methods=['POST'])" in route_source assert "generate_product_pick_list(engine" in route_source