update
This commit is contained in:
parent
2992a2631c
commit
853ffcf5d0
|
|
@ -65,7 +65,7 @@
|
||||||
- `ldap_verify_tls`: 是否校验证书,默认 `true`。
|
- `ldap_verify_tls`: 是否校验证书,默认 `true`。
|
||||||
- `proxy_alias_domain`: 生成 `smtp:<sAMAccountName>@domain` 别名时使用的域名。
|
- `proxy_alias_domain`: 生成 `smtp:<sAMAccountName>@domain` 别名时使用的域名。
|
||||||
- `department_code_ad_attribute`: 部门编码写入的 AD 属性,默认 `departmentNumber`。
|
- `department_code_ad_attribute`: 部门编码写入的 AD 属性,默认 `departmentNumber`。
|
||||||
- `street_address_key`: 具体地址字段编码,默认 `extgzddxx_606508_618643707`,写入 AD 的 `streetAddress`。
|
- `street_address_key`: 具体地址字段编码,默认 `WorkSpacevalue`,写入 AD 的 `streetAddress`;取不到时会兜底尝试 `extgzddxx_606508_618643707`。
|
||||||
- `default_company`: 固定公司名;不传时尝试取 EHR 根组织名称。
|
- `default_company`: 固定公司名;不传时尝试取 EHR 根组织名称。
|
||||||
- `location_mappings`: 工作地点到 AD 国家、省、市字段的映射。
|
- `location_mappings`: 工作地点到 AD 国家、省、市字段的映射。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ logger = logging.getLogger("connecthub.extensions.sync_ehr_to_ad")
|
||||||
|
|
||||||
_EHR_AD_ACCOUNT_KEY = "extADAccountName_606508_511687157"
|
_EHR_AD_ACCOUNT_KEY = "extADAccountName_606508_511687157"
|
||||||
_EHR_WORK_LOCATION_TEXT_KEY = "extgzddxx1_606508_892394263Text"
|
_EHR_WORK_LOCATION_TEXT_KEY = "extgzddxx1_606508_892394263Text"
|
||||||
_EHR_STREET_ADDRESS_KEY = "extgzddxx_606508_618643707"
|
_EHR_STREET_ADDRESS_KEY = "WorkSpacevalue"
|
||||||
|
_EHR_STREET_ADDRESS_FALLBACK_KEYS = ("extgzddxx_606508_618643707",)
|
||||||
|
|
||||||
|
|
||||||
def _to_bool_or_none(v: Any) -> bool | None:
|
def _to_bool_or_none(v: Any) -> bool | None:
|
||||||
|
|
@ -38,17 +39,12 @@ def _to_int_safe(v: Any) -> int:
|
||||||
|
|
||||||
def _scalar_value(raw: Any) -> str:
|
def _scalar_value(raw: Any) -> str:
|
||||||
if isinstance(raw, dict):
|
if isinstance(raw, dict):
|
||||||
val = None
|
val = raw.get("value")
|
||||||
for value_key in ("value", "showValue", "text", "Text", "displayValue", "DisplayValue"):
|
if val is None or str(val).strip() == "":
|
||||||
val = raw.get(value_key)
|
val = raw.get("showValue")
|
||||||
if val is not None and str(val).strip() != "":
|
|
||||||
break
|
|
||||||
if isinstance(val, (dict, list, tuple, set)):
|
|
||||||
return _scalar_value(val)
|
|
||||||
return str(val or "").strip()
|
return str(val or "").strip()
|
||||||
if isinstance(raw, (list, tuple, set)):
|
if isinstance(raw, (list, tuple, set)):
|
||||||
values = [_scalar_value(x) for x in raw]
|
return "\n".join([str(x).strip() for x in raw if x is not None and str(x).strip() != ""])
|
||||||
return "\n".join([x for x in values if x])
|
|
||||||
return str(raw or "").strip()
|
return str(raw or "").strip()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -75,76 +71,6 @@ def _translate_value(node: dict[str, Any], key: str | None) -> str:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _candidate_keys(key: str | None) -> list[str]:
|
|
||||||
if not key:
|
|
||||||
return []
|
|
||||||
candidates = [key, f"{key}Text"]
|
|
||||||
if key.endswith("Text"):
|
|
||||||
candidates.insert(0, key)
|
|
||||||
candidates.append(key[:-4])
|
|
||||||
out: list[str] = []
|
|
||||||
seen: set[str] = set()
|
|
||||||
for candidate in candidates:
|
|
||||||
s = str(candidate or "").strip()
|
|
||||||
if s and s not in seen:
|
|
||||||
seen.add(s)
|
|
||||||
out.append(s)
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
def _deep_field_value(node: Any, key: str | None) -> str:
|
|
||||||
candidates = set(_candidate_keys(key))
|
|
||||||
if not candidates:
|
|
||||||
return ""
|
|
||||||
if isinstance(node, dict):
|
|
||||||
for candidate in candidates:
|
|
||||||
if candidate in node:
|
|
||||||
s = _scalar_value(node.get(candidate))
|
|
||||||
if s:
|
|
||||||
return s
|
|
||||||
for value in node.values():
|
|
||||||
s = _deep_field_value(value, key)
|
|
||||||
if s:
|
|
||||||
return s
|
|
||||||
elif isinstance(node, list):
|
|
||||||
for value in node:
|
|
||||||
s = _deep_field_value(value, key)
|
|
||||||
if s:
|
|
||||||
return s
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def _collect_matching_keys(node: Any, keyword: str, *, limit: int = 30) -> list[str]:
|
|
||||||
matches: list[str] = []
|
|
||||||
seen: set[str] = set()
|
|
||||||
needle = str(keyword or "").lower()
|
|
||||||
if not needle:
|
|
||||||
return matches
|
|
||||||
|
|
||||||
def walk(current: Any) -> None:
|
|
||||||
if len(matches) >= limit:
|
|
||||||
return
|
|
||||||
if isinstance(current, dict):
|
|
||||||
for k, v in current.items():
|
|
||||||
key = str(k)
|
|
||||||
if needle in key.lower() and key not in seen:
|
|
||||||
seen.add(key)
|
|
||||||
matches.append(key)
|
|
||||||
if len(matches) >= limit:
|
|
||||||
return
|
|
||||||
walk(v)
|
|
||||||
if len(matches) >= limit:
|
|
||||||
return
|
|
||||||
elif isinstance(current, list):
|
|
||||||
for v in current:
|
|
||||||
walk(v)
|
|
||||||
if len(matches) >= limit:
|
|
||||||
return
|
|
||||||
|
|
||||||
walk(node)
|
|
||||||
return matches
|
|
||||||
|
|
||||||
|
|
||||||
def _field_value(item: dict[str, Any], key: str) -> str:
|
def _field_value(item: dict[str, Any], key: str) -> str:
|
||||||
emp = item.get("employeeInfo") or {}
|
emp = item.get("employeeInfo") or {}
|
||||||
rec = item.get("recordInfo") or {}
|
rec = item.get("recordInfo") or {}
|
||||||
|
|
@ -160,7 +86,16 @@ def _field_value(item: dict[str, Any], key: str) -> str:
|
||||||
s = _custom_prop_value(node.get("customProperties"), key)
|
s = _custom_prop_value(node.get("customProperties"), key)
|
||||||
if s:
|
if s:
|
||||||
return s
|
return s
|
||||||
s = _deep_field_value(node, key)
|
for child in node.values():
|
||||||
|
if not isinstance(child, dict):
|
||||||
|
continue
|
||||||
|
s = _scalar_value(child.get(key))
|
||||||
|
if s:
|
||||||
|
return s
|
||||||
|
s = _translate_value(child, key)
|
||||||
|
if s:
|
||||||
|
return s
|
||||||
|
s = _custom_prop_value(child.get("customProperties"), key)
|
||||||
if s:
|
if s:
|
||||||
return s
|
return s
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -620,14 +555,19 @@ class SyncEhrToAdUserJob(BaseJob):
|
||||||
office = _field_translate_or_value(item, "Place")
|
office = _field_translate_or_value(item, "Place")
|
||||||
workplace_text = _field_translate_or_value(item, work_location_text_key)
|
workplace_text = _field_translate_or_value(item, work_location_text_key)
|
||||||
street_address = _field_value(item, street_address_key)
|
street_address = _field_value(item, street_address_key)
|
||||||
|
if not street_address:
|
||||||
|
for fallback_key in _EHR_STREET_ADDRESS_FALLBACK_KEYS:
|
||||||
|
if fallback_key == street_address_key:
|
||||||
|
continue
|
||||||
|
street_address = _field_value(item, fallback_key)
|
||||||
|
if street_address:
|
||||||
|
break
|
||||||
if verbose_trace:
|
if verbose_trace:
|
||||||
address_candidate_keys = _collect_matching_keys(item, "extgzddxx") if not street_address else []
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"AD 地址字段解析:sam=%s street_address_key=%s streetAddress=%r candidate_keys=%s",
|
"AD 地址字段解析:sam=%s street_address_key=%s streetAddress=%r",
|
||||||
sam,
|
sam,
|
||||||
street_address_key,
|
street_address_key,
|
||||||
street_address,
|
street_address,
|
||||||
address_candidate_keys,
|
|
||||||
)
|
)
|
||||||
company = default_company or _root_org_name(org, org_by_oid)
|
company = default_company or _root_org_name(org, org_by_oid)
|
||||||
location_attrs = _location_from_workplace(workplace_text or office, location_mappings)
|
location_attrs = _location_from_workplace(workplace_text or office, location_mappings)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue