mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-02 14:36:33 +08:00
修复了全局节点连接异常异常。
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.NodeFlow.Services;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.NodeFlow.Model.Operation;
|
||||
using Serein.NodeFlow.Services;
|
||||
using System.Diagnostics;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Env;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
|
||||
353
NodeFlow/Model/Infos/FlowApiMethodInfo.cs
Normal file
353
NodeFlow/Model/Infos/FlowApiMethodInfo.cs
Normal file
@@ -0,0 +1,353 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.NodeFlow.Services;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Infos
|
||||
{
|
||||
/// <summary>
|
||||
/// 指示流程接口方法需要生成什么代码
|
||||
/// </summary>
|
||||
internal class FlowApiMethodInfo
|
||||
{
|
||||
|
||||
public FlowApiMethodInfo(SingleFlowCallNode singleFlowCallNode)
|
||||
{
|
||||
NodeModel = singleFlowCallNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对应的流程节点
|
||||
/// </summary>
|
||||
public SingleFlowCallNode NodeModel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成的接口名称
|
||||
/// </summary>
|
||||
public string ApiMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回类型
|
||||
/// </summary>
|
||||
public Type ReturnType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 参数实体信息
|
||||
/// </summary>
|
||||
public List<ParamInfo> ParamInfos { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 参数实体类型名称
|
||||
/// </summary>
|
||||
public string ParamTypeName => $"FlowApiInvoke_{ApiMethodName}";
|
||||
|
||||
public class ParamInfo
|
||||
{
|
||||
public ParamInfo(Type type, string paramName)
|
||||
{
|
||||
Type = type;
|
||||
ParamName = paramName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Type Type { get; set; }
|
||||
/// <summary>
|
||||
/// 参数名称
|
||||
/// </summary>
|
||||
public string ParamName { get; set; }
|
||||
/// <summary>
|
||||
/// 注释备注
|
||||
/// </summary>
|
||||
public string Comments { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public enum ParamType
|
||||
{
|
||||
Defute, // 仅使用方法参数
|
||||
HasToken, // 包含取消令牌和方法参数
|
||||
HasContextAndToken, // 包含上下文、取消令牌和方法参数
|
||||
}
|
||||
|
||||
|
||||
public bool IsVoid => ReturnType == typeof(void);
|
||||
public string ObjPoolName => $"_objPool{ParamTypeName.ToPascalCase()}";
|
||||
/// <summary>
|
||||
/// 生成所需的参数类的签名代码
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string ToParamterClassSignature()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
|
||||
//sb.AppendCode(2, $"private static readonly global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}> {ObjPoolName} = ");
|
||||
//sb.AppendCode(2, $" new global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}>(");
|
||||
//sb.AppendCode(2, $" new global::Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy<{ParamTypeName}>()); ");
|
||||
sb.AppendLine();
|
||||
var classXmlComments = $"流程接口[{ApiMethodName}]需要的参数类".ToXmlComments(2);
|
||||
sb.AppendCode(2, classXmlComments);
|
||||
sb.AppendCode(2, $"public class {ParamTypeName}");
|
||||
sb.AppendCode(2, $"{{");
|
||||
for (int index = 0; index < ParamInfos.Count; index++)
|
||||
{
|
||||
ParamInfo? info = ParamInfos[index];
|
||||
var argXmlComments = $"[{index}]流程接口参数{(string.IsNullOrWhiteSpace(info.Comments) ? string.Empty : $",{info.Comments}。")}".ToXmlComments(2);
|
||||
sb.AppendCode(3, argXmlComments);
|
||||
sb.AppendCode(3, $"public global::{info.Type.FullName} {info.ParamName.ToPascalCase()} {{ get; set; }}");
|
||||
}
|
||||
sb.AppendCode(2, $"}}");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ToObjPoolSignature()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendCode(2, $"private static readonly global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}> {ObjPoolName} = ");
|
||||
sb.AppendCode(4, $"new global::Microsoft.Extensions.ObjectPool.DefaultObjectPool<{ParamTypeName}>(");
|
||||
sb.AppendCode(5, $"new global::Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy<{ParamTypeName}>()); ");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成接口的签名方法
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public string ToInterfaceMethodSignature(ParamType type)
|
||||
{
|
||||
var taskTypeFullName = $"global::System.Threading.Tasks.Task";
|
||||
var contextFullName = $"global::{typeof(IFlowContext).FullName}";
|
||||
var tokenFullName = $"global::{typeof(CancellationToken).FullName}";
|
||||
var returnContext = IsVoid ? taskTypeFullName : $"{taskTypeFullName}<{ReturnType.FullName}>";
|
||||
if (type == ParamType.Defute)
|
||||
{
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
|
||||
return $"{returnContext} {ApiMethodName}({paramSignature});";
|
||||
}
|
||||
else if (type == ParamType.HasToken)
|
||||
{
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
|
||||
return $"{returnContext} {ApiMethodName}({tokenFullName} token, {paramSignature});";
|
||||
}
|
||||
else if (type == ParamType.HasContextAndToken)
|
||||
{
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
|
||||
return $"{returnContext} {ApiMethodName}({contextFullName} flowContext, {tokenFullName} token, {paramSignature});";
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成实现方法的签名代码
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public string ToImpleMethodSignature(ParamType type)
|
||||
{
|
||||
var taskTypeFullName = $"global::System.Threading.Tasks.Task";
|
||||
var contextApiFullName = $"global::{typeof(IFlowContext).FullName}";
|
||||
var contextImpleFullName = $"global::{typeof(FlowContext).FullName}";
|
||||
var tokenSourceFullName = $"global::{typeof(CancellationTokenSource).FullName}";
|
||||
var tokenFullName = $"global::{typeof(CancellationToken).FullName}";
|
||||
var flowContextPoolName = $"global::{typeof(LightweightFlowControl).FullName}";
|
||||
string flowEnvironment = nameof(flowEnvironment);
|
||||
string flowContext = nameof(flowContext);
|
||||
string token = nameof(token);
|
||||
|
||||
var returnTypeContext = IsVoid ? taskTypeFullName : $"{taskTypeFullName}<{ReturnType.FullName}>";
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (IsVoid)
|
||||
{
|
||||
if (type == ParamType.Defute)
|
||||
{
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}"));
|
||||
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
|
||||
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({paramSignature})");
|
||||
sb.AppendCode(2, $"{{");
|
||||
sb.AppendCode(3, $"{contextApiFullName} {flowContext} = {flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Allocate)}(); // 从对象池获取一个上下文");
|
||||
sb.AppendCode(3, $"{tokenSourceFullName} cts = new {tokenSourceFullName}(); // 创建取消令牌");
|
||||
sb.AppendCode(3, $"try");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"await {ApiMethodName}({flowContext}, cts.Token, {invokeParamSignature}); // 调用目标方法");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"catch (Exception)");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"throw;");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"finally");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"{flowContext}.{nameof(IFlowContext.Reset)}(); ");
|
||||
sb.AppendCode(4, $"cts.{nameof(CancellationTokenSource.Dispose)}(); ");
|
||||
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(2, $"}}");
|
||||
return sb.ToString();
|
||||
}
|
||||
else if (type == ParamType.HasToken)
|
||||
{
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}"));
|
||||
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
|
||||
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({tokenFullName} {token}, {paramSignature})");
|
||||
sb.AppendCode(2, $"{{");
|
||||
sb.AppendCode(3, $"{contextApiFullName} {flowContext} = {flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Allocate)}(); // 从对象池获取一个上下文");
|
||||
sb.AppendCode(3, $"try");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"await {ApiMethodName}({flowContext}, {token}, {invokeParamSignature}); // 调用目标方法");
|
||||
sb.AppendCode(4, $"{flowContext}.{nameof(IFlowContext.Reset)}(); ");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"catch (Exception)");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"throw;");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"finally");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(2, $"}}");
|
||||
return sb.ToString();
|
||||
}
|
||||
else if (type == ParamType.HasContextAndToken)
|
||||
{
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{{{p.Type.FullName} {p.ParamName}"));
|
||||
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
|
||||
|
||||
|
||||
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({contextApiFullName} {flowContext}, {tokenFullName} token, {paramSignature})");
|
||||
sb.AppendCode(2, $"{{");
|
||||
sb.AppendCode(3, $"token.ThrowIfCancellationRequested(); // 检查任务是否取消");
|
||||
sb.AppendCode(3, $"try");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(3, $"global::{ParamTypeName} data = {ObjPoolName}.Get(); // 从对象池获取一个对象");
|
||||
for (int index = 0; index < ParamInfos.Count; index++)
|
||||
{
|
||||
ParamInfo? info = ParamInfos[index];
|
||||
sb.AppendCode(4, $"data.{info.ParamName.ToPascalCase()} = {info.ParamName}; // [{index}] {info.Comments}");
|
||||
}
|
||||
sb.AppendCode(3, $"{flowContext}.{nameof(IFlowContext.AddOrUpdate)}(\"{ApiMethodName}\", data);");
|
||||
sb.AppendCode(3, $"{flowContext}.{nameof(IFlowContext.SetPreviousNode)}(\"{NodeModel.TargetNode.Guid}\", \"{ApiMethodName}\");");
|
||||
sb.AppendCode(3, $"global::{typeof(CallNode).FullName} node = Get(\"{NodeModel.Guid}\");");
|
||||
sb.AppendCode(3, $"await node.{nameof(CallNode.StartFlowAsync)}({flowContext}, {token}); // 调用目标方法");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"catch (Exception)");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"throw;");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"finally");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(2, $"}}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string flowResult = nameof(flowResult);
|
||||
if (type == ParamType.Defute)
|
||||
{
|
||||
//sb.AppendCode(3, $"{contextApiFullName} {flowContext} = new {contextImpleFullName}({flowEnvironment}); // 创建上下文");
|
||||
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
|
||||
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
|
||||
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({paramSignature})");
|
||||
sb.AppendCode(2, $"{{");
|
||||
sb.AppendCode(3, $"{contextApiFullName} {flowContext} = {flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Allocate)}(); // 从对象池获取一个上下文");
|
||||
sb.AppendCode(3, $"{tokenSourceFullName} cts = new {tokenSourceFullName}(); // 创建取消令牌");
|
||||
sb.AppendCode(3, $"try");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"{ReturnType.FullName} {flowResult} = await {ApiMethodName}({flowContext}, cts.{nameof(CancellationTokenSource.Token)}, {invokeParamSignature}); // 调用目标方法");
|
||||
sb.AppendCode(4, $"return {flowResult};");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"catch (Exception)");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"throw;");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"finally");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"{flowContext}.{nameof(IFlowContext.Reset)}(); ");
|
||||
sb.AppendCode(4, $"cts.{nameof(CancellationTokenSource.Dispose)}(); ");
|
||||
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(2, $"}}");
|
||||
return sb.ToString();
|
||||
}
|
||||
else if (type == ParamType.HasToken)
|
||||
{
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
|
||||
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
|
||||
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({tokenFullName} {token}, {paramSignature})");
|
||||
sb.AppendCode(2, $"{{");
|
||||
sb.AppendCode(3, $"{contextApiFullName} {flowContext} = {flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Allocate)}(); // 从对象池获取一个上下文");
|
||||
sb.AppendCode(3, $"try");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"{ReturnType.FullName} {flowResult} = await {ApiMethodName}({flowContext}, {token}, {invokeParamSignature}); // 调用目标方法");
|
||||
sb.AppendCode(4, $"return {flowResult};");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"catch (Exception)");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"throw;");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"finally");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"{flowContext}.{nameof(IFlowContext.Reset)}(); ");
|
||||
sb.AppendCode(4, $"{flowContextPoolName}.{nameof(LightweightFlowControl.FlowContextPool)}.{nameof(LightweightFlowControl.FlowContextPool.Free)}({flowContext}); // 释放上下文");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(2, $"}}");
|
||||
return sb.ToString();
|
||||
}
|
||||
else if (type == ParamType.HasContextAndToken)
|
||||
{
|
||||
var paramSignature = string.Join(", ", ParamInfos.Select(p => $"global::{p.Type.FullName} {p.ParamName}"));
|
||||
var invokeParamSignature = string.Join(", ", ParamInfos.Select(p => p.ParamName));
|
||||
sb.AppendCode(2, $"public async {returnTypeContext} {ApiMethodName}({contextApiFullName} {flowContext}, {tokenFullName} token, {paramSignature})");
|
||||
sb.AppendCode(2, $"{{");
|
||||
sb.AppendCode(3, $"token.ThrowIfCancellationRequested(); // 检查任务是否取消");
|
||||
sb.AppendCode(3, $"global::{ParamTypeName} data = {ObjPoolName}.Get(); // 从对象池获取一个对象");
|
||||
for (int index = 0; index < ParamInfos.Count; index++)
|
||||
{
|
||||
ParamInfo? info = ParamInfos[index];
|
||||
sb.AppendCode(4, $"data.{info.ParamName.ToPascalCase()} = {info.ParamName}; // [{index}] {info.Comments}"); // 进行赋值
|
||||
}
|
||||
sb.AppendCode(3, $"{flowContext}.{nameof(IFlowContext.AddOrUpdate)}(\"{ApiMethodName}\", data);");
|
||||
sb.AppendCode(3, $"{flowContext}.{nameof(IFlowContext.SetPreviousNode)}(\"{NodeModel.Guid}\", \"{ApiMethodName}\");");
|
||||
sb.AppendCode(3, $"global::{typeof(CallNode).FullName} node = Get(\"{NodeModel.Guid}\");");
|
||||
sb.AppendCode(3, $"global::{typeof(FlowResult).FullName} {flowResult} = await node.{nameof(CallNode.StartFlowAsync)}({flowContext}, {token}); // 调用目标方法");
|
||||
if(ReturnType == typeof(object))
|
||||
{
|
||||
sb.AppendCode(3, $"if ({flowResult}.{nameof(FlowResult.Value)} is null)");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"return null;");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"else", isWrapping :false);
|
||||
}
|
||||
sb.AppendCode(3, $"if ({flowResult}.{nameof(FlowResult.Value)} is global::{ReturnType.FullName} result)");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"return result;");
|
||||
sb.AppendCode(3, $"}}");
|
||||
sb.AppendCode(3, $"else");
|
||||
sb.AppendCode(3, $"{{");
|
||||
sb.AppendCode(4, $"throw new ArgumentNullException($\"类型转换失败,{{(flowResult.Value is null ? \"返回数据为 null\" : $\"返回数据与需求类型不匹配,当前返回类型为[{{flowResult.Value.GetType().FullName}}。\")}}\");");
|
||||
sb.AppendCode(3, $"}}");
|
||||
//sb.AppendCode(3, $"return {flowResult};");
|
||||
sb.AppendCode(2, $"}}");
|
||||
return sb.ToString();
|
||||
// throw new ArgumentNullException($"类型转换失败,{(flowResult.Value is null ? "返回数据为 null" : $"返回数据与需求类型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}。")}");
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System.ComponentModel;
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using Serein.Library;
|
||||
using System.Security.AccessControl;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
/// <summary>
|
||||
/// 单动作节点(用于动作控件)
|
||||
|
||||
@@ -4,7 +4,7 @@ using Serein.Script;
|
||||
using System.Dynamic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
/// <summary>
|
||||
/// 条件节点(用于条件控件)
|
||||
@@ -177,7 +177,7 @@ namespace Serein.NodeFlow.Model
|
||||
context.ExceptionOfRuning = ex;
|
||||
}
|
||||
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"{result} {Expression} -> " + context.NextOrientation);
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"{Expression} -> " + context.NextOrientation);
|
||||
//return result;
|
||||
return new FlowResult(this.Guid, context, judgmentResult);
|
||||
}
|
||||
@@ -236,19 +236,18 @@ namespace Serein.NodeFlow.Model
|
||||
var dataName = nameof(data);
|
||||
if (!expression.Equals(conditionExpression))
|
||||
{
|
||||
conditionExpression = expression;
|
||||
conditionExpression = expression.Trim();
|
||||
conditionScript = new SereinScript();
|
||||
var dataType = data is null ? typeof(object) : data.GetType();
|
||||
conditionExpression = expression.Trim();
|
||||
if (expression[0] == '.')
|
||||
{
|
||||
// 对象取值
|
||||
conditionExpression = $"return {dataName}{expression};";
|
||||
conditionExpression = $"return {dataName}{conditionExpression};";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 直接表达式
|
||||
conditionExpression = $"return {dataName}.{expression};";
|
||||
conditionExpression = $"return {dataName}.{conditionExpression};";
|
||||
}
|
||||
var resultType = conditionScript.ParserScript(conditionExpression, new Dictionary<string, Type>
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using Serein.Library.Api;
|
||||
using Serein.Script;
|
||||
using System.Dynamic;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
/// <summary>
|
||||
/// Expression Operation - 表达式操作
|
||||
|
||||
@@ -3,13 +3,14 @@ using Serein.Library;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
/// <summary>
|
||||
/// 触发器节点
|
||||
/// </summary>
|
||||
public class SingleFlipflopNode : NodeModelBase
|
||||
{
|
||||
|
||||
public SingleFlipflopNode(IFlowEnvironment environment) : base(environment)
|
||||
{
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using Serein.NodeFlow.Services;
|
||||
using System.Dynamic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
|
||||
[NodeProperty(ValuePath = NodeValuePath.Node)]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using Serein.Library.Api;
|
||||
using System.Dynamic;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Serein.NodeFlow.Model
|
||||
public partial class SingleGlobalDataNode : NodeModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 表达式
|
||||
/// 全局数据的Key名称
|
||||
/// </summary>
|
||||
[PropertyInfo(IsNotification = true)]
|
||||
private string _keyName;
|
||||
@@ -43,7 +43,7 @@ namespace Serein.NodeFlow.Model
|
||||
/// <summary>
|
||||
/// 数据来源的节点
|
||||
/// </summary>
|
||||
private IFlowNode? DataNode;
|
||||
public IFlowNode? DataNode { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 有节点被放置
|
||||
@@ -52,12 +52,27 @@ namespace Serein.NodeFlow.Model
|
||||
/// <returns></returns>
|
||||
public bool PlaceNode(IFlowNode nodeModel)
|
||||
{
|
||||
if(nodeModel.ControlType is not (NodeControlType.Action or NodeControlType.Script))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, "放置在全局数据必须是有返回值的[Action]节点,[Script]节点。");
|
||||
return false;
|
||||
}
|
||||
if (nodeModel.MethodDetails?.ReturnType is null
|
||||
|| nodeModel.MethodDetails.ReturnType == typeof(void))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, "放置在全局数据必须是有返回值的[Action]节点,[Script]节点。");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(DataNode is null)
|
||||
{
|
||||
// 放置节点
|
||||
nodeModel.ContainerNode = this;
|
||||
ChildrenNode.Add(nodeModel);
|
||||
DataNode = nodeModel;
|
||||
|
||||
MethodDetails.IsAsync = nodeModel.MethodDetails.IsAsync;
|
||||
MethodDetails.ReturnType = nodeModel.MethodDetails.ReturnType;
|
||||
return true;
|
||||
}
|
||||
else if (DataNode.Guid != nodeModel.Guid)
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
|
||||
[NodeProperty(ValuePath = NodeValuePath.Node)]
|
||||
|
||||
@@ -13,7 +13,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
|
||||
[NodeProperty(ValuePath = NodeValuePath.Node)]
|
||||
@@ -263,20 +263,6 @@ namespace Serein.NodeFlow.Model
|
||||
{
|
||||
if (token.IsCancellationRequested) return new FlowResult(this.Guid, context);
|
||||
var @params = await flowCallNode.GetParametersAsync(context, token);
|
||||
//if (token.IsCancellationRequested) return new FlowResult(this.Guid, context);
|
||||
//context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改
|
||||
|
||||
/* if (IsScriptChanged)
|
||||
{
|
||||
lock (@params) {
|
||||
if (IsScriptChanged)
|
||||
{
|
||||
ReloadScript();// 执行时检查是否需要重新解析
|
||||
IsScriptChanged = false;
|
||||
context.Env.WriteLine(InfoType.INFO, $"[{Guid}]脚本解析完成");
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
IScriptInvokeContext scriptContext = new ScriptInvokeContext(context);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
public class SingleUINode : NodeModelBase
|
||||
{
|
||||
|
||||
@@ -172,40 +172,58 @@ namespace Serein.NodeFlow.Model.Operation
|
||||
#region 类型检查
|
||||
bool checkTypeState = true;
|
||||
List<ParameterDetails> toPds = new List<ParameterDetails>();
|
||||
if(ToNode.MethodDetails.ParameterDetailss is null)
|
||||
|
||||
if (FromNode.ContainerNode is not null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "目标节点没有入参参数,无法进行连接");
|
||||
SereinEnv.WriteLine(InfoType.WARN, "连接失败,起始节点处于容器中");
|
||||
return false;
|
||||
}
|
||||
if (ToNode.MethodDetails.ParameterDetailss.Length > 0)
|
||||
|
||||
if (ToNode.ContainerNode is not null)
|
||||
{
|
||||
var fromNoeReturnType = fromNode.MethodDetails.ReturnType;
|
||||
if (fromNoeReturnType != null
|
||||
&& fromNoeReturnType != typeof(object)
|
||||
&& fromNoeReturnType != typeof(void)
|
||||
&& fromNoeReturnType != typeof(Unit))
|
||||
SereinEnv.WriteLine(InfoType.WARN, "连接失败,目标节点处于容器中");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ToNode.ControlType != NodeControlType.GlobalData)
|
||||
{
|
||||
|
||||
if (ToNode.MethodDetails.ParameterDetailss is null)
|
||||
{
|
||||
var toNodePds = toNode.MethodDetails.ParameterDetailss;
|
||||
foreach (ParameterDetails toNodePd in toNodePds)
|
||||
SereinEnv.WriteLine(InfoType.WARN, "连接失败,目标节点没有入参参数。");
|
||||
return false;
|
||||
}
|
||||
if (ToNode.MethodDetails.ParameterDetailss.Length > 0)
|
||||
{
|
||||
var fromNoeReturnType = fromNode.MethodDetails.ReturnType;
|
||||
if (fromNoeReturnType != null
|
||||
&& fromNoeReturnType != typeof(object)
|
||||
&& fromNoeReturnType != typeof(void)
|
||||
&& fromNoeReturnType != typeof(Unit))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(toNodePd.ArgDataSourceNodeGuid) // 入参没有设置数据来源节点
|
||||
&& toNodePd.DataType.IsAssignableFrom(fromNoeReturnType)) // 返回值与目标入参相同(或可转换为目标入参)
|
||||
var toNodePds = toNode.MethodDetails.ParameterDetailss;
|
||||
foreach (ParameterDetails toNodePd in toNodePds)
|
||||
{
|
||||
|
||||
toPds.Add(toNodePd);
|
||||
if (string.IsNullOrWhiteSpace(toNodePd.ArgDataSourceNodeGuid) // 入参没有设置数据来源节点
|
||||
&& toNodePd.DataType.IsAssignableFrom(fromNoeReturnType)) // 返回值与目标入参相同(或可转换为目标入参)
|
||||
{
|
||||
|
||||
toPds.Add(toNodePd);
|
||||
}
|
||||
}
|
||||
if (toPds.Count == 0)
|
||||
{
|
||||
var any = toNodePds.Any(pd => pd.ArgDataSourceNodeGuid == fromNode.Guid); // 判断目标节点是否已有该节点的连接
|
||||
checkTypeState = any;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkTypeState = true; // 类型检查初步通过
|
||||
}
|
||||
}
|
||||
if (toPds.Count == 0)
|
||||
{
|
||||
var any = toNodePds.Any(pd => pd.ArgDataSourceNodeGuid == fromNode.Guid); // 判断目标节点是否已有该节点的连接
|
||||
checkTypeState = any;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkTypeState = true; // 类型检查初步通过
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!checkTypeState) // 类型检查不通过
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, "创建失败,目标节点没有合适的入参接收返回值");
|
||||
@@ -396,7 +414,11 @@ namespace Serein.NodeFlow.Model.Operation
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"连接失败,节点参数入参不允许接收多个节点返回值。起始节点[{FromNode.Guid}],目标节点[{FromNode.Guid}]。");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FromNode.ContainerNode is not null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "连接失败,参数来源节点处于容器中");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断是否建立过连接关系
|
||||
if (FromNode.Guid == toNodeArgSourceGuid && toNodeArgSourceType == ConnectionArgSourceType)
|
||||
|
||||
211
NodeFlow/Services/CoreGenerateExtension.cs
Normal file
211
NodeFlow/Services/CoreGenerateExtension.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Infos;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.NodeFlow.Services
|
||||
{
|
||||
internal static class CoreGenerateExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成流程接口信息描述
|
||||
/// </summary>
|
||||
/// <param name="flowCallNode"></param>
|
||||
/// <returns></returns>
|
||||
public static FlowApiMethodInfo? ToFlowApiMethodInfo(this SingleFlowCallNode flowCallNode)
|
||||
{
|
||||
var targetNode = flowCallNode.TargetNode;
|
||||
if (targetNode.ControlType is not (NodeControlType.Action or NodeControlType.Script)) return null;
|
||||
if (flowCallNode.MethodDetails is null) return null;
|
||||
if (string.IsNullOrWhiteSpace(flowCallNode.ApiGlobalName)) return null;
|
||||
|
||||
|
||||
FlowApiMethodInfo flowApiMethodInfo = new FlowApiMethodInfo(flowCallNode);
|
||||
flowApiMethodInfo.ReturnType = targetNode.ControlType == NodeControlType.Script ? typeof(object)
|
||||
: flowCallNode.MethodDetails.ReturnType;
|
||||
|
||||
flowApiMethodInfo.ApiMethodName = flowCallNode.ApiGlobalName;
|
||||
|
||||
List<FlowApiMethodInfo.ParamInfo> list = [];
|
||||
|
||||
int index = 0;
|
||||
foreach (var pd in flowCallNode.MethodDetails.ParameterDetailss)
|
||||
{
|
||||
if (pd.DataType is null || string.IsNullOrWhiteSpace(pd.Name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (pd.IsParams)
|
||||
{
|
||||
list.Add(new FlowApiMethodInfo.ParamInfo(pd.DataType, $"{pd.Name}{index++}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(new FlowApiMethodInfo.ParamInfo(pd.DataType, pd.Name));
|
||||
}
|
||||
}
|
||||
|
||||
flowApiMethodInfo.ParamInfos = list;
|
||||
return flowApiMethodInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成方法名称
|
||||
/// </summary>
|
||||
/// <param name="flowNode"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string ToNodeMethodName(this IFlowNode flowNode)
|
||||
{
|
||||
/*if (!flowLibraryService.TryGetMethodInfo(flowNode.MethodDetails.AssemblyName,
|
||||
flowNode.MethodDetails.MethodName,
|
||||
out var methodInfo))
|
||||
{
|
||||
throw new Exception();
|
||||
}*/
|
||||
var guid = flowNode.Guid;
|
||||
var tmp = guid.Replace("-", "");
|
||||
var methodName = $"FlowMethod_{tmp}";
|
||||
return methodName;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成完全的xml注释
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToXmlComments(this string context, int retractCount = 0)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
var startLine = "/// <summary>";
|
||||
var endLine = "/// </summary>";
|
||||
sb.AppendLine(startLine);
|
||||
var rows = context.Split(Environment.NewLine);
|
||||
string retract = new string(' ', retractCount * 4);
|
||||
foreach (var row in rows)
|
||||
{
|
||||
// 处理转义
|
||||
var value = row.Replace("<", "<")
|
||||
.Replace(">", ">");
|
||||
sb.AppendLine($"{retract}/// <para>{value}</para>");
|
||||
}
|
||||
sb.AppendLine(endLine);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成类型的驼峰命名法名称(首字母小写)
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string ToCamelCase(this Type type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
}
|
||||
|
||||
// 获取类型名称(不包括命名空间)
|
||||
string typeName = type.Name;
|
||||
|
||||
if (string.IsNullOrEmpty(typeName))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// 处理泛型类型(去掉后面的`N)
|
||||
int indexOfBacktick = typeName.IndexOf('`');
|
||||
if (indexOfBacktick > 0)
|
||||
{
|
||||
typeName = typeName.Substring(0, indexOfBacktick);
|
||||
}
|
||||
|
||||
// 如果是接口且以"I"开头,去掉第一个字母
|
||||
if (type.IsInterface && typeName.Length > 1 && typeName[0] == 'I' && char.IsUpper(typeName[1]))
|
||||
{
|
||||
typeName = typeName.Substring(1);
|
||||
}
|
||||
|
||||
// 转换为驼峰命名法:首字母小写,其余不变
|
||||
if (typeName.Length > 0)
|
||||
{
|
||||
return char.ToLowerInvariant(typeName[0]) + typeName.Substring(1);
|
||||
}
|
||||
|
||||
return typeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成类型的大驼峰命名法名称(PascalCase)
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string ToPascalCase(this Type type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
}
|
||||
|
||||
string typeName = type.Name;
|
||||
|
||||
if (string.IsNullOrEmpty(typeName))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// 去掉泛型标记(如 `1)
|
||||
int indexOfBacktick = typeName.IndexOf('`');
|
||||
if (indexOfBacktick > 0)
|
||||
{
|
||||
typeName = typeName.Substring(0, indexOfBacktick);
|
||||
}
|
||||
|
||||
// 如果是接口以 I 开头,且后面是大写,去掉前缀 I
|
||||
if (type.IsInterface && typeName.Length > 1 && typeName[0] == 'I' && char.IsUpper(typeName[1]))
|
||||
{
|
||||
typeName = typeName.Substring(1);
|
||||
}
|
||||
|
||||
// 首字母转为大写(如果有需要)
|
||||
if (typeName.Length > 0)
|
||||
{
|
||||
return char.ToUpperInvariant(typeName[0]) + typeName.Substring(1);
|
||||
}
|
||||
|
||||
return typeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串首字母大写(PascalCase)
|
||||
/// </summary>
|
||||
/// <param name="text">原始文本</param>
|
||||
/// <returns>首字母大写的文本</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string ToPascalCase(this string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (char.IsUpper(text[0]))
|
||||
{
|
||||
return text; // 已是大写
|
||||
}
|
||||
|
||||
return char.ToUpperInvariant(text[0]) + text.Substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
|
||||
namespace Serein.NodeFlow.Services
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -285,7 +286,7 @@ namespace Serein.NodeFlow.Services
|
||||
checkpoints["执行流程"] = sw.Elapsed;
|
||||
|
||||
|
||||
if (context.IsRecordInvokeInfo)
|
||||
if (context.IsRecordInvokeInfo && false)
|
||||
{
|
||||
var invokeInfos = context.GetAllInvokeInfos();
|
||||
_ = Task.Delay(100).ContinueWith(async (task) =>
|
||||
@@ -309,9 +310,7 @@ namespace Serein.NodeFlow.Services
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"平均耗时:{total / invokeInfos.Count}");
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"总耗时:{total}");
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
@@ -324,6 +323,7 @@ namespace Serein.NodeFlow.Services
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
//var checkpoints = new Dictionary<string, TimeSpan>();
|
||||
var last = TimeSpan.Zero;
|
||||
foreach (var kv in checkpoints)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user