feat: [workflow] 统一飞书款式身份配置
This commit is contained in:
@@ -64,7 +64,66 @@ def test_database_row_uses_explicit_style_name_not_style_content() -> None:
|
||||
"main_image_bitable_url": "",
|
||||
"persona_bitable_url": "",
|
||||
"dy_persona_bitable_url": "",
|
||||
"jd_persona_bitable_url": "",
|
||||
"style_analysis_bitable_url": "",
|
||||
}
|
||||
"jd_persona_bitable_url": "",
|
||||
"style_analysis_bitable_url": "",
|
||||
"destinations": [],
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_erp_only_style_is_skipped_by_every_product_collection_gate() -> None:
|
||||
loader = StyleConfigLoader(
|
||||
records_provider=lambda: [
|
||||
{
|
||||
"platform": "天猫",
|
||||
"style_name": "研发款",
|
||||
"erp_codes": ["ERP-ONLY"],
|
||||
"item_ids": [],
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/sales?table=tblSales",
|
||||
"main_image_bitable_url": "https://example.feishu.cn/base/image?table=tblImage",
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
daily = loader.get_daily_styles()
|
||||
erp_daily = loader.get_erp_daily_styles()
|
||||
|
||||
assert daily["tm"]["collectable"] == []
|
||||
assert daily["tm"]["skipped"] == [
|
||||
{"style": "研发款", "reason": "缺 平台商品编码/tm商品编码"}
|
||||
]
|
||||
assert erp_daily["collectable"] == []
|
||||
assert erp_daily["skipped"] == [
|
||||
{"style": "研发款", "reason": "缺 平台商品编码"}
|
||||
]
|
||||
assert loader.get_erp_styles_input() == []
|
||||
assert loader.get_main_image_styles() == []
|
||||
|
||||
|
||||
def test_complete_identity_allows_only_configured_platforms() -> None:
|
||||
loader = StyleConfigLoader(
|
||||
records_provider=lambda: [
|
||||
{
|
||||
"platform": "天猫",
|
||||
"style_name": "完整款",
|
||||
"erp_codes": ["ERP-1"],
|
||||
"item_ids": ["TM-1"],
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/sales?table=tblSales",
|
||||
"main_image_bitable_url": "https://example.feishu.cn/base/image?table=tblImage",
|
||||
},
|
||||
{
|
||||
"platform": "京东旗舰店",
|
||||
"style_name": "完整款",
|
||||
"erp_codes": [],
|
||||
"item_ids": ["JD-1"],
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
daily = loader.get_daily_styles()
|
||||
|
||||
assert daily["tm"]["collectable"] == ["完整款"]
|
||||
assert daily["jd"]["collectable"] == ["完整款"]
|
||||
assert daily["dy"]["collectable"] == []
|
||||
assert loader.get_erp_daily_styles()["collectable"] == ["完整款"]
|
||||
assert loader.get_main_image_styles() == ["完整款"]
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
from gyxx_flow.migration.sync_feishu_style_identity import (
|
||||
StyleIdentity,
|
||||
_merge_target_row,
|
||||
build_sync_plan,
|
||||
)
|
||||
|
||||
|
||||
def _row(record_id: str, **config):
|
||||
return {"source_record_id": record_id, "config": config}
|
||||
|
||||
|
||||
def test_build_sync_plan_groups_platform_rows_and_deduplicates_ids():
|
||||
styles, report = build_sync_plan(
|
||||
[
|
||||
_row(
|
||||
"tm-1",
|
||||
style_name=" 极星pro ",
|
||||
brand="光影行星",
|
||||
erp_codes="10398,10398",
|
||||
platform="天猫",
|
||||
item_ids="TM-1,TM-2",
|
||||
),
|
||||
_row(
|
||||
"jd-1",
|
||||
style_name="极星pro",
|
||||
brand="光影行星",
|
||||
erp_codes=["10398"],
|
||||
platform="京东旗舰店",
|
||||
item_ids=["JD-1", "JD-1"],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
assert [style.name for style in styles] == ["极星pro"]
|
||||
assert styles[0].erp_codes == ["10398"]
|
||||
assert styles[0].platform_product_ids == {
|
||||
"tm": ["TM-1", "TM-2"],
|
||||
"jd": ["JD-1"],
|
||||
"jd_self": [],
|
||||
"dy": [],
|
||||
}
|
||||
assert report["collectable_styles"] == ["极星pro"]
|
||||
|
||||
|
||||
def test_build_sync_plan_reports_unknown_platform_and_blank_style():
|
||||
styles, report = build_sync_plan(
|
||||
[
|
||||
_row(
|
||||
"pdd-1",
|
||||
style_name="宙斯",
|
||||
brand="光影行星",
|
||||
erp_codes="10489",
|
||||
platform="拼多多",
|
||||
item_ids="PDD-1",
|
||||
),
|
||||
_row(
|
||||
"blank-1",
|
||||
style_name="",
|
||||
brand="",
|
||||
erp_codes="",
|
||||
platform="天猫",
|
||||
item_ids="",
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
assert [style.name for style in styles] == ["宙斯"]
|
||||
assert report["unknown_platform_rows"] == [
|
||||
{
|
||||
"style": "宙斯",
|
||||
"platform": "拼多多",
|
||||
"item_count": 1,
|
||||
"record_id": "pdd-1",
|
||||
}
|
||||
]
|
||||
assert report["skipped_rows"] == [
|
||||
{"record_id": "blank-1", "reason": "款式名称为空"}
|
||||
]
|
||||
|
||||
|
||||
def test_merge_target_row_does_not_erase_existing_identity_with_blank_source():
|
||||
style = StyleIdentity(name="宙斯")
|
||||
merged = _merge_target_row(
|
||||
style,
|
||||
{
|
||||
"name": "宙斯",
|
||||
"brand": "光影行星",
|
||||
"erp_style_codes": ["10489"],
|
||||
"platform_product_ids": {
|
||||
"天猫": ["OLD-TM"],
|
||||
"tm": ["TM-1"],
|
||||
"jd": ["JD-1"],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert merged["brand"] == "光影行星"
|
||||
assert merged["erp_style_codes"] == ["10489"]
|
||||
assert merged["platform_product_ids"]["tm"] == ["TM-1"]
|
||||
assert merged["platform_product_ids"]["jd"] == ["JD-1"]
|
||||
assert merged["platform_product_ids"]["jd_self"] == []
|
||||
assert merged["platform_product_ids"]["dy"] == []
|
||||
@@ -11,6 +11,7 @@ import pytest
|
||||
from gyxx_flow.adapters.workflow_config import (
|
||||
PostgresWorkflowConfigStore,
|
||||
WorkflowConfigConflictError,
|
||||
merge_lifecycle_style_records,
|
||||
normalize_config,
|
||||
normalize_style_config,
|
||||
)
|
||||
@@ -160,6 +161,107 @@ 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:
|
||||
dynamic_records = [
|
||||
{
|
||||
"style_name": "款式甲",
|
||||
"brand": "旧品牌",
|
||||
"erp_codes": ["OLD-ERP"],
|
||||
"platform": "天猫",
|
||||
"item_ids": ["OLD-TM"],
|
||||
"sales_bitable_url": "https://example.feishu.cn/base/sales?table=tblSales",
|
||||
"tm_persona_bitable_url": "tm-persona",
|
||||
}
|
||||
]
|
||||
lifecycle_rows = [
|
||||
{
|
||||
"id": 8,
|
||||
"name": "款式甲",
|
||||
"brand": "光影行星",
|
||||
"erp_style_codes": ["NEW-ERP"],
|
||||
"platform_product_ids": {
|
||||
"tm": ["NEW-TM"],
|
||||
"jd": ["NEW-JD"],
|
||||
"jd_self": [],
|
||||
"dy": [],
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
records = merge_lifecycle_style_records(dynamic_records, lifecycle_rows)
|
||||
|
||||
by_platform = {row["platform"]: row for row in records}
|
||||
assert by_platform["天猫"]["item_ids"] == ["NEW-TM"]
|
||||
assert by_platform["京东旗舰店"]["item_ids"] == ["NEW-JD"]
|
||||
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"
|
||||
|
||||
|
||||
def test_runtime_records_empty_lifecycle_ids_clear_legacy_platform_ids() -> None:
|
||||
records = merge_lifecycle_style_records(
|
||||
[
|
||||
{
|
||||
"style_name": "研发款",
|
||||
"erp_codes": ["ERP-1"],
|
||||
"platform": "天猫",
|
||||
"item_ids": ["LEGACY-TM"],
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"id": 9,
|
||||
"name": "研发款",
|
||||
"erp_style_codes": ["ERP-1"],
|
||||
"platform_product_ids": {
|
||||
"tm": [],
|
||||
"jd": [],
|
||||
"jd_self": [],
|
||||
"dy": [],
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
assert all(not row["item_ids"] for row in records)
|
||||
|
||||
|
||||
def test_lifecycle_merge_respects_disabled_dynamic_platform() -> None:
|
||||
records = merge_lifecycle_style_records(
|
||||
[
|
||||
{
|
||||
"style_name": "款式甲",
|
||||
"platform": "天猫",
|
||||
"item_ids": ["TM-1"],
|
||||
"enabled": False,
|
||||
},
|
||||
{
|
||||
"style_name": "款式甲",
|
||||
"platform": "京东旗舰店",
|
||||
"item_ids": ["JD-1"],
|
||||
"enabled": True,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"id": 10,
|
||||
"name": "款式甲",
|
||||
"erp_style_codes": ["ERP-1"],
|
||||
"platform_product_ids": {
|
||||
"tm": ["TM-1"],
|
||||
"jd": ["JD-1"],
|
||||
"jd_self": [],
|
||||
"dy": [],
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
assert {row["platform"] for row in records} == {"京东旗舰店", "京东自营", "抖音"}
|
||||
|
||||
|
||||
def test_normalize_style_config_supports_extensible_business_targets() -> None:
|
||||
payload = _payload()
|
||||
payload["destinations"] = [
|
||||
|
||||
Reference in New Issue
Block a user