diff --git a/services/competitor_intel_repository.py b/services/competitor_intel_repository.py index 94c8742..7ea9769 100644 --- a/services/competitor_intel_repository.py +++ b/services/competitor_intel_repository.py @@ -329,19 +329,7 @@ def _fetch_top_competitor_risks_uncached(engine, limit: int = 10) -> list[dict]: limit = max(1, min(int(limit or 10), 50)) sql = text(f""" - WITH latest_momo AS ( - SELECT - p.id AS product_id, - p.i_code AS sku, - p.name, - p.category, - pr.price AS momo_price, - ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY pr.timestamp DESC, pr.id DESC) AS rn - FROM products p - JOIN price_records pr ON pr.product_id = p.id - WHERE p.status = 'ACTIVE' - ), - valid_competitor AS ( + WITH valid_competitor AS ( SELECT DISTINCT ON (cp.sku) cp.sku, cp.price AS pchome_price, @@ -359,21 +347,29 @@ def _fetch_top_competitor_risks_uncached(engine, limit: int = 10) -> list[dict]: ORDER BY cp.sku, cp.crawled_at DESC NULLS LAST ) SELECT - lm.sku, - lm.name, - lm.category, - lm.momo_price, + p.i_code AS sku, + p.name, + p.category, + latest_price.momo_price, vc.pchome_price, vc.competitor_product_id, vc.competitor_product_name, vc.match_score, vc.crawled_at, - (lm.momo_price - vc.pchome_price) AS gap_amount, - ((lm.momo_price - vc.pchome_price) / vc.pchome_price * 100) AS gap_pct - FROM latest_momo lm - JOIN valid_competitor vc ON vc.sku = lm.sku - WHERE lm.rn = 1 - AND lm.momo_price > vc.pchome_price * 1.05 + (latest_price.momo_price - vc.pchome_price) AS gap_amount, + ((latest_price.momo_price - vc.pchome_price) / vc.pchome_price * 100) AS gap_pct + FROM valid_competitor vc + JOIN products p + ON p.i_code = vc.sku + AND p.status = 'ACTIVE' + JOIN LATERAL ( + SELECT pr.price AS momo_price + FROM price_records pr + WHERE pr.product_id = p.id + ORDER BY pr.timestamp DESC, pr.id DESC + LIMIT 1 + ) latest_price ON TRUE + WHERE latest_price.momo_price > vc.pchome_price * 1.05 ORDER BY gap_pct DESC NULLS LAST, gap_amount DESC NULLS LAST LIMIT :limit """) diff --git a/tests/test_competitor_intel_cache.py b/tests/test_competitor_intel_cache.py index 654f5a8..af1bf01 100644 --- a/tests/test_competitor_intel_cache.py +++ b/tests/test_competitor_intel_cache.py @@ -58,3 +58,12 @@ def test_competitor_ppt_prompt_uses_neutral_ewooc_viewpoint(): assert "待補身份/價格" in source assert "我方 = PChome" not in source assert "請以 PChome 視角" not in source + + +def test_top_competitor_risks_reads_latest_momo_price_after_valid_competitor_filter(): + source = (ROOT / "services" / "competitor_intel_repository.py").read_text(encoding="utf-8") + + assert "FROM valid_competitor vc" in source + assert "JOIN LATERAL" in source + assert "WHERE pr.product_id = p.id" in source + assert "ROW_NUMBER() OVER (PARTITION BY p.id" not in source.split("def _fetch_top_competitor_risks_uncached", 1)[1].split("def fetch_competitor_comparison_results", 1)[0]