Files
zkxw/backend/app/services/oa_resignation_kit_sync.py
T

136 lines
4.3 KiB
Python

"""离职套件:审批通过后回写发起人 last_work_day。"""
from __future__ import annotations
import json
import logging
import re
from datetime import date, datetime
from typing import Any, Dict, List, Optional
from sqlalchemy.orm import Session
from app.models.models import OaAppVersion, OaInstance, SysUser
logger = logging.getLogger(__name__)
_YMD = re.compile(r"^(\d{4})-(\d{2})-(\d{2})$")
def _loads(raw: Any, default: Any = None) -> Any:
if raw is None or raw == "":
return default
if isinstance(raw, (dict, list)):
return raw
try:
return json.loads(raw)
except Exception:
return default
def _extract_rules(form_schema: Any) -> List[Dict[str, Any]]:
if form_schema is None:
return []
if isinstance(form_schema, list):
return [r for r in form_schema if isinstance(r, dict)]
if isinstance(form_schema, dict):
rules = form_schema.get("rule") or form_schema.get("rules") or form_schema.get("fields")
if isinstance(rules, list):
return [r for r in rules if isinstance(r, dict)]
if form_schema.get("type") or form_schema.get("field"):
return [form_schema]
return []
def _walk(rules: List[Dict[str, Any]], out: List[Dict[str, Any]]) -> None:
for rule in rules:
out.append(rule)
children = rule.get("children") or (rule.get("props") or {}).get("rule") or []
if isinstance(children, list):
_walk([c for c in children if isinstance(c, dict)], out)
def _last_work_day_field(kit: Dict[str, Any]) -> Optional[str]:
props = kit.get("props") if isinstance(kit.get("props"), dict) else {}
if props.get("lastWorkDayField"):
return str(props["lastWorkDayField"])
children = kit.get("children") or props.get("rule") or []
if isinstance(children, list):
for c in children:
if not isinstance(c, dict) or not c.get("field"):
continue
cprops = c.get("props") if isinstance(c.get("props"), dict) else {}
if str(cprops.get("_fc_kit_field") or "") == "lastWorkDay":
return str(c["field"])
return None
def _parse_ymd(value: Any) -> Optional[date]:
if value is None or value == "":
return None
if isinstance(value, datetime):
return value.date()
if isinstance(value, date):
return value
s = str(value).strip()[:10]
if not _YMD.match(s):
return None
try:
return datetime.strptime(s, "%Y-%m-%d").date()
except ValueError:
return None
def extract_last_work_day(schema: Any, form_data: Dict[str, Any]) -> Optional[date]:
flat: List[Dict[str, Any]] = []
_walk(_extract_rules(schema), flat)
for rule in flat:
if str(rule.get("type") or "") != "resignationKit":
continue
field = _last_work_day_field(rule)
if not field:
continue
parsed = _parse_ymd(form_data.get(field))
if parsed is not None:
return parsed
return None
def apply_resignation_kit_user_sync(db: Session, inst: OaInstance) -> None:
try:
if not inst or not getattr(inst, "id", None):
return
form_data = _loads(getattr(inst, "form_data_json", None), {}) or {}
if not isinstance(form_data, dict):
form_data = {}
ver = (
db.query(OaAppVersion)
.filter(OaAppVersion.id == inst.app_version_id, OaAppVersion.del_flag == 0)
.first()
)
schema = _loads(getattr(ver, "form_schema_json", None) if ver else None, {}) or {}
last_day = extract_last_work_day(schema, form_data)
if last_day is None:
return
user = (
db.query(SysUser)
.filter(
SysUser.id == inst.initiator_user_id,
SysUser.tenant_no == inst.tenant_no,
SysUser.del_flag == 0,
)
.first()
)
if not user:
logger.warning(
"resignation kit sync: initiator not found id=%s",
inst.initiator_user_id,
)
return
user.last_work_day = last_day
user.update_time = datetime.now()
db.add(user)
except Exception:
logger.exception(
"resignation kit user sync failed instance=%s", getattr(inst, "id", None)
)