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

142 lines
4.4 KiB
Python

"""OA 表单设计器:部门选择控件 form_data 校验"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
def _walk_rules(rules: Any, out: List[dict]) -> None:
if not isinstance(rules, list):
return
for r in rules:
if not isinstance(r, dict):
continue
out.append(r)
children = r.get("children") or (r.get("props") or {}).get("rule") or []
if isinstance(children, list):
_walk_rules(children, out)
def _schema_rules(schema: Any) -> List[dict]:
if isinstance(schema, list):
out: List[dict] = []
_walk_rules(schema, out)
return out
if not isinstance(schema, dict):
return []
out: List[dict] = []
_walk_rules(schema.get("rule") or schema.get("rules") or [], out)
return out
def _rule_props(rule: dict) -> dict:
props = rule.get("props")
return props if isinstance(props, dict) else {}
def _field_label(rule: dict) -> str:
return str(rule.get("title") or rule.get("label") or rule.get("field") or "部门")
def _is_required(rule: dict) -> bool:
props = _rule_props(rule)
if rule.get("$required") is True:
return True
if rule.get("required") is True:
return True
if props.get("required") is True:
return True
return False
def _is_multiple(rule: dict) -> bool:
props = _rule_props(rule)
multiple = props.get("multiple")
if multiple is None:
return True
return multiple is True
def _max_count(rule: dict) -> Optional[int]:
raw = _rule_props(rule).get("max")
if raw is None or raw == "":
return None
try:
value = int(raw)
except (TypeError, ValueError):
return None
if value <= 0:
return None
return value
def _is_valid_dept_item(item: Any) -> bool:
if not isinstance(item, dict):
return False
dept_id = item.get("id")
name = item.get("name")
if not isinstance(dept_id, str) or not dept_id.strip():
return False
if not isinstance(name, str) or not name.strip():
return False
return True
def _validate_dept_rule(rule: dict, data: Dict[str, Any]) -> List[str]:
field = str(rule.get("field") or "").strip()
if not field:
return []
label = _field_label(rule)
required = _is_required(rule)
multiple = _is_multiple(rule)
max_count = _max_count(rule)
value = data.get(field)
errors: List[str] = []
if multiple:
if value is None:
if required:
errors.append(f"「{label}」为必填部门字段")
return errors
if not isinstance(value, list):
errors.append(f"「{label}」部门字段格式不正确,应为部门列表")
return errors
if required and len(value) == 0:
errors.append(f"「{label}」为必填部门字段")
return errors
for item in value:
if not _is_valid_dept_item(item):
errors.append(f"「{label}」部门项格式不正确,需包含有效的 id 与 name")
break
if max_count is not None and len(value) > max_count:
errors.append(f"「{label}」选择部门数量超过最大上限 {max_count} 个")
return errors
if value is None or value == "":
if required:
errors.append(f"「{label}」为必填部门字段")
return errors
if isinstance(value, list):
errors.append(f"「{label}」部门字段格式不正确,应为单个部门对象")
return errors
if not _is_valid_dept_item(value):
errors.append(f"「{label}」部门字段格式不正确,需包含有效的 id 与 name")
return errors
def validate_dept_form_values(schema: Any, form_data: Optional[Dict[str, Any]]) -> List[str]:
"""校验 form_data 中部门 / 归属部门控件的值格式,返回中文错误列表(空表示通过)。"""
data = form_data or {}
errors: List[str] = []
for rule in _schema_rules(schema):
typ = str(rule.get("type") or "")
if typ == "dept":
errors.extend(_validate_dept_rule(rule, data))
elif typ == "ownerDept":
forced = dict(rule)
props = dict(_rule_props(rule))
props["multiple"] = False
forced["props"] = props
errors.extend(_validate_dept_rule(forced, data))
return errors