from __future__ import annotations import json from typing import Any from .mappings import plate_order_related_field_definitions, plate_order_relation_control_map def _json_loads_safe(value: str) -> Any: try: return json.loads(value) except Exception: return value def normalize_mdy_value(value: Any) -> Any: """尽量把明道云返回的字符串 JSON 规范化为 Python 对象。 常见情况: - Relation/Attachment 可能返回字符串形式的 "[]" / "[{...}]" - 空值经常是 "[]"(字符串) """ if value is None: return None if isinstance(value, str): s = value.strip() if s == "[]": return [] if (s.startswith("[") and s.endswith("]")) or (s.startswith("{") and s.endswith("}")): return _json_loads_safe(s) return value return value def extract_relation_rowids(value: Any) -> list[str]: """从 Relation 字段的 value 中提取 rowid 列表。 兼容形态: - list[dict](常见:[{rowid,name,link}, ...]) - str(可能是 JSON 字符串数组,如 "[{...}]" 或 "[]";也可能是逗号分割 rowId) - dict(单条) """ value = normalize_mdy_value(value) if value is None: return [] if isinstance(value, list): out: list[str] = [] for item in value: if isinstance(item, dict): rid = item.get("rowid") or item.get("rowId") if rid: out.append(str(rid)) elif isinstance(item, str) and item.strip(): out.append(item.strip()) return out if isinstance(value, dict): rid = value.get("rowid") or value.get("rowId") return [str(rid)] if rid else [] if isinstance(value, str): s = value.strip() if not s or s == "[]": return [] # 兜底:逗号分割 return [x for x in (p.strip() for p in s.split(",")) if x] return [] def extract_plate_order_related_rowids(plate_order_row: dict[str, Any]) -> dict[str, list[str]]: """从开版主表行记录中提取各关联表的 rowid 列表。 返回: - {related_key: [rowid, ...], ...} """ result: dict[str, list[str]] = {} for control_id, related_key in plate_order_relation_control_map.items(): rowids = extract_relation_rowids(plate_order_row.get(control_id)) if rowids: result[related_key] = rowids return result def flatten_row_by_field_definitions( row: dict[str, Any], field_definitions: dict[str, dict[str, Any]], *, drop_types: set[str] | None = None, drop_names: set[str] | None = None, include_system_keys: bool = True, ) -> dict[str, Any]: """按字段定义把一条明道云 row 转成“可读 dict”。 - key 使用字段中文名(若缺失则回退 controlId) - value 做一定的 JSON 字符串规范化 - 默认包含系统字段:rowid/ctime/utime(若存在) """ drop_types = drop_types or set() drop_names = drop_names or set() out: dict[str, Any] = {} if include_system_keys: for k in ("rowid", "ctime", "utime"): if k in row: out[k] = row.get(k) for field_id, meta in field_definitions.items(): name = meta.get("name") or field_id ftype = meta.get("type") if ftype in drop_types: continue if name in drop_names: continue out[name] = normalize_mdy_value(row.get(field_id)) return out def flatten_plate_order_related_row(related_key: str, related_row: dict[str, Any]) -> dict[str, Any]: """将开版关联表的一条 row 扁平化为 dict(用于入库 staging.related)。 - 自动删除回溯 Relation 字段(开版管理/开发管理等) - 保留 rowid/ctime/utime(若存在) - 额外写入 source 信息,便于排查 """ meta = plate_order_related_field_definitions.get(related_key) or {} wsid = meta.get("worksheet_id") cn_name = meta.get("name") fields = meta.get("fields") or {} flat = flatten_row_by_field_definitions( related_row, fields, drop_types={"Relation"}, drop_names={"开版管理", "开发管理"}, include_system_keys=True, ) # 扁平化结构:[{key: val}, ...] 里的每个元素是一条记录 flat["source"] = related_key if cn_name: flat["source_name"] = cn_name if wsid: flat["worksheet_id"] = wsid return flat