feat: [workflow] 统一飞书款式身份配置

This commit is contained in:
2026-09-07 15:01:04 +08:00
parent 92289b17ed
commit 485e77ee74
13 changed files with 1153 additions and 135 deletions
@@ -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() == ["完整款"]
+102
View File
@@ -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"] == []
+102
View File
@@ -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"] = [