diff --git a/config.py b/config.py index 62edb28..a8bff74 100644 --- a/config.py +++ b/config.py @@ -323,7 +323,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.356" +SYSTEM_VERSION = "V10.357" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/services/marketplace_product_matcher.py b/services/marketplace_product_matcher.py index 280904a..998bc5f 100644 --- a/services/marketplace_product_matcher.py +++ b/services/marketplace_product_matcher.py @@ -94,6 +94,9 @@ NOISE_PHRASES = ( "可調光", "聖誕禮物", "聖誕節禮物", + "懶人霜", + "打造素顏女神", + "第三代經典版白", ) GENERIC_TOKENS = { @@ -205,6 +208,15 @@ SEARCH_NOISE_PHRASES = ( "官方直營", "官方", "經典款", + "校色", + "控油", + "好氣色", + "懶人霜", + "打造素顏女神", + "我愛修膚", + "第三代經典版白", + "第三代", + "經典版", ) SEARCH_NOISE_TOKENS = { @@ -241,9 +253,21 @@ SEARCH_NOISE_TOKENS = { "經典款", "款", "pdrn", + "校色", + "控油", + "好氣色", + "懶人霜", + "我愛修膚", + "第三代", + "經典版", + "版白", } SEARCH_IDENTITY_ANCHORS = ( + "無極限保濕防曬妝前乳", + "水凝光透 妝前防護乳", + "水凝光透妝前防護乳", + "經典素顏霜", "時尚潮流美甲片", "止汗爽身噴霧", "止汗爽身乳膏pro", @@ -300,6 +324,9 @@ SEARCH_IDENTITY_ANCHORS = ( "屁屁噴", "身體乳", "緊實乳", + "妝前防護乳", + "妝前乳", + "素顏霜", "潔膚露", "浴潔露", "潔淨液", @@ -387,6 +414,8 @@ PRODUCT_TYPES = { "潔膚露": ("潔膚露", "浴潔露", "護潔露", "沐浴露", "wash", "私密潔浴露"), "私密噴霧": ("私密噴霧", "抑菌噴霧", "醒肌抑菌噴霧"), "私密凝露": ("凝露", "激淨凝露", "緊實凝露", "亮白凝露"), + "妝前乳": ("妝前乳", "妝前防護乳", "妝前隔離", "primer"), + "素顏霜": ("素顏霜", "tone up cream"), "氣墊粉餅": ("氣墊粉餅", "cushion"), "眼影盤": ("眼影盤",), "打亮液": ("打亮液",), @@ -1725,6 +1754,19 @@ def build_search_terms(name: str, max_terms: int = 3) -> list[str]: core_phrases = _ranked_search_core_phrases(identity, limit=4) core_short = " ".join(core_phrases[:2]) core_primary = core_phrases[0] if core_phrases else "" + product_type_aliases = set(PRODUCT_TYPES.get(identity.product_type or "", ())) + chinese_detail_phrases = [ + phrase + for phrase in core_phrases[1:] + if re.search(r"[\u4e00-\u9fff]", phrase) + and phrase != core_primary + and phrase != (identity.product_type or "") + and phrase not in SEARCH_BROAD_ANCHORS + and not any(phrase == alias or phrase in alias or alias in phrase for alias in product_type_aliases) + ] + modifier_with_primary = " ".join( + part for part in (chinese_detail_phrases[0] if chinese_detail_phrases else "", core_primary) if part + ) variant_descriptors = sorted(_variant_descriptors(identity), key=lambda token: (len(token), token)) variant_primary = variant_descriptors[0] if variant_descriptors else "" model_phrases = [ @@ -1741,6 +1783,9 @@ def build_search_terms(name: str, max_terms: int = 3) -> list[str]: " ".join(part for part in (brand_part, core_primary, variant_primary, spec_part) if part) if variant_sensitive and variant_primary else "", + " ".join(part for part in (brand_part, modifier_with_primary, spec_part) if part) + if modifier_with_primary and identity.product_type and identity.product_type in core_primary + else "", " ".join(part for part in (brand_part, primary_with_model, spec_part) if part), " ".join(part for part in (brand_part, core_short, spec_part) if part), " ".join(part for part in (brand_part, core_short) if part), diff --git a/tests/test_marketplace_product_matcher.py b/tests/test_marketplace_product_matcher.py index 69e9122..6f324cd 100644 --- a/tests/test_marketplace_product_matcher.py +++ b/tests/test_marketplace_product_matcher.py @@ -710,6 +710,21 @@ def test_marketplace_matcher_promotes_lactacyd_private_wash_exact_line(): assert "shared_identity_anchor" in diagnostics.reasons +def test_marketplace_matcher_promotes_eaoron_classic_tone_up_cream_exact_line(): + from services.marketplace_product_matcher import score_marketplace_match + + diagnostics = score_marketplace_match( + "【Eaoron】澳洲 經典素顏霜 50ml#0000199#(面霜 素顏霜 懶人霜 打造素顏女神)", + "【澳洲 EAORON】第三代經典版白素顏霜 50ml", + momo_price=399, + competitor_price=399, + ) + + assert diagnostics.score >= 0.76 + assert diagnostics.hard_veto is False + assert "strong_exact_spec_match" in diagnostics.reasons + + def test_marketplace_matcher_promotes_shared_model_token_for_exact_model(): from services.marketplace_product_matcher import score_marketplace_match @@ -818,6 +833,30 @@ def test_marketplace_search_terms_prioritize_private_wash_identity_phrase(): assert "滋潤緊緻" in terms[1] +def test_marketplace_search_terms_prioritize_precise_primer_identity_phrase(): + from services.marketplace_product_matcher import build_search_terms + + shu_terms = build_search_terms( + "【植村秀】官方直營 無極限保濕防曬妝前乳30ml(Shu uemura/防曬/保濕/校色", + max_terms=5, + ) + meme_terms = build_search_terms( + "【im meme】我愛修膚/水凝光透/控油/好氣色妝前防護乳(SPF50+PA++++)", + max_terms=5, + ) + eaoron_terms = build_search_terms( + "【Eaoron】澳洲 經典素顏霜 50ml#0000199#(面霜 素顏霜 懶人霜 打造素顏女神)", + max_terms=5, + ) + + assert shu_terms[0] == "植村秀 無極限保濕防曬妝前乳 30ml" + assert "校色" not in " ".join(shu_terms[:3]) + assert any("水凝光透 妝前防護乳" in term for term in meme_terms[:3]) + assert "好氣色" not in " ".join(meme_terms[:3]) + assert eaoron_terms[0] == "eaoron 素顏霜 50ml" + assert "懶人霜" not in " ".join(eaoron_terms[:3]) + + def test_marketplace_search_terms_prefer_specific_line_over_generic_usage_words(): from services.marketplace_product_matcher import build_search_terms