feat(product-commerce): 系列聚合去重与采集开关两层语义
配置来源收敛 - 系列平台编码 tm/jd/dy/jd_self 与 9 类飞书地址改为整系列并集去重后挂锚点, 子款编码保留在 series.platform_ids 备查;多表冲突保留最高优先级并告警 - 写表脚本(当日/天猫主图/京东主图)去掉本地 JSON 回退,地址缺失或读取失败 直接 fail closed,避免库与表地址不一致被静默掩盖 - 测试注入 records_provider 时禁用配置缓存读写,防止假数据写入生产快照 采集开关(dim_style.collect_enabled) - 锚点为系列总开关:false 则整系列不采 - 锚点为 true 时,子款自身 false 仅把自己的 ERP 编码排除出聚合,其余子款照常 - 排除结果记录在 series.erp_excluded 并在运行时提示 聚水潭全店铺日报防错 - 列布局 fail closed、行 td 数校验、满页判定 + 总页数兜底、值域交叉校验 - 新增单日总量闸:超近 14 天中位数 8 倍拒绝落库 其他 - 新增电商费用归集配置 ad_cost_rollup.json 与积分兑换周报采集 - 店铺情报、供应链到货同步、通知路由与相关配置/文档同步更新 - 忽略本地 dsh workbench 脚手架(含 node_modules)
This commit is contained in:
1 parent
2f31d5872f
commit
622a609800
66 files changed
+4156
-469
No files matched your search
@@ -161,7 +161,9 @@ def test_normalize_style_config_owns_erp_once_and_platform_ids_per_child() -> No
|
||||
]
|
||||
|
||||
|
||||
def test_runtime_records_use_lifecycle_identity_and_keep_dynamic_targets() -> None:
|
||||
def test_runtime_records_use_lifecycle_identity_and_lifecycle_targets() -> None:
|
||||
"""地址以 dim_style 为准:动态配置里的旧地址不再覆盖建档值。"""
|
||||
|
||||
dynamic_records = [
|
||||
{
|
||||
"style_name": "款式甲",
|
||||
@@ -185,6 +187,8 @@ def test_runtime_records_use_lifecycle_identity_and_keep_dynamic_targets() -> No
|
||||
"jd_self": [],
|
||||
"dy": [],
|
||||
},
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/newsales?table=tblNewSales",
|
||||
"tm_persona_bitable_url": "tm-persona-new",
|
||||
}
|
||||
]
|
||||
|
||||
@@ -196,8 +200,138 @@ def test_runtime_records_use_lifecycle_identity_and_keep_dynamic_targets() -> No
|
||||
assert by_platform["京东自营"]["item_ids"] == []
|
||||
assert by_platform["抖音"]["item_ids"] == []
|
||||
assert all(row["erp_codes"] == ["NEW-ERP"] for row in records)
|
||||
assert by_platform["天猫"]["sales_bitable_url"].endswith("table=tblSales")
|
||||
assert by_platform["天猫"]["tm_persona_bitable_url"] == "tm-persona"
|
||||
assert by_platform["天猫"]["sales_bitable_url"].endswith("table=tblNewSales")
|
||||
assert by_platform["天猫"]["tm_persona_bitable_url"] == "tm-persona-new"
|
||||
|
||||
|
||||
def test_dynamic_targets_are_ignored_when_lifecycle_has_none() -> None:
|
||||
"""建档地址为空时不再回退旧表:飞书地址只有 dim_style 一个来源。"""
|
||||
|
||||
records = merge_lifecycle_style_records(
|
||||
[
|
||||
{
|
||||
"style_name": "款式乙",
|
||||
"erp_codes": ["ERP-B"],
|
||||
"platform": "天猫",
|
||||
"item_ids": ["TM-B"],
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/legacy?table=tblLegacy",
|
||||
"main_image_bitable_url": "https://example.feishu.cn/base/legacy?table=tblLegacyMain",
|
||||
"creator_bitable_url": "https://example.feishu.cn/base/legacy?table=tblLegacyCreator",
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"id": 9,
|
||||
"name": "款式乙",
|
||||
"erp_style_codes": ["ERP-B"],
|
||||
"platform_product_ids": {"tm": ["TM-B"], "jd": [], "jd_self": [], "dy": []},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
row = next(r for r in records if r["platform"] == "天猫")
|
||||
assert row["sales_bitable_url"] == ""
|
||||
assert row["main_image_bitable_url"] == ""
|
||||
assert row["creator_bitable_url"] == ""
|
||||
|
||||
|
||||
def test_series_targets_propagate_from_anchor() -> None:
|
||||
"""锚点的 9 类地址下发到同系列子款;子款自身的旧地址被覆盖。"""
|
||||
|
||||
lifecycle_rows = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "逐星",
|
||||
"erp_style_codes": ["E-ANCHOR"],
|
||||
"series_name": "逐星",
|
||||
"is_series_anchor": True,
|
||||
"platform_product_ids": {"tm": ["TM-A"], "jd": [], "jd_self": [], "dy": []},
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/anchor?table=tblAnchorSales",
|
||||
"main_image_bitable_url": "https://example.feishu.cn/base/anchor?table=tblAnchorMain",
|
||||
"creator_bitable_url": "https://example.feishu.cn/base/anchor?table=tblAnchorCreator",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "逐星GT",
|
||||
"erp_style_codes": ["E-CHILD"],
|
||||
"series_name": "逐星",
|
||||
"is_series_anchor": False,
|
||||
"platform_product_ids": {"tm": ["TM-C"], "jd": [], "jd_self": [], "dy": []},
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/child?table=tblChildSales",
|
||||
},
|
||||
]
|
||||
|
||||
records = merge_lifecycle_style_records([], lifecycle_rows)
|
||||
child = next(r for r in records if r["style_name"] == "逐星GT")
|
||||
|
||||
assert "anchor" in child["sales_bitable_url"]
|
||||
assert "anchor" in child["main_image_bitable_url"]
|
||||
assert "anchor" in child["creator_bitable_url"]
|
||||
|
||||
|
||||
def test_series_targets_aggregate_from_child_when_anchor_empty() -> None:
|
||||
"""子款误填地址、锚点为空:聚合上浮到锚点并下发整条系列,不丢配置。"""
|
||||
|
||||
lifecycle_rows = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "云栖",
|
||||
"erp_style_codes": ["E-ANCHOR"],
|
||||
"series_name": "云栖",
|
||||
"is_series_anchor": True,
|
||||
"platform_product_ids": {"tm": ["TM-A"], "jd": [], "jd_self": [], "dy": []},
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "云栖标准版",
|
||||
"erp_style_codes": ["E-CHILD"],
|
||||
"series_name": "云栖",
|
||||
"is_series_anchor": False,
|
||||
"platform_product_ids": {"tm": [], "jd": [], "jd_self": [], "dy": []},
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/child?table=tblChildSales",
|
||||
"main_image_bitable_url": "https://example.feishu.cn/base/child?table=tblChildMain",
|
||||
},
|
||||
]
|
||||
|
||||
records = merge_lifecycle_style_records([], lifecycle_rows)
|
||||
|
||||
anchor_sales = {r["sales_bitable_url"] for r in records if r["style_name"] == "云栖"}
|
||||
child_sales = {r["sales_bitable_url"] for r in records if r["style_name"] == "云栖标准版"}
|
||||
# 子款地址上浮到锚点,并统一下发到整条系列
|
||||
assert anchor_sales == {"https://example.feishu.cn/base/child?table=tblChildSales"}
|
||||
assert child_sales == anchor_sales
|
||||
|
||||
|
||||
def test_series_targets_conflict_keeps_anchor_address(capsys) -> None:
|
||||
"""同一字段多张表:保留锚点那条并告警,避免同系列拆到两个表。"""
|
||||
|
||||
lifecycle_rows = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "冲突款",
|
||||
"erp_style_codes": ["E-ANCHOR"],
|
||||
"series_name": "冲突系列",
|
||||
"is_series_anchor": True,
|
||||
"platform_product_ids": {"tm": ["TM-A"], "jd": [], "jd_self": [], "dy": []},
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/anchor?table=tblAnchorSales",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "冲突款-2",
|
||||
"erp_style_codes": ["E-CHILD"],
|
||||
"series_name": "冲突系列",
|
||||
"is_series_anchor": False,
|
||||
"platform_product_ids": {"tm": [], "jd": [], "jd_self": [], "dy": []},
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/child?table=tblChildSales",
|
||||
},
|
||||
]
|
||||
|
||||
records = merge_lifecycle_style_records([], lifecycle_rows)
|
||||
|
||||
assert {r["sales_bitable_url"] for r in records} == {
|
||||
"https://example.feishu.cn/base/anchor?table=tblAnchorSales"
|
||||
}
|
||||
assert "个不同地址" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_runtime_records_empty_lifecycle_ids_clear_legacy_platform_ids() -> None:
|
||||
|
||||
Reference in new issue
Block a user