From e389dbb967651c80e14030abfe6a508e7d4269a0 Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Mon, 4 Aug 2025 20:13:03 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E6=9B=B4=E6=96=B0=E4=BA=86=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=85=A5=E5=8F=82=E7=9A=84=E6=9D=A1=E4=BB=B6=E5=88=A4?= =?UTF-8?q?=E6=96=AD=EF=BC=9A=E5=85=A5=E5=8F=82=E7=B1=BB=E5=9E=8B=E4=B8=BA?= =?UTF-8?q?IFlowContext=EF=BC=88=E6=B5=81=E7=A8=8B=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87=EF=BC=89=E6=97=B6=E7=A6=81=E6=AD=A2=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=9D=A5=E6=BA=90=E8=BF=9E=E6=8E=A5=E3=80=82?= =?UTF-8?q?=202.=20[Script]=E8=84=9A=E6=9C=AC=E8=8A=82=E7=82=B9=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E4=BA=86=E2=80=9CgetFlowContext=E2=80=9D=E5=86=85?= =?UTF-8?q?=E7=BD=AE=E6=96=B9=E6=B3=95=EF=BC=8C=E6=94=B9=E4=B8=BA=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=AF=86=E5=88=AB=E5=85=A5=E5=8F=82=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E4=B8=BA=E2=80=9Ccontext""flowContext""flow=5Fcontext"?= =?UTF-8?q?=EF=BC=8C=E5=A6=82=E6=9E=9C=E6=98=AF=EF=BC=8C=E5=B0=86=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E4=BD=BF=E7=94=A8=20IFlowContext=20=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=8F=82=E6=95=B0=EF=BC=88=E8=BF=90=E8=A1=8C=E6=97=B6=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=BB=99=E5=AE=9A=EF=BC=89=203.=20NodeFlow=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E4=B8=AD=EF=BC=8CFlowLibraryService=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=86GetType(string)=E4=BB=A5=E5=8F=8ATryGetType(string,Typ?= =?UTF-8?q?e=3F)=E6=96=B9=E6=B3=95=EF=BC=8C=E7=94=A8=E4=BA=8E=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E7=8E=AF=E5=A2=83=E6=90=9C=E7=B4=A2=E5=A4=96=E9=83=A8?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=9A=84=E7=A8=8B=E5=BA=8F=E9=9B=86=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/FlowNode/ParameterDetails.cs | 46 ++++++++++++++++++- NodeFlow/Model/Nodes/SingleConditionNode.cs | 4 +- NodeFlow/Model/Nodes/SingleExpOpNode.cs | 2 +- NodeFlow/Model/Nodes/SingleScriptNode.cs | 10 ++-- .../ChangeNodeConnectionOperation.cs | 6 +++ NodeFlow/Services/FlowLibraryService.cs | 40 ++++++++++++++++ Serein.Script/IScriptInvokeContext.cs | 4 -- Serein.Script/ScriptInvokeContext.cs | 13 ------ Serein.Script/SereinScriptInterpreter.cs | 11 +---- Workbench/Themes/MethodDetailsControl.xaml | 4 +- 10 files changed, 103 insertions(+), 37 deletions(-) diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index 80f0b2e..e572334 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -168,15 +168,57 @@ namespace Serein.Library IsParams = info.IsParams; } - + /// + /// 禁止将 IFlowContext 类型显式入参设置为 true + /// + /// + /// partial void BeforeTheIsExplicitData(ref bool __isAllow, bool newValue) { - if(DataType == typeof(IFlowContext)) + if(DataType == typeof(IFlowContext) && newValue == true) { __isAllow = false; } } + /// + /// 脚本节点的类型缓存。 + /// + private Type? cacheType; + private bool cacheIsExplicit; + + /// + /// 脚本节点的名称变更为流程上下文时,调整 DataType 和 IsExplicitData 的值。 + /// + /// + /// + partial void OnNameChanged(string oldValue, string newValue) + { + if (NodeModel is null) + return; + if (NodeModel.ControlType == NodeControlType.Script) + { + var isIgnore = StringComparison.OrdinalIgnoreCase; + if ("context".Equals(newValue, isIgnore) || + "flowcontext".Equals(newValue, isIgnore) || + "flow_context".Equals(newValue, isIgnore)) + { + cacheType = DataType; + cacheIsExplicit = IsExplicitData; + DataType = typeof(IFlowContext); + IsExplicitData = false; + } + else + { + if (cacheType is not null) + { + DataType = cacheType; + IsExplicitData = cacheIsExplicit; + } + } + } + } + /// /// 转为描述 diff --git a/NodeFlow/Model/Nodes/SingleConditionNode.cs b/NodeFlow/Model/Nodes/SingleConditionNode.cs index 38a9f10..1f7e290 100644 --- a/NodeFlow/Model/Nodes/SingleConditionNode.cs +++ b/NodeFlow/Model/Nodes/SingleConditionNode.cs @@ -223,7 +223,7 @@ namespace Serein.NodeFlow.Model.Nodes } - IScriptInvokeContext scriptContext = new ScriptInvokeContext(flowContext); + IScriptInvokeContext scriptContext = new ScriptInvokeContext(); scriptContext.SetVarValue(dataName, data); var result = await getValueScript.InterpreterAsync(scriptContext); @@ -262,7 +262,7 @@ namespace Serein.NodeFlow.Model.Nodes }); } - IScriptInvokeContext scriptContext = new ScriptInvokeContext(flowContext); + IScriptInvokeContext scriptContext = new ScriptInvokeContext(); scriptContext.SetVarValue(dataName, data); var result = await conditionScript.InterpreterAsync(scriptContext); diff --git a/NodeFlow/Model/Nodes/SingleExpOpNode.cs b/NodeFlow/Model/Nodes/SingleExpOpNode.cs index 2e7397c..61f9226 100644 --- a/NodeFlow/Model/Nodes/SingleExpOpNode.cs +++ b/NodeFlow/Model/Nodes/SingleExpOpNode.cs @@ -178,7 +178,7 @@ namespace Serein.NodeFlow.Model.Nodes } - IScriptInvokeContext scriptContext = new ScriptInvokeContext(flowContext); + IScriptInvokeContext scriptContext = new ScriptInvokeContext(); scriptContext.SetVarValue(dataName, data); var result = await getValueScript.InterpreterAsync(scriptContext); diff --git a/NodeFlow/Model/Nodes/SingleScriptNode.cs b/NodeFlow/Model/Nodes/SingleScriptNode.cs index e4f4227..1807748 100644 --- a/NodeFlow/Model/Nodes/SingleScriptNode.cs +++ b/NodeFlow/Model/Nodes/SingleScriptNode.cs @@ -1,6 +1,7 @@ using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils; +using Serein.NodeFlow.Services; using Serein.Script; using Serein.Script.Node.FlowControl; using System; @@ -49,7 +50,6 @@ namespace Serein.NodeFlow.Model.Nodes public SingleScriptNode(IFlowEnvironment environment) : base(environment) { sereinScript = new SereinScript(); - } static SingleScriptNode() @@ -195,7 +195,11 @@ namespace Serein.NodeFlow.Model.Nodes ScriptArgInfo[] array = JsonHelper.Deserialize(paramsTypeNameJson); string returnTypeName = nodeInfo.CustomData?.ReturnTypeName ?? typeof(object); - Type?[] argType = array.Select(item => string.IsNullOrWhiteSpace(item.ArgType) ? null : Type.GetType(item.ArgType) ?? typeof(Unit)).ToArray(); + + var flowLibService = Env.IOC.Get(); + + Type?[] argType = array.Select(item => string.IsNullOrWhiteSpace(item.ArgType) ? null : flowLibService.GetType(item.ArgType) ?? typeof(Unit)).ToArray(); + Type? resType = Type.GetType(returnTypeName); for (int i = 0; i < paramCount; i++) { @@ -352,7 +356,7 @@ namespace Serein.NodeFlow.Model.Nodes var @params = await flowCallNode.GetParametersAsync(context, token); - IScriptInvokeContext scriptContext = new ScriptInvokeContext(context); + IScriptInvokeContext scriptContext = new ScriptInvokeContext(); if (@params[0] is object[] agrDatas) { diff --git a/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs b/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs index 4f3e63b..2700059 100644 --- a/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs +++ b/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reactive; +using System.Reflection; using System.Security.AccessControl; using System.Text; using System.Threading.Tasks; @@ -429,6 +430,11 @@ namespace Serein.NodeFlow.Model.Operations return false; } + if (typeof(IFlowContext).IsAssignableFrom(ToNode.MethodDetails.ParameterDetailss[ArgIndex].DataType)) + { + SereinEnv.WriteLine(InfoType.WARN, $"连接失败, IFlowContext 流程上下文由运行环境自动注入,作为节点入参时不允许外部给定。起始节点[{FromNode.Guid}],目标节点[{FromNode.Guid}]。"); + return false; + } var toNodeArgSourceGuid = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid; // 目标节点对应参数可能已经有其它连接 var toNodeArgSourceType = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType; diff --git a/NodeFlow/Services/FlowLibraryService.cs b/NodeFlow/Services/FlowLibraryService.cs index 499ca4d..02b1360 100644 --- a/NodeFlow/Services/FlowLibraryService.cs +++ b/NodeFlow/Services/FlowLibraryService.cs @@ -147,6 +147,46 @@ namespace Serein.NodeFlow.Services #region 获取流程依赖的相关方法 + /// + /// 搜索类型 + /// + /// + /// + /// + public bool TryGetType(string fullName,[NotNullWhen(true)] out Type? type) + { + var assemblys = _flowLibraryCaches.Values.Select(key => key.Assembly).ToArray(); + foreach(var assembly in assemblys) + { + type = assembly.GetType(fullName); + if(type is not null) + { + return true; + } + } + type = null; + return false; + } + /// + /// 搜索类型 + /// + /// + /// + public Type? GetType(string fullName) + { + var assemblys = _flowLibraryCaches.Values.Select(key => key.Assembly).ToArray(); + Type? type; + foreach (var assembly in assemblys) + { + type = assembly.GetType(fullName); + if(type is not null) + { + return type; + } + } + return null; + } + /// /// 获取方法描述 /// diff --git a/Serein.Script/IScriptInvokeContext.cs b/Serein.Script/IScriptInvokeContext.cs index a5dfa3c..c842a8a 100644 --- a/Serein.Script/IScriptInvokeContext.cs +++ b/Serein.Script/IScriptInvokeContext.cs @@ -7,10 +7,6 @@ namespace Serein.Script /// public interface IScriptInvokeContext { - /// - /// 脚本运行的流程上下文,包含了流程上下文和变量等信息 - /// - IFlowContext FlowContext { get; } /// /// 是否该退出了(由 TokenSource 控制,用于响应外部发出停止信号) diff --git a/Serein.Script/ScriptInvokeContext.cs b/Serein.Script/ScriptInvokeContext.cs index 8d3b71c..7ad1b15 100644 --- a/Serein.Script/ScriptInvokeContext.cs +++ b/Serein.Script/ScriptInvokeContext.cs @@ -4,15 +4,6 @@ namespace Serein.Script { public sealed class ScriptInvokeContext : IScriptInvokeContext { - /// - /// 脚本使用流程上下文 - /// - /// - public ScriptInvokeContext(IFlowContext flowContext) - { - FlowContext = flowContext; - } - /// /// 不使用流程上下文 /// @@ -20,10 +11,6 @@ namespace Serein.Script { } -#pragma warning disable CS8766 // 返回类型中引用类型的为 Null 性与隐式实现的成员不匹配(可能是由于为 Null 性特性)。 - public IFlowContext? FlowContext{ get; } -#pragma warning restore CS8766 // 返回类型中引用类型的为 Null 性与隐式实现的成员不匹配(可能是由于为 Null 性特性)。 - /// /// 定义的变量 /// diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs index 139d6a7..8583a3c 100644 --- a/Serein.Script/SereinScriptInterpreter.cs +++ b/Serein.Script/SereinScriptInterpreter.cs @@ -13,10 +13,10 @@ namespace Serein.Script public class SereinScriptInterpreter { private readonly Dictionary symbolInfos; + /// /// 缓存对象方法调用节点 /// - //private Dictionary MethodNodeDelegateCaches { get; } = new Dictionary(); private static Dictionary ASTDelegateDetails { get; } = new Dictionary(); @@ -322,15 +322,6 @@ namespace Serein.Script case FunctionCallNode functionCallNode: // 外部挂载的函数调用 async Task InterpreterFunctionCallNodeAsync(IScriptInvokeContext context, FunctionCallNode functionCallNode) { - // 获取流程上下文 - if (context.FlowContext != null && functionCallNode.FunctionName.Equals("getFlowContext", StringComparison.OrdinalIgnoreCase)) - { - return context.FlowContext; - } - else if (functionCallNode.FunctionName.Equals("getScriptContext", StringComparison.OrdinalIgnoreCase)) - { - return context; - } // 获取参数 var arguments = functionCallNode.Arguments.Count == 0 ? [] : diff --git a/Workbench/Themes/MethodDetailsControl.xaml b/Workbench/Themes/MethodDetailsControl.xaml index 074594e..eb22be5 100644 --- a/Workbench/Themes/MethodDetailsControl.xaml +++ b/Workbench/Themes/MethodDetailsControl.xaml @@ -52,7 +52,7 @@ - + @@ -73,7 +73,7 @@ - +