""" 应用创建节点 创建一个新的应用(子应用) """ import logging from typing import Any, Dict, Optional from ..base import BaseNode, NodeContext, NodeResult from ..registry import NodeRegistry logger = logging.getLogger(__name__) @NodeRegistry.register class AppCreateNode(BaseNode): """ 应用创建节点 创建一个新的应用(低代码平台的顶层容器) """ node_type = 'app_create' node_name = '创建应用' node_category = 'application' node_icon = 'app-window' node_description = '创建一个新的应用(子应用)' inputs = [ { 'name': 'name', 'type': 'string', 'description': '应用名称', 'required': True, }, { 'name': 'code', 'type': 'string', 'description': '应用编码(唯一标识,用于URL路由,只能包含字母、数字、下划线和连字符,必须以字母开头)', 'required': True, }, { 'name': 'description', 'type': 'string', 'description': '应用描述', 'required': False, }, { 'name': 'icon', 'type': 'string', 'description': '应用图标', 'required': False, }, { 'name': 'app_type', 'type': 'string', 'description': '应用类型: form-表单应用, workflow-流程应用, dashboard-数据应用, screen-大屏应用, mixed-混合应用', 'required': False, }, ] outputs = [ { 'name': 'app_id', 'type': 'string', 'description': '创建的应用ID', }, { 'name': 'app_code', 'type': 'string', 'description': '应用编码', }, { 'name': 'app_name', 'type': 'string', 'description': '应用名称', }, { 'name': 'success', 'type': 'boolean', 'description': '是否创建成功', }, ] def execute(self, context: NodeContext) -> NodeResult: """执行节点(同步方法,通过运行异步方法实现)""" import asyncio loop = asyncio.get_event_loop() if loop.is_running(): import concurrent.futures with concurrent.futures.ThreadPoolExecutor() as executor: future = executor.submit(asyncio.run, self.execute_async(context)) return future.result() else: return loop.run_until_complete(self.execute_async(context)) async def execute_async(self, context: NodeContext) -> NodeResult: """异步执行节点""" try: # 获取输入参数并解析变量引用 name = context.resolve_template(self.config.get('name', '')) code_base = context.resolve_template(self.config.get('code', '')) description = context.resolve_template(self.config.get('description', '')) icon = context.resolve_template(self.config.get('icon', '')) app_type = context.resolve_template(self.config.get('app_type', 'mixed')) # 配置选项 update_if_exists = self.config.get('update_if_exists', False) logger.info(f'AppCreateNode - 开始创建应用: name={name}, code={code_base}') # 验证必要参数 if not name: return NodeResult( success=False, error='应用名称不能为空', ) if not code_base: return NodeResult( success=False, error='应用编码不能为空', ) # 验证编码格式(字母开头,只能包含字母、数字、下划线和连字符) import re if not re.match(r'^[a-zA-Z][a-zA-Z0-9_-]*$', code_base): return NodeResult( success=False, error='应用编码格式不正确,必须以字母开头,只能包含字母、数字、下划线和连字符', ) # 导入服务和数据库会话 from app.database import AsyncSessionLocal from core.application.service import ApplicationService from core.application.schema import ApplicationCreate, ApplicationUpdate async with AsyncSessionLocal() as db: # 生成唯一的 code(如果已存在则自动添加后缀) code = await self._get_unique_code(db, code_base, ApplicationService) logger.info(f'AppCreateNode - 使用 code: {code} (原始: {code_base})') # 检查应用编码是否已存在(理论上不应该存在,因为已经生成了唯一 code) existing_app = await ApplicationService.get_by_code(db, code) if existing_app: if update_if_exists: # 更新已存在的应用 update_data = ApplicationUpdate( name=name, description=description or None, icon=icon or None, app_type=app_type or None, ) updated_app = await ApplicationService.update( db, record_id=existing_app.id, data=update_data, auto_commit=True ) if updated_app: logger.info(f'AppCreateNode - 应用已存在,更新成功: id={updated_app.id}') return NodeResult( success=True, output=f'应用 {name} 更新成功', output_variables={ 'app_id': updated_app.id, 'app_code': updated_app.code, 'app_name': updated_app.name, 'success': True, } ) else: return NodeResult( success=False, error=f'更新应用失败', ) else: # 应用已存在,返回已存在的应用信息 logger.info(f'AppCreateNode - 应用编码已存在: code={code}, id={existing_app.id}') return NodeResult( success=True, output=f'应用编码 {code} 已存在', output_variables={ 'app_id': existing_app.id, 'app_code': existing_app.code, 'app_name': existing_app.name, 'success': False, # 标记为未创建新应用 } ) # 创建新应用 create_data = ApplicationCreate( name=name, code=code, description=description or '', icon=icon or '', app_type=app_type or 'mixed', ) new_app = await ApplicationService.create(db, data=create_data, auto_commit=True) if new_app: logger.info(f'AppCreateNode - 应用创建成功: id={new_app.id}, code={new_app.code}') return NodeResult( success=True, output=f'应用 {name} 创建成功', output_variables={ 'app_id': new_app.id, 'app_code': new_app.code, 'app_name': new_app.name, 'success': True, } ) else: return NodeResult( success=False, error='创建应用失败', ) except Exception as e: logger.exception(f'AppCreateNode - 执行异常: {e}') return NodeResult( success=False, error=f'创建应用异常: {str(e)}', ) async def _get_unique_code(self, db, base_code: str, service) -> str: """ 生成唯一的 code,如果已存在则自动添加数字后缀 Args: db: 数据库会话 base_code: 基础 code service: 服务类(需要有 get_by_code 方法) Returns: 唯一的 code """ code = base_code counter = 1 while True: try: existing = await service.get_by_code(db, code) if not existing: # code 不存在,可以使用 break # code 已存在,添加后缀 code = f"{base_code}_{counter}" counter += 1 except Exception: # 查询出错,认为 code 可用 break return code