From 6c804b7b840686190e0fd3e65f66b5f32c20962c Mon Sep 17 00:00:00 2001 From: Marsway Date: Mon, 30 Mar 2026 17:19:14 +0800 Subject: [PATCH] update --- extensions/sync_ehr_leaves_to_oa/job.py | 96 ++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/extensions/sync_ehr_leaves_to_oa/job.py b/extensions/sync_ehr_leaves_to_oa/job.py index 33d53f9..2339798 100644 --- a/extensions/sync_ehr_leaves_to_oa/job.py +++ b/extensions/sync_ehr_leaves_to_oa/job.py @@ -80,6 +80,66 @@ def _to_int_safe(v: Any) -> int: return 0 +def _cell_value(cell: Any) -> str: + if isinstance(cell, dict): + v = cell.get("value") + if v is None or str(v).strip() == "": + v = cell.get("showValue") + return str(v or "").strip() + return str(cell or "").strip() + + +def _cell_show_value(cell: Any) -> str: + if isinstance(cell, dict): + return str(cell.get("showValue") or "").strip() + return "" + + +def _extract_oa_row_id_and_fields(row: dict[str, Any]) -> tuple[int | None, dict[str, Any]]: + field_map: dict[str, Any] = {} + row_id: int | None = None + + master = row.get("masterData") + if isinstance(master, dict): + for k, v in master.items(): + if isinstance(k, str) and k.startswith("field"): + field_map[k] = v + for candidate in (row.get("id"), row.get("masterDataId"), master.get("id")): + if candidate is None: + continue + rid = _to_int_safe(candidate) + if rid > 0: + row_id = rid + break + + master_table = row.get("masterTable") + if isinstance(master_table, dict): + record = master_table.get("record") + if isinstance(record, dict): + fields = record.get("fields") + if isinstance(fields, list): + for fld in fields: + if not isinstance(fld, dict): + continue + name = str(fld.get("name") or "").strip() + if name: + field_map[name] = fld + if row_id is None: + rid = _to_int_safe(record.get("id")) + if rid > 0: + row_id = rid + + row_fields = row.get("fields") + if isinstance(row_fields, list): + for fld in row_fields: + if not isinstance(fld, dict): + continue + name = str(fld.get("name") or "").strip() + if name: + field_map[name] = fld + return row_id, field_map + + def _normalize_decimal_1(v: Any) -> str: return _decimal_to_str(_to_decimal(v)) @@ -207,6 +267,9 @@ class SyncEhrLeavesToOaMonthJob(BaseJob): field_leave_days = display_to_code[display_leave_days] field_name = display_to_code.get(display_name) master_table_name = _OA_SQLSERVER_TABLE + rows = form.get("data") or [] + if not isinstance(rows, list): + rows = [] vacations = ehr.get_vacations_in_date_range( start_date=month_start, @@ -270,7 +333,32 @@ class SyncEhrLeavesToOaMonthJob(BaseJob): if staff_name and key not in name_map: name_map[key] = staff_name - existing_row_map = ehr.get_oa_rows_by_job_and_date( + existing_row_map_by_export: dict[tuple[str, str], dict[str, str]] = {} + for row in rows: + if not isinstance(row, dict): + continue + rid, field_map = _extract_oa_row_id_and_fields(row) + job_no = _cell_value(field_map.get(field_job_no)) + leave_date = _cell_show_value(field_map.get(field_leave_date)) or _date_only(_cell_value(field_map.get(field_leave_date))) + if not job_no or not leave_date: + continue + leave_days_val = _cell_value(field_map.get(field_leave_days)) + name_val = _cell_value(field_map.get(field_name)) if field_name else "" + existing_row_map_by_export[(job_no, leave_date)] = { + "id": str(rid or ""), + "job_no": job_no, + "leave_date": leave_date, + "leave_days": leave_days_val, + "name": name_val, + } + logger.info( + "OA export 现有记录索引完成:month=%s~%s indexed=%s", + month_start.isoformat(), + month_end.isoformat(), + len(existing_row_map_by_export), + ) + + existing_row_map_by_sql = ehr.get_oa_rows_by_job_and_date( table_name=_OA_SQLSERVER_TABLE, schema=_OA_SQLSERVER_SCHEMA, job_no_column=field_job_no, @@ -280,6 +368,9 @@ class SyncEhrLeavesToOaMonthJob(BaseJob): start_date=month_start, end_date=month_end, ) + # 以 export 为主(日期 showValue 更稳定),SQL 兜底补齐 + existing_row_map = dict(existing_row_map_by_sql) + existing_row_map.update(existing_row_map_by_export) data_list: list[dict[str, Any]] = [] to_update = 0 @@ -382,13 +473,14 @@ class SyncEhrLeavesToOaMonthJob(BaseJob): failed_data[str(k)] = str(v) logger.info( - "EHR 请假月度同步完成:month=%s~%s vacations=%s aggregated=%s to_update=%s to_insert=%s success=%s failed=%s", + "EHR 请假月度同步完成:month=%s~%s vacations=%s aggregated=%s to_update=%s to_insert=%s skipped_unchanged=%s success=%s failed=%s", month_start.isoformat(), month_end.isoformat(), len(vacations), len(agg), to_update, to_insert, + skipped_unchanged, success_count, failed_count, )