450 lines
16 KiB
Python
450 lines
16 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
工作流引擎辅助工具
|
|
包含节点查找、表单数据处理等通用方法
|
|
"""
|
|
import logging
|
|
from typing import Dict, Optional, Tuple
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FlowUtils:
|
|
"""流程工具类"""
|
|
|
|
@staticmethod
|
|
def find_node_by_id(flow_def: Dict, node_id: str) -> Optional[Dict]:
|
|
"""
|
|
在流程定义中查找节点
|
|
|
|
Args:
|
|
flow_def: 流程定义
|
|
node_id: 节点ID
|
|
|
|
Returns:
|
|
节点配置字典,未找到返回 None
|
|
"""
|
|
nodes = flow_def.get('nodes')
|
|
if not nodes:
|
|
return None
|
|
|
|
return FlowUtils._find_node_recursive(nodes, node_id)
|
|
|
|
@staticmethod
|
|
def _find_node_recursive(node: Dict, target_id: str) -> Optional[Dict]:
|
|
"""递归查找节点"""
|
|
if node.get('id') == target_id:
|
|
return node
|
|
|
|
# 在子节点中查找
|
|
children = node.get('children')
|
|
if children:
|
|
result = FlowUtils._find_node_recursive(children, target_id)
|
|
if result:
|
|
return result
|
|
|
|
# 在分支中查找
|
|
branches = node.get('branches', [])
|
|
for branch in branches:
|
|
branch_children = branch.get('children')
|
|
if branch_children:
|
|
result = FlowUtils._find_node_recursive(branch_children, target_id)
|
|
if result:
|
|
return result
|
|
|
|
return None
|
|
|
|
@staticmethod
|
|
def find_parallel_branch_for_node(flow_def: Dict, node_id: str) -> Optional[Tuple[Dict, Dict]]:
|
|
"""
|
|
查找节点所在的并行分支
|
|
|
|
Args:
|
|
flow_def: 流程定义
|
|
node_id: 节点ID
|
|
|
|
Returns:
|
|
(parallel_node, branch) 元组,如果节点不在并行分支内则返回 None
|
|
"""
|
|
nodes = flow_def.get('nodes')
|
|
if not nodes:
|
|
return None
|
|
|
|
return FlowUtils._find_parallel_branch_recursive(nodes, node_id, None, None)
|
|
|
|
@staticmethod
|
|
def _find_parallel_branch_recursive(
|
|
node: Dict,
|
|
target_id: str,
|
|
current_parallel: Optional[Dict],
|
|
current_branch: Optional[Dict]
|
|
) -> Optional[Tuple[Dict, Dict]]:
|
|
"""递归查找节点所在的并行分支"""
|
|
if not node or not isinstance(node, dict):
|
|
return None
|
|
|
|
# 检查当前节点
|
|
if node.get('id') == target_id:
|
|
if current_parallel and current_branch:
|
|
return (current_parallel, current_branch)
|
|
return None
|
|
|
|
node_type = node.get('type')
|
|
|
|
# 如果是并行节点,在其分支中查找
|
|
if node_type == 'parallel':
|
|
branches = node.get('branches', [])
|
|
for branch in branches:
|
|
branch_children = branch.get('children')
|
|
if branch_children:
|
|
result = FlowUtils._find_parallel_branch_recursive(
|
|
branch_children, target_id, node, branch
|
|
)
|
|
if result:
|
|
return result
|
|
|
|
# 检查子节点
|
|
children = node.get('children')
|
|
if children:
|
|
result = FlowUtils._find_parallel_branch_recursive(
|
|
children, target_id, current_parallel, current_branch
|
|
)
|
|
if result:
|
|
return result
|
|
|
|
# 检查条件分支
|
|
if node_type == 'condition':
|
|
branches = node.get('branches', [])
|
|
for branch in branches:
|
|
branch_children = branch.get('children')
|
|
if branch_children:
|
|
result = FlowUtils._find_parallel_branch_recursive(
|
|
branch_children, target_id, current_parallel, current_branch
|
|
)
|
|
if result:
|
|
return result
|
|
|
|
return None
|
|
|
|
@staticmethod
|
|
def find_condition_branch_for_node(flow_def: Dict, node_id: str) -> Optional[Dict]:
|
|
"""
|
|
查找节点所在的条件分支,返回条件节点
|
|
|
|
Args:
|
|
flow_def: 流程定义
|
|
node_id: 节点ID
|
|
|
|
Returns:
|
|
条件节点字典,如果节点不在条件分支内则返回 None
|
|
"""
|
|
nodes = flow_def.get('nodes')
|
|
if not nodes:
|
|
return None
|
|
|
|
return FlowUtils._find_condition_branch_recursive(nodes, node_id, None)
|
|
|
|
@staticmethod
|
|
def _find_condition_branch_recursive(
|
|
node: Dict,
|
|
target_id: str,
|
|
current_condition: Optional[Dict],
|
|
) -> Optional[Dict]:
|
|
"""递归查找节点所在的条件分支"""
|
|
if not node or not isinstance(node, dict):
|
|
return None
|
|
|
|
if node.get('id') == target_id:
|
|
return current_condition
|
|
|
|
node_type = node.get('type')
|
|
|
|
# 如果是条件节点,在其分支中查找(标记当前条件节点)
|
|
if node_type == 'condition':
|
|
branches = node.get('branches', [])
|
|
for branch in branches:
|
|
branch_children = branch.get('children')
|
|
if branch_children:
|
|
result = FlowUtils._find_condition_branch_recursive(
|
|
branch_children, target_id, node
|
|
)
|
|
if result is not None:
|
|
return result
|
|
|
|
# 如果是并行节点,在其分支中查找(保持当前条件上下文)
|
|
if node_type == 'parallel':
|
|
branches = node.get('branches', [])
|
|
for branch in branches:
|
|
branch_children = branch.get('children')
|
|
if branch_children:
|
|
result = FlowUtils._find_condition_branch_recursive(
|
|
branch_children, target_id, current_condition
|
|
)
|
|
if result is not None:
|
|
return result
|
|
|
|
# 检查子节点
|
|
children = node.get('children')
|
|
if children:
|
|
result = FlowUtils._find_condition_branch_recursive(
|
|
children, target_id, current_condition
|
|
)
|
|
if result is not None:
|
|
return result
|
|
|
|
return None
|
|
|
|
@staticmethod
|
|
def find_innermost_branch_for_node(
|
|
flow_def: Dict, node_id: str
|
|
) -> Optional[Tuple[str, Dict, Optional[Dict]]]:
|
|
"""
|
|
查找节点所在的最内层分支归属(可能是条件分支或并行分支)。
|
|
用于 _advance_to_next 中正确处理嵌套场景(如并行内嵌条件、条件内嵌并行)。
|
|
|
|
Returns:
|
|
('condition', condition_node, None) 或
|
|
('parallel', parallel_node, branch) 或
|
|
None
|
|
"""
|
|
nodes = flow_def.get('nodes')
|
|
if not nodes:
|
|
return None
|
|
|
|
return FlowUtils._find_innermost_branch_recursive(nodes, node_id, None)
|
|
|
|
@staticmethod
|
|
def _find_innermost_branch_recursive(
|
|
node: Dict,
|
|
target_id: str,
|
|
innermost: Optional[Tuple[str, Dict, Optional[Dict]]],
|
|
) -> Optional[Tuple[str, Dict, Optional[Dict]]]:
|
|
if not node or not isinstance(node, dict):
|
|
return None
|
|
|
|
if node.get('id') == target_id:
|
|
return innermost
|
|
|
|
node_type = node.get('type')
|
|
|
|
if node_type == 'condition':
|
|
for branch in node.get('branches', []):
|
|
branch_children = branch.get('children')
|
|
if branch_children:
|
|
result = FlowUtils._find_innermost_branch_recursive(
|
|
branch_children, target_id, ('condition', node, None)
|
|
)
|
|
if result is not None:
|
|
return result
|
|
|
|
if node_type == 'parallel':
|
|
for branch in node.get('branches', []):
|
|
branch_children = branch.get('children')
|
|
if branch_children:
|
|
result = FlowUtils._find_innermost_branch_recursive(
|
|
branch_children, target_id, ('parallel', node, branch)
|
|
)
|
|
if result is not None:
|
|
return result
|
|
|
|
children = node.get('children')
|
|
if children:
|
|
result = FlowUtils._find_innermost_branch_recursive(
|
|
children, target_id, innermost
|
|
)
|
|
if result is not None:
|
|
return result
|
|
|
|
return None
|
|
|
|
@staticmethod
|
|
def find_parent_node_id(flow_definition: Dict, target_node_id: str) -> Optional[str]:
|
|
"""
|
|
从流程定义中查找目标节点的父节点ID(仅审批/办理节点)
|
|
"""
|
|
nodes = flow_definition.get('nodes', {})
|
|
|
|
def find_parent(node: Dict, parent_id: Optional[str] = None) -> Optional[str]:
|
|
if not node or not isinstance(node, dict):
|
|
return None
|
|
|
|
node_id = node.get('id')
|
|
node_type = node.get('type')
|
|
|
|
# 检查子节点
|
|
children = node.get('children')
|
|
if children:
|
|
if isinstance(children, dict):
|
|
if children.get('id') == target_node_id:
|
|
if node_type in ('approval', 'handle'):
|
|
return node_id
|
|
return parent_id
|
|
result = find_parent(children, node_id if node_type in ('approval', 'handle') else parent_id)
|
|
if result:
|
|
return result
|
|
|
|
# 检查分支
|
|
branches = node.get('branches', [])
|
|
for branch in branches:
|
|
branch_children = branch.get('children')
|
|
if branch_children:
|
|
if isinstance(branch_children, dict) and branch_children.get('id') == target_node_id:
|
|
if node_type in ('approval', 'handle'):
|
|
return node_id
|
|
return parent_id
|
|
result = find_parent(branch_children, node_id if node_type in ('approval', 'handle') else parent_id)
|
|
if result:
|
|
return result
|
|
|
|
return None
|
|
|
|
return find_parent(nodes)
|
|
|
|
|
|
class FormDataUtils:
|
|
"""表单数据工具类"""
|
|
|
|
@staticmethod
|
|
async def save_form_data(db, form_code: str, form_data: Dict) -> str:
|
|
"""
|
|
保存表单数据
|
|
|
|
Args:
|
|
db: 数据库会话
|
|
form_code: 表单编码
|
|
form_data: 表单数据(可能是扁平结构或 {main, sub_tables} 结构)
|
|
|
|
Returns:
|
|
str: 数据ID
|
|
"""
|
|
from online_dev.form_data_manager.service import FormDataService
|
|
|
|
# 创建表单数据服务
|
|
service = await FormDataService.create_service(db, form_code)
|
|
|
|
# 判断 form_data 是否已经是结构化数据
|
|
if "main" in form_data:
|
|
# 已经是 {main, sub_tables} 结构,直接使用
|
|
data = form_data
|
|
else:
|
|
# 扁平结构,需要包装
|
|
data = {"main": form_data, "sub_tables": {}}
|
|
|
|
# 保存数据
|
|
try:
|
|
result = await service.create(db, data)
|
|
return result.get("id", "")
|
|
except Exception as e:
|
|
logger.error(f"保存表单数据失败: {e}")
|
|
await db.rollback()
|
|
raise ValueError(f"保存表单数据失败: {e}")
|
|
|
|
@staticmethod
|
|
async def load_form_data(db, form_code: str, form_data_id: str) -> Dict:
|
|
"""
|
|
加载表单数据
|
|
|
|
Args:
|
|
db: 数据库会话
|
|
form_code: 表单编码
|
|
form_data_id: 表单数据ID
|
|
|
|
Returns:
|
|
Dict: 表单数据(扁平结构,包含关联字段的 _name 后缀)
|
|
"""
|
|
if not form_code or not form_data_id:
|
|
return {}
|
|
|
|
try:
|
|
from online_dev.form_data_manager.service import FormDataService
|
|
|
|
# 创建表单数据服务
|
|
service = await FormDataService.create_service(db, form_code)
|
|
|
|
# 获取数据
|
|
result = await service.get(db, form_data_id)
|
|
|
|
# 提取主表数据,转为扁平结构
|
|
form_data = {}
|
|
if result:
|
|
# 主表字段
|
|
for key, value in result.items():
|
|
if key not in ('sub_tables', 'id'):
|
|
form_data[key] = value
|
|
|
|
# 填充关联字段的显示名称(_name 后缀)
|
|
if form_data:
|
|
# 从 form_config 中提取所有关联字段
|
|
relation_fields = {}
|
|
form_config = service._form_config
|
|
|
|
def extract_relation_fields(items):
|
|
"""递归提取关联字段"""
|
|
if not items:
|
|
return
|
|
for item in items:
|
|
item_type = item.get("type", "")
|
|
field = item.get("field", "")
|
|
|
|
# 容器类型,递归处理
|
|
if item_type == "grid":
|
|
for col in item.get("columns", []):
|
|
extract_relation_fields(col.get("children", []))
|
|
elif item_type in ["collapse", "tabs"]:
|
|
for panel in item.get("children", []) or item.get("items", []):
|
|
extract_relation_fields(panel.get("children", []))
|
|
elif item_type == "sub-table":
|
|
extract_relation_fields(item.get("children", []))
|
|
elif field and item_type in ["user-selector", "dept-selector", "post-selector", "role-selector", "region-selector"]:
|
|
# 关联字段类型映射
|
|
type_mapping = {
|
|
"user-selector": "core_user",
|
|
"dept-selector": "core_dept",
|
|
"post-selector": "core_post",
|
|
"role-selector": "core_role",
|
|
"region-selector": "core_region",
|
|
}
|
|
relation_table = type_mapping.get(item_type)
|
|
if relation_table:
|
|
relation_fields[field] = {
|
|
"display_field": f"{field}_name",
|
|
"relation_table": relation_table,
|
|
"relation_key": "id",
|
|
"display_column": "name",
|
|
}
|
|
|
|
extract_relation_fields(form_config.get("items", []))
|
|
|
|
if relation_fields:
|
|
filled = await service._fill_relation_display_names(db, [form_data], relation_fields)
|
|
if filled:
|
|
form_data = filled[0]
|
|
|
|
return form_data
|
|
|
|
except Exception as e:
|
|
logger.error(f"加载表单数据失败: {e}")
|
|
return {}
|
|
|
|
@staticmethod
|
|
async def update_form_data(db, form_code: str, form_data_id: str, form_data: Dict) -> None:
|
|
"""更新表单数据"""
|
|
if not form_code or not form_data_id:
|
|
return
|
|
|
|
try:
|
|
from online_dev.form_data_manager.service import FormDataService
|
|
|
|
# 创建表单数据服务
|
|
service = await FormDataService.create_service(db, form_code)
|
|
|
|
# 构建数据结构
|
|
data = {"main": form_data, "sub_tables": {}}
|
|
|
|
# 更新数据
|
|
await service.update(db, form_data_id, data)
|
|
|
|
except Exception as e:
|
|
logger.error(f"更新表单数据失败: {e}")
|