""" 开始节点 """ import logging from typing import Any, Dict, List from ..base import BaseNode, NodeContext, NodeResult from ..registry import NodeRegistry logger = logging.getLogger(__name__) @NodeRegistry.register class StartNode(BaseNode): """ 开始节点 工作流的入口节点,接收用户输入 """ node_type = 'start' node_name = '开始' node_category = 'basic' node_icon = 'play-circle' node_description = '工作流的开始节点,接收用户输入' inputs = [] outputs = [ { 'name': 'user_input', 'type': 'string', 'description': '用户输入', }, { 'name': 'application_id', 'type': 'string', 'description': '子应用ID(在子应用模式下自动注入)', }, { 'name': 'application_code', 'type': 'string', 'description': '子应用编码(在子应用模式下自动注入)', }, { 'name': 'form_code', 'type': 'string', 'description': '表单编码(从表单列表调用时自动注入)', }, ] def execute(self, context: NodeContext) -> NodeResult: """执行开始节点""" output_variables = {} # 调试日志 logger.info(f"StartNode execute - context.variables: {list(context.variables.keys())}") logger.info(f"StartNode execute - application_id in variables: {'application_id' in context.variables}") if 'application_id' in context.variables: logger.info(f"StartNode execute - application_id value: {context.variables['application_id']}") # 首先将 user_input 作为开始节点的输出变量 # 这样下游节点可以通过 {{start-xxx.user_input}} 引用 if context.user_input: output_variables['user_input'] = context.user_input elif 'user_input' in context.variables: output_variables['user_input'] = context.variables['user_input'] # 将 application_id 作为开始节点的输出变量 # 这样下游节点可以通过 {{start-xxx.application_id}} 引用 # 主应用模式下为空字符串,子应用模式下为实际的应用ID output_variables['application_id'] = context.variables.get('application_id', 'main') # 将 application_code 作为开始节点的输出变量 # 这样下游节点可以通过 {{start-xxx.application_code}} 引用 # 主应用模式下为空字符串,子应用模式下为实际的应用编码 output_variables['application_code'] = context.variables.get('application_code', '') # 将 form_code 作为开始节点的输出变量 # 这样下游节点可以通过 {{start-xxx.form_code}} 引用 # 从表单列表调用时会自动注入,否则为空字符串 output_variables['form_code'] = context.variables.get('form_code', '') logger.info(f"StartNode execute - output_variables: {output_variables}") # 处理前端定义的变量(variables 数组) # 格式: [{ variable: 'name', type: 'string', label: '名称', required: true, default_value: '' }] variables = self.config.get('variables', []) for var in variables: var_name = var.get('variable', '') if not var_name: continue default_value = var.get('default_value', '') var_type = var.get('type', 'string') # 如果 context 中已有该变量(从 inputs 传入),使用传入的值 # 否则使用默认值 if var_name in context.variables and context.variables[var_name]: value = context.variables[var_name] else: value = default_value # 类型转换 if var_type == 'number' and value: try: value = float(value) if '.' in str(value) else int(value) except (ValueError, TypeError): pass elif var_type == 'boolean': if isinstance(value, str): value = value.lower() in ('true', '1', 'yes') context.set_variable(var_name, value) output_variables[var_name] = value # 处理自定义输入变量(兼容旧格式 input_variables) input_variables = self.config.get('input_variables', []) for var in input_variables: var_name = var.get('name', '') var_value = var.get('default', '') if var_name: # 如果 context 中没有该变量,使用默认值 if var_name not in context.variables or not context.variables[var_name]: context.set_variable(var_name, var_value) output_variables[var_name] = context.get_variable(var_name) return NodeResult( success=True, output=output_variables, output_variables=output_variables, ) @classmethod def get_config_schema(cls) -> Dict[str, Any]: """获取配置 Schema""" return { 'type': 'object', 'properties': { 'input_variables': { 'type': 'array', 'title': '输入变量', 'items': { 'type': 'object', 'properties': { 'name': {'type': 'string', 'title': '变量名'}, 'type': {'type': 'string', 'title': '类型', 'enum': ['string', 'number', 'boolean']}, 'description': {'type': 'string', 'title': '描述'}, 'default': {'type': 'string', 'title': '默认值'}, 'required': {'type': 'boolean', 'title': '是否必填'}, }, }, }, }, }