oa_resignation_kit_sync.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """离职套件:审批通过后回写发起人 last_work_day。"""
  2. from __future__ import annotations
  3. import json
  4. import logging
  5. import re
  6. from datetime import date, datetime
  7. from typing import Any, Dict, List, Optional
  8. from sqlalchemy.orm import Session
  9. from app.models.models import OaAppVersion, OaInstance, SysUser
  10. logger = logging.getLogger(__name__)
  11. _YMD = re.compile(r"^(\d{4})-(\d{2})-(\d{2})$")
  12. def _loads(raw: Any, default: Any = None) -> Any:
  13. if raw is None or raw == "":
  14. return default
  15. if isinstance(raw, (dict, list)):
  16. return raw
  17. try:
  18. return json.loads(raw)
  19. except Exception:
  20. return default
  21. def _extract_rules(form_schema: Any) -> List[Dict[str, Any]]:
  22. if form_schema is None:
  23. return []
  24. if isinstance(form_schema, list):
  25. return [r for r in form_schema if isinstance(r, dict)]
  26. if isinstance(form_schema, dict):
  27. rules = form_schema.get("rule") or form_schema.get("rules") or form_schema.get("fields")
  28. if isinstance(rules, list):
  29. return [r for r in rules if isinstance(r, dict)]
  30. if form_schema.get("type") or form_schema.get("field"):
  31. return [form_schema]
  32. return []
  33. def _walk(rules: List[Dict[str, Any]], out: List[Dict[str, Any]]) -> None:
  34. for rule in rules:
  35. out.append(rule)
  36. children = rule.get("children") or (rule.get("props") or {}).get("rule") or []
  37. if isinstance(children, list):
  38. _walk([c for c in children if isinstance(c, dict)], out)
  39. def _last_work_day_field(kit: Dict[str, Any]) -> Optional[str]:
  40. props = kit.get("props") if isinstance(kit.get("props"), dict) else {}
  41. if props.get("lastWorkDayField"):
  42. return str(props["lastWorkDayField"])
  43. children = kit.get("children") or props.get("rule") or []
  44. if isinstance(children, list):
  45. for c in children:
  46. if not isinstance(c, dict) or not c.get("field"):
  47. continue
  48. cprops = c.get("props") if isinstance(c.get("props"), dict) else {}
  49. if str(cprops.get("_fc_kit_field") or "") == "lastWorkDay":
  50. return str(c["field"])
  51. return None
  52. def _parse_ymd(value: Any) -> Optional[date]:
  53. if value is None or value == "":
  54. return None
  55. if isinstance(value, datetime):
  56. return value.date()
  57. if isinstance(value, date):
  58. return value
  59. s = str(value).strip()[:10]
  60. if not _YMD.match(s):
  61. return None
  62. try:
  63. return datetime.strptime(s, "%Y-%m-%d").date()
  64. except ValueError:
  65. return None
  66. def extract_last_work_day(schema: Any, form_data: Dict[str, Any]) -> Optional[date]:
  67. flat: List[Dict[str, Any]] = []
  68. _walk(_extract_rules(schema), flat)
  69. for rule in flat:
  70. if str(rule.get("type") or "") != "resignationKit":
  71. continue
  72. field = _last_work_day_field(rule)
  73. if not field:
  74. continue
  75. parsed = _parse_ymd(form_data.get(field))
  76. if parsed is not None:
  77. return parsed
  78. return None
  79. def apply_resignation_kit_user_sync(db: Session, inst: OaInstance) -> None:
  80. try:
  81. if not inst or not getattr(inst, "id", None):
  82. return
  83. form_data = _loads(getattr(inst, "form_data_json", None), {}) or {}
  84. if not isinstance(form_data, dict):
  85. form_data = {}
  86. ver = (
  87. db.query(OaAppVersion)
  88. .filter(OaAppVersion.id == inst.app_version_id, OaAppVersion.del_flag == 0)
  89. .first()
  90. )
  91. schema = _loads(getattr(ver, "form_schema_json", None) if ver else None, {}) or {}
  92. last_day = extract_last_work_day(schema, form_data)
  93. if last_day is None:
  94. return
  95. user = (
  96. db.query(SysUser)
  97. .filter(
  98. SysUser.id == inst.initiator_user_id,
  99. SysUser.tenant_no == inst.tenant_no,
  100. SysUser.del_flag == 0,
  101. )
  102. .first()
  103. )
  104. if not user:
  105. logger.warning(
  106. "resignation kit sync: initiator not found id=%s",
  107. inst.initiator_user_id,
  108. )
  109. return
  110. user.last_work_day = last_day
  111. user.update_time = datetime.now()
  112. db.add(user)
  113. except Exception:
  114. logger.exception(
  115. "resignation kit user sync failed instance=%s", getattr(inst, "id", None)
  116. )