This commit is contained in:
Marsway 2026-04-30 15:13:44 +08:00
parent ea5e6cca0e
commit d12cc71bd4
1 changed files with 82 additions and 4 deletions

View File

@ -173,6 +173,50 @@ def _proxy_addresses(email: str, sam: str, alias_domain: str | None) -> list[str
return out
def _target_values(value: Any) -> list[str]:
if isinstance(value, (list, tuple, set)):
return [str(x).strip() for x in value if x is not None and str(x).strip() != ""]
if value is None or str(value).strip() == "":
return []
return [str(value).strip()]
def _ad_values(attrs: dict[str, Any], attr_name: str) -> list[str]:
raw = attrs.get(attr_name)
if raw is None:
lower_attr = attr_name.lower()
for key, value in attrs.items():
if str(key).lower() == lower_attr:
raw = value
break
if isinstance(raw, (list, tuple, set)):
return [str(x).strip() for x in raw if x is not None and str(x).strip() != ""]
if raw is None or str(raw).strip() == "":
return []
return [str(raw).strip()]
def _canonical_values(attr_name: str, values: list[str]) -> list[str]:
if attr_name in {"proxyAddresses"}:
return sorted(values)
if attr_name in {"manager", "mail", "sAMAccountName"}:
return sorted([v.lower() for v in values])
return sorted(values)
def _diff_ad_attributes(ad_attrs: dict[str, Any], target_attrs: dict[str, Any]) -> dict[str, Any]:
diff: dict[str, Any] = {}
for attr_name, target_value in target_attrs.items():
wanted = _target_values(target_value)
if not wanted:
continue
current = _ad_values(ad_attrs, attr_name)
if _canonical_values(attr_name, current) == _canonical_values(attr_name, wanted):
continue
diff[attr_name] = target_value
return diff
def _location_from_workplace(workplace: str, mappings: dict[str, Any] | None = None) -> dict[str, Any]:
text = str(workplace or "").strip()
defaults: dict[str, dict[str, Any]] = {
@ -409,10 +453,36 @@ class SyncEhrToAdUserJob(BaseJob):
processed = 0
updated = 0
skipped_unchanged = 0
skipped_missing_sam = 0
skipped_not_found_ad = 0
failed = 0
manager_dn_cache: dict[str, str] = {}
ad_compare_attributes = [
"sAMAccountName",
"givenName",
"sn",
"title",
"department",
"manager",
"proxyAddresses",
"co",
"c",
"countryCode",
"company",
"displayName",
"mail",
"employeeID",
"employeeType",
"mobile",
"physicalDeliveryOfficeName",
"postalCode",
"st",
"l",
"streetAddress",
]
if department_code_attr:
ad_compare_attributes.append(department_code_attr)
for sam_key, item in users_by_sam.items():
emp = item.get("employeeInfo") or {}
@ -429,7 +499,7 @@ class SyncEhrToAdUserJob(BaseJob):
processed += 1
try:
ad_user = ad.find_user(sam)
ad_user = ad.find_user(sam, attributes=ad_compare_attributes)
if not ad_user:
skipped_not_found_ad += 1
logger.warning("AD 用户不存在跳过sAMAccountName=%s", sam)
@ -493,15 +563,22 @@ class SyncEhrToAdUserJob(BaseJob):
if department_code_attr and department_code:
attributes[department_code_attr] = department_code
changed = ad.modify_user(str(ad_user["dn"]), attributes, dry_run=dry_run)
diff_attributes = _diff_ad_attributes(ad_user.get("attributes") or {}, attributes)
if not diff_attributes:
skipped_unchanged += 1
if verbose_trace:
logger.info("AD 用户信息一致跳过更新sam=%s dn=%s", sam, ad_user["dn"])
continue
changed = ad.modify_user(str(ad_user["dn"]), diff_attributes, dry_run=dry_run)
if changed:
updated += 1
if verbose_trace:
logger.info(
"AD 用户同步完成sam=%s dn=%s attrs=%s",
"AD 用户同步完成sam=%s dn=%s changed_attrs=%s",
sam,
ad_user["dn"],
json.dumps({k: v for k, v in attributes.items() if v}, ensure_ascii=False, default=str),
json.dumps({k: v for k, v in diff_attributes.items() if v}, ensure_ascii=False, default=str),
)
except Exception as e: # noqa: BLE001
failed += 1
@ -514,6 +591,7 @@ class SyncEhrToAdUserJob(BaseJob):
"ehr_current_users_with_ad_account": len(users_by_sam),
"processed": processed,
"updated": updated,
"skipped_unchanged": skipped_unchanged,
"skipped_missing_sam": skipped_missing_sam,
"skipped_not_found_ad": skipped_not_found_ad,
"failed": failed,