feat: deepen pchome momo backfill guardrails
All checks were successful
CD Pipeline / deploy (push) Successful in 1m8s

This commit is contained in:
OoO
2026-06-19 00:41:20 +08:00
parent 4c59b74ced
commit 9d84cbfd43
9 changed files with 208 additions and 6 deletions

View File

@@ -684,6 +684,34 @@ def _targeted_candidate_auto_type(candidate: dict[str, Any]) -> str:
return "manual_review"
def _targeted_candidate_needs_review(candidate: dict[str, Any]) -> bool:
"""總價自動同步前的最後防線,避免高分但款式待確認的候選進作戰清單。"""
if candidate.get("target_hard_veto") is True:
return True
price_basis = str(candidate.get("target_price_basis") or "").strip()
alert_tier = str(candidate.get("target_alert_tier") or "").strip()
if price_basis and price_basis != "total_price":
return True
if alert_tier and alert_tier != "price_alert_exact":
return True
review_reason_markers = {
"manual_review",
"identity_review",
"unit_price_review",
"variant_selection_review",
"variant_option_conflict",
"variant_descriptor_conflict",
"makeup_catalog_selection_gap",
"commercial_condition_gap",
"count_conflict",
"bundle_offer_conflict",
"multi_component_conflict",
"component_count_conflict",
}
reasons = {str(reason or "") for reason in (candidate.get("target_match_reasons") or [])}
return bool(reasons & review_reason_markers)
def _targeted_candidate_to_external_offer(
candidate: dict[str, Any],
*,
@@ -692,6 +720,8 @@ def _targeted_candidate_to_external_offer(
auto_type = _targeted_candidate_auto_type(candidate)
if auto_type not in {"total_price", "unit_price"}:
return None, "不是可自動使用的候選"
if auto_type == "total_price" and _targeted_candidate_needs_review(candidate):
return None, "候選仍需人工確認"
momo_sku = str(candidate.get("product_id") or candidate.get("goodsCode") or candidate.get("id") or "").strip()
pchome_product_id = str(candidate.get("target_pchome_product_id") or "").strip()

View File

@@ -712,6 +712,7 @@ VARIANT_OPTION_COLOR_WORDS = {
"棕色",
"咖啡色",
"灰色",
"rose",
"白色",
"紅色",
"粉色",

View File

@@ -659,16 +659,24 @@ def search_momo_products_for_pchome_products(
continue
hard_veto = bool(getattr(diagnostics, "hard_veto", False))
comparison_mode = getattr(diagnostics, "comparison_mode", "exact_identity")
diagnostic_price_basis = str(getattr(diagnostics, "price_basis", "") or "")
diagnostic_alert_tier = str(getattr(diagnostics, "alert_tier", "") or "")
diagnostic_match_type = str(getattr(diagnostics, "match_type", "") or "")
unit_price_comparison = {}
auto_compare_type = "manual_review"
price_basis = "none"
review_status = "需人工確認"
if not hard_veto and comparison_mode == "exact_identity":
if (
not hard_veto
and comparison_mode == "exact_identity"
and diagnostic_price_basis == "total_price"
and diagnostic_alert_tier == "price_alert_exact"
):
can_auto_compare = True
auto_compare_type = "total_price"
price_basis = "total_price"
review_status = "可直接比價"
elif comparison_mode == "unit_comparable":
elif comparison_mode == "unit_comparable" or diagnostic_price_basis == "unit_price":
unit_price_comparison = build_unit_price_comparison(
momo_name,
pchome_name,
@@ -713,6 +721,8 @@ def search_momo_products_for_pchome_products(
"target_search_term": term,
"target_match_reasons": list(getattr(diagnostics, "reasons", ()) or ()),
"target_comparison_mode": comparison_mode,
"target_match_type": diagnostic_match_type,
"target_alert_tier": diagnostic_alert_tier,
"target_hard_veto": hard_veto,
"can_auto_compare": can_auto_compare,
"auto_compare_type": auto_compare_type,

View File

@@ -4,9 +4,26 @@
from __future__ import annotations
import os
from typing import Any, Callable
def _int_env(name: str, default: int, *, minimum: int, maximum: int) -> int:
try:
value = int(os.getenv(name, str(default)))
except (TypeError, ValueError):
value = default
return max(minimum, min(value, maximum))
def _float_env(name: str, default: float, *, minimum: float, maximum: float) -> float:
try:
value = float(os.getenv(name, str(default)))
except (TypeError, ValueError):
value = default
return max(minimum, min(value, maximum))
def candidate_auto_compare_type(candidate: dict[str, Any]) -> str:
auto_type = str(candidate.get("auto_compare_type") or "").strip()
if auto_type in {"total_price", "unit_price"}:
@@ -53,9 +70,24 @@ def _default_search_candidates(targets: list[dict[str, Any]], limit: int):
return search_momo_products_for_pchome_products(
targets,
max_products=limit,
limit_per_product=6,
max_terms_per_product=4,
min_score=0.45,
limit_per_product=_int_env(
"PCHOME_GROWTH_MOMO_BACKFILL_LIMIT_PER_TERM",
8,
minimum=3,
maximum=12,
),
max_terms_per_product=_int_env(
"PCHOME_GROWTH_MOMO_BACKFILL_MAX_TERMS",
8,
minimum=3,
maximum=10,
),
min_score=_float_env(
"PCHOME_GROWTH_MOMO_BACKFILL_MIN_SCORE",
0.45,
minimum=0.35,
maximum=0.8,
),
)