整体同步代码

This commit is contained in:
strepsiades
2026-03-26 14:10:47 +08:00
parent 4b81b83fd8
commit f50dc1aed0
9 changed files with 188 additions and 32 deletions
@@ -2,7 +2,7 @@ from __future__ import annotations
import copy
import inspect
from typing import Any, Dict, Optional
from typing import Any, Dict, Mapping, Optional
async def build_data_id_normalization_map(
@@ -39,18 +39,42 @@ def is_id_like_field(field_name: Optional[str]) -> bool:
return lowered.endswith("_id") or lowered.endswith("_ids")
def _strip_ignored_list_item_fields(
field_name: Optional[str],
value: Any,
ignore_list_item_fields: Mapping[str, list[str]],
) -> Any:
if not isinstance(value, list):
return value
ignored_keys = set(ignore_list_item_fields.get(field_name or "", []))
if not ignored_keys:
return value
normalized_items = []
for item in value:
if isinstance(item, dict):
normalized_items.append({key: nested for key, nested in item.items() if key not in ignored_keys})
else:
normalized_items.append(item)
return normalized_items
def normalize_compare_value(
field_name: Optional[str],
value: Any,
data_id_map: Dict[str, str],
ignore_list_item_fields: Optional[Mapping[str, list[str]]] = None,
) -> Any:
ignored_list_fields = ignore_list_item_fields or {}
if isinstance(value, dict):
return {
key: normalize_compare_value(key, nested, data_id_map)
key: normalize_compare_value(key, nested, data_id_map, ignored_list_fields)
for key, nested in value.items()
}
if isinstance(value, list):
return [normalize_compare_value(field_name, item, data_id_map) for item in value]
value = _strip_ignored_list_item_fields(field_name, value, ignored_list_fields)
return [normalize_compare_value(field_name, item, data_id_map, ignored_list_fields) for item in value]
if value == "":
value = None
@@ -64,6 +88,7 @@ def normalized_data_for_compare(
data: Optional[Dict[str, Any]],
data_id_map: Optional[Dict[str, str]] = None,
ignore_fields: Optional[set[str]] = None,
ignore_list_item_fields: Optional[Mapping[str, list[str]]] = None,
) -> Dict[str, Any]:
payload = copy.deepcopy(data or {})
payload.pop("id", None)
@@ -71,7 +96,7 @@ def normalized_data_for_compare(
ignored = ignore_fields or set()
mapping = data_id_map or {}
return {
key: normalize_compare_value(key, value, mapping)
key: normalize_compare_value(key, value, mapping, ignore_list_item_fields)
for key, value in payload.items()
if key not in ignored
}
@@ -55,15 +55,17 @@ def default_needs_update(
return False
ignore_fields = set(strategy.config.compare_ignore_fields)
normalized_source = strategy.normalize_compare_payload(
normalized_source = normalized_data_for_compare(
source_data,
data_id_map,
ignore_fields=ignore_fields,
ignore_list_item_fields=strategy.config.post_check_ignore_list_item_fields,
)
normalized_target = strategy.normalize_compare_payload(
normalized_target = normalized_data_for_compare(
target_data,
data_id_map,
ignore_fields=ignore_fields,
ignore_list_item_fields=strategy.config.post_check_ignore_list_item_fields,
)
diff_map = strategy.get_schema_diff_validator().compare_payloads(
normalized_source,
@@ -85,26 +87,28 @@ def resolve_needs_update_check(strategy) -> Callable:
def _collect_diff_descriptions(
strategy,
source_data,
target_data,
*,
data_id_map,
ignore_fields: set[str],
ignore_list_item_fields: dict[str, list[str]],
max_items: int = 8,
) -> List[str]:
if source_data is None or target_data is None:
return []
normalized_source = strategy.normalize_compare_payload(
normalized_source = normalized_data_for_compare(
source_data,
data_id_map,
ignore_fields=ignore_fields,
ignore_list_item_fields=ignore_list_item_fields,
)
normalized_target = strategy.normalize_compare_payload(
normalized_target = normalized_data_for_compare(
target_data,
data_id_map,
ignore_fields=ignore_fields,
ignore_list_item_fields=ignore_list_item_fields,
)
diff_map = collect_payload_diffs(
normalized_source,
@@ -202,11 +206,11 @@ async def add_update_action(
has_diff = bool(should_update)
if has_diff:
diff_descriptions = _collect_diff_descriptions(
strategy,
data,
target_node.get_data(),
data_id_map=data_id_map or {},
ignore_fields=set(strategy.config.compare_ignore_fields),
ignore_list_item_fields=dict(strategy.config.post_check_ignore_list_item_fields),
)
if steps_changed:
@@ -318,6 +322,7 @@ async def run_update(strategy) -> List["SyncNode"]:
f" (Pull, field_direction_key={field_key})"
)
strategy.emit_schema_diff_report()
return updated_nodes
# ── 无字段级覆盖:原有逻辑不变 ──
@@ -350,4 +355,5 @@ async def run_update(strategy) -> List["SyncNode"]:
f"[{strategy.node_type}] update_direction=BIDIRECTIONAL requires custom implementation"
)
strategy.emit_schema_diff_report()
return updated_nodes