fixing
This commit is contained in:
parent
ecc79128f6
commit
473343f548
|
|
@ -59,6 +59,26 @@ def _to_bool_or_none(v: Any) -> bool | None:
|
||||||
return bool(v)
|
return bool(v)
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_job_no(v: Any) -> str:
|
||||||
|
"""
|
||||||
|
工号标准化:
|
||||||
|
- 去首尾空白、去内部空格
|
||||||
|
- 数值型字符串如 123.0 -> 123(常见于表单数字字段)
|
||||||
|
- 统一大写,便于大小写不敏感匹配
|
||||||
|
"""
|
||||||
|
s = str(v or "").strip()
|
||||||
|
if not s:
|
||||||
|
return ""
|
||||||
|
s = s.replace(" ", "")
|
||||||
|
try:
|
||||||
|
if "." in s and s.endswith(".0"):
|
||||||
|
i = int(float(s))
|
||||||
|
s = str(i)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return s.upper()
|
||||||
|
|
||||||
|
|
||||||
def _choose_better_record(current: dict[str, Any], candidate: dict[str, Any]) -> dict[str, Any]:
|
def _choose_better_record(current: dict[str, Any], candidate: dict[str, Any]) -> dict[str, Any]:
|
||||||
def _score(item: dict[str, Any]) -> str:
|
def _score(item: dict[str, Any]) -> str:
|
||||||
record = item.get("recordInfo") or {}
|
record = item.get("recordInfo") or {}
|
||||||
|
|
@ -127,6 +147,9 @@ class SyncEhrToOaFormJob(BaseJob):
|
||||||
|
|
||||||
rd_attr_custom_key = str(params.get("rd_attr_custom_key") or "").strip() or None
|
rd_attr_custom_key = str(params.get("rd_attr_custom_key") or "").strip() or None
|
||||||
domain_custom_key = str(params.get("domain_account_custom_key") or "").strip() or None
|
domain_custom_key = str(params.get("domain_account_custom_key") or "").strip() or None
|
||||||
|
verbose_trace = _to_bool_or_none(params.get("verbose_trace"))
|
||||||
|
if verbose_trace is None:
|
||||||
|
verbose_trace = True
|
||||||
|
|
||||||
seeyon = SeeyonClient(base_url=oa_base_url, rest_user=rest_user, rest_password=rest_password, loginName=login_name)
|
seeyon = SeeyonClient(base_url=oa_base_url, rest_user=rest_user, rest_password=rest_password, loginName=login_name)
|
||||||
ehr = SyncEhrToOaApi(secret_params={"app_key": app_key, "app_secret": app_secret})
|
ehr = SyncEhrToOaApi(secret_params={"app_key": app_key, "app_secret": app_secret})
|
||||||
|
|
@ -152,6 +175,7 @@ class SyncEhrToOaFormJob(BaseJob):
|
||||||
|
|
||||||
# 3) 员工按工号归并(同工号保留“最新”记录)
|
# 3) 员工按工号归并(同工号保留“最新”记录)
|
||||||
ehr_by_job_no: dict[str, dict[str, Any]] = {}
|
ehr_by_job_no: dict[str, dict[str, Any]] = {}
|
||||||
|
ehr_by_job_no_norm: dict[str, dict[str, Any]] = {}
|
||||||
for item in emp_rows:
|
for item in emp_rows:
|
||||||
if not isinstance(item, dict):
|
if not isinstance(item, dict):
|
||||||
continue
|
continue
|
||||||
|
|
@ -163,12 +187,20 @@ class SyncEhrToOaFormJob(BaseJob):
|
||||||
continue
|
continue
|
||||||
existing = ehr_by_job_no.get(job_no)
|
existing = ehr_by_job_no.get(job_no)
|
||||||
ehr_by_job_no[job_no] = item if existing is None else _choose_better_record(existing, item)
|
ehr_by_job_no[job_no] = item if existing is None else _choose_better_record(existing, item)
|
||||||
|
job_no_norm = _normalize_job_no(job_no)
|
||||||
|
if job_no_norm:
|
||||||
|
ex2 = ehr_by_job_no_norm.get(job_no_norm)
|
||||||
|
ehr_by_job_no_norm[job_no_norm] = item if ex2 is None else _choose_better_record(ex2, item)
|
||||||
logger.info(
|
logger.info(
|
||||||
"EHR 数据准备完成:employee_rows=%s organization_rows=%s distinct_job_numbers=%s",
|
"EHR 数据准备完成:employee_rows=%s organization_rows=%s distinct_job_numbers=%s distinct_job_numbers_norm=%s",
|
||||||
len(emp_rows),
|
len(emp_rows),
|
||||||
len(org_rows),
|
len(org_rows),
|
||||||
len(ehr_by_job_no),
|
len(ehr_by_job_no),
|
||||||
|
len(ehr_by_job_no_norm),
|
||||||
)
|
)
|
||||||
|
if verbose_trace:
|
||||||
|
for job_no in list(ehr_by_job_no.keys()):
|
||||||
|
logger.info("EHR 工号明细:raw=%s norm=%s", job_no, _normalize_job_no(job_no))
|
||||||
|
|
||||||
# 4) 导出 OA 表单,建立字段映射 + 工号到记录ID映射
|
# 4) 导出 OA 表单,建立字段映射 + 工号到记录ID映射
|
||||||
exp_resp = seeyon.export_cap4_form_soap(
|
exp_resp = seeyon.export_cap4_form_soap(
|
||||||
|
|
@ -259,6 +291,7 @@ class SyncEhrToOaFormJob(BaseJob):
|
||||||
|
|
||||||
job_field_code = display_to_code["工号"]
|
job_field_code = display_to_code["工号"]
|
||||||
oa_id_by_job_no: dict[str, int] = {}
|
oa_id_by_job_no: dict[str, int] = {}
|
||||||
|
oa_id_by_job_no_norm: dict[str, int] = {}
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if not isinstance(row, dict):
|
if not isinstance(row, dict):
|
||||||
continue
|
continue
|
||||||
|
|
@ -281,16 +314,43 @@ class SyncEhrToOaFormJob(BaseJob):
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
oa_id_by_job_no[job_no] = row_id
|
oa_id_by_job_no[job_no] = row_id
|
||||||
logger.info("OA 工号索引完成:indexed_job_numbers=%s", len(oa_id_by_job_no))
|
job_no_norm = _normalize_job_no(job_no)
|
||||||
|
if job_no_norm:
|
||||||
|
oa_id_by_job_no_norm[job_no_norm] = row_id
|
||||||
|
logger.info(
|
||||||
|
"OA 工号索引完成:indexed_job_numbers=%s indexed_job_numbers_norm=%s",
|
||||||
|
len(oa_id_by_job_no),
|
||||||
|
len(oa_id_by_job_no_norm),
|
||||||
|
)
|
||||||
|
if verbose_trace:
|
||||||
|
for job_no, row_id in list(oa_id_by_job_no.items()):
|
||||||
|
logger.info("OA 工号索引明细:raw=%s norm=%s row_id=%s", job_no, _normalize_job_no(job_no), row_id)
|
||||||
|
|
||||||
# 5) 组装批量更新数据
|
# 5) 组装批量更新数据
|
||||||
data_list: list[dict[str, Any]] = []
|
data_list: list[dict[str, Any]] = []
|
||||||
not_found_in_oa = 0
|
not_found_in_oa = 0
|
||||||
|
unmatched_samples: list[str] = []
|
||||||
for job_no, item in ehr_by_job_no.items():
|
for job_no, item in ehr_by_job_no.items():
|
||||||
oa_record_id = oa_id_by_job_no.get(job_no)
|
oa_record_id = oa_id_by_job_no.get(job_no)
|
||||||
|
matched_by = "raw"
|
||||||
|
if oa_record_id is None:
|
||||||
|
oa_record_id = oa_id_by_job_no_norm.get(_normalize_job_no(job_no))
|
||||||
|
matched_by = "normalized"
|
||||||
if oa_record_id is None:
|
if oa_record_id is None:
|
||||||
not_found_in_oa += 1
|
not_found_in_oa += 1
|
||||||
|
if len(unmatched_samples) < 20:
|
||||||
|
unmatched_samples.append(job_no)
|
||||||
|
if verbose_trace:
|
||||||
|
logger.info("匹配失败:job_no=%s norm=%s", job_no, _normalize_job_no(job_no))
|
||||||
continue
|
continue
|
||||||
|
if verbose_trace:
|
||||||
|
logger.info(
|
||||||
|
"匹配成功:job_no=%s norm=%s row_id=%s matched_by=%s",
|
||||||
|
job_no,
|
||||||
|
_normalize_job_no(job_no),
|
||||||
|
oa_record_id,
|
||||||
|
matched_by,
|
||||||
|
)
|
||||||
|
|
||||||
emp = item.get("employeeInfo") or {}
|
emp = item.get("employeeInfo") or {}
|
||||||
rec = item.get("recordInfo") or {}
|
rec = item.get("recordInfo") or {}
|
||||||
|
|
@ -329,6 +389,10 @@ class SyncEhrToOaFormJob(BaseJob):
|
||||||
{"name": display_to_code["在离职"], "value": is_leaving, "showValue": is_leaving},
|
{"name": display_to_code["在离职"], "value": is_leaving, "showValue": is_leaving},
|
||||||
{"name": display_to_code["域账号"], "value": domain_account, "showValue": domain_account},
|
{"name": display_to_code["域账号"], "value": domain_account, "showValue": domain_account},
|
||||||
]
|
]
|
||||||
|
if verbose_trace:
|
||||||
|
logger.info("字段映射:job_no=%s row_id=%s", job_no, oa_record_id)
|
||||||
|
for fld in fields_payload:
|
||||||
|
logger.info("字段映射明细:job_no=%s field=%s value=%s", job_no, fld["name"], fld["value"])
|
||||||
|
|
||||||
data_list.append(
|
data_list.append(
|
||||||
{
|
{
|
||||||
|
|
@ -350,7 +414,8 @@ class SyncEhrToOaFormJob(BaseJob):
|
||||||
)
|
)
|
||||||
if not data_list:
|
if not data_list:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"No updates prepared for OA batch-update (check jobNumber matching between EHR and OA, and form field mapping)"
|
"No updates prepared for OA batch-update (check jobNumber matching between EHR and OA, and form field mapping). "
|
||||||
|
f"unmatched_sample={unmatched_samples}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 6) 分批执行 batch-update
|
# 6) 分批执行 batch-update
|
||||||
|
|
@ -360,6 +425,19 @@ class SyncEhrToOaFormJob(BaseJob):
|
||||||
do_trigger_bool = _to_bool_or_none(do_trigger)
|
do_trigger_bool = _to_bool_or_none(do_trigger)
|
||||||
for i in range(0, len(data_list), batch_size):
|
for i in range(0, len(data_list), batch_size):
|
||||||
chunk = data_list[i : i + batch_size]
|
chunk = data_list[i : i + batch_size]
|
||||||
|
if verbose_trace:
|
||||||
|
logger.info("批量更新尝试:chunk_index=%s chunk_size=%s", i // batch_size + 1, len(chunk))
|
||||||
|
for row in chunk:
|
||||||
|
try:
|
||||||
|
record = (((row or {}).get("masterTable") or {}).get("record") or {})
|
||||||
|
row_id = record.get("id")
|
||||||
|
fields = record.get("fields") or []
|
||||||
|
logger.info("批量更新行:row_id=%s fields_count=%s", row_id, len(fields))
|
||||||
|
for fld in fields:
|
||||||
|
if isinstance(fld, dict):
|
||||||
|
logger.info("批量更新字段:row_id=%s field=%s value=%s", row_id, fld.get("name"), fld.get("value"))
|
||||||
|
except Exception:
|
||||||
|
logger.info("批量更新行日志输出失败,已忽略")
|
||||||
resp = seeyon.batch_update_cap4_form_soap(
|
resp = seeyon.batch_update_cap4_form_soap(
|
||||||
formCode=oa_form_code,
|
formCode=oa_form_code,
|
||||||
loginName=oa_login_name,
|
loginName=oa_login_name,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue