|
|
@@ -0,0 +1,188 @@
|
|
|
+"""OA 表单设计器:收款账户控件 form_data 校验 + 账户字段规范化"""
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
+from typing import Any, Dict, List, Optional, Tuple
|
|
|
+
|
|
|
+ACCOUNT_TYPES = frozenset({"alipay", "personal", "corporate"})
|
|
|
+
|
|
|
+_BANK_FIELDS = ("bank_name", "bank_province", "bank_city", "bank_branch")
|
|
|
+_BANK_FIELD_ALIASES = {
|
|
|
+ "bank_name": ("bankName", "bank_name"),
|
|
|
+ "bank_province": ("bankProvince", "bank_province"),
|
|
|
+ "bank_city": ("bankCity", "bank_city"),
|
|
|
+ "bank_branch": ("bankBranch", "bank_branch"),
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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 _pick(body: dict, camel: str, snake: str, default: Any = "") -> Any:
|
|
|
+ if camel in body:
|
|
|
+ return body[camel]
|
|
|
+ if snake in body:
|
|
|
+ return body[snake]
|
|
|
+ return default
|
|
|
+
|
|
|
+
|
|
|
+def _pick_str(body: dict, camel: str, snake: str, default: str = "") -> str:
|
|
|
+ raw = _pick(body, camel, snake, default)
|
|
|
+ if raw is None:
|
|
|
+ return default
|
|
|
+ return str(raw).strip()
|
|
|
+
|
|
|
+
|
|
|
+def normalize_account_fields(body: dict) -> Tuple[bool, Optional[str], Dict[str, Any]]:
|
|
|
+ """规范化收款账户写入字段,返回 (ok, error, snake_case dict)。"""
|
|
|
+ if not isinstance(body, dict):
|
|
|
+ return False, "请求体格式不正确", {}
|
|
|
+
|
|
|
+ account_type = _pick_str(body, "accountType", "account_type")
|
|
|
+ account_name = _pick_str(body, "accountName", "account_name")
|
|
|
+ account_no = _pick_str(body, "accountNo", "account_no")
|
|
|
+ bank_name = _pick_str(body, "bankName", "bank_name")
|
|
|
+ bank_province = _pick_str(body, "bankProvince", "bank_province")
|
|
|
+ bank_city = _pick_str(body, "bankCity", "bank_city")
|
|
|
+ bank_branch = _pick_str(body, "bankBranch", "bank_branch")
|
|
|
+ order_num = _pick(body, "orderNum", "order_num")
|
|
|
+ remark = _pick_str(body, "remark", "remark")
|
|
|
+
|
|
|
+ if not account_name:
|
|
|
+ return False, "户名不能为空", {}
|
|
|
+ if not account_no:
|
|
|
+ return False, "账号不能为空", {}
|
|
|
+ if account_type not in ACCOUNT_TYPES:
|
|
|
+ return False, "账户类型无效", {}
|
|
|
+
|
|
|
+ if account_type == "alipay":
|
|
|
+ bank_name = ""
|
|
|
+ bank_province = ""
|
|
|
+ bank_city = ""
|
|
|
+ bank_branch = ""
|
|
|
+ elif not all([bank_name, bank_province, bank_city, bank_branch]):
|
|
|
+ return False, "银行信息不完整", {}
|
|
|
+
|
|
|
+ return True, None, {
|
|
|
+ "account_type": account_type,
|
|
|
+ "account_name": account_name,
|
|
|
+ "account_no": account_no,
|
|
|
+ "bank_name": bank_name,
|
|
|
+ "bank_province": bank_province,
|
|
|
+ "bank_city": bank_city,
|
|
|
+ "bank_branch": bank_branch,
|
|
|
+ "order_num": order_num,
|
|
|
+ "remark": remark,
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+def _is_non_empty_str(value: Any) -> bool:
|
|
|
+ return isinstance(value, str) and bool(value.strip())
|
|
|
+
|
|
|
+
|
|
|
+def _is_valid_receive_account_item(item: Any) -> bool:
|
|
|
+ if not isinstance(item, dict):
|
|
|
+ return False
|
|
|
+ item_id = item.get("id")
|
|
|
+ account_type = item.get("accountType") or item.get("account_type")
|
|
|
+ account_name = item.get("accountName") or item.get("account_name")
|
|
|
+ account_no = item.get("accountNo") or item.get("account_no")
|
|
|
+ if not _is_non_empty_str(item_id):
|
|
|
+ return False
|
|
|
+ if not _is_non_empty_str(account_type):
|
|
|
+ return False
|
|
|
+ if str(account_type).strip() not in ACCOUNT_TYPES:
|
|
|
+ return False
|
|
|
+ if not _is_non_empty_str(account_name):
|
|
|
+ return False
|
|
|
+ if not _is_non_empty_str(account_no):
|
|
|
+ return False
|
|
|
+ if str(account_type).strip() == "alipay":
|
|
|
+ return True
|
|
|
+ for snake, (camel, _) in _BANK_FIELD_ALIASES.items():
|
|
|
+ val = item.get(camel)
|
|
|
+ if val is None:
|
|
|
+ val = item.get(snake)
|
|
|
+ if not _is_non_empty_str(val):
|
|
|
+ return False
|
|
|
+ return True
|
|
|
+
|
|
|
+
|
|
|
+def _validate_receive_account_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)
|
|
|
+ value = data.get(field)
|
|
|
+ errors: List[str] = []
|
|
|
+
|
|
|
+ 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_receive_account_item(item):
|
|
|
+ errors.append(f"「{label}」收款账户项格式不正确")
|
|
|
+ break
|
|
|
+ return errors
|
|
|
+
|
|
|
+
|
|
|
+def validate_receive_account_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):
|
|
|
+ if str(rule.get("type") or "") != "receiveAccount":
|
|
|
+ continue
|
|
|
+ errors.extend(_validate_receive_account_rule(rule, data))
|
|
|
+ return errors
|