From 1a569ce7271ee618fa4f579d12b2bc7c128b80ee Mon Sep 17 00:00:00 2001 From: hub-gif <2487812171@qq.com> Date: Fri, 17 Apr 2026 17:33:18 +0800 Subject: [PATCH] =?UTF-8?q?fix(strategy):=20=E8=A7=84=E5=88=99=E5=BA=95?= =?UTF-8?q?=E7=A8=BF=E8=A1=A5=E5=85=85=E5=88=97=E8=A1=A8=E5=BA=97=E9=93=BA?= =?UTF-8?q?=E5=8E=BB=E9=87=8DSKU=E5=8F=A3=E5=BE=84=E4=B8=8E=E6=91=98?= =?UTF-8?q?=E8=A6=81=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 竞争结构节在 shops_from_list.unique_sku_basis 存在时输出对照行,便于策略 LLM 与人工复核;新增单测。 Made-with: Cursor --- backend/pipeline/reporting/strategy_draft.py | 21 ++++++++++++++ backend/pipeline/tests/test_strategy_draft.py | 28 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/backend/pipeline/reporting/strategy_draft.py b/backend/pipeline/reporting/strategy_draft.py index 9bda1e3..8672cab 100644 --- a/backend/pipeline/reporting/strategy_draft.py +++ b/backend/pipeline/reporting/strategy_draft.py @@ -77,6 +77,25 @@ def _cr_narrative(label: str, cr1: Any, cr3: Any, top: Any) -> str | None: return f"- **{label}**:头部为「{top_s}」(缺少占比时可结合列表与商详数据补全)。" +def _shop_unique_sku_basis_lines(shops: dict[str, Any]) -> list[str]: + """ + ``shops_from_list.unique_sku_basis`` 与竞品报告/摘要一致:按去重 SKU 的店铺集中度对照口径。 + """ + usb = shops.get("unique_sku_basis") if isinstance(shops, dict) else None + if not isinstance(usb, dict) or not usb.get("n_unique_skus"): + return [] + u1 = concentration_first_share(usb) + u3 = concentration_top_three_share(usb) + utop = _esc(usb.get("top_label") or "") + if u1 is None or not utop: + return [] + return [ + f"- **列表侧店铺(按去重 SKU)**:共 **{_num(usb.get('n_unique_skus'))}** 个去重 SKU;" + f"第一大店铺「{utop}」约占 **{_pct(u1)}**;前三合计 **{_pct(u3)}**。" + "*(与上行「按列表行」可能因同一 SKU 多行曝光而差异;非销量/市占。)*" + ] + + def _goal_bullet(label: str, user_val: str, placeholder: str) -> str: v = _esc(user_val).strip() if v: @@ -230,6 +249,8 @@ def build_strategy_draft_markdown( ) if n_shop: lines.append(n_shop) + for uline in _shop_unique_sku_basis_lines(shops): + lines.append(uline) if n_brand: lines.append(n_brand) if not n_shop and not n_brand: diff --git a/backend/pipeline/tests/test_strategy_draft.py b/backend/pipeline/tests/test_strategy_draft.py index 774a6aa..f6b7d85 100644 --- a/backend/pipeline/tests/test_strategy_draft.py +++ b/backend/pipeline/tests/test_strategy_draft.py @@ -105,3 +105,31 @@ class StrategyDraftTests(SimpleTestCase): ) self.assertIn("「口感」:子串统计命中约 **501** 次", md) self.assertIn("场景「控糖/血糖相关」:约 **305** 条", md) + + def test_shops_unique_sku_basis_rendered(self) -> None: + brief = { + "schema_version": 1, + "keyword": "测试", + "concentration": { + "shops_from_list": { + "first_share": 0.6, + "top_three_combined_share": 0.85, + "top_label": "京东自营", + "unique_sku_basis": { + "first_share": 0.35, + "top_three_combined_share": 0.7, + "top_label": "京东自营", + "n_unique_skus": 120, + }, + }, + "detail_brand_among_merged": {}, + }, + } + md = build_strategy_draft_markdown( + job_id=1, + keyword="测试", + brief=brief, + ) + self.assertIn("按去重 SKU", md) + self.assertIn("120", md) + self.assertIn("35.0%", md)