From 5d229e104fe02ee132fd50284177f314ae738d2a Mon Sep 17 00:00:00 2001 From: "HOME\\wangxuewen01" Date: Fri, 23 Jan 2026 16:40:14 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E3=80=81=E4=BA=8B=E4=BB=B6=E7=9B=91=E5=90=AC=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=EF=BC=8C=E8=AE=BE=E5=A4=87=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E5=99=A8=E6=B7=BB=E5=8A=A0=EF=BC=8C=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E6=8E=A8=E9=80=81=E5=8F=82=E6=95=B0=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E7=BC=96=E6=8E=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/flow/spi/NodeDeserializerFactory.java | 4 +- .../edp/ai/flow/spi/NodeExecutorFactory.java | 4 +- ai-flow-mcp/pom.xml | 29 ++++ .../edp/ai/flow/mcp/factory/NodeFactory.java | 37 +++++ .../ai/flow/mcp/tool/NodeGenerateServer.java | 86 +++++++++++ .../edp/ai/flow/mcp/vo/FlowRequest.java | 18 +++ .../inspur/edp/ai/flow/mcp/vo/SimpleNode.java | 42 ++++++ .../edp/ai/flow/mcp/vo/SimpleParameter.java | 25 +++ .../ai/flow/mcp/vo/SimpleSelectorBranch.java | 24 +++ .../ai/flow/node/DeviceEventListenNode.java | 20 +++ .../DeviceEventListenNodeDeserializer.java | 21 +++ .../node/DeviceEventListenNodeExecutor.java | 26 ++++ .../inspur/edp/ai/flow/node/DeviceNode.java | 97 ++++++++++++ .../ai/flow/node/DeviceNodeDeserializer.java | 21 +++ .../edp/ai/flow/node/DeviceNodeExecutor.java | 142 ++++++++++++++++++ pom.xml | 1 + 16 files changed, 593 insertions(+), 4 deletions(-) create mode 100644 ai-flow-mcp/pom.xml create mode 100644 ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/factory/NodeFactory.java create mode 100644 ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/tool/NodeGenerateServer.java create mode 100644 ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/FlowRequest.java create mode 100644 ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleNode.java create mode 100644 ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleParameter.java create mode 100644 ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleSelectorBranch.java create mode 100644 ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNode.java create mode 100644 ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNodeDeserializer.java create mode 100644 ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNodeExecutor.java create mode 100644 ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNode.java create mode 100644 ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeDeserializer.java create mode 100644 ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java diff --git a/ai-flow-api/src/main/java/com/inspur/edp/ai/flow/spi/NodeDeserializerFactory.java b/ai-flow-api/src/main/java/com/inspur/edp/ai/flow/spi/NodeDeserializerFactory.java index c0398a2..7e62de7 100644 --- a/ai-flow-api/src/main/java/com/inspur/edp/ai/flow/spi/NodeDeserializerFactory.java +++ b/ai-flow-api/src/main/java/com/inspur/edp/ai/flow/spi/NodeDeserializerFactory.java @@ -6,7 +6,6 @@ import com.inspur.edp.ai.flow.metadata.AbsNode; import com.inspur.edp.common.type.enhanced.EnhancedServiceLoader; import com.inspur.edp.common.type.utils.JsonPropUtil; import com.inspur.edp.common.type.utils.JsonSerializeUtil; -import io.iec.edp.caf.commons.utils.SpringBeanUtils; import io.iec.edp.caf.commons.utils.StringUtils; import java.util.HashMap; @@ -53,7 +52,8 @@ public class NodeDeserializerFactory { } NodeDeserializer nodeDeserializer = NodeDeserializerFactory.get(expressKind); if (nodeDeserializer == null) { - throw new FlowException(String.format("There is no %s expression deserializer", expressKind)); + nodeDeserializer = NodeDeserializerFactory.get("device"); +// throw new FlowException(String.format("There is no %s expression deserializer", expressKind)); } if (!jsonObject.isObject()) { throw new FlowException("json node must be object node."); diff --git a/ai-flow-api/src/main/java/com/inspur/edp/ai/flow/spi/NodeExecutorFactory.java b/ai-flow-api/src/main/java/com/inspur/edp/ai/flow/spi/NodeExecutorFactory.java index b0e833a..150053d 100644 --- a/ai-flow-api/src/main/java/com/inspur/edp/ai/flow/spi/NodeExecutorFactory.java +++ b/ai-flow-api/src/main/java/com/inspur/edp/ai/flow/spi/NodeExecutorFactory.java @@ -2,7 +2,6 @@ package com.inspur.edp.ai.flow.spi; import com.inspur.edp.ai.flow.exception.FlowException; import com.inspur.edp.common.type.enhanced.EnhancedServiceLoader; -import io.iec.edp.caf.commons.utils.SpringBeanUtils; import io.iec.edp.caf.commons.utils.StringUtils; import java.util.List; @@ -40,7 +39,8 @@ public class NodeExecutorFactory { if (executor != null) { return executor; } else { - throw new FlowException("not find node executor provider " + kind); + return PROVIDER_MAP.get("device"); +// throw new FlowException("not find node executor provider " + kind); } } diff --git a/ai-flow-mcp/pom.xml b/ai-flow-mcp/pom.xml new file mode 100644 index 0000000..ddf386d --- /dev/null +++ b/ai-flow-mcp/pom.xml @@ -0,0 +1,29 @@ + + + + ai-flow-backend + com.inspur.edp + 1.0.0 + + 4.0.0 + + ai-flow-mcp + + + com.inspur.edp + ai-flow-api + 1.0.0 + compile + + + com.inspur.edp + ai-flow-node + 1.0.0 + compile + + + + + \ No newline at end of file diff --git a/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/factory/NodeFactory.java b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/factory/NodeFactory.java new file mode 100644 index 0000000..7453ccc --- /dev/null +++ b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/factory/NodeFactory.java @@ -0,0 +1,37 @@ +package com.inspur.edp.ai.flow.mcp.factory; + +import com.inspur.edp.ai.flow.metadata.AbsNode; +import com.inspur.edp.ai.flow.node.*; + +/** + * @author xww + * @Description 根据类型获取节点 + * @createTime 2026年01月20日 09:38:00 + */ +public class NodeFactory { + public static AbsNode getNodeByType(String type){ + switch (type){ + case VariableDefNode.KIND: + return new VariableDefNode(); + case StartNode.KIND: + return new StartNode(); + case EndNode.KIND: + return new EndNode(); + case SelectorNode.KIND: + return new SelectorNode(); + case BatchAssignValueNode.KIND: + return new BatchAssignValueNode(); + case LoopNode.KIND: + return new LoopNode(); + case DeviceEventListenNode.KIND: + return new DeviceEventListenNode(); + default: + return new DeviceNode() { + @Override + public String getKind() { + return type; + } + }; + } + } +} diff --git a/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/tool/NodeGenerateServer.java b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/tool/NodeGenerateServer.java new file mode 100644 index 0000000..ec18a1b --- /dev/null +++ b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/tool/NodeGenerateServer.java @@ -0,0 +1,86 @@ +package com.inspur.edp.ai.flow.mcp.tool; + +/** + * @author xww + * @Description 一个管理节点生成的服务器 + * @createTime 2026年01月19日 15:51:00 + */ +import com.fasterxml.jackson.databind.JsonNode; +import com.inspur.edp.ai.flow.mcp.factory.NodeFactory; +import com.inspur.edp.ai.flow.mcp.vo.FlowRequest; +import com.inspur.edp.ai.flow.mcp.vo.SimpleNode; +import com.inspur.edp.ai.flow.metadata.AbsNode; +import com.inspur.edp.ai.flow.metadata.FlowMetadata; +import com.inspur.edp.ai.flow.node.BatchAssignValueNode; +import com.inspur.edp.ai.flow.node.DeviceEventListenNode; +import com.inspur.edp.ai.flow.node.DeviceNode; +import com.inspur.edp.ai.flow.node.SelectorNode; +import com.inspur.edp.common.expr.expresses.setvalue.AssignValueExpr; +import com.inspur.edp.common.type.utils.JsonSerializeUtil; +import io.iec.edp.caf.commons.utils.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class NodeGenerateServer { + public static String generateFlow(FlowRequest flowRequest) { + FlowMetadata flowMetadata=new FlowMetadata(); + List absNodes=new ArrayList<>(); + List simpleNodes=flowRequest.getNodes(); + simpleNodes.forEach(simpleNode -> { + AbsNode node= NodeFactory.getNodeByType(simpleNode.getKind()); + JsonNode graphMeta= JsonSerializeUtil.toJsonNode("{position: {x: 1007.1470588235293, y: -208.15257352941177}, deletable: true}"); + node.setGraphMeta(graphMeta); + node.setId(simpleNode.getId()); + node.setName(simpleNode.getName()); + node.setInputPorts(simpleNode.getInputPorts()); + node.setOutputPorts(simpleNode.getOutputPorts()); + //设置入参 + node.setInputParams(simpleNode.getInputParams()); + //设置出参 + node.setOutputParams(simpleNode.getOutputParams()); + node.setCode(node.getKind()+"_"+node.getId()); + //赋值节点处理表达式 + if(BatchAssignValueNode.KIND.equals(node.getKind())){ + List expresses=simpleNode.getExpresses(); + ((BatchAssignValueNode)node).setExpresses(expresses); + } + //条件节点处理表达式 + if(SelectorNode.KIND.equals(node.getKind())){ + List branches=simpleNode.getBranches(); + ((SelectorNode)node).setBranches(branches); + } + //设备节点处理,设备节点的kind是动态随机的 + try{ + DeviceNode deviceNode=(DeviceNode)node; + if(!StringUtils.isEmpty(deviceNode.getDeviceId())){ + ((DeviceNode)node).setDeviceId(simpleNode.getDeviceId()); + ((DeviceNode)node).setDeviceAction(simpleNode.getDeviceAction()); + ((DeviceNode)node).setRequestType(simpleNode.getRequestType()); + ((DeviceNode)node).setApiEndpoint(simpleNode.getApiEndpoint()); + } + }catch (Throwable e){ + //暂不处理 + } + //设备监听节点处理 + if(DeviceEventListenNode.KIND.equals(node.getKind())){ + ((DeviceEventListenNode)node).setDeviceEvent(simpleNode.getDeviceEvent()); + ((DeviceEventListenNode)node).setDeviceCategory(simpleNode.getDeviceCategory()); + } + absNodes.add(node); + }); + flowMetadata.setNodes(absNodes); + flowMetadata.setEdges(flowRequest.getEdges()); + flowMetadata.setId(UUID.randomUUID().toString()); + flowMetadata.setName(flowRequest.getName()); + flowMetadata.setKind("workflow"); + flowMetadata.setCode(UUID.randomUUID().toString()); + flowMetadata.setBizTypeId("BP"); + flowMetadata.setSysInit(false); + flowMetadata.setVersion("v1"); + JsonNode graphMeta= JsonSerializeUtil.toJsonNode("{__initialized__: true}"); + flowMetadata.setExtension(graphMeta); + return JsonSerializeUtil.toJson(flowMetadata); + } +} \ No newline at end of file diff --git a/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/FlowRequest.java b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/FlowRequest.java new file mode 100644 index 0000000..ea97d49 --- /dev/null +++ b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/FlowRequest.java @@ -0,0 +1,18 @@ +package com.inspur.edp.ai.flow.mcp.vo; + +import com.inspur.edp.ai.flow.metadata.Edge; +import lombok.Data; + +import java.util.List; + +/** + * @author xww + * @Description TODO + * @createTime 2026年01月20日 16:40:00 + */ +@Data +public class FlowRequest { + private List nodes; + private String name; + private List edges; +} diff --git a/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleNode.java b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleNode.java new file mode 100644 index 0000000..aba98fa --- /dev/null +++ b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleNode.java @@ -0,0 +1,42 @@ +package com.inspur.edp.ai.flow.mcp.vo; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.inspur.edp.ai.flow.metadata.Parameter; +import com.inspur.edp.ai.flow.node.SelectorNode; +import com.inspur.edp.common.expr.ExprListJsonDeserializer; +import com.inspur.edp.common.expr.Express; +import com.inspur.edp.common.expr.expresses.setvalue.AssignValueExpr; +import lombok.Data; + +import java.util.List; + +/** + * @author xww + * @Description TODO + * @createTime 2026年01月20日 16:43:00 + */ +@Data +public class SimpleNode { + private String id; + private String name; + private String kind; + private List inputPorts; + private List outputPorts; + private List inputParams; + private List outputParams; + @JsonDeserialize(using = ExprListJsonDeserializer.class) + private List expresses; + private List branches; + /** + * 设备节点相关信息 + */ + private String deviceId; + private String deviceAction; + private String apiEndpoint; + private String requestType; + /** + * 设备监听节点相关信息 + */ + private String deviceCategory; + private String deviceEvent; +} diff --git a/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleParameter.java b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleParameter.java new file mode 100644 index 0000000..37dbf25 --- /dev/null +++ b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleParameter.java @@ -0,0 +1,25 @@ +package com.inspur.edp.ai.flow.mcp.vo; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.inspur.edp.common.expr.ExprJsonDeserializer; +import com.inspur.edp.common.expr.Express; +import lombok.Data; + +import java.util.List; + +/** + * @author xww + * @Description TODO + * @createTime 2026年01月20日 16:45:00 + */ +@Data +public class SimpleParameter { + private String code; + private List typeIds; + @JsonDeserialize(using = ExprJsonDeserializer.class) + private Express valueExpr; + /** + * 支持手写的场景 + */ + private String value; +} diff --git a/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleSelectorBranch.java b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleSelectorBranch.java new file mode 100644 index 0000000..43ef2c7 --- /dev/null +++ b/ai-flow-mcp/src/main/java/com/inspur/edp/ai/flow/mcp/vo/SimpleSelectorBranch.java @@ -0,0 +1,24 @@ +package com.inspur.edp.ai.flow.mcp.vo; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.inspur.edp.common.expr.ExprJsonDeserializer; +import com.inspur.edp.common.expr.ExprListJsonDeserializer; +import com.inspur.edp.common.expr.Express; +import com.inspur.edp.common.expr.expresses.logic.LogicExpr; +import com.inspur.edp.common.expr.expresses.logic.LogicOperator; +import lombok.Data; + +import java.util.List; + +/** + * @author xww + * @Description TODO + * @createTime 2026年01月20日 18:26:00 + */ +@Data +public class SimpleSelectorBranch { + @JsonDeserialize(using = LogicExpr.LogicOperatorDeserializer.class) + private LogicOperator operator; + @JsonDeserialize(using = ExprListJsonDeserializer.class) + private List expresses; +} diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNode.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNode.java new file mode 100644 index 0000000..05a3fc7 --- /dev/null +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNode.java @@ -0,0 +1,20 @@ +package com.inspur.edp.ai.flow.node; + +import com.inspur.edp.ai.flow.metadata.AbsNode; +import lombok.Data; + +/** + * @author xww + * @Description 设备监听节点 + * @createTime 2026年01月23日 09:50:00 + */ +@Data +public class DeviceEventListenNode extends AbsNode { + public static final String KIND = "deviceEventListen"; + + public DeviceEventListenNode() { + this.setKind(KIND); + } + private String deviceCategory; + private String deviceEvent; +} diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNodeDeserializer.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNodeDeserializer.java new file mode 100644 index 0000000..71fc7ee --- /dev/null +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNodeDeserializer.java @@ -0,0 +1,21 @@ +package com.inspur.edp.ai.flow.node; + +import com.inspur.edp.ai.flow.spi.NodeDeserializer; + +/** + * @author lizhaorui + * @date 2025/9/24 + * @description + */ +public class DeviceEventListenNodeDeserializer implements NodeDeserializer { + + @Override + public String getKind() { + return DeviceEventListenNode.KIND; + } + + @Override + public Class getNodeClass() { + return DeviceEventListenNode.class; + } +} \ No newline at end of file diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNodeExecutor.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNodeExecutor.java new file mode 100644 index 0000000..abc4751 --- /dev/null +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceEventListenNodeExecutor.java @@ -0,0 +1,26 @@ +package com.inspur.edp.ai.flow.node; + +import com.inspur.edp.ai.flow.engine.FlowContext; +import com.inspur.edp.ai.flow.metadata.AbsNode; +import com.inspur.edp.ai.flow.spi.NodeExecuteResult; +import com.inspur.edp.ai.flow.spi.NodeExecutor; + +import java.util.Map; + +/** + * @author xww + * @Description 设备监听执行器 + * @createTime 2026年01月23日 09:54:00 + */ +public class DeviceEventListenNodeExecutor implements NodeExecutor { + @Override + public String getKind() { + return DeviceEventListenNode.KIND; + } + + @Override + public NodeExecuteResult execute(FlowContext context, AbsNode node, Map argMap) { + //监听到设备发来的消息,当作开始节点开始执行 + return null; + } +} diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNode.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNode.java new file mode 100644 index 0000000..fa329a4 --- /dev/null +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNode.java @@ -0,0 +1,97 @@ +package com.inspur.edp.ai.flow.node; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; +import com.inspur.edp.ai.flow.metadata.AbsNode; +import com.inspur.edp.ai.flow.metadata.Parameter; +import com.inspur.edp.common.expr.evaluator.ExpressEvaluator; +import lombok.Data; + +import java.util.List; + +/** + * @author xww + * @Description 设备节点:各种类型的设备 + * @createTime 2026年01月22日 11:25:00 + */ +@Data +public class DeviceNode extends AbsNode{ + public static final String KIND = "device"; + private String id; + //设备类型:摄像头、咖啡机、机器人等 + private String kind; + + private String code; + + private String name; + + private String description; + /** + * 是否为有状态节点 + */ + private boolean stateful; + private boolean inputSelf; + + /** + * 输入参数列表 + */ + private List inputParams; + + /** + * 输出参数列表 + */ + private List outputParams; + + /** + * 输入端口列表 + */ + private List inputPorts; + + /** + * 输出端口列表 + */ + private List outputPorts; + + /** + * 前端画布属性,json结构如下: + * { + * "position": { + * "x": 820, + * "y": 33.30000000000001 + * } + * } + */ + private JsonNode graphMeta; + + /** + * 是否为返回值节点 + */ + private boolean isReturnNode; + /** + * 设备ID + */ + private String deviceId; + /** + * 请求url + */ + private String apiEndpoint; + /** + * 请求类型:POST/GET + */ + private String requestType; + /** + * 设备调用方法 + */ + private String deviceAction; + /** + * 超时时间 + */ + private int timeOut; + /** + * 各个设备节点其他属性信息 + */ + private JsonNode properties; + + @JsonIgnore + private List expressEvaluators; +} diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeDeserializer.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeDeserializer.java new file mode 100644 index 0000000..9b921eb --- /dev/null +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeDeserializer.java @@ -0,0 +1,21 @@ +package com.inspur.edp.ai.flow.node; + +import com.inspur.edp.ai.flow.spi.NodeDeserializer; + +/** + * @author lizhaorui + * @date 2025/9/24 + * @description + */ +public class DeviceNodeDeserializer implements NodeDeserializer { + + @Override + public String getKind() { + return DeviceNode.KIND; + } + + @Override + public Class getNodeClass() { + return DeviceNode.class; + } +} \ No newline at end of file diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java new file mode 100644 index 0000000..ca251dd --- /dev/null +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java @@ -0,0 +1,142 @@ +package com.inspur.edp.ai.flow.node; +import com.inspur.edp.ai.flow.engine.FlowContext; +import com.inspur.edp.ai.flow.engine.strategy.DefaultFlowStrategy; +import com.inspur.edp.ai.flow.metadata.AbsNode; +import com.inspur.edp.ai.flow.metadata.Parameter; +import com.inspur.edp.ai.flow.spi.NodeExecuteResult; +import com.inspur.edp.ai.flow.spi.NodeExecutor; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +/** + * @author xww + * @Description 设备统一执行器 + * @createTime 2026年01月22日 14:34:00 + */ +public class DeviceNodeExecutor implements NodeExecutor { + @Override + public String getKind() { + return "device"; + } + + @Override + public NodeExecuteResult execute(FlowContext context, AbsNode node, Map argMap) { + DeviceNode deviceNode = (DeviceNode) node; + //根据节点信息组织参数、请求http并获取返回值 + Object result = invokeRestService(deviceNode,argMap); + //返回结果 + NodeExecuteResult nodeExecuteResult = new NodeExecuteResult(); + DefaultFlowStrategy strategy = new DefaultFlowStrategy(node.getId(), node.getOutputPorts().get(0)); + nodeExecuteResult.setFlowStrategy(strategy); + List outputParams = node.getOutputParams(); + if (outputParams != null && !outputParams.isEmpty()) { + Map outputObjects = new HashMap<>(); + outputObjects.put(node.getOutputParams().get(0).getCode(), result); + nodeExecuteResult.setVariablesToAdd(outputObjects); + } + return nodeExecuteResult; + } + + private Object invokeRestService(DeviceNode deviceNode,Map argMap) { + String apiEndPoint=deviceNode.getApiEndpoint(); + String action=deviceNode.getDeviceAction(); + String deviceId=deviceNode.getDeviceId(); + String requestType=deviceNode.getRequestType(); + //根据需要组织参数,目前传递设备id和执行动作 + StringBuffer params=new StringBuffer("{\"deviceId\":\""+deviceId+"\",\"action\":\""+action+"\""); + List parameters=deviceNode.getInputParams(); + for (Parameter parameter : parameters) { + Object paramValue=null; + if (parameter.getValueExprEvaluator() != null) { + paramValue = parameter.getValueExprEvaluator().evaluate(argMap); + } + if (paramValue instanceof Integer || paramValue instanceof Long || paramValue instanceof Float || paramValue instanceof Double ||paramValue instanceof Short || paramValue instanceof BigDecimal){ + params.append(",\"").append(parameter.getCode()).append("\":").append(paramValue); + }else{ + params.append(",\"").append(parameter.getCode()).append("\":").append("\"").append(paramValue).append("\""); + } + } + params.append("}"); + int timeOut= deviceNode.getTimeOut(); + if(timeOut==0){//默认超时时间5分钟 + timeOut=5*60*1000; + } + HttpServletRequest request; + Cookie[] cookies = new Cookie[0]; + HttpURLConnection conn = null; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + conn = (HttpURLConnection) new URL(apiEndPoint).openConnection(); + if (RequestContextHolder.getRequestAttributes() != null) { + request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + cookies = request.getCookies(); + } + StringBuilder cookieStr = new StringBuilder(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookieStr.length() > 0) { + cookieStr.append(";"); + } + cookieStr.append(cookie.getName()).append("=").append(cookie.getValue()); + } + } + conn.setRequestMethod(requestType); + conn.setRequestProperty("Charset", "UTF-8"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Cookie", cookieStr.toString()); + conn.setRequestProperty("Accept-Encoding", "gzip,deflate"); + conn.setFollowRedirects(true); + conn.setConnectTimeout(timeOut); + conn.setReadTimeout(timeOut); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setAllowUserInteraction(false); + try (OutputStream out = conn.getOutputStream()) { + out.write(params.toString().getBytes()); + out.flush(); + } + int responseCode = conn.getResponseCode(); + if (responseCode != 200) { + return "Error responseCode:" + responseCode+" deviceId is "+deviceNode.getDeviceId(); + } + String contentEncoding = conn.getHeaderField("Content-Encoding"); + try (InputStream inputStream = conn.getInputStream(); + InputStream decompressedStream = "gzip".equals(contentEncoding) ? new GZIPInputStream(inputStream) : inputStream; + ) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = decompressedStream.read(buffer)) != -1) { + baos.write(buffer, 0, bytesRead); + } + } + return new String(baos.toByteArray(), StandardCharsets.UTF_8); + } catch (Exception e) { + return "error, deviceId is "+deviceNode.getDeviceId() +"detail:"+e.getMessage(); + } finally { + if (conn != null) { + conn.disconnect(); + } + if(baos!=null){ + try { + baos.close(); + } catch (IOException e) { + } + } + } + } +} diff --git a/pom.xml b/pom.xml index e341df8..85b7163 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ common-type-ext common-function ai-flow-demo + ai-flow-mcp -- Gitee From ae4ffd9e4d553e2b9aa07cee0e0cf1a41f5e2091 Mon Sep 17 00:00:00 2001 From: "HOME\\wangxuewen01" Date: Tue, 10 Feb 2026 10:19:01 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=9B=91=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai-flow-node/pom.xml | 6 ++- .../edp/ai/flow/node/DeviceNodeExecutor.java | 16 ++++++- .../inspur/edp/ai/flow/node/EventTest.java | 47 +++++++++++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/EventTest.java diff --git a/ai-flow-node/pom.xml b/ai-flow-node/pom.xml index 4dcf72f..4d09f23 100644 --- a/ai-flow-node/pom.xml +++ b/ai-flow-node/pom.xml @@ -18,7 +18,11 @@ compile - + + org.java-websocket + Java-WebSocket + 1.5.2 + diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java index ca251dd..638cfb7 100644 --- a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java @@ -18,6 +18,7 @@ import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -51,8 +52,19 @@ public class DeviceNodeExecutor implements NodeExecutor { } return nodeExecuteResult; } - - private Object invokeRestService(DeviceNode deviceNode,Map argMap) { + public static void main(String[] args){ + DeviceNode deviceNode=new DeviceNode(); + deviceNode.setApiEndpoint("http://localhost:3000/devices/command"); + deviceNode.setRequestType("POST"); + deviceNode.setInputParams(new ArrayList<>()); + deviceNode.setDeviceId("ac001"); + deviceNode.setDeviceAction("setTemperature"); + List inputParams=new ArrayList<>(); + deviceNode.setInputParams(inputParams); + String result=(String)invokeRestService(deviceNode,null); + System.out.println(result); + } + private static Object invokeRestService(DeviceNode deviceNode,Map argMap) { String apiEndPoint=deviceNode.getApiEndpoint(); String action=deviceNode.getDeviceAction(); String deviceId=deviceNode.getDeviceId(); diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/EventTest.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/EventTest.java new file mode 100644 index 0000000..3cdd435 --- /dev/null +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/EventTest.java @@ -0,0 +1,47 @@ +package com.inspur.edp.ai.flow.node; + + +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; + +import java.net.URI; +import java.net.URISyntaxException; +/** + * @author xww + * @Description websocket测试类 + * @createTime 2026年02月10日 09:38:00 + */ +public class EventTest extends WebSocketClient { + public EventTest(URI serverUri) { + super(serverUri); + } + @Override + public void onOpen(ServerHandshake handshakedata) { + System.out.println("新连接已打开"); + } + + @Override + public void onMessage(String message) { + System.out.println("收到消息: " + message); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + System.out.println("连接已关闭: " + reason); + } + + @Override + public void onError(Exception ex) { + System.err.println("发生错误: " + ex.getMessage()); + } + + + public static void main(String[] args) { + try { + EventTest client = new EventTest(new URI("ws://localhost:3000/ws")); // 替换为你的WebSocket服务器地址和端点 + client.connect(); // 尝试连接服务器 + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } +} -- Gitee From 8f4a8888b2dd75d72f3baf2da8d3a6245de7dcb5 Mon Sep 17 00:00:00 2001 From: "HOME\\wangxuewen01" Date: Thu, 12 Feb 2026 15:42:19 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=B5=8B=E8=AF=95demo=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../edp/ai/flow/node/DeviceNodeExecutor.java | 28 +++++++++++++++---- .../inspur/edp/ai/flow/node/EventTest.java | 2 +- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java index 638cfb7..6814240 100644 --- a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/DeviceNodeExecutor.java @@ -5,6 +5,8 @@ import com.inspur.edp.ai.flow.metadata.AbsNode; import com.inspur.edp.ai.flow.metadata.Parameter; import com.inspur.edp.ai.flow.spi.NodeExecuteResult; import com.inspur.edp.ai.flow.spi.NodeExecutor; +import com.inspur.edp.common.expr.evaluator.StringConstEvaluator; +import com.inspur.edp.common.expr.expresses.constant.StringConstExpr; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -54,12 +56,20 @@ public class DeviceNodeExecutor implements NodeExecutor { } public static void main(String[] args){ DeviceNode deviceNode=new DeviceNode(); - deviceNode.setApiEndpoint("http://localhost:3000/devices/command"); + deviceNode.setApiEndpoint("http://139.196.239.110:5174/devices/command"); deviceNode.setRequestType("POST"); deviceNode.setInputParams(new ArrayList<>()); - deviceNode.setDeviceId("ac001"); - deviceNode.setDeviceAction("setTemperature"); + deviceNode.setDeviceId("coffee001"); + deviceNode.setDeviceAction("makeCoffee"); List inputParams=new ArrayList<>(); + Parameter parameter=new Parameter(); + parameter.setCode("coffee_type"); + parameter.setName("coffee_type"); + StringConstExpr stringConstExpr =new StringConstExpr(); + parameter.setValue("Espresso"); + parameter.setValueExpr(stringConstExpr); + parameter.setValueExprEvaluator(new StringConstEvaluator("Espresso")); + inputParams.add(parameter); deviceNode.setInputParams(inputParams); String result=(String)invokeRestService(deviceNode,null); System.out.println(result); @@ -72,18 +82,26 @@ public class DeviceNodeExecutor implements NodeExecutor { //根据需要组织参数,目前传递设备id和执行动作 StringBuffer params=new StringBuffer("{\"deviceId\":\""+deviceId+"\",\"action\":\""+action+"\""); List parameters=deviceNode.getInputParams(); + params.append(",\"").append("params").append("\":").append("{"); + int i=0; for (Parameter parameter : parameters) { + if(i>0){ + params.append(","); + } Object paramValue=null; if (parameter.getValueExprEvaluator() != null) { paramValue = parameter.getValueExprEvaluator().evaluate(argMap); } if (paramValue instanceof Integer || paramValue instanceof Long || paramValue instanceof Float || paramValue instanceof Double ||paramValue instanceof Short || paramValue instanceof BigDecimal){ - params.append(",\"").append(parameter.getCode()).append("\":").append(paramValue); + params.append("\"").append(parameter.getCode()).append("\":").append(paramValue); }else{ - params.append(",\"").append(parameter.getCode()).append("\":").append("\"").append(paramValue).append("\""); + params.append("\"").append(parameter.getCode()).append("\":").append("\"").append(paramValue).append("\""); } + i++; } params.append("}"); + params.append("}"); + System.out.println(params); int timeOut= deviceNode.getTimeOut(); if(timeOut==0){//默认超时时间5分钟 timeOut=5*60*1000; diff --git a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/EventTest.java b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/EventTest.java index 3cdd435..0e05987 100644 --- a/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/EventTest.java +++ b/ai-flow-node/src/main/java/com/inspur/edp/ai/flow/node/EventTest.java @@ -38,7 +38,7 @@ public class EventTest extends WebSocketClient { public static void main(String[] args) { try { - EventTest client = new EventTest(new URI("ws://localhost:3000/ws")); // 替换为你的WebSocket服务器地址和端点 + EventTest client = new EventTest(new URI("ws://139.196.239.110:5174/ws")); // 替换为你的WebSocket服务器地址和端点 client.connect(); // 尝试连接服务器 } catch (URISyntaxException e) { e.printStackTrace(); -- Gitee