Files
ecm_sync_system/sync_state_machine/sync_system/utils.py
T
strepsiades 4a9c444b10 first commit
2026-03-09 16:31:42 +08:00

129 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
同步系统工具函数模块
提供业务键提取等通用功能。
"""
from typing import List, Tuple, Optional, Any, Dict
import logging
logger = logging.getLogger(__name__)
def is_id_field(field_name: str) -> bool:
"""
判断字段是否为ID字段。
ID字段的判断规则:
- 以 _id 结尾(如 project_id, company_id
- 字段名为 id
Args:
field_name: 字段名
Returns:
如果是ID字段返回True
"""
return field_name.endswith("_id") or field_name == "id"
def extract_biz_key_dict(
data: Any,
field_names: List[str]
) -> Optional[Dict[str, str]]:
"""
从数据对象提取业务键字段,返回字典格式。
Args:
data: 数据对象(dict或pydantic model
field_names: 业务键字段名列表
Returns:
业务键字段字典,如果任何必需字段缺失则返回None
"""
if not field_names or not data:
return None
# 转换为字典
if isinstance(data, dict):
d = data
else:
d = data.model_dump()
key_dict = {}
for field_name in field_names:
# 区分:字段不存在 vs 字段存在但值为None
if field_name not in d:
return None # 字段不存在,视为缺失
value = d.get(field_name)
# 值为None时保留为空字符串,允许作为有效业务键值
key_dict[field_name] = str(value) if value is not None else ""
return key_dict if key_dict else None
def dict_to_biz_key_tuple(
key_dict: Dict[str, str],
field_names: List[str]
) -> Tuple:
"""
将业务键字典转换为tuple(按field_names顺序)。
Args:
key_dict: 业务键字段字典
field_names: 字段名顺序
Returns:
业务键tuple
"""
return tuple(key_dict[fn] for fn in field_names)
async def resolve_id_fields_in_key_dict(
key_dict: Dict[str, str],
id_resolver,
node_type_hints: Dict[str, str]
) -> tuple[Optional[Dict[str, str]], Optional[str]]:
"""
解析业务键字典中的ID字段,将本地ID转换为远程ID。
只处理ID字段(以_id结尾或为id),其他字段保持不变。
特殊处理:
- 如果 ID 字段值为空字符串 "",直接保留(空值也是有效的业务键值)
- 只有非空 ID 才需要解析
Args:
key_dict: 业务键字典
id_resolver: ID解析器实例
node_type_hints: 字段名到node_type的映射
Returns:
(resolved_dict, error_msg): 成功时返回 (解析后的字典, None),失败时返回 (None, 错误描述)
"""
resolved_dict = {}
for field_name, value in key_dict.items():
if is_id_field(field_name):
# 这是ID字段
if value == "" or value is None:
# 空值直接保留,不尝试解析
# 空值也是有效的业务键值,只要两边一致就能匹配
resolved_dict[field_name] = value or ""
else:
# 非空ID需要解析
node_type = node_type_hints.get(field_name)
remote_id = await id_resolver.resolve_id(
value, node_type_hint=node_type
)
if remote_id is None:
# ID解析失败,返回详细错误信息
node_type_info = f"({node_type})" if node_type else "(未知类型)"
error_msg = f"字段 {field_name}{node_type_info} 的 ID '{value}' 解析失败(依赖节点未绑定)"
return None, error_msg
resolved_dict[field_name] = remote_id
else:
# 非ID字段,直接使用原值
resolved_dict[field_name] = value
return resolved_dict, None