oa_person_select.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. """OA 表单设计器:人员选择控件 form_data 校验"""
  2. from __future__ import annotations
  3. from typing import Any, Dict, List, Optional, Set
  4. def _walk_rules(rules: Any, out: List[dict]) -> None:
  5. if not isinstance(rules, list):
  6. return
  7. for r in rules:
  8. if not isinstance(r, dict):
  9. continue
  10. out.append(r)
  11. children = r.get("children") or (r.get("props") or {}).get("rule") or []
  12. if isinstance(children, list):
  13. _walk_rules(children, out)
  14. def _schema_rules(schema: Any) -> List[dict]:
  15. if isinstance(schema, list):
  16. out: List[dict] = []
  17. _walk_rules(schema, out)
  18. return out
  19. if not isinstance(schema, dict):
  20. return []
  21. out: List[dict] = []
  22. _walk_rules(schema.get("rule") or schema.get("rules") or [], out)
  23. return out
  24. def _rule_props(rule: dict) -> dict:
  25. props = rule.get("props")
  26. return props if isinstance(props, dict) else {}
  27. def _field_label(rule: dict) -> str:
  28. return str(rule.get("title") or rule.get("label") or rule.get("field") or "人员")
  29. def _is_required(rule: dict) -> bool:
  30. props = _rule_props(rule)
  31. if rule.get("$required") is True:
  32. return True
  33. if rule.get("required") is True:
  34. return True
  35. if props.get("required") is True:
  36. return True
  37. return False
  38. def _is_multiple(rule: dict) -> bool:
  39. props = _rule_props(rule)
  40. multiple = props.get("multiple")
  41. if multiple is None:
  42. return True
  43. return multiple is True
  44. def _max_count(rule: dict) -> Optional[int]:
  45. raw = _rule_props(rule).get("max")
  46. if raw is None or raw == "":
  47. return None
  48. try:
  49. value = int(raw)
  50. except (TypeError, ValueError):
  51. return None
  52. if value <= 0:
  53. return None
  54. return value
  55. def _is_valid_person_item(item: Any) -> bool:
  56. if not isinstance(item, dict):
  57. return False
  58. person_id = item.get("id")
  59. name = item.get("name")
  60. if not isinstance(person_id, str) or not person_id.strip():
  61. return False
  62. if not isinstance(name, str) or not name.strip():
  63. return False
  64. return True
  65. def _validate_person_rule(rule: dict, data: Dict[str, Any]) -> List[str]:
  66. field = str(rule.get("field") or "").strip()
  67. if not field:
  68. return []
  69. label = _field_label(rule)
  70. required = _is_required(rule)
  71. multiple = _is_multiple(rule)
  72. max_count = _max_count(rule)
  73. value = data.get(field)
  74. errors: List[str] = []
  75. if multiple:
  76. if value is None:
  77. if required:
  78. errors.append(f"「{label}」为必填人员字段")
  79. return errors
  80. if not isinstance(value, list):
  81. errors.append(f"「{label}」人员字段格式不正确,应为人员列表")
  82. return errors
  83. if required and len(value) == 0:
  84. errors.append(f"「{label}」为必填人员字段")
  85. return errors
  86. for item in value:
  87. if not _is_valid_person_item(item):
  88. errors.append(f"「{label}」人员项格式不正确,需包含有效的 id 与 name")
  89. break
  90. if max_count is not None and len(value) > max_count:
  91. errors.append(f"「{label}」选择人员数量超过最大上限 {max_count} 个")
  92. return errors
  93. if value is None or value == "":
  94. if required:
  95. errors.append(f"「{label}」为必填人员字段")
  96. return errors
  97. if isinstance(value, list):
  98. errors.append(f"「{label}」人员字段格式不正确,应为单个人员对象")
  99. return errors
  100. if not _is_valid_person_item(value):
  101. errors.append(f"「{label}」人员字段格式不正确,需包含有效的 id 与 name")
  102. return errors
  103. def validate_person_form_values(schema: Any, form_data: Optional[Dict[str, Any]]) -> List[str]:
  104. """校验 form_data 中人员 / 归属人控件的值格式,返回中文错误列表(空表示通过)。"""
  105. data = form_data or {}
  106. errors: List[str] = []
  107. for rule in _schema_rules(schema):
  108. typ = str(rule.get("type") or "")
  109. if typ == "person":
  110. errors.extend(_validate_person_rule(rule, data))
  111. elif typ == "ownerPerson":
  112. # 归属人固定单选
  113. forced = dict(rule)
  114. props = dict(_rule_props(rule))
  115. props["multiple"] = False
  116. forced["props"] = props
  117. errors.extend(_validate_person_rule(forced, data))
  118. return errors
  119. def extract_person_ids(raw: Any) -> Set[str]:
  120. ids: Set[str] = set()
  121. items = raw if isinstance(raw, list) else ([raw] if isinstance(raw, dict) else [])
  122. for item in items:
  123. if not isinstance(item, dict):
  124. continue
  125. pid = str(item.get("id") or "").strip()
  126. if pid:
  127. ids.add(pid)
  128. return ids
  129. def person_contains_match(form_value: Any, cond_ids: Any) -> bool:
  130. form_ids = extract_person_ids(form_value)
  131. if isinstance(cond_ids, str):
  132. want = {cond_ids.strip()} if cond_ids.strip() else set()
  133. elif isinstance(cond_ids, list):
  134. want = {str(x).strip() for x in cond_ids if str(x).strip()}
  135. else:
  136. want = set()
  137. return bool(form_ids & want)