Files

219 lines
6.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
条件表达式求值器
负责解析和执行条件分支的表达式
"""
import logging
import operator
from decimal import Decimal
from typing import Any, Dict, List, Optional
logger = logging.getLogger(__name__)
class ConditionEvaluator:
"""
条件求值器
支持的操作符:
- eq: 等于
- ne: 不等于
- gt: 大于
- gte: 大于等于
- lt: 小于
- lte: 小于等于
- contains: 包含
- not_contains: 不包含
- in: 在...中
- not_in: 不在...中
- empty: 为空
- not_empty: 不为空
"""
# 操作符映射
OPERATORS = {
'eq': operator.eq,
'ne': operator.ne,
'gt': operator.gt,
'gte': operator.ge,
'lt': operator.lt,
'lte': operator.le,
}
def evaluate_groups(self, groups: List[Dict], form_data: Dict) -> bool:
"""
求值条件组列表(组之间是 OR 关系)
Args:
groups: 条件组列表,每个组包含 conditions 数组
form_data: 表单数据
Returns:
bool: 任一组满足则返回 True
"""
if not groups:
return True
for group in groups:
if self.evaluate_group(group, form_data):
return True
return False
def evaluate_group(self, group: Dict, form_data: Dict) -> bool:
"""
求值单个条件组(组内条件是 AND 关系)
Args:
group: 条件组,包含 conditions 数组
form_data: 表单数据
Returns:
bool: 所有条件都满足才返回 True
"""
conditions = group.get('conditions', [])
if not conditions:
return True
for condition in conditions:
if not self.evaluate_condition(condition, form_data):
return False
return True
def evaluate_condition(self, condition: Dict, form_data: Dict) -> bool:
"""
求值单个条件
Args:
condition: 条件定义 {field, operator, value}
form_data: 表单数据
Returns:
bool: 条件是否满足
"""
field = condition.get('field', '')
op = condition.get('operator', 'eq')
expected_value = condition.get('value')
actual_value = self._get_field_value(form_data, field)
logger.info(
f"条件求值: field={field}, operator={op}, "
f"expected={expected_value!r}(type={type(expected_value).__name__}), "
f"actual={actual_value!r}(type={type(actual_value).__name__})"
)
try:
result = self._compare(actual_value, op, expected_value)
logger.info(f"条件求值结果: {result}")
return result
except Exception as e:
logger.warning(f"条件求值失败: {condition}, 错误: {e}")
return False
def _get_field_value(self, data: Dict, field: str) -> Any:
"""
获取字段值,支持嵌套路径(如 user.dept.name
"""
if not field:
return None
parts = field.split('.')
value = data
for part in parts:
if isinstance(value, dict):
value = value.get(part)
else:
return None
return value
def _compare(self, actual: Any, op: str, expected: Any) -> bool:
"""
执行比较操作
"""
# 空值检查
if op == 'empty':
return self._is_empty(actual)
if op == 'not_empty':
return not self._is_empty(actual)
# 包含检查
if op == 'contains':
return self._contains(actual, expected)
if op == 'not_contains':
return not self._contains(actual, expected)
# 集合检查
if op == 'in':
return self._in_list(actual, expected)
if op == 'not_in':
return not self._in_list(actual, expected)
# 数值比较(需要类型转换)
if op in ('gt', 'gte', 'lt', 'lte'):
actual = self._to_number(actual)
expected = self._to_number(expected)
if actual is None or expected is None:
return False
# 等于/不等于:先尝试原始比较,如果类型不同则统一转字符串再比较
if op in ('eq', 'ne'):
if type(actual) != type(expected) and actual is not None and expected is not None:
result = self.OPERATORS[op](str(actual).strip(), str(expected).strip())
logger.debug(f"类型不一致,转字符串比较: {str(actual)!r} {op} {str(expected)!r} = {result}")
return result
return self.OPERATORS[op](actual, expected)
# 其他标准比较
if op in self.OPERATORS:
return self.OPERATORS[op](actual, expected)
return actual == expected
def _is_empty(self, value: Any) -> bool:
"""检查值是否为空"""
if value is None:
return True
if isinstance(value, str) and value.strip() == '':
return True
if isinstance(value, (list, dict)) and len(value) == 0:
return True
return False
def _contains(self, actual: Any, expected: Any) -> bool:
"""检查是否包含"""
if actual is None:
return False
if isinstance(actual, str):
return str(expected) in actual
if isinstance(actual, (list, tuple)):
return expected in actual
return False
def _in_list(self, actual: Any, expected: Any) -> bool:
"""检查是否在列表中"""
if not isinstance(expected, (list, tuple)):
expected = [expected]
return actual in expected
def _to_number(self, value: Any) -> Optional[float]:
"""转换为数字"""
if value is None:
return None
if isinstance(value, (int, float, Decimal)):
return float(value)
if isinstance(value, str):
try:
return float(value)
except ValueError:
return None
return None
# 全局实例
condition_evaluator = ConditionEvaluator()