diff --git a/.drone.yml b/.drone.yml
index d608371..b4599fa 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -27,11 +27,9 @@ steps:
from_secret: docker_password
REGISTRY:
from_secret: registry
- REGISTRY_NAMESPACE:
- from_secret: registry_namespace
commands:
- sed -i 's/$REGISTRY/'"$REGISTRY"'/' deployment.yml
- - sed -i 's/$REGISTRY_NAMESPACE/'"$REGISTRY_NAMESPACE"'/' deployment.yml
+ - sed -i 's/$REGISTRY_NAMESPACE/'"metis"'/' deployment.yml
- sed -i 's/$DRONE_COMMIT/${DRONE_COMMIT}/' deployment.yml
- sed -i 's/$DRONE_REPO_NAME/${DRONE_REPO_NAME}/' deployment.yml
- echo $DOCKER_PASSWORD | docker login $REGISTRY --username $DOCKER_USERNAME --password-stdin
@@ -47,6 +45,35 @@ steps:
- kubectl apply -f deployment.yml -n metis --kubeconfig=/app/config/base-taishan-kubectl.yml
+ - name: notify
+ image: 10.7.127.190:38080/plugins/webhook:latest
+ environment:
+ NOTIFY_WX_URL:
+ from_secret: notify_wx_url
+ when:
+ status: [ success,failure ]
+ settings:
+ urls:
+ from_secret: notify_wx_url
+ content_type: application/json
+ template: |
+ {
+ "msgtype": "markdown",
+ "markdown": {
+ "content": "{{#success build.status}}✅ 构建成功{{else}}❌ 构建失败{{/success}}
+ >**构建编号**: #{{build.number}}
+ >**构建状态**: {{build.status}}
+ >**代码分支**: {{build.branch}}
+ >**提交哈希**: {{build.commit}}
+ >**提交作者**: {{build.author}}
+ >**持续时间**: {{build.duration}}秒
+ >**提交信息**: {{build.message}}
+ >[查看构建详情]({{build.link}})
+ >{{^success build.status}}[查看失败日志]({{build.link}}/logs){{/success}}"
+ }
+ }
+
+
volumes:
- name: config
host:
diff --git a/Dockerfile b/Dockerfile
index 8da31a9..2d4faca 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,6 +6,6 @@ WORKDIR /app
# 定义时区参数并设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
-COPY ./target/metis-1.0.0-SNAPSHOT.jar /app/metis.jar
+COPY ./metis-applicant/target/metis-applicant-1.0.0-SNAPSHOT.jar /app/metis.jar
RUN chmod 755 -R /app/
CMD java -jar /app/metis.jar -Xms256m -Xmx512m --spring.profiles.active=${PROFILES} --server.port=${SERVICE_PORTS}
diff --git a/metis-applicant/pom.xml b/metis-applicant/pom.xml
new file mode 100644
index 0000000..6900ad5
--- /dev/null
+++ b/metis-applicant/pom.xml
@@ -0,0 +1,93 @@
+
+
+ 4.0.0
+
+ com.metis
+ metis
+ 1.0.0-SNAPSHOT
+
+
+ metis-applicant
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ com.metis
+ metis-starter
+ 1.0.0-SNAPSHOT
+
+
+
+ com.github.xiaoymin
+ knife4j-openapi3-jakarta-spring-boot-starter
+ 4.4.0
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+ org.mapstruct
+ mapstruct-processor
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 17
+ 17
+ UTF-8
+
+ -parameters
+ --add-opens
+ java.base/java.lang=ALL-UNNAMED
+
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${org.mapstruct.version}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/metis/MetisApplication.java b/metis-applicant/src/main/java/com/metisapp/MetisApplication.java
similarity index 93%
rename from src/main/java/com/metis/MetisApplication.java
rename to metis-applicant/src/main/java/com/metisapp/MetisApplication.java
index b9dc604..5feb08a 100644
--- a/src/main/java/com/metis/MetisApplication.java
+++ b/metis-applicant/src/main/java/com/metisapp/MetisApplication.java
@@ -1,4 +1,4 @@
-package com.metis;
+package com.metisapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git a/src/main/java/com/metis/config/SwaggerConfig.java b/metis-applicant/src/main/java/com/metisapp/config/SwaggerConfig.java
similarity index 94%
rename from src/main/java/com/metis/config/SwaggerConfig.java
rename to metis-applicant/src/main/java/com/metisapp/config/SwaggerConfig.java
index 744fe14..77feba0 100644
--- a/src/main/java/com/metis/config/SwaggerConfig.java
+++ b/metis-applicant/src/main/java/com/metisapp/config/SwaggerConfig.java
@@ -1,7 +1,6 @@
-package com.metis.config;
+package com.metisapp.config;
import io.swagger.v3.oas.models.OpenAPI;
-import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
diff --git a/metis-applicant/src/main/java/com/metisapp/controller/TestController.java b/metis-applicant/src/main/java/com/metisapp/controller/TestController.java
new file mode 100644
index 0000000..be0f467
--- /dev/null
+++ b/metis-applicant/src/main/java/com/metisapp/controller/TestController.java
@@ -0,0 +1,38 @@
+package com.metisapp.controller;
+
+import com.metis.domain.bo.BuildApp;
+import com.metis.engine.AppFlowEngineRunnerService;
+import com.metis.runner.FlowRunningContext;
+import com.metis.runner.RunnerResult;
+import com.metis.validator.ValidatorService;
+import com.metis.result.Result;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/test")
+@RequiredArgsConstructor
+public class TestController {
+
+
+ private final ValidatorService validatorService;
+ private final AppFlowEngineRunnerService engineRunnerService;
+
+ @PostMapping
+ public Result test(@RequestBody BuildApp app) {
+ validatorService.validate(app);
+ return Result.ok("测试成功");
+ }
+
+
+ @PostMapping("/run")
+ public Result run(@RequestBody FlowRunningContext context) {
+ RunnerResult running = engineRunnerService.running(context);
+ return Result.ok(running);
+ }
+
+
+}
diff --git a/metis-applicant/src/main/java/com/metisapp/custom/CustomTestConfig.java b/metis-applicant/src/main/java/com/metisapp/custom/CustomTestConfig.java
new file mode 100644
index 0000000..b7028c1
--- /dev/null
+++ b/metis-applicant/src/main/java/com/metisapp/custom/CustomTestConfig.java
@@ -0,0 +1,10 @@
+package com.metisapp.custom;
+
+import com.metis.domain.entity.base.NodeConfig;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CustomTestConfig extends NodeConfig {
+}
diff --git a/metis-applicant/src/main/java/com/metisapp/custom/CustomTestRunner.java b/metis-applicant/src/main/java/com/metisapp/custom/CustomTestRunner.java
new file mode 100644
index 0000000..d3ec3be
--- /dev/null
+++ b/metis-applicant/src/main/java/com/metisapp/custom/CustomTestRunner.java
@@ -0,0 +1,27 @@
+package com.metisapp.custom;
+
+import com.metis.domain.context.RunningContext;
+import com.metis.domain.context.RunningResult;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.runner.CustomNodeRunner;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+public class CustomTestRunner implements CustomNodeRunner {
+
+ @Override
+ public String getCustomNodeType() {
+ return "test";
+ }
+
+ @Override
+ public RunningResult run(RunningContext context, Node node, List edges) {
+ log.info("自定义节点测试");
+ return RunningResult.buildResult();
+ }
+}
diff --git a/metis-applicant/src/main/java/com/metisapp/custom/CustomTestValidator.java b/metis-applicant/src/main/java/com/metisapp/custom/CustomTestValidator.java
new file mode 100644
index 0000000..60ce8b7
--- /dev/null
+++ b/metis-applicant/src/main/java/com/metisapp/custom/CustomTestValidator.java
@@ -0,0 +1,32 @@
+package com.metisapp.custom;
+
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.validator.CustomNodeValidator;
+import com.metis.validator.ValidatorResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+public class CustomTestValidator implements CustomNodeValidator {
+
+ @Override
+ public String getCustomNodeType() {
+ return "test";
+ }
+
+ @Override
+ public ValidatorResult validateValue(Node node) {
+ CustomTestConfig config = node.getConfig();
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public ValidatorResult validateRelation(Node node, List sources, List targets) {
+ CustomTestConfig config = node.getConfig();
+ return ValidatorResult.valid();
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/metis-applicant/src/main/resources/application-dev.yml
similarity index 100%
rename from src/main/resources/application-dev.yml
rename to metis-applicant/src/main/resources/application-dev.yml
diff --git a/src/main/resources/application-test.yml b/metis-applicant/src/main/resources/application-test.yml
similarity index 100%
rename from src/main/resources/application-test.yml
rename to metis-applicant/src/main/resources/application-test.yml
diff --git a/src/main/resources/application.yml b/metis-applicant/src/main/resources/application.yml
similarity index 91%
rename from src/main/resources/application.yml
rename to metis-applicant/src/main/resources/application.yml
index 57c0935..d183ce5 100644
--- a/src/main/resources/application.yml
+++ b/metis-applicant/src/main/resources/application.yml
@@ -34,10 +34,6 @@ springdoc:
swagger-ui:
tags-sorter: alpha
group-configs:
- - group: bis
- display-name: "业务接口文档"
- paths-to-match: '/**'
- packages-to-scan: org.shi9.module.bis
- group: system
display-name: "系统接口文档"
paths-to-match: '/**'
diff --git a/metis-applicant/src/main/resources/json/run.json b/metis-applicant/src/main/resources/json/run.json
new file mode 100644
index 0000000..c7e457d
--- /dev/null
+++ b/metis-applicant/src/main/resources/json/run.json
@@ -0,0 +1,7 @@
+{
+ "appId": "1909636986931470336",
+ "userId": 1,
+ "custom": {
+ "context": "测试内容!"
+ }
+}
\ No newline at end of file
diff --git a/metis-applicant/src/main/resources/json/test.json b/metis-applicant/src/main/resources/json/test.json
new file mode 100644
index 0000000..05521ec
--- /dev/null
+++ b/metis-applicant/src/main/resources/json/test.json
@@ -0,0 +1,567 @@
+{
+ "id": 0,
+ "name": "测试流程",
+ "description": "测试流程",
+ "graph": {
+ "nodes": [
+ {
+ "id": "5",
+ "type": "start",
+ "initialized": false,
+ "position": {
+ "x": -81.81250000000003,
+ "y": 275.49609375
+ },
+ "data": {
+ "label": "开始",
+ "icon": "SuitcaseLine",
+ "toolbarPosition": "right",
+ "description": "开始述描述",
+ "config": {
+ "parent": "234"
+ },
+ "handles": [
+ {
+ "id": "51",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ],
+ "item": {
+ "id": "2",
+ "type": "run",
+ "labelType": "knowledge",
+ "label": "知识检索"
+ }
+ },
+ "width": 200,
+ "height": 40
+ },
+ {
+ "id": "6",
+ "type": "end",
+ "initialized": false,
+ "position": {
+ "x": 1281.582055572882,
+ "y": 236.2912067630247
+ },
+ "data": {
+ "label": "结束",
+ "toolbarPosition": "right",
+ "handles": [
+ {
+ "id": "61",
+ "position": "left",
+ "type": "target",
+ "connectable": true
+ }
+ ]
+ },
+ "width": 200,
+ "height": 40
+ },
+ {
+ "id": "188",
+ "type": "custom",
+ "customType": "test",
+ "initialized": false,
+ "position": {
+ "x": 265.87532955148635,
+ "y": 71.40983063296031
+ },
+ "data": {
+ "label": "llm1",
+ "icon": "",
+ "description": "llm描述描述",
+ "toolbarPosition": "right",
+ "config": {
+ "labelType": "llm"
+ },
+ "handles": [
+ {
+ "id": "11",
+ "position": "left",
+ "type": "source",
+ "connectable": true
+ },
+ {
+ "id": "45",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ]
+ },
+ "width": 200,
+ "height": null
+ },
+ {
+ "id": "850",
+ "type": "llm",
+ "initialized": false,
+ "position": {
+ "x": 269.7896091295129,
+ "y": 253.80570624004747
+ },
+ "data": {
+ "label": "llm2",
+ "icon": "",
+ "description": "llm描述描述",
+ "toolbarPosition": "right",
+ "config": {
+ "labelType": "llm"
+ },
+ "handles": [
+ {
+ "id": "43",
+ "position": "left",
+ "type": "source",
+ "connectable": true
+ },
+ {
+ "id": "57",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ]
+ },
+ "width": 200,
+ "height": null
+ },
+ {
+ "id": "979",
+ "type": "llm",
+ "initialized": false,
+ "position": {
+ "x": 257.0893454883237,
+ "y": 360
+ },
+ "data": {
+ "label": "llm3",
+ "icon": "",
+ "description": "llm描述描述",
+ "toolbarPosition": "right",
+ "config": {
+ "labelType": "llm"
+ },
+ "handles": [
+ {
+ "id": "40",
+ "position": "left",
+ "type": "source",
+ "connectable": true
+ },
+ {
+ "id": "46",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ],
+ "item": {
+ "id": "1",
+ "type": "run",
+ "labelType": "llm",
+ "label": "llm"
+ }
+ },
+ "width": 200,
+ "height": null
+ },
+ {
+ "id": "818",
+ "type": "questionClassifier",
+ "initialized": false,
+ "position": {
+ "x": 247.56414775743187,
+ "y": 467.44099824508874
+ },
+ "data": {
+ "label": "知识检索条件",
+ "icon": "",
+ "description": "知识检索描述描述",
+ "toolbarPosition": "right",
+ "config": {
+ "labelType": "knowledge"
+ },
+ "handles": [
+ {
+ "id": "86",
+ "position": "left",
+ "type": "source",
+ "connectable": true
+ },
+ {
+ "id": "11",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ],
+ "item": {
+ "id": "1",
+ "type": "run",
+ "labelType": "llm",
+ "label": "llm"
+ }
+ },
+ "width": 200,
+ "height": null
+ },
+ {
+ "id": "288",
+ "type": "llm",
+ "initialized": false,
+ "position": {
+ "x": 545.6891477574318,
+ "y": 431.4042601585389
+ },
+ "data": {
+ "label": "llm5",
+ "icon": "",
+ "description": "llm描述描述",
+ "toolbarPosition": "right",
+ "config": {
+ "labelType": "llm"
+ },
+ "handles": [
+ {
+ "id": "27",
+ "position": "left",
+ "type": "source",
+ "connectable": true
+ },
+ {
+ "id": "90",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ]
+ },
+ "width": 200,
+ "height": null
+ },
+ {
+ "id": "873",
+ "type": "llm",
+ "initialized": false,
+ "position": {
+ "x": 561.0654659633774,
+ "y": 547.6673703535477
+ },
+ "data": {
+ "label": "llm6",
+ "icon": "",
+ "description": "llm描述描述",
+ "toolbarPosition": "right",
+ "config": {
+ "labelType": "llm"
+ },
+ "handles": [
+ {
+ "id": "31",
+ "position": "left",
+ "type": "source",
+ "connectable": true
+ },
+ {
+ "id": "51",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ]
+ },
+ "width": 200,
+ "height": null
+ },
+ {
+ "id": "244",
+ "type": "llm",
+ "initialized": false,
+ "position": {
+ "x": 543.1540601095024,
+ "y": 653.309197614774
+ },
+ "data": {
+ "label": "llm7",
+ "icon": "",
+ "description": "llm描述描述",
+ "toolbarPosition": "right",
+ "config": {
+ "labelType": "llm"
+ },
+ "handles": [
+ {
+ "id": "33",
+ "position": "left",
+ "type": "source",
+ "connectable": true
+ },
+ {
+ "id": "13",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ]
+ },
+ "width": 200,
+ "height": null
+ },
+ {
+ "id": "308",
+ "type": "llm",
+ "initialized": false,
+ "position": {
+ "x": 588.7086470279171,
+ "y": 318.8609378786052
+ },
+ "data": {
+ "label": "llm4",
+ "icon": "",
+ "description": "llm描述描述",
+ "toolbarPosition": "right",
+ "config": {
+ "labelType": "llm"
+ },
+ "handles": [
+ {
+ "id": "17",
+ "position": "left",
+ "type": "source",
+ "connectable": true
+ },
+ {
+ "id": "73",
+ "position": "right",
+ "type": "source",
+ "connectable": true
+ }
+ ]
+ },
+ "width": 200,
+ "height": null
+ }
+ ],
+ "edges": [
+ {
+ "id": "188",
+ "type": "default",
+ "source": "5",
+ "target": "188",
+ "sourceHandle": "51",
+ "targetHandle": "11",
+ "data": {},
+ "label": "",
+ "animated": true,
+ "sourceX": 124.52083333333331,
+ "sourceY": 295.3294270833333,
+ "targetX": 262.70866773530213,
+ "targetY": 98.2431395846395
+ },
+ {
+ "id": "vueflow__edge-18845-661",
+ "type": "default",
+ "source": "188",
+ "target": "6",
+ "sourceHandle": "45",
+ "targetHandle": "61",
+ "data": {},
+ "label": "",
+ "sourceX": 472.20841119628,
+ "sourceY": 98.2431395846395,
+ "targetX": 1278.4153889062152,
+ "targetY": 256.12454009635803
+ },
+ {
+ "id": "850",
+ "type": "default",
+ "source": "5",
+ "target": "850",
+ "sourceHandle": "51",
+ "targetHandle": "43",
+ "data": {},
+ "label": "",
+ "animated": true,
+ "sourceX": 124.52083333333331,
+ "sourceY": 295.3294270833333,
+ "targetX": 266.62293379655364,
+ "targetY": 280.6389855757115
+ },
+ {
+ "id": "vueflow__edge-85057-661",
+ "type": "default",
+ "source": "850",
+ "target": "6",
+ "sourceHandle": "57",
+ "targetHandle": "61",
+ "data": {},
+ "label": "",
+ "sourceX": 476.1223496202179,
+ "sourceY": 280.6389855757115,
+ "targetX": 1278.4153889062152,
+ "targetY": 256.12454009635803
+ },
+ {
+ "id": "979",
+ "type": "default",
+ "source": "5",
+ "target": "979",
+ "sourceHandle": "51",
+ "targetHandle": "40",
+ "data": {},
+ "label": "",
+ "animated": true,
+ "sourceX": 124.52083333333331,
+ "sourceY": 295.3294270833333,
+ "targetX": 253.92267015536444,
+ "targetY": 386.83332778318527
+ },
+ {
+ "id": "818",
+ "type": "default",
+ "source": "5",
+ "target": "818",
+ "sourceHandle": "51",
+ "targetHandle": "86",
+ "data": {},
+ "label": "",
+ "animated": true,
+ "sourceX": 124.52083333333331,
+ "sourceY": 295.3294270833333,
+ "targetX": 244.3974724244726,
+ "targetY": 494.2742291332315
+ },
+ {
+ "id": "288",
+ "type": "default",
+ "source": "818",
+ "target": "288",
+ "sourceHandle": "11",
+ "targetHandle": "27",
+ "data": {},
+ "label": "",
+ "animated": true,
+ "sourceX": 453.89688824813686,
+ "sourceY": 494.2742291332315,
+ "targetX": 542.5224239769512,
+ "targetY": 458.2374910466816
+ },
+ {
+ "id": "873",
+ "type": "default",
+ "source": "818",
+ "target": "873",
+ "sourceHandle": "11",
+ "targetHandle": "31",
+ "data": {},
+ "label": "",
+ "animated": true,
+ "sourceX": 453.89688824813686,
+ "sourceY": 494.2742291332315,
+ "targetX": 557.8988921284383,
+ "targetY": 574.5006486940537
+ },
+ {
+ "id": "244",
+ "type": "default",
+ "source": "818",
+ "target": "244",
+ "sourceHandle": "11",
+ "targetHandle": "33",
+ "data": {},
+ "label": "",
+ "animated": true,
+ "sourceX": 453.89688824813686,
+ "sourceY": 494.2742291332315,
+ "targetX": 539.9875200651626,
+ "targetY": 680.1424930622838
+ },
+ {
+ "id": "vueflow__edge-28890-661",
+ "type": "default",
+ "source": "288",
+ "target": "6",
+ "sourceHandle": "90",
+ "targetHandle": "61",
+ "data": {},
+ "label": "",
+ "sourceX": 752.0218882481367,
+ "sourceY": 458.2374910466816,
+ "targetX": 1278.4153889062152,
+ "targetY": 256.12454009635803
+ },
+ {
+ "id": "vueflow__edge-87351-661",
+ "type": "default",
+ "source": "873",
+ "target": "6",
+ "sourceHandle": "51",
+ "targetHandle": "61",
+ "data": {},
+ "label": "",
+ "sourceX": 767.3988903613488,
+ "sourceY": 574.5006486940537,
+ "targetX": 1278.4153889062152,
+ "targetY": 256.12454009635803
+ },
+ {
+ "id": "vueflow__edge-24413-661",
+ "type": "default",
+ "source": "244",
+ "target": "6",
+ "sourceHandle": "13",
+ "targetHandle": "61",
+ "data": {},
+ "label": "",
+ "sourceX": 749.4874144445799,
+ "sourceY": 680.1424930622838,
+ "targetX": 1278.4153889062152,
+ "targetY": 256.12454009635803
+ },
+ {
+ "id": "308",
+ "type": "default",
+ "source": "979",
+ "target": "308",
+ "sourceHandle": "46",
+ "targetHandle": "17",
+ "data": {},
+ "label": "",
+ "animated": true,
+ "sourceX": 463.42208597902874,
+ "sourceY": 386.83332778318527,
+ "targetX": 585.5419963013785,
+ "targetY": 345.6941732530918
+ },
+ {
+ "id": "vueflow__edge-30873-661",
+ "type": "default",
+ "source": "308",
+ "target": "6",
+ "sourceHandle": "73",
+ "targetHandle": "61",
+ "data": {},
+ "label": "",
+ "sourceX": 795.0417833691082,
+ "sourceY": 345.6941732530918,
+ "targetX": 1278.4153889062152,
+ "targetY": 256.12454009635803
+ }
+ ],
+ "position": [
+ 447.2060780237498,
+ 377.80084997067763
+ ],
+ "zoom": 0.8311688311688327,
+ "viewport": {
+ "x": 447.2060780237498,
+ "y": 377.80084997067763,
+ "zoom": 0.8311688311688327
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/script/sse.html b/metis-applicant/src/main/resources/script/sse.html
similarity index 100%
rename from src/main/resources/script/sse.html
rename to metis-applicant/src/main/resources/script/sse.html
diff --git a/metis-starter/pom.xml b/metis-starter/pom.xml
new file mode 100644
index 0000000..ea66a81
--- /dev/null
+++ b/metis-starter/pom.xml
@@ -0,0 +1,104 @@
+
+
+ 4.0.0
+
+ com.metis
+ metis
+ 1.0.0-SNAPSHOT
+
+
+ metis-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ dev.langchain4j
+ langchain4j-open-ai
+
+
+ dev.langchain4j
+ langchain4j-mcp
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ 2.0.52
+
+
+ org.projectlombok
+ lombok
+
+
+ com.mikesamuel
+ json-sanitizer
+
+
+ com.baomidou
+ mybatis-plus-spring-boot3-starter
+
+
+ mysql
+ mysql-connector-java
+
+
+ cn.hutool
+ hutool-all
+
+
+
+ org.mapstruct
+ mapstruct
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-api
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 17
+ 17
+ UTF-8
+
+ -parameters
+
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${org.mapstruct.version}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/metis-starter/src/main/java/com/metis/config/MetisStarterAutoConfiguration.java b/metis-starter/src/main/java/com/metis/config/MetisStarterAutoConfiguration.java
new file mode 100644
index 0000000..f96631b
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/config/MetisStarterAutoConfiguration.java
@@ -0,0 +1,19 @@
+package com.metis.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Slf4j
+@Configuration
+@MapperScan(basePackages = {"com.metis.mapper"})
+@ComponentScan("com.metis.*")
+public class MetisStarterAutoConfiguration {
+
+
+ static {
+
+ }
+
+}
diff --git a/src/main/java/com/metis/flow/constant/BaseConstant.java b/metis-starter/src/main/java/com/metis/constant/BaseConstant.java
similarity index 67%
rename from src/main/java/com/metis/flow/constant/BaseConstant.java
rename to metis-starter/src/main/java/com/metis/constant/BaseConstant.java
index 7e0011a..bfac152 100644
--- a/src/main/java/com/metis/flow/constant/BaseConstant.java
+++ b/metis-starter/src/main/java/com/metis/constant/BaseConstant.java
@@ -1,4 +1,4 @@
-package com.metis.flow.constant;
+package com.metis.constant;
public interface BaseConstant {
diff --git a/src/main/java/com/metis/controller/ProcessDefinitionController.java b/metis-starter/src/main/java/com/metis/controller/ProcessDefinitionController.java
similarity index 83%
rename from src/main/java/com/metis/controller/ProcessDefinitionController.java
rename to metis-starter/src/main/java/com/metis/controller/ProcessDefinitionController.java
index 58ebbf5..8f16bc5 100644
--- a/src/main/java/com/metis/controller/ProcessDefinitionController.java
+++ b/metis-starter/src/main/java/com/metis/controller/ProcessDefinitionController.java
@@ -2,7 +2,7 @@ package com.metis.controller;
import com.metis.domain.bo.ProcessBo;
import com.metis.facade.ProcessDefinitionFacade;
-import com.metis.flow.domain.entity.App;
+import com.metis.domain.entity.App;
import com.metis.result.Result;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@@ -16,9 +16,9 @@ public class ProcessDefinitionController {
@PostMapping("/create")
- public Result create(@RequestBody ProcessBo processBo) {
- processDefinitionFacade.create(processBo);
- return Result.ok();
+ public Result create(@RequestBody ProcessBo processBo) {
+ Long workflowId = processDefinitionFacade.create(processBo);
+ return Result.ok(workflowId);
}
@PutMapping("/update")
diff --git a/src/main/java/com/metis/flow/convert/BaseAppConvert.java b/metis-starter/src/main/java/com/metis/convert/BaseAppConvert.java
similarity index 57%
rename from src/main/java/com/metis/flow/convert/BaseAppConvert.java
rename to metis-starter/src/main/java/com/metis/convert/BaseAppConvert.java
index 5029018..7a1122b 100644
--- a/src/main/java/com/metis/flow/convert/BaseAppConvert.java
+++ b/metis-starter/src/main/java/com/metis/convert/BaseAppConvert.java
@@ -1,9 +1,10 @@
-package com.metis.flow.convert;
+package com.metis.convert;
-import com.metis.flow.domain.entity.*;
-import com.metis.flow.domain.bo.BuildApp;
-import com.metis.flow.domain.bo.CreateApp;
-import com.metis.flow.domain.bo.UpdateApp;
+import com.metis.domain.bo.BuildApp;
+import com.metis.domain.bo.CreateApp;
+import com.metis.domain.bo.UpdateApp;
+import com.metis.domain.entity.App;
+import com.metis.domain.entity.BaseApp;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
@@ -23,6 +24,13 @@ public interface BaseAppConvert {
* @param buildApp 基础应用
* @return {@link App }
*/
+ @Mappings({
+ @Mapping(target = "id", ignore = true),
+ @Mapping(target = "workflowId", ignore = true),
+ @Mapping(target = "createTime", ignore = true),
+ @Mapping(target = "version", ignore = true),
+ @Mapping(target = "defaultUse", ignore = true),
+ })
App toApp(BuildApp buildApp);
/**
@@ -32,7 +40,7 @@ public interface BaseAppConvert {
* @return {@link App }
*/
@Mappings({
- @Mapping(target = "graph", expression = "java(com.alibaba.fastjson2.JSON.parseObject(baseApp.getGraphJson(), com.metis.flow.domain.bo.Graph.class))"),
+ @Mapping(target = "graph", expression = "java(com.alibaba.fastjson2.JSON.parseObject(baseApp.getGraphJson(), com.metis.domain.entity.base.Graph.class))"),
@Mapping(target = "workflowId", source = "id")
})
App toApp(BaseApp baseApp);
@@ -44,7 +52,13 @@ public interface BaseAppConvert {
* @return {@link BaseApp }
*/
@Mappings({
- @Mapping(target = "graphJson", expression = "java(com.alibaba.fastjson2.JSON.toJSONString(buildApp.getGraph()))")
+ @Mapping(target = "graphJson", expression = "java(com.alibaba.fastjson2.JSON.toJSONString(buildApp.getGraph()))"),
+ @Mapping(target = "createTime", ignore = true),
+ @Mapping(target = "updateTime", ignore = true),
+ @Mapping(target = "isDeleted", ignore = true),
+ @Mapping(target = "id", ignore = true),
+ @Mapping(target = "createUserId", ignore = true),
+ @Mapping(target = "defaultUse", ignore = true)
})
BaseApp toBaseApp(BuildApp buildApp);
@@ -63,6 +77,9 @@ public interface BaseAppConvert {
* @param createApp 创建应用程序
* @return {@link BuildApp }
*/
+ @Mappings({
+ @Mapping(target = "appId", ignore = true)
+ })
BuildApp toBuildApp(CreateApp createApp);
/**
@@ -71,6 +88,9 @@ public interface BaseAppConvert {
* @param updateApp 更新应用程序
* @return {@link BuildApp }
*/
+ @Mappings({
+ @Mapping(target = "userId", ignore = true)
+ })
BuildApp toBuildApp(UpdateApp updateApp);
}
diff --git a/metis-starter/src/main/java/com/metis/convert/GraphConvert.java b/metis-starter/src/main/java/com/metis/convert/GraphConvert.java
new file mode 100644
index 0000000..da2fa78
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/convert/GraphConvert.java
@@ -0,0 +1,17 @@
+package com.metis.convert;
+
+
+import com.metis.domain.bo.GraphBO;
+import com.metis.domain.entity.base.Graph;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface GraphConvert {
+
+ GraphConvert INSTANCE = Mappers.getMapper(GraphConvert.class);
+
+
+ Graph toEntity(GraphBO graph);
+
+}
diff --git a/src/main/java/com/metis/domain/SimpleBaseEntity.java b/metis-starter/src/main/java/com/metis/domain/SimpleBaseEntity.java
similarity index 100%
rename from src/main/java/com/metis/domain/SimpleBaseEntity.java
rename to metis-starter/src/main/java/com/metis/domain/SimpleBaseEntity.java
diff --git a/src/main/java/com/metis/flow/domain/bo/BuildApp.java b/metis-starter/src/main/java/com/metis/domain/bo/BuildApp.java
similarity index 88%
rename from src/main/java/com/metis/flow/domain/bo/BuildApp.java
rename to metis-starter/src/main/java/com/metis/domain/bo/BuildApp.java
index fd78ff5..28d6540 100644
--- a/src/main/java/com/metis/flow/domain/bo/BuildApp.java
+++ b/metis-starter/src/main/java/com/metis/domain/bo/BuildApp.java
@@ -1,5 +1,6 @@
-package com.metis.flow.domain.bo;
+package com.metis.domain.bo;
+import com.metis.domain.entity.base.Graph;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
diff --git a/src/main/java/com/metis/flow/domain/bo/CreateApp.java b/metis-starter/src/main/java/com/metis/domain/bo/CreateApp.java
similarity index 88%
rename from src/main/java/com/metis/flow/domain/bo/CreateApp.java
rename to metis-starter/src/main/java/com/metis/domain/bo/CreateApp.java
index 42f2a66..a8a9c1a 100644
--- a/src/main/java/com/metis/flow/domain/bo/CreateApp.java
+++ b/metis-starter/src/main/java/com/metis/domain/bo/CreateApp.java
@@ -1,5 +1,6 @@
-package com.metis.flow.domain.bo;
+package com.metis.domain.bo;
+import com.metis.domain.entity.base.Graph;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
diff --git a/metis-starter/src/main/java/com/metis/domain/bo/EdgeBO.java b/metis-starter/src/main/java/com/metis/domain/bo/EdgeBO.java
new file mode 100644
index 0000000..0de8c4b
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/bo/EdgeBO.java
@@ -0,0 +1,66 @@
+package com.metis.domain.bo;
+
+import com.metis.enums.EdgeType;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class EdgeBO {
+
+ /**
+ * 唯一标识符
+ */
+ @NotNull(message = "唯一标识符不能为空")
+ private String id;
+
+ /**
+ * 标签
+ */
+ private String label;
+
+ /**
+ * 节点类型
+ */
+ @NotNull(message = "线类型不能为空")
+ private EdgeType type;
+
+ /**
+ * 源节点ID,对应节点id
+ */
+ @NotNull(message = "源节点ID不能为空")
+ private Long source;
+
+ /**
+ * 目标节点ID,对应节点id
+ */
+ @NotNull(message = "目标节点ID不能为空")
+ private Long target;
+ /**
+ * 源句柄id
+ */
+ @NotNull(message = "源句柄ID不能为空")
+ private Long sourceHandle;
+
+ /**
+ * 目标句柄id
+ */
+ @NotNull(message = "目标句柄ID不能为空")
+ private Long targetHandle;
+
+ /**
+ * 边是否动画true/false
+ */
+ private Boolean animated;
+
+ /**
+ * 开始标志
+ */
+ private String markerStart;
+
+ /**
+ * 结束标记
+ */
+ private String markerEnd;
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/bo/GraphBO.java b/metis-starter/src/main/java/com/metis/domain/bo/GraphBO.java
new file mode 100644
index 0000000..a5b51f5
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/bo/GraphBO.java
@@ -0,0 +1,43 @@
+package com.metis.domain.bo;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class GraphBO {
+
+ /**
+ * 边缘
+ */
+ @Valid
+ @NotEmpty(message = "连线不能为空")
+ private List edges;
+
+ /**
+ * 节点
+ */
+ @Valid
+ @NotEmpty(message = "节点不能为空")
+ private List nodes;
+
+
+ /**
+ * 位置
+ */
+ private List position;
+
+ /**
+ * 变焦
+ */
+ private Double zoom;
+
+ /**
+ * 视窗
+ */
+ private ViewportBo viewport;
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/bo/HandleBO.java b/metis-starter/src/main/java/com/metis/domain/bo/HandleBO.java
new file mode 100644
index 0000000..510557d
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/bo/HandleBO.java
@@ -0,0 +1,36 @@
+package com.metis.domain.bo;
+
+import com.metis.enums.HandleType;
+import com.metis.enums.PositionType;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * 句柄对象
+ */
+@Data
+public class HandleBO {
+ /**
+ * 句柄id
+ */
+ @NotNull(message = "句柄id不能为空")
+ private Long id;
+
+ /**
+ * 句柄类型
+ */
+ @NotNull(message = "句柄类型不能为空")
+ private HandleType type;
+
+ /**
+ * 句柄位置
+ */
+ @NotNull(message = "句柄位置不能为空")
+ private PositionType position;
+
+ /**
+ * 是否可以连接
+ */
+ @NotNull(message = "是否可以连接不能为空")
+ private Boolean connectable;
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/bo/NodeBO.java b/metis-starter/src/main/java/com/metis/domain/bo/NodeBO.java
new file mode 100644
index 0000000..9bab140
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/bo/NodeBO.java
@@ -0,0 +1,60 @@
+package com.metis.domain.bo;
+
+import com.metis.enums.NodeType;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class NodeBO {
+
+ /**
+ * id
+ */
+ @NotNull(message = "节点id不能为空")
+ private Long id;
+
+ /**
+ * 类型
+ */
+ @NotNull(message = "节点类型不能为空")
+ private NodeType type;
+
+ /**
+ * 自定义类型
+ */
+ private String customType;
+
+ /**
+ * 位置
+ */
+ @Valid
+ @NotNull(message = "节点位置不能为空")
+ private PositionBO position;
+
+ /**
+ * 业务数据
+ */
+ @Valid
+ @NotNull(message = "节点业务数据不能为空")
+ private NodeDataBO data;
+
+ /**
+ * 宽度
+ */
+// @NotNull(message = "节点宽度不能为空")
+ private Integer width;
+
+ /**
+ * 高度
+ */
+// @NotNull(message = "节点高度不能为空")
+ private Integer height;
+
+ /**
+ * 节点是否选中
+ */
+ private Boolean selected;
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/bo/NodeDataBO.java b/metis-starter/src/main/java/com/metis/domain/bo/NodeDataBO.java
new file mode 100644
index 0000000..04e0c82
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/bo/NodeDataBO.java
@@ -0,0 +1,45 @@
+package com.metis.domain.bo;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.metis.enums.PositionType;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class NodeDataBO {
+
+ /**
+ * 标签
+ */
+ @NotBlank(message = "标签不能为空")
+ private String label;
+
+ /**
+ * 图标
+ */
+ private String icon;
+
+ /**
+ * 工具栏位置
+ */
+ private PositionType toolbarPosition;
+
+
+ /**
+ * 配置
+ */
+ private JSONObject config;
+
+ /**
+ * 句柄列表
+ */
+ @Valid
+ @NotEmpty(message = "句柄列表不能为空")
+ private List handles;
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/bo/PositionBO.java b/metis-starter/src/main/java/com/metis/domain/bo/PositionBO.java
new file mode 100644
index 0000000..00f0b50
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/bo/PositionBO.java
@@ -0,0 +1,20 @@
+package com.metis.domain.bo;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class PositionBO {
+ /**
+ * x坐标
+ */
+ @NotNull(message = "x坐标不能为空")
+ private Double x;
+
+ /**
+ * y坐标
+ */
+ @NotNull(message = "y坐标不能为空")
+ private Double y;
+
+}
diff --git a/src/main/java/com/metis/domain/bo/ProcessBo.java b/metis-starter/src/main/java/com/metis/domain/bo/ProcessBo.java
similarity index 79%
rename from src/main/java/com/metis/domain/bo/ProcessBo.java
rename to metis-starter/src/main/java/com/metis/domain/bo/ProcessBo.java
index 325018e..c0075af 100644
--- a/src/main/java/com/metis/domain/bo/ProcessBo.java
+++ b/metis-starter/src/main/java/com/metis/domain/bo/ProcessBo.java
@@ -1,7 +1,7 @@
package com.metis.domain.bo;
import com.metis.enums.YesOrNoEnum;
-import com.metis.flow.domain.bo.Graph;
+import com.metis.domain.bo.GraphBO;
import lombok.Data;
@Data
@@ -13,7 +13,7 @@ public class ProcessBo {
private String description;
- private Graph graph;
+ private GraphBO graph;
private YesOrNoEnum defaultUse;
diff --git a/src/main/java/com/metis/flow/domain/bo/UpdateApp.java b/metis-starter/src/main/java/com/metis/domain/bo/UpdateApp.java
similarity index 89%
rename from src/main/java/com/metis/flow/domain/bo/UpdateApp.java
rename to metis-starter/src/main/java/com/metis/domain/bo/UpdateApp.java
index a6a0050..e4c3238 100644
--- a/src/main/java/com/metis/flow/domain/bo/UpdateApp.java
+++ b/metis-starter/src/main/java/com/metis/domain/bo/UpdateApp.java
@@ -1,6 +1,7 @@
-package com.metis.flow.domain.bo;
+package com.metis.domain.bo;
import com.metis.enums.YesOrNoEnum;
+import com.metis.domain.entity.base.Graph;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
diff --git a/metis-starter/src/main/java/com/metis/domain/bo/ViewportBo.java b/metis-starter/src/main/java/com/metis/domain/bo/ViewportBo.java
new file mode 100644
index 0000000..7209088
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/bo/ViewportBo.java
@@ -0,0 +1,10 @@
+package com.metis.domain.bo;
+
+import lombok.Data;
+
+@Data
+public class ViewportBo {
+ private Double x;
+ private Double y;
+ private Double zoom;
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/context/RunningContext.java b/metis-starter/src/main/java/com/metis/domain/context/RunningContext.java
new file mode 100644
index 0000000..c2d1d68
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/context/RunningContext.java
@@ -0,0 +1,68 @@
+package com.metis.domain.context;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.metis.runner.FlowRunningContext;
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+@Getter
+@Builder
+public class RunningContext {
+
+ /**
+ * 系统数据
+ */
+ private SysContext sys;
+
+ /**
+ * 自定义数据
+ */
+ private JSONObject custom;
+
+ /**
+ * 节点运行上下文, 需要数据进行传递
+ */
+ private Map nodeRunningContext;
+
+ /**
+ * 下一个运行节点id集合, 可能是多个, 执行器每一次清空该节点
+ */
+ private Set nextRunNodeId;
+
+
+ /**
+ * 添加节点运行环境
+ *
+ * @param nodeId 节点id
+ * @param nodeRunningContext 节点运行背景信息
+ */
+ public void addNodeRunningContext(Long nodeId, JSONObject nodeRunningContext) {
+ this.nodeRunningContext.put(nodeId, nodeRunningContext);
+ }
+
+ public JSONObject getRunningContext(Long nodeId) {
+ return this.nodeRunningContext.get(nodeId);
+ }
+
+
+ /**
+ * 构建上下文
+ *
+ * @param context 上下文
+ * @return {@link RunningContext }
+ */
+ public static RunningContext buildContext(SysContext sysContext, FlowRunningContext context) {
+ return RunningContext.builder()
+ .sys(sysContext)
+ .custom(context.getCustom())
+ .nodeRunningContext(new HashMap<>())
+ .build();
+ }
+
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/context/RunningResult.java b/metis-starter/src/main/java/com/metis/domain/context/RunningResult.java
new file mode 100644
index 0000000..556e6a6
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/context/RunningResult.java
@@ -0,0 +1,68 @@
+package com.metis.domain.context;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.Set;
+
+@Data
+@Builder
+public class RunningResult {
+
+ /**
+ * 节点上下文
+ */
+ private JSONObject nodeContext;
+
+ /**
+ * 下一个运行节点id, 一些特殊节点需要, 必须条件节点满足后, 才会运行下一个节点
+ */
+ private Set nextRunNodeId;
+
+
+ /**
+ * 构建结果
+ *
+ * @param nodeContext 节点上下文
+ * @param nextRunNodeId 下一个运行节点id
+ * @return {@link RunningResult }
+ */
+ public static RunningResult buildResult(JSONObject nodeContext, Set nextRunNodeId) {
+ return RunningResult.builder()
+ .nodeContext(nodeContext)
+ .nextRunNodeId(nextRunNodeId)
+ .build();
+ }
+
+ /**
+ * 构建结果
+ *
+ * @param nextRunNodeId 下一个运行节点id
+ * @return {@link RunningResult }
+ */
+ public static RunningResult buildResult(Set nextRunNodeId) {
+ return RunningResult.builder()
+ .nextRunNodeId(nextRunNodeId)
+ .build();
+ }
+
+ /**
+ * 构建结果
+ *
+ * @param nodeContext 节点上下文
+ * @return {@link RunningResult }
+ */
+ public static RunningResult buildResult(JSONObject nodeContext) {
+ return RunningResult.builder()
+ .nodeContext(nodeContext)
+ .build();
+ }
+
+ public static RunningResult buildResult() {
+ return RunningResult.builder()
+ .build();
+ }
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/context/SysContext.java b/metis-starter/src/main/java/com/metis/domain/context/SysContext.java
new file mode 100644
index 0000000..a2327e9
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/context/SysContext.java
@@ -0,0 +1,51 @@
+package com.metis.domain.context;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@Builder
+public class SysContext {
+
+ /**
+ * 文件列表
+ */
+ private List files;
+
+ /**
+ * 沟通的内容
+ */
+ private String query;
+
+ /**
+ * 对话数
+ */
+ private Integer dialogueCount;
+
+ /**
+ * 沟通的id
+ */
+ private String conversationId;
+
+ /**
+ * 应用程序id
+ */
+ private Long appId;
+
+ /**
+ * 用户id
+ */
+ private Long userId;
+
+ /**
+ * 工作流id
+ */
+ private Long workflowId;
+
+ /**
+ * 实例id
+ */
+ private Long instanceId;
+}
diff --git a/src/main/java/com/metis/flow/domain/entity/App.java b/metis-starter/src/main/java/com/metis/domain/entity/App.java
similarity index 88%
rename from src/main/java/com/metis/flow/domain/entity/App.java
rename to metis-starter/src/main/java/com/metis/domain/entity/App.java
index 5b97af1..ef48d44 100644
--- a/src/main/java/com/metis/flow/domain/entity/App.java
+++ b/metis-starter/src/main/java/com/metis/domain/entity/App.java
@@ -1,8 +1,8 @@
-package com.metis.flow.domain.entity;
+package com.metis.domain.entity;
import com.metis.enums.YesOrNoEnum;
-import com.metis.flow.domain.bo.Graph;
+import com.metis.domain.entity.base.Graph;
import lombok.Data;
import java.time.LocalDateTime;
diff --git a/src/main/java/com/metis/flow/domain/entity/BaseApp.java b/metis-starter/src/main/java/com/metis/domain/entity/BaseApp.java
similarity index 97%
rename from src/main/java/com/metis/flow/domain/entity/BaseApp.java
rename to metis-starter/src/main/java/com/metis/domain/entity/BaseApp.java
index 48e9521..53204e0 100644
--- a/src/main/java/com/metis/flow/domain/entity/BaseApp.java
+++ b/metis-starter/src/main/java/com/metis/domain/entity/BaseApp.java
@@ -1,4 +1,4 @@
-package com.metis.flow.domain.entity;
+package com.metis.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/GraphDto.java b/metis-starter/src/main/java/com/metis/domain/entity/GraphDto.java
new file mode 100644
index 0000000..5de1cc2
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/GraphDto.java
@@ -0,0 +1,131 @@
+package com.metis.domain.entity;
+
+
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Graph;
+import com.metis.domain.entity.base.Node;
+import com.metis.enums.NodeType;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class GraphDto {
+
+ private final Map nodeMap;
+
+ private final Map nodeReadyMap;
+
+ private final Map> edgeMap;
+
+ private final Map> adjacencyList = new HashMap<>();
+
+ private final List sortedNodes = new ArrayList<>();
+
+
+ public List getSortedNodes() {
+ return new ArrayList<>(sortedNodes);
+ }
+
+ public List getEdgeNodeId(Long nodeId) {
+ return edgeMap.getOrDefault(nodeId, new ArrayList<>());
+ }
+
+ public Node getEndNode(){
+ return sortedNodes.stream()
+ .filter(node -> NodeType.END.equals(node.getType()))
+ .findFirst()
+ .orElse(null);
+ }
+
+ public Node getNode(Long nodeId) {
+ return nodeMap.get(nodeId);
+ }
+
+ public void updateNodeReadyMap(Long nodeId, Boolean ready) {
+ nodeReadyMap.put(nodeId, ready);
+ }
+
+ public Boolean isNodeReady(Long nodeId) {
+ return nodeReadyMap.get(nodeId);
+ }
+
+
+ private GraphDto(List nodes, List edges) {
+ this.edgeMap = edges.stream()
+ .collect(Collectors.groupingBy(Edge::getSource));
+ this.nodeMap = nodes.stream()
+ .collect(Collectors.toMap(Node::getId, Function.identity()));
+ this.nodeReadyMap = nodes.stream()
+ .collect(Collectors.toMap(Node::getId, node -> false));
+ initAdjacencyList(edges);
+ List nodeList = topologicalSort();
+ this.sortedNodes.addAll(nodeList);
+ Node node = sortedNodes.get(0);
+ if (NodeType.START.equals(node.getType())) {
+ nodeReadyMap.put(node.getId(), true);
+ }
+ }
+
+ private void initAdjacencyList(List edges) {
+ for (Edge edge : edges) {
+ List targetList = adjacencyList.getOrDefault(edge.getSource(), new ArrayList<>());
+ targetList.add(edge.getTarget());
+ adjacencyList.put(edge.getSource(), targetList);
+ }
+ }
+
+
+ /**
+ * 拓扑排序
+ *
+ * @return {@link List }<{@link Node }>
+ */
+ private List topologicalSort() {
+ List sortedNodes = new ArrayList<>();
+ Set visited = new HashSet<>();
+ Set visiting = new HashSet<>();
+ for (Long nodeId : nodeMap.keySet()) {
+ if (!visited.contains(nodeId)) {
+ dfs(nodeId, visited, visiting, sortedNodes);
+ }
+ }
+ Collections.reverse(sortedNodes);
+ return sortedNodes;
+ }
+
+ /**
+ * 深度遍历找到运行顺序
+ *
+ * @param nodeId 节点id
+ * @param visited 参观了
+ * @param visiting 参观
+ * @param sortedNodes 排序节点
+ */
+ private void dfs(Long nodeId, Set visited, Set visiting, List sortedNodes) {
+ if (visiting.contains(nodeId)) {
+ throw new IllegalStateException("Cycle detected in the graph");
+ }
+ if (!visited.contains(nodeId)) {
+ visiting.add(nodeId);
+ for (Long neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
+ dfs(neighbor, visited, visiting, sortedNodes);
+ }
+ visiting.remove(nodeId);
+ visited.add(nodeId);
+ sortedNodes.add(nodeMap.get(nodeId));
+ }
+ }
+
+
+ /**
+ * 构建对象
+ *
+ * @param graph 图
+ * @return {@link GraphDto }
+ */
+ public static GraphDto of(Graph graph) {
+ return new GraphDto(graph.getNodes(), graph.getEdges());
+ }
+
+}
diff --git a/src/main/java/com/metis/flow/domain/entity/base/Edge.java b/metis-starter/src/main/java/com/metis/domain/entity/base/Edge.java
similarity index 58%
rename from src/main/java/com/metis/flow/domain/entity/base/Edge.java
rename to metis-starter/src/main/java/com/metis/domain/entity/base/Edge.java
index ce657d1..d389fec 100644
--- a/src/main/java/com/metis/flow/domain/entity/base/Edge.java
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/Edge.java
@@ -1,7 +1,6 @@
-package com.metis.flow.domain.entity.base;
+package com.metis.domain.entity.base;
-import com.metis.flow.enums.EdgeType;
-import jakarta.validation.constraints.NotBlank;
+import com.metis.enums.EdgeType;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -11,7 +10,7 @@ public class Edge {
/**
* 唯一标识符
*/
- @NotBlank(message = "唯一标识符不能为空")
+ @NotNull(message = "唯一标识符不能为空")
private String id;
/**
@@ -28,25 +27,25 @@ public class Edge {
/**
* 源节点ID,对应节点id
*/
- @NotBlank(message = "源节点ID不能为空")
- private String source;
+ @NotNull(message = "源节点ID不能为空")
+ private Long source;
/**
* 目标节点ID,对应节点id
*/
- @NotBlank(message = "目标节点ID不能为空")
- private String target;
+ @NotNull(message = "目标节点ID不能为空")
+ private Long target;
/**
* 源句柄id
*/
- @NotBlank(message = "源句柄ID不能为空")
- private String sourceHandle;
+ @NotNull(message = "源句柄ID不能为空")
+ private Long sourceHandle;
/**
* 目标句柄id
*/
- @NotBlank(message = "目标句柄ID不能为空")
- private String targetHandle;
+ @NotNull(message = "目标句柄ID不能为空")
+ private Long targetHandle;
/**
* 边是否动画true/false
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/base/Graph.java b/metis-starter/src/main/java/com/metis/domain/entity/base/Graph.java
new file mode 100644
index 0000000..c440eb3
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/Graph.java
@@ -0,0 +1,47 @@
+package com.metis.domain.entity.base;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Graph {
+
+ /**
+ * 边缘
+ */
+ @Valid
+ @NotEmpty(message = "连线不能为空")
+ private List edges;
+
+ /**
+ * 节点
+ */
+ @Valid
+ @NotEmpty(message = "节点不能为空")
+ private List nodes;
+
+ /**
+ * 位置
+ */
+ private List position;
+
+ /**
+ * 变焦
+ */
+ private Double zoom;
+
+ /**
+ * 视窗
+ */
+ private Viewport viewport;
+
+
+
+}
diff --git a/src/main/java/com/metis/flow/domain/entity/base/Handle.java b/metis-starter/src/main/java/com/metis/domain/entity/base/Handle.java
similarity index 68%
rename from src/main/java/com/metis/flow/domain/entity/base/Handle.java
rename to metis-starter/src/main/java/com/metis/domain/entity/base/Handle.java
index f49dda3..f0e3e32 100644
--- a/src/main/java/com/metis/flow/domain/entity/base/Handle.java
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/Handle.java
@@ -1,8 +1,7 @@
-package com.metis.flow.domain.entity.base;
+package com.metis.domain.entity.base;
-import com.metis.flow.enums.PositionType;
-import com.metis.flow.enums.HandleType;
-import jakarta.validation.constraints.NotBlank;
+import com.metis.enums.HandleType;
+import com.metis.enums.PositionType;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -14,8 +13,8 @@ public class Handle {
/**
* 句柄id
*/
- @NotBlank(message = "句柄id不能为空")
- private String id;
+ @NotNull(message = "句柄id不能为空")
+ private Long id;
/**
* 句柄类型
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/base/Node.java b/metis-starter/src/main/java/com/metis/domain/entity/base/Node.java
new file mode 100644
index 0000000..c5d976b
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/Node.java
@@ -0,0 +1,84 @@
+package com.metis.domain.entity.base;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.metis.enums.NodeType;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class Node {
+
+ /**
+ * id
+ */
+ @NotNull(message = "节点id不能为空")
+ private Long id;
+
+ /**
+ * 类型
+ */
+ @NotNull(message = "节点类型不能为空")
+ private NodeType type;
+
+ private String customType;
+
+ /**
+ * 位置
+ */
+ @Valid
+ @NotNull(message = "节点位置不能为空")
+ private Position position;
+
+ /**
+ * 业务数据
+ */
+ @Valid
+ @NotNull(message = "节点业务数据不能为空")
+ private NodeData data;
+
+ /**
+ * 宽度
+ */
+// @NotNull(message = "节点宽度不能为空")
+ private Integer width;
+
+ /**
+ * 高度
+ */
+// @NotNull(message = "节点高度不能为空")
+ private Integer height;
+
+ /**
+ * 节点是否选中
+ */
+ private Boolean selected;
+
+ private transient Class> configClass;
+
+
+ /**
+ * 获取配置
+ *
+ * @return {@link T }
+ */
+ public T getConfig() {
+ if (ObjectUtil.isNull(data.getConfig())) {
+ return null;
+ }
+ return (T) data.getConfig().to(configClass);
+ }
+
+ /**
+ * 设置配置类
+ *
+ * @param configClass 配置类
+ */
+ public void setConfigClass(Class configClass) {
+ if (ObjectUtil.isNotNull(this.configClass)) {
+ return;
+ }
+ this.configClass = configClass;
+ }
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/base/NodeConfig.java b/metis-starter/src/main/java/com/metis/domain/entity/base/NodeConfig.java
new file mode 100644
index 0000000..f96afd2
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/NodeConfig.java
@@ -0,0 +1,4 @@
+package com.metis.domain.entity.base;
+
+public abstract class NodeConfig {
+}
diff --git a/src/main/java/com/metis/flow/domain/entity/base/NodeData.java b/metis-starter/src/main/java/com/metis/domain/entity/base/NodeData.java
similarity index 63%
rename from src/main/java/com/metis/flow/domain/entity/base/NodeData.java
rename to metis-starter/src/main/java/com/metis/domain/entity/base/NodeData.java
index 5ae8089..9aa53f7 100644
--- a/src/main/java/com/metis/flow/domain/entity/base/NodeData.java
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/NodeData.java
@@ -1,10 +1,10 @@
-package com.metis.flow.domain.entity.base;
+package com.metis.domain.entity.base;
import com.alibaba.fastjson2.JSONObject;
-import com.metis.flow.enums.PositionType;
+import com.metis.enums.PositionType;
import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@@ -15,6 +15,7 @@ public class NodeData {
/**
* 标签
*/
+ @NotBlank(message = "标签不能为空")
private String label;
/**
@@ -27,6 +28,7 @@ public class NodeData {
*/
private PositionType toolbarPosition;
+
/**
* 配置
*/
@@ -36,8 +38,7 @@ public class NodeData {
* 句柄列表
*/
@Valid
- @NotNull(message = "句柄列表不能为空")
- @Size(min = 1, message = "句柄列表不能为空")
+ @NotEmpty(message = "句柄列表不能为空")
private List handles;
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/base/NodeVariable.java b/metis-starter/src/main/java/com/metis/domain/entity/base/NodeVariable.java
new file mode 100644
index 0000000..3058bbd
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/NodeVariable.java
@@ -0,0 +1,98 @@
+package com.metis.domain.entity.base;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson2.JSONObject;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.metis.enums.FileUploadType;
+import com.metis.enums.NodeVariableType;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class NodeVariable {
+
+ /**
+ * 参数字段
+ */
+ @NotBlank(message = "参数字段不能为空")
+ private String variable;
+
+ /**
+ * 标签
+ */
+ private String label;
+
+ /**
+ * 最大长度
+ */
+ private Integer maxLength;
+
+ /**
+ * 类型
+ */
+ @NotNull(message = "类型不能为空")
+ private String type;
+
+ /**
+ * 是否必填
+ */
+ @NotNull(message = "是否必填不能为空")
+ private Boolean required;
+
+ /**
+ * 选项
+ */
+ @Valid
+ private List options;
+
+ /**
+ * 允许上传方式
+ */
+ private List allowedFileUploadMethods;
+
+ /**
+ * 允许文件类型
+ */
+ private List allowedFileTypes;
+
+ /**
+ * 允许文件扩展名
+ */
+ private List allowedFileExtensions;
+
+
+ @JsonIgnore
+ public Object getValue(JSONObject custom) {
+ Object serializable = getSerializable(custom);
+ Assert.isTrue(!(ObjectUtil.isNull(serializable) && ObjectUtil.isNotNull(required) && required), "参数字段 {} 的值不能为空", variable);
+ return serializable;
+ }
+
+ private Object getSerializable(JSONObject custom) {
+ switch (getVariableType()) {
+ case TEXT_INPUT, PARAGRAPH, SELECT, FILE -> {
+ return custom.getString(variable);
+ }
+ case NUMBER -> {
+ return custom.getInteger(variable);
+ }
+ case FILE_LIST -> {
+ return custom.getList(variable, String.class);
+ }
+ default -> throw new RuntimeException("不支持的类型");
+ }
+ }
+
+
+ @JsonIgnore
+ public NodeVariableType getVariableType() {
+ return NodeVariableType.get(type);
+ }
+
+
+}
diff --git a/src/main/java/com/metis/flow/domain/entity/base/Position.java b/metis-starter/src/main/java/com/metis/domain/entity/base/Position.java
similarity index 87%
rename from src/main/java/com/metis/flow/domain/entity/base/Position.java
rename to metis-starter/src/main/java/com/metis/domain/entity/base/Position.java
index 291cf55..2beec9f 100644
--- a/src/main/java/com/metis/flow/domain/entity/base/Position.java
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/Position.java
@@ -1,4 +1,4 @@
-package com.metis.flow.domain.entity.base;
+package com.metis.domain.entity.base;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/base/VariableOption.java b/metis-starter/src/main/java/com/metis/domain/entity/base/VariableOption.java
new file mode 100644
index 0000000..7ac3e13
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/VariableOption.java
@@ -0,0 +1,22 @@
+package com.metis.domain.entity.base;
+
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Data
+public class VariableOption {
+
+ /**
+ * 标签
+ */
+ private String label;
+
+ /**
+ * 值
+ */
+ @NotNull(message = "值不能为空")
+ private String value;
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/base/Viewport.java b/metis-starter/src/main/java/com/metis/domain/entity/base/Viewport.java
new file mode 100644
index 0000000..039f996
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/base/Viewport.java
@@ -0,0 +1,10 @@
+package com.metis.domain.entity.base;
+
+import lombok.Data;
+
+@Data
+public class Viewport {
+ private Double x;
+ private Double y;
+ private Double zoom;
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/config/node/DocumentExtractorNodeConfig.java b/metis-starter/src/main/java/com/metis/domain/entity/config/node/DocumentExtractorNodeConfig.java
new file mode 100644
index 0000000..eae223c
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/config/node/DocumentExtractorNodeConfig.java
@@ -0,0 +1,15 @@
+package com.metis.domain.entity.config.node;
+
+import com.metis.domain.entity.base.NodeConfig;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DocumentExtractorNodeConfig extends NodeConfig {
+
+ @NotBlank(message = "文件类型不能为空")
+ private String fileType;
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/config/node/EndNodeConfig.java b/metis-starter/src/main/java/com/metis/domain/entity/config/node/EndNodeConfig.java
new file mode 100644
index 0000000..202e601
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/config/node/EndNodeConfig.java
@@ -0,0 +1,10 @@
+package com.metis.domain.entity.config.node;
+
+import com.metis.domain.entity.base.NodeConfig;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class EndNodeConfig extends NodeConfig {
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/config/node/LLMNodeConfig.java b/metis-starter/src/main/java/com/metis/domain/entity/config/node/LLMNodeConfig.java
new file mode 100644
index 0000000..6a2df06
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/config/node/LLMNodeConfig.java
@@ -0,0 +1,10 @@
+package com.metis.domain.entity.config.node;
+
+import com.metis.domain.entity.base.NodeConfig;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LLMNodeConfig extends NodeConfig {
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/config/node/QuestionClassifierConfig.java b/metis-starter/src/main/java/com/metis/domain/entity/config/node/QuestionClassifierConfig.java
new file mode 100644
index 0000000..caef097
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/config/node/QuestionClassifierConfig.java
@@ -0,0 +1,10 @@
+package com.metis.domain.entity.config.node;
+
+import com.metis.domain.entity.base.NodeConfig;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class QuestionClassifierConfig extends NodeConfig {
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/config/node/StartNodeConfig.java b/metis-starter/src/main/java/com/metis/domain/entity/config/node/StartNodeConfig.java
new file mode 100644
index 0000000..4f37e13
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/config/node/StartNodeConfig.java
@@ -0,0 +1,19 @@
+package com.metis.domain.entity.config.node;
+
+import com.metis.domain.entity.base.NodeConfig;
+import com.metis.domain.entity.base.NodeVariable;
+import jakarta.validation.Valid;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class StartNodeConfig extends NodeConfig {
+
+ @Valid
+ private List variables;
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/domain/entity/config/package-info.java b/metis-starter/src/main/java/com/metis/domain/entity/config/package-info.java
new file mode 100644
index 0000000..e836bd2
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/domain/entity/config/package-info.java
@@ -0,0 +1 @@
+package com.metis.domain.entity.config;
\ No newline at end of file
diff --git a/src/main/java/com/metis/flow/domain/query/AppQuery.java b/metis-starter/src/main/java/com/metis/domain/query/AppQuery.java
similarity index 82%
rename from src/main/java/com/metis/flow/domain/query/AppQuery.java
rename to metis-starter/src/main/java/com/metis/domain/query/AppQuery.java
index 2cd0ebe..5fb28ce 100644
--- a/src/main/java/com/metis/flow/domain/query/AppQuery.java
+++ b/metis-starter/src/main/java/com/metis/domain/query/AppQuery.java
@@ -1,4 +1,4 @@
-package com.metis.flow.domain.query;
+package com.metis.domain.query;
import lombok.Builder;
import lombok.Data;
diff --git a/src/main/java/com/metis/flow/engine/AppEngineService.java b/metis-starter/src/main/java/com/metis/engine/AppEngineService.java
similarity index 73%
rename from src/main/java/com/metis/flow/engine/AppEngineService.java
rename to metis-starter/src/main/java/com/metis/engine/AppEngineService.java
index d2fe8d9..dac3ee6 100644
--- a/src/main/java/com/metis/flow/engine/AppEngineService.java
+++ b/metis-starter/src/main/java/com/metis/engine/AppEngineService.java
@@ -1,9 +1,10 @@
-package com.metis.flow.engine;
+package com.metis.engine;
-import com.metis.flow.domain.query.AppQuery;
-import com.metis.flow.domain.entity.App;
-import com.metis.flow.domain.bo.CreateApp;
-import com.metis.flow.domain.bo.UpdateApp;
+
+import com.metis.domain.bo.CreateApp;
+import com.metis.domain.bo.UpdateApp;
+import com.metis.domain.entity.App;
+import com.metis.domain.query.AppQuery;
import java.util.List;
@@ -26,6 +27,14 @@ public interface AppEngineService {
List list(AppQuery query);
+ /**
+ * 通过应用id获取
+ *
+ * @param appId 应用程序id
+ * @return {@link App }
+ */
+ App getByAppId(Long appId);
+
/**
* 按身份证领取
*
@@ -37,10 +46,10 @@ public interface AppEngineService {
/**
* 通过部署id获取
*
- * @param deploymentId 部署id
+ * @param workflowId 部署id
* @return {@link App }
*/
- App getByDeploymentId(Long deploymentId);
+ App getByWorkflowId(Long workflowId);
/**
* 按id列表
diff --git a/metis-starter/src/main/java/com/metis/engine/AppFlowEngineRunnerService.java b/metis-starter/src/main/java/com/metis/engine/AppFlowEngineRunnerService.java
new file mode 100644
index 0000000..fe0d367
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/engine/AppFlowEngineRunnerService.java
@@ -0,0 +1,27 @@
+package com.metis.engine;
+
+import com.metis.runner.FlowRunningContext;
+import com.metis.runner.RunnerResult;
+
+/**
+ * 应用引擎运行器服务
+ *
+ * @author clay
+ * @date 2025/04/07
+ */
+public interface AppFlowEngineRunnerService {
+
+
+ /**
+ * 运行
+ *
+ * @param context 上下文
+ * @return {@link RunnerResult }
+ */
+ RunnerResult running(FlowRunningContext context);
+
+
+
+
+
+}
diff --git a/src/main/java/com/metis/flow/engine/impl/AppEngineServiceImpl.java b/metis-starter/src/main/java/com/metis/engine/impl/AppEngineServiceImpl.java
similarity index 84%
rename from src/main/java/com/metis/flow/engine/impl/AppEngineServiceImpl.java
rename to metis-starter/src/main/java/com/metis/engine/impl/AppEngineServiceImpl.java
index 7a5b117..0a3a993 100644
--- a/src/main/java/com/metis/flow/engine/impl/AppEngineServiceImpl.java
+++ b/metis-starter/src/main/java/com/metis/engine/impl/AppEngineServiceImpl.java
@@ -1,19 +1,20 @@
-package com.metis.flow.engine.impl;
+package com.metis.engine.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
+import com.metis.constant.BaseConstant;
+import com.metis.convert.BaseAppConvert;
+import com.metis.domain.bo.BuildApp;
+import com.metis.domain.bo.CreateApp;
+import com.metis.domain.bo.UpdateApp;
+import com.metis.domain.entity.App;
+import com.metis.domain.entity.BaseApp;
+import com.metis.domain.query.AppQuery;
+import com.metis.engine.AppEngineService;
import com.metis.enums.YesOrNoEnum;
-import com.metis.flow.constant.BaseConstant;
-import com.metis.flow.convert.BaseAppConvert;
-import com.metis.flow.domain.bo.BuildApp;
-import com.metis.flow.domain.bo.CreateApp;
-import com.metis.flow.domain.bo.UpdateApp;
-import com.metis.flow.domain.query.AppQuery;
-import com.metis.flow.domain.entity.*;
-import com.metis.flow.engine.AppEngineService;
-import com.metis.flow.service.BaseAppService;
-import com.metis.flow.validator.ValidatorService;
+import com.metis.service.BaseAppService;
+import com.metis.validator.ValidatorService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -42,6 +43,12 @@ public class AppEngineServiceImpl implements AppEngineService {
return BaseAppConvert.INSTANCE.toApps(list);
}
+ @Override
+ public App getByAppId(Long appId) {
+ BaseApp baseApp = baseAppService.getByAppId(appId);
+ return BaseAppConvert.INSTANCE.toApp(baseApp);
+ }
+
@Override
public App getByAppId(Long appId, Integer version) {
BaseApp baseApp = baseAppService.getByAppIdAndVersion(appId, version);
@@ -49,8 +56,8 @@ public class AppEngineServiceImpl implements AppEngineService {
}
@Override
- public App getByDeploymentId(Long deploymentId) {
- BaseApp baseApp = baseAppService.getById(deploymentId);
+ public App getByWorkflowId(Long workflowId) {
+ BaseApp baseApp = baseAppService.getById(workflowId);
return BaseAppConvert.INSTANCE.toApp(baseApp);
}
@@ -61,6 +68,7 @@ public class AppEngineServiceImpl implements AppEngineService {
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public App create(CreateApp createApp) {
BuildApp buildApp = BaseAppConvert.INSTANCE.toBuildApp(createApp);
// 校验
diff --git a/metis-starter/src/main/java/com/metis/engine/impl/AppFlowEngineRunnerServiceImpl.java b/metis-starter/src/main/java/com/metis/engine/impl/AppFlowEngineRunnerServiceImpl.java
new file mode 100644
index 0000000..9f64f8f
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/engine/impl/AppFlowEngineRunnerServiceImpl.java
@@ -0,0 +1,159 @@
+package com.metis.engine.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.metis.domain.context.RunningContext;
+import com.metis.domain.context.RunningResult;
+import com.metis.domain.context.SysContext;
+import com.metis.domain.entity.App;
+import com.metis.domain.entity.GraphDto;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.engine.AppEngineService;
+import com.metis.engine.AppFlowEngineRunnerService;
+import com.metis.enums.NodeType;
+import com.metis.runner.FlowRunningContext;
+import com.metis.runner.NodeRunner;
+import com.metis.runner.RunnerResult;
+import com.metis.runner.factory.NodeRunnerFactory;
+import com.metis.utils.GenericInterfacesUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AppFlowEngineRunnerServiceImpl implements AppFlowEngineRunnerService {
+
+ private final AppEngineService appEngineService;
+
+
+ @Override
+ public RunnerResult running(FlowRunningContext context) {
+ App app = getApp(context);
+ Assert.isTrue(ObjectUtil.isNotNull(app), "app为空");
+ // 构建运行实例, 并将运行实例放入上下文
+ Long instanceId = IdUtil.getSnowflakeNextId();
+ // 构建系统上下文信息
+ SysContext sysContext = SysContext.builder()
+ .files(context.getFiles())
+ .appId(app.getId())
+ .workflowId(app.getWorkflowId())
+ .instanceId(instanceId)
+ .build();
+ // 构建运行中上下文
+ RunningContext runningContext = RunningContext.buildContext(sysContext, context);
+ // 构建节点映射对象
+ GraphDto graph = GraphDto.of(app.getGraph());
+ Set readyRunningNode = new HashSet<>();
+
+ // 获取到开始节点
+ // 开始节点为空,则表示数据存在异常
+ Assert.isTrue(ObjectUtil.isNotNull(readyRunningNode), "流程图不存在开始节点");
+ for (Node node : graph.getSortedNodes()) {
+ Long nodeId = node.getId();
+ if (!graph.isNodeReady(nodeId)) {
+ continue;
+ }
+ log.info("当前运行节点 id:{}, name:{}, type:{}", node.getId(), node.getData().getLabel(), node.getType());
+ // 当前节点接下来的连接线信息
+ List edges = graph.getEdgeNodeId(nodeId);
+ // 执行
+ NodeRunner nodeRunner = getNodeRunner(node);
+ node.setConfigClass(GenericInterfacesUtils.getClass(nodeRunner));
+ // 下一个需要运行的节点id加入到可以运行的节点中
+ // 获取到返回结果
+ RunningResult result = nodeRunner.run(runningContext, node, edges);
+ log.info("节点执行结果:{}", JSON.toJSONString(result));
+ // 节点执行结果参数放入上下文中
+ if (ObjectUtil.isNotNull(result.getNodeContext())) {
+ runningContext.addNodeRunningContext(node.getId(), result.getNodeContext());
+ }
+ // 下一个需要运行的节点id加入到可以运行的节点中
+ if (CollUtil.isNotEmpty(result.getNextRunNodeId())) {
+ for (Long nextNodeId : result.getNextRunNodeId()) {
+ graph.updateNodeReadyMap(nextNodeId, true);
+ }
+ } else {
+ // 如果没有返回, 则认为所有的下级节点都需要运行
+ edges.forEach(edge -> {
+ graph.updateNodeReadyMap(edge.getTarget(), true);
+ });
+ }
+ }
+ Node endNode = graph.getEndNode();
+
+ JSONObject endRunningContext = runningContext.getRunningContext(endNode.getId());
+
+ return RunnerResult.builder()
+ .result(endRunningContext)
+ .context(sysContext)
+ .build();
+
+ }
+
+ private void doRunning(Set readyRunningNode, Map> edgeMap, RunningContext runningContext) {
+ Set nextRunningNodeId = new HashSet<>();
+ for (Node runningNode : readyRunningNode) {
+ // 当前节点接下来的连接线信息
+ List edges = edgeMap.getOrDefault(runningNode.getId(), new ArrayList<>());
+ // 执行
+ NodeRunner nodeRunner = getNodeRunner(runningNode);
+ runningNode.setConfigClass(GenericInterfacesUtils.getClass(nodeRunner));
+ // 获取到返回结果
+ RunningResult result = nodeRunner.run(runningContext, runningNode, edges);
+ // 节点执行结果参数放入上下文中
+ if (ObjectUtil.isNotNull(result.getNodeContext())) {
+ runningContext.addNodeRunningContext(runningNode.getId(), result.getNodeContext());
+ }
+ if (CollUtil.isNotEmpty(result.getNextRunNodeId())) {
+ nextRunningNodeId.addAll(result.getNextRunNodeId());
+ }
+ }
+ }
+
+
+ private RunningResult doRunning(RunningContext runningContext, Node node, List edges) {
+ NodeRunner nodeRunner = getNodeRunner(node);
+ node.setConfigClass(GenericInterfacesUtils.getClass(nodeRunner));
+ // 获取到返回结果
+ return nodeRunner.run(runningContext, node, edges);
+ }
+
+
+ /**
+ * 获取节点运行程序
+ *
+ * @param node 节点
+ * @return {@link NodeRunner }
+ */
+ private NodeRunner getNodeRunner(Node node) {
+ if (NodeType.CUSTOM.equals(node.getType())) {
+ Assert.isTrue(StrUtil.isNotBlank(node.getCustomType()), "自定义节点类型不能为空");
+ return NodeRunnerFactory.getCustom(node.getCustomType());
+ }
+ return NodeRunnerFactory.get(node.getType());
+ }
+
+
+ /**
+ * 获取到应用程序信息
+ *
+ * @param context 上下文
+ * @return {@link App }
+ */
+ private App getApp(FlowRunningContext context) {
+ if (ObjectUtil.isNotNull(context.getWorkflowId())) {
+ return appEngineService.getByWorkflowId(context.getWorkflowId());
+ }
+ return appEngineService.getByAppId(context.getAppId());
+ }
+}
diff --git a/src/main/java/com/metis/enums/BaseEnum.java b/metis-starter/src/main/java/com/metis/enums/BaseEnum.java
similarity index 100%
rename from src/main/java/com/metis/enums/BaseEnum.java
rename to metis-starter/src/main/java/com/metis/enums/BaseEnum.java
diff --git a/src/main/java/com/metis/flow/enums/EdgeType.java b/metis-starter/src/main/java/com/metis/enums/EdgeType.java
similarity index 96%
rename from src/main/java/com/metis/flow/enums/EdgeType.java
rename to metis-starter/src/main/java/com/metis/enums/EdgeType.java
index 4113c13..0a53b1a 100644
--- a/src/main/java/com/metis/flow/enums/EdgeType.java
+++ b/metis-starter/src/main/java/com/metis/enums/EdgeType.java
@@ -1,4 +1,4 @@
-package com.metis.flow.enums;
+package com.metis.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
diff --git a/metis-starter/src/main/java/com/metis/enums/FileUploadType.java b/metis-starter/src/main/java/com/metis/enums/FileUploadType.java
new file mode 100644
index 0000000..f1bde0a
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/enums/FileUploadType.java
@@ -0,0 +1,45 @@
+package com.metis.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 文件上传类型
+ *
+ * @author clay
+ * @date 2025/04/08
+ */
+@Getter
+@AllArgsConstructor
+public enum FileUploadType {
+ LOCAL_FILE(1, "localFile", "本地文件"),
+ REMOTE_URL(2, "remoteUrl", "远程URL");
+
+
+ private final Integer code;
+
+ @JsonValue
+ private final String value;
+
+ private final String name;
+
+
+ /**
+ * 枚举序列化器(前端传code时自动转换为对应枚举)
+ *
+ * @param value 值
+ * @return 枚举
+ */
+ @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+ public static FileUploadType get(String value) {
+ return Arrays.stream(FileUploadType.class.getEnumConstants())
+ .filter(e -> e.getValue().equals(value))
+ .findFirst()
+ .orElse(null);
+ }
+
+ }
diff --git a/src/main/java/com/metis/flow/enums/HandleType.java b/metis-starter/src/main/java/com/metis/enums/HandleType.java
similarity index 96%
rename from src/main/java/com/metis/flow/enums/HandleType.java
rename to metis-starter/src/main/java/com/metis/enums/HandleType.java
index 37dc51e..227ac69 100644
--- a/src/main/java/com/metis/flow/enums/HandleType.java
+++ b/metis-starter/src/main/java/com/metis/enums/HandleType.java
@@ -1,4 +1,4 @@
-package com.metis.flow.enums;
+package com.metis.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
diff --git a/src/main/java/com/metis/flow/enums/NodeType.java b/metis-starter/src/main/java/com/metis/enums/NodeType.java
similarity index 70%
rename from src/main/java/com/metis/flow/enums/NodeType.java
rename to metis-starter/src/main/java/com/metis/enums/NodeType.java
index 0c16ad4..89717e7 100644
--- a/src/main/java/com/metis/flow/enums/NodeType.java
+++ b/metis-starter/src/main/java/com/metis/enums/NodeType.java
@@ -1,4 +1,4 @@
-package com.metis.flow.enums;
+package com.metis.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
@@ -11,8 +11,14 @@ import java.util.Arrays;
@AllArgsConstructor
public enum NodeType {
- START(1, "start", "开始", Object.class),
- END(2, "end", "结束", Object.class),
+ START(1, "start", "开始"),
+ END(2, "end", "结束"),
+ DOCUMENT_EXTRACTOR(3, "documentExtractor", "文档提取器"),
+ CUSTOM(4, "custom", "自定义节点"),
+ LLM(5, "llm", "LLM"),
+ QUESTION_CLASSIFIER(6, "questionClassifier", "问题分类器"),
+ IF_ELSE(7, "ifElse", "条件判断"),
+
;
@@ -24,8 +30,6 @@ public enum NodeType {
private final String name;
- private final Class> configClass;
-
/**
* 枚举序列化器(前端传code时自动转换为对应枚举)
diff --git a/metis-starter/src/main/java/com/metis/enums/NodeVariableType.java b/metis-starter/src/main/java/com/metis/enums/NodeVariableType.java
new file mode 100644
index 0000000..09ca06d
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/enums/NodeVariableType.java
@@ -0,0 +1,50 @@
+package com.metis.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 节点变量类型
+ *
+ * @author clay
+ * @date 2025/04/08
+ */
+@Getter
+@AllArgsConstructor
+public enum NodeVariableType {
+ TEXT_INPUT(1, "text-input", "文本"),
+ PARAGRAPH(2, "paragraph", "段落"),
+ SELECT(3, "select", "下拉框"),
+ NUMBER(4, "number", "数字"),
+ FILE(5, "file", "文件"),
+ FILE_LIST(6, "file-list", "文件列表")
+ ;
+
+
+ private final Integer code;
+
+ @JsonValue
+ private final String value;
+
+ private final String name;
+
+
+ /**
+ * 枚举序列化器(前端传code时自动转换为对应枚举)
+ *
+ * @param value 值
+ * @return 枚举
+ */
+ @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
+ public static NodeVariableType get(String value) {
+ return Arrays.stream(NodeVariableType.class.getEnumConstants())
+ .filter(e -> e.getValue().equals(value))
+ .findFirst()
+ .orElse(null);
+ }
+
+}
diff --git a/src/main/java/com/metis/flow/enums/PositionType.java b/metis-starter/src/main/java/com/metis/enums/PositionType.java
similarity index 97%
rename from src/main/java/com/metis/flow/enums/PositionType.java
rename to metis-starter/src/main/java/com/metis/enums/PositionType.java
index 98eb1d0..a5d67a1 100644
--- a/src/main/java/com/metis/flow/enums/PositionType.java
+++ b/metis-starter/src/main/java/com/metis/enums/PositionType.java
@@ -1,4 +1,4 @@
-package com.metis.flow.enums;
+package com.metis.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
diff --git a/src/main/java/com/metis/enums/ResultEnum.java b/metis-starter/src/main/java/com/metis/enums/ResultEnum.java
similarity index 100%
rename from src/main/java/com/metis/enums/ResultEnum.java
rename to metis-starter/src/main/java/com/metis/enums/ResultEnum.java
diff --git a/src/main/java/com/metis/enums/YesOrNoEnum.java b/metis-starter/src/main/java/com/metis/enums/YesOrNoEnum.java
similarity index 100%
rename from src/main/java/com/metis/enums/YesOrNoEnum.java
rename to metis-starter/src/main/java/com/metis/enums/YesOrNoEnum.java
diff --git a/src/main/java/com/metis/facade/ProcessDefinitionFacade.java b/metis-starter/src/main/java/com/metis/facade/ProcessDefinitionFacade.java
similarity index 62%
rename from src/main/java/com/metis/facade/ProcessDefinitionFacade.java
rename to metis-starter/src/main/java/com/metis/facade/ProcessDefinitionFacade.java
index 5272714..7926301 100644
--- a/src/main/java/com/metis/facade/ProcessDefinitionFacade.java
+++ b/metis-starter/src/main/java/com/metis/facade/ProcessDefinitionFacade.java
@@ -1,10 +1,12 @@
package com.metis.facade;
+import com.metis.convert.GraphConvert;
+import com.metis.domain.bo.CreateApp;
import com.metis.domain.bo.ProcessBo;
-import com.metis.flow.domain.entity.App;
-import com.metis.flow.domain.bo.CreateApp;
-import com.metis.flow.domain.bo.UpdateApp;
-import com.metis.flow.engine.AppEngineService;
+import com.metis.domain.bo.UpdateApp;
+import com.metis.domain.entity.App;
+import com.metis.domain.entity.base.Graph;
+import com.metis.engine.AppEngineService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -21,24 +23,27 @@ public class ProcessDefinitionFacade {
*
* @param processBo 过程业务对象
*/
- public void create(ProcessBo processBo) {
+ public Long create(ProcessBo processBo) {
+ Graph graph = GraphConvert.INSTANCE.toEntity(processBo.getGraph());
CreateApp createApp = CreateApp.builder()
.name(processBo.getName())
- .graph(processBo.getGraph())
+ .graph(graph)
.build();
App app = appEngineService.create(createApp);
+ return app.getWorkflowId();
}
public App getByDeploymentId(Long deploymentId) {
- return appEngineService.getByDeploymentId(deploymentId);
+ return appEngineService.getByWorkflowId(deploymentId);
}
public void update(ProcessBo processBo) {
+ Graph graph = GraphConvert.INSTANCE.toEntity(processBo.getGraph());
appEngineService.update(UpdateApp.builder()
.defaultUse(processBo.getDefaultUse())
.appId(processBo.getAppId())
.name(processBo.getName())
- .graph(processBo.getGraph())
+ .graph(graph)
.build());
}
diff --git a/src/main/java/com/metis/handle/GlobalExceptionHandler.java b/metis-starter/src/main/java/com/metis/handle/GlobalExceptionHandler.java
similarity index 100%
rename from src/main/java/com/metis/handle/GlobalExceptionHandler.java
rename to metis-starter/src/main/java/com/metis/handle/GlobalExceptionHandler.java
diff --git a/src/main/java/com/metis/flow/mapper/BaseAppMapper.java b/metis-starter/src/main/java/com/metis/mapper/BaseAppMapper.java
similarity index 76%
rename from src/main/java/com/metis/flow/mapper/BaseAppMapper.java
rename to metis-starter/src/main/java/com/metis/mapper/BaseAppMapper.java
index cb09b20..e9b88fc 100644
--- a/src/main/java/com/metis/flow/mapper/BaseAppMapper.java
+++ b/metis-starter/src/main/java/com/metis/mapper/BaseAppMapper.java
@@ -1,6 +1,6 @@
-package com.metis.flow.mapper;
+package com.metis.mapper;
-import com.metis.flow.domain.entity.BaseApp;
+import com.metis.domain.entity.BaseApp;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
diff --git a/src/main/java/com/metis/mybatis/MybatisPlusConfiguration.java b/metis-starter/src/main/java/com/metis/mybatis/MybatisPlusConfiguration.java
similarity index 100%
rename from src/main/java/com/metis/mybatis/MybatisPlusConfiguration.java
rename to metis-starter/src/main/java/com/metis/mybatis/MybatisPlusConfiguration.java
diff --git a/src/main/java/com/metis/mybatis/handler/BaseEntityMetaObjectHandler.java b/metis-starter/src/main/java/com/metis/mybatis/handler/BaseEntityMetaObjectHandler.java
similarity index 100%
rename from src/main/java/com/metis/mybatis/handler/BaseEntityMetaObjectHandler.java
rename to metis-starter/src/main/java/com/metis/mybatis/handler/BaseEntityMetaObjectHandler.java
diff --git a/src/main/java/com/metis/mybatis/logic/SelectIgnoreLogicDelete.java b/metis-starter/src/main/java/com/metis/mybatis/logic/SelectIgnoreLogicDelete.java
similarity index 100%
rename from src/main/java/com/metis/mybatis/logic/SelectIgnoreLogicDelete.java
rename to metis-starter/src/main/java/com/metis/mybatis/logic/SelectIgnoreLogicDelete.java
diff --git a/src/main/java/com/metis/mybatis/support/CustomSqlInjector.java b/metis-starter/src/main/java/com/metis/mybatis/support/CustomSqlInjector.java
similarity index 100%
rename from src/main/java/com/metis/mybatis/support/CustomSqlInjector.java
rename to metis-starter/src/main/java/com/metis/mybatis/support/CustomSqlInjector.java
diff --git a/src/main/java/com/metis/result/Result.java b/metis-starter/src/main/java/com/metis/result/Result.java
similarity index 100%
rename from src/main/java/com/metis/result/Result.java
rename to metis-starter/src/main/java/com/metis/result/Result.java
diff --git a/src/main/java/com/metis/result/page/TableDataInfo.java b/metis-starter/src/main/java/com/metis/result/page/TableDataInfo.java
similarity index 100%
rename from src/main/java/com/metis/result/page/TableDataInfo.java
rename to metis-starter/src/main/java/com/metis/result/page/TableDataInfo.java
diff --git a/metis-starter/src/main/java/com/metis/runner/CustomNodeRunner.java b/metis-starter/src/main/java/com/metis/runner/CustomNodeRunner.java
new file mode 100644
index 0000000..c0b7143
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/CustomNodeRunner.java
@@ -0,0 +1,33 @@
+package com.metis.runner;
+
+import com.metis.domain.entity.base.NodeConfig;
+import com.metis.enums.NodeType;
+
+/**
+ * 自定义节点运行器
+ *
+ * @author clay
+ * @date 2025/04/20
+ */
+public interface CustomNodeRunner extends NodeRunner {
+
+
+ /**
+ * 获取自定义节点的节点类型
+ *
+ * @return {@link String }
+ */
+ String getCustomNodeType();
+
+
+ /**
+ * 得到类型
+ *
+ * @return {@link NodeType }
+ */
+ default NodeType getType() {
+ return NodeType.CUSTOM;
+ }
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/FlowRunningContext.java b/metis-starter/src/main/java/com/metis/runner/FlowRunningContext.java
new file mode 100644
index 0000000..022f693
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/FlowRunningContext.java
@@ -0,0 +1,49 @@
+package com.metis.runner;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 运行上下文
+ *
+ * @author clay
+ * @date 2025/04/07
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class FlowRunningContext {
+
+ /**
+ * 文件列表
+ */
+ private List files;
+
+ /**
+ * 应用程序id
+ */
+ private Long appId;
+
+ /**
+ * 用户id
+ */
+ private Long userId;
+
+ /**
+ * 工作流id
+ */
+ private Long workflowId;
+
+ /**
+ * 自定义
+ */
+ private JSONObject custom;
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/NodeRunner.java b/metis-starter/src/main/java/com/metis/runner/NodeRunner.java
new file mode 100644
index 0000000..cd59b92
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/NodeRunner.java
@@ -0,0 +1,56 @@
+package com.metis.runner;
+
+import cn.hutool.core.collection.CollUtil;
+import com.metis.domain.context.RunningContext;
+import com.metis.domain.context.RunningResult;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.base.NodeConfig;
+import com.metis.enums.NodeType;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 内置节点运行器
+ *
+ * @author clay
+ * @date 2025/04/20
+ */
+public interface NodeRunner {
+
+
+ /**
+ * 运行
+ *
+ * @param context 上下文
+ * @param node 节点配置信息
+ * @param edges 当前接下来的连线信息, 一些特殊节点需要节点内部判断下一个运行节点
+ * @return {@link RunningContext }
+ */
+ RunningResult run(RunningContext context, Node node, List edges);
+
+
+ /**
+ * 获取节点类型
+ *
+ * @return {@link NodeType }
+ */
+ NodeType getType();
+
+
+ /**
+ * 获取下一个节点id
+ *
+ * @param edges 边缘
+ * @return {@link Set }<{@link Long }>
+ */
+ default Set getNextNodeIds(List edges) {
+ if (CollUtil.isEmpty(edges)) {
+ return Set.of();
+ }
+ return edges.stream().map(Edge::getTarget).collect(Collectors.toSet());
+ }
+
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/RunnerResult.java b/metis-starter/src/main/java/com/metis/runner/RunnerResult.java
new file mode 100644
index 0000000..3306ea5
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/RunnerResult.java
@@ -0,0 +1,32 @@
+package com.metis.runner;
+
+
+import com.alibaba.fastjson2.JSONObject;
+import com.metis.domain.context.SysContext;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 运行结果
+ *
+ * @author clay
+ * @date 2025/04/07
+ */
+@Data
+@Builder
+public class RunnerResult {
+
+ /**
+ * 运行内容
+ */
+ private JSONObject result;
+
+ /**
+ * 上下文
+ */
+ private SysContext context;
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/factory/NodeRunnerFactory.java b/metis-starter/src/main/java/com/metis/runner/factory/NodeRunnerFactory.java
new file mode 100644
index 0000000..a2204fa
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/factory/NodeRunnerFactory.java
@@ -0,0 +1,64 @@
+package com.metis.runner.factory;
+
+import cn.hutool.core.lang.Assert;
+import com.metis.enums.NodeType;
+import com.metis.runner.CustomNodeRunner;
+import com.metis.runner.NodeRunner;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class NodeRunnerFactory {
+
+ /**
+ * 内置节点运行器
+ */
+ private static final Map NODE_MAP = new ConcurrentHashMap<>(8);
+ /**
+ * 自定义节点映射
+ */
+ private static final Map CUSTOM_NODE_MAP = new ConcurrentHashMap<>(8);
+
+ /**
+ * 注册
+ *
+ * @param runner 跑步者
+ */
+ static void register(NodeRunner runner) {
+ NODE_MAP.put(runner.getType(), runner);
+ }
+
+
+ /**
+ * 得到
+ *
+ * @param type 类型
+ * @return {@link NodeRunner }
+ */
+ public static NodeRunner get(NodeType type) {
+ return NODE_MAP.get(type);
+ }
+
+
+ /**
+ * 注册自定义节点
+ *
+ * @param runner 跑步者
+ */
+ static void registerCustom(CustomNodeRunner runner) {
+ Assert.isTrue(!CUSTOM_NODE_MAP.containsKey(runner.getCustomNodeType()), "已存在类型:{}, class:{}的运行器", runner.getCustomNodeType(), runner.getClass());
+ CUSTOM_NODE_MAP.put(runner.getCustomNodeType(), runner);
+ }
+
+ /**
+ * 得到自定义节点运行器
+ *
+ * @param type 类型
+ * @return {@link NodeRunner }
+ */
+ public static NodeRunner getCustom(String type) {
+ return CUSTOM_NODE_MAP.get(type);
+ }
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/factory/RunnerInitialize.java b/metis-starter/src/main/java/com/metis/runner/factory/RunnerInitialize.java
new file mode 100644
index 0000000..b02873a
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/factory/RunnerInitialize.java
@@ -0,0 +1,38 @@
+package com.metis.runner.factory;
+
+import cn.hutool.core.lang.Assert;
+import com.metis.enums.NodeType;
+import com.metis.runner.CustomNodeRunner;
+import com.metis.runner.NodeRunner;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+/**
+ * runner初始化
+ *
+ * @author clay
+ * @date 2025/04/07
+ */
+@Service
+public class RunnerInitialize implements ApplicationContextAware {
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+
+ Map runnerMap = applicationContext.getBeansOfType(NodeRunner.class);
+
+ runnerMap.forEach((runnerBeanName, runner) -> {
+ if (NodeType.CUSTOM.equals(runner.getType())) {
+ Assert.isTrue(runner instanceof CustomNodeRunner, "自定义节点必须实现CustomNodeRunner接口");
+ NodeRunnerFactory.registerCustom((CustomNodeRunner) runner);
+ } else {
+ NodeRunnerFactory.register(runner);
+ }
+ });
+ }
+
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/impl/EndNodeRunner.java b/metis-starter/src/main/java/com/metis/runner/impl/EndNodeRunner.java
new file mode 100644
index 0000000..4aacda8
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/impl/EndNodeRunner.java
@@ -0,0 +1,33 @@
+package com.metis.runner.impl;
+
+
+import com.alibaba.fastjson2.JSONObject;
+import com.metis.domain.context.RunningContext;
+import com.metis.domain.context.RunningResult;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.config.node.EndNodeConfig;
+import com.metis.enums.NodeType;
+import com.metis.runner.NodeRunner;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+public class EndNodeRunner implements NodeRunner {
+
+ @Override
+ public RunningResult run(RunningContext context, Node node, List edges) {
+ JSONObject contextNodeValue = new JSONObject();
+ contextNodeValue.put("userId", context.getSys().getAppId());
+ return RunningResult.buildResult(contextNodeValue);
+ }
+
+ @Override
+ public NodeType getType() {
+ return NodeType.END;
+ }
+
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/impl/LLMNodeRunner.java b/metis-starter/src/main/java/com/metis/runner/impl/LLMNodeRunner.java
new file mode 100644
index 0000000..1779a34
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/impl/LLMNodeRunner.java
@@ -0,0 +1,27 @@
+package com.metis.runner.impl;
+
+import com.metis.domain.context.RunningContext;
+import com.metis.domain.context.RunningResult;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.config.node.LLMNodeConfig;
+import com.metis.enums.NodeType;
+import com.metis.runner.NodeRunner;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+public class LLMNodeRunner implements NodeRunner {
+ @Override
+ public RunningResult run(RunningContext context, Node node, List edges) {
+ return RunningResult.buildResult();
+ }
+
+ @Override
+ public NodeType getType() {
+ return NodeType.LLM;
+ }
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/impl/QuestionClassifierRunner.java b/metis-starter/src/main/java/com/metis/runner/impl/QuestionClassifierRunner.java
new file mode 100644
index 0000000..d23f7a5
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/impl/QuestionClassifierRunner.java
@@ -0,0 +1,36 @@
+package com.metis.runner.impl;
+
+import com.metis.domain.context.RunningContext;
+import com.metis.domain.context.RunningResult;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.config.node.QuestionClassifierConfig;
+import com.metis.enums.NodeType;
+import com.metis.runner.NodeRunner;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+@Slf4j
+@Service
+public class QuestionClassifierRunner implements NodeRunner {
+
+ @Override
+ public RunningResult run(RunningContext context, Node node, List edges) {
+ Set nextNodeIds = getNextNodeIds(edges);
+ // 生成随机索引
+ Random random = new Random();
+ int randomIndex = random.nextInt(nextNodeIds.size());
+ List nodeIds = new ArrayList<>(nextNodeIds);
+ return RunningResult.buildResult(Set.of(nodeIds.get(randomIndex)));
+ }
+
+ @Override
+ public NodeType getType() {
+ return NodeType.QUESTION_CLASSIFIER;
+ }
+}
diff --git a/metis-starter/src/main/java/com/metis/runner/impl/StartNodeRunner.java b/metis-starter/src/main/java/com/metis/runner/impl/StartNodeRunner.java
new file mode 100644
index 0000000..7019f92
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/runner/impl/StartNodeRunner.java
@@ -0,0 +1,52 @@
+package com.metis.runner.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import com.alibaba.fastjson2.JSONObject;
+import com.metis.domain.context.RunningContext;
+import com.metis.domain.context.RunningResult;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.base.NodeVariable;
+import com.metis.domain.entity.config.node.StartNodeConfig;
+import com.metis.enums.NodeType;
+import com.metis.runner.NodeRunner;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 启动节点运行器, 开始节点功能主要为将用户传入的参数进行校验, 并放入到上下文中
+ *
+ * @author clay
+ * @date 2025/04/08
+ */
+@Slf4j
+@Service
+public class StartNodeRunner implements NodeRunner {
+
+ @Override
+ public RunningResult run(RunningContext context, Node node, List edges) {
+ StartNodeConfig config = node.getConfig();
+ // 获取到节点的自定义参数
+ List variables = config.getVariables();
+ // 如果没有自定义参数, 则直接返回
+ if (CollUtil.isEmpty(variables)) {
+ return RunningResult.buildResult();
+ }
+ // 获取用户自定义参数
+ JSONObject custom = context.getCustom();
+ JSONObject contextNodeValue = new JSONObject();
+ for (NodeVariable variable : variables) {
+ Object value = variable.getValue(custom);
+ contextNodeValue.put(variable.getVariable(), value);
+ }
+ return RunningResult.buildResult(contextNodeValue);
+ }
+
+
+ @Override
+ public NodeType getType() {
+ return NodeType.START;
+ }
+}
diff --git a/src/main/java/com/metis/flow/service/BaseAppService.java b/metis-starter/src/main/java/com/metis/service/BaseAppService.java
similarity index 92%
rename from src/main/java/com/metis/flow/service/BaseAppService.java
rename to metis-starter/src/main/java/com/metis/service/BaseAppService.java
index 2fa231b..96d1c31 100644
--- a/src/main/java/com/metis/flow/service/BaseAppService.java
+++ b/metis-starter/src/main/java/com/metis/service/BaseAppService.java
@@ -1,8 +1,8 @@
-package com.metis.flow.service;
+package com.metis.service;
import com.metis.enums.YesOrNoEnum;
-import com.metis.flow.domain.query.AppQuery;
-import com.metis.flow.domain.entity.BaseApp;
+import com.metis.domain.query.AppQuery;
+import com.metis.domain.entity.BaseApp;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
diff --git a/src/main/java/com/metis/flow/service/impl/BaseAppServiceImpl.java b/metis-starter/src/main/java/com/metis/service/impl/BaseAppServiceImpl.java
similarity index 93%
rename from src/main/java/com/metis/flow/service/impl/BaseAppServiceImpl.java
rename to metis-starter/src/main/java/com/metis/service/impl/BaseAppServiceImpl.java
index 5cbe09a..c9263ab 100644
--- a/src/main/java/com/metis/flow/service/impl/BaseAppServiceImpl.java
+++ b/metis-starter/src/main/java/com/metis/service/impl/BaseAppServiceImpl.java
@@ -1,4 +1,4 @@
-package com.metis.flow.service.impl;
+package com.metis.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
@@ -6,10 +6,10 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.metis.enums.YesOrNoEnum;
-import com.metis.flow.domain.query.AppQuery;
-import com.metis.flow.domain.entity.BaseApp;
-import com.metis.flow.mapper.BaseAppMapper;
-import com.metis.flow.service.BaseAppService;
+import com.metis.domain.query.AppQuery;
+import com.metis.domain.entity.BaseApp;
+import com.metis.mapper.BaseAppMapper;
+import com.metis.service.BaseAppService;
import com.metis.utils.PageConditionUtil;
import org.springframework.stereotype.Service;
diff --git a/src/main/java/com/metis/sseclient/ToolSpecificationHelper.java b/metis-starter/src/main/java/com/metis/sseclient/ToolSpecificationHelper.java
similarity index 100%
rename from src/main/java/com/metis/sseclient/ToolSpecificationHelper.java
rename to metis-starter/src/main/java/com/metis/sseclient/ToolSpecificationHelper.java
diff --git a/src/main/java/com/metis/sseclient/check/SseCheck.java b/metis-starter/src/main/java/com/metis/sseclient/check/SseCheck.java
similarity index 100%
rename from src/main/java/com/metis/sseclient/check/SseCheck.java
rename to metis-starter/src/main/java/com/metis/sseclient/check/SseCheck.java
diff --git a/src/main/java/com/metis/sseclient/event/SseEventListener.java b/metis-starter/src/main/java/com/metis/sseclient/event/SseEventListener.java
similarity index 100%
rename from src/main/java/com/metis/sseclient/event/SseEventListener.java
rename to metis-starter/src/main/java/com/metis/sseclient/event/SseEventListener.java
diff --git a/src/main/java/com/metis/sseclient/handler/McpOperationHandler.java b/metis-starter/src/main/java/com/metis/sseclient/handler/McpOperationHandler.java
similarity index 100%
rename from src/main/java/com/metis/sseclient/handler/McpOperationHandler.java
rename to metis-starter/src/main/java/com/metis/sseclient/handler/McpOperationHandler.java
diff --git a/metis-starter/src/main/java/com/metis/utils/GenericInterfacesUtils.java b/metis-starter/src/main/java/com/metis/utils/GenericInterfacesUtils.java
new file mode 100644
index 0000000..c8b14f8
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/utils/GenericInterfacesUtils.java
@@ -0,0 +1,25 @@
+package com.metis.utils;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+public class GenericInterfacesUtils {
+
+
+ public static Class getClass(Object object) {
+
+ Type[] genericInterfaces = object.getClass().getGenericInterfaces();
+ for (Type genericInterface : genericInterfaces) {
+ if (genericInterface instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
+ Type[] typeArguments = parameterizedType.getActualTypeArguments();
+ if (typeArguments.length > 0) {
+ return (Class) typeArguments[0];
+ }
+ }
+ }
+ throw new IllegalStateException("无法获取泛型类型");
+ }
+
+
+}
diff --git a/src/main/java/com/metis/utils/LocalDateTimeUtils.java b/metis-starter/src/main/java/com/metis/utils/LocalDateTimeUtils.java
similarity index 100%
rename from src/main/java/com/metis/utils/LocalDateTimeUtils.java
rename to metis-starter/src/main/java/com/metis/utils/LocalDateTimeUtils.java
diff --git a/src/main/java/com/metis/utils/PageConditionUtil.java b/metis-starter/src/main/java/com/metis/utils/PageConditionUtil.java
similarity index 100%
rename from src/main/java/com/metis/utils/PageConditionUtil.java
rename to metis-starter/src/main/java/com/metis/utils/PageConditionUtil.java
diff --git a/src/main/java/com/metis/utils/PageInfo.java b/metis-starter/src/main/java/com/metis/utils/PageInfo.java
similarity index 100%
rename from src/main/java/com/metis/utils/PageInfo.java
rename to metis-starter/src/main/java/com/metis/utils/PageInfo.java
diff --git a/src/main/java/com/metis/utils/TableSupport.java b/metis-starter/src/main/java/com/metis/utils/TableSupport.java
similarity index 100%
rename from src/main/java/com/metis/utils/TableSupport.java
rename to metis-starter/src/main/java/com/metis/utils/TableSupport.java
diff --git a/src/main/java/com/metis/utils/TransactionalUtils.java b/metis-starter/src/main/java/com/metis/utils/TransactionalUtils.java
similarity index 100%
rename from src/main/java/com/metis/utils/TransactionalUtils.java
rename to metis-starter/src/main/java/com/metis/utils/TransactionalUtils.java
diff --git a/metis-starter/src/main/java/com/metis/validator/CustomNodeValidator.java b/metis-starter/src/main/java/com/metis/validator/CustomNodeValidator.java
new file mode 100644
index 0000000..ee37924
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/CustomNodeValidator.java
@@ -0,0 +1,34 @@
+package com.metis.validator;
+
+import com.metis.domain.entity.base.NodeConfig;
+import com.metis.enums.NodeType;
+
+/**
+ * 自定义节点验证器
+ *
+ * @author clay
+ * @date 2025/04/20
+ */
+public interface CustomNodeValidator extends NodeValidator {
+
+
+ /**
+ * 获取自定义节点的节点类型
+ *
+ * @return {@link String }
+ */
+ String getCustomNodeType();
+
+
+ /**
+ * 得到类型
+ *
+ * @return {@link NodeType }
+ */
+ default NodeType getType() {
+ return NodeType.CUSTOM;
+ }
+
+
+
+}
diff --git a/src/main/java/com/metis/flow/validator/EdgeValidator.java b/metis-starter/src/main/java/com/metis/validator/EdgeValidator.java
similarity index 70%
rename from src/main/java/com/metis/flow/validator/EdgeValidator.java
rename to metis-starter/src/main/java/com/metis/validator/EdgeValidator.java
index a02d1e2..74f87b7 100644
--- a/src/main/java/com/metis/flow/validator/EdgeValidator.java
+++ b/metis-starter/src/main/java/com/metis/validator/EdgeValidator.java
@@ -1,7 +1,7 @@
-package com.metis.flow.validator;
+package com.metis.validator;
-import com.metis.flow.domain.entity.base.Edge;
-import com.metis.flow.enums.EdgeType;
+import com.metis.domain.entity.base.Edge;
+import com.metis.enums.EdgeType;
public interface EdgeValidator {
diff --git a/metis-starter/src/main/java/com/metis/validator/NodeValidator.java b/metis-starter/src/main/java/com/metis/validator/NodeValidator.java
new file mode 100644
index 0000000..27aac24
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/NodeValidator.java
@@ -0,0 +1,40 @@
+package com.metis.validator;
+
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.base.NodeConfig;
+import com.metis.enums.NodeType;
+
+import java.util.List;
+
+public interface NodeValidator {
+
+
+ /**
+ * 节点验证
+ *
+ * @param node 节点
+ * @return {@link ValidatorResult }
+ */
+ ValidatorResult validateValue(Node node);
+
+
+ /**
+ * 验证关系
+ *
+ * @param node 节点
+ * @param sources 来源
+ * @param targets 目标
+ * @return {@link ValidatorResult }
+ */
+ ValidatorResult validateRelation(Node node, List sources, List targets);
+
+
+ /**
+ * 得到类型
+ *
+ * @return {@link NodeType }
+ */
+ NodeType getType();
+
+}
diff --git a/metis-starter/src/main/java/com/metis/validator/ValidatorCodeService.java b/metis-starter/src/main/java/com/metis/validator/ValidatorCodeService.java
new file mode 100644
index 0000000..a05e326
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/ValidatorCodeService.java
@@ -0,0 +1,65 @@
+package com.metis.validator;
+
+import cn.hutool.core.collection.CollUtil;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.Validator;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Set;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ValidatorCodeService {
+
+ private final Validator globalValidator;
+
+ /**
+ * 验证, 出现校验未通过则抛出异常
+ *
+ * @param validObject 有效对象
+ */
+ public void validateThrow(T validObject) {
+ // validation 编程式校验
+ Set> validates = globalValidator.validate(validObject);
+ if (CollUtil.isNotEmpty(validates)) {
+ List errorMessage = validates.stream()
+ .map(ConstraintViolation::getMessage).toList();
+ throw new RuntimeException(String.join(",", errorMessage));
+ }
+ }
+
+ /**
+ * 验证错误信息
+ *
+ * @param validObject 有效对象
+ * @return {@link String }
+ */
+ public String validateErrorMsg(T validObject) {
+ // validation 编程式校验
+ Set> validates = globalValidator.validate(validObject);
+ if (CollUtil.isNotEmpty(validates)) {
+ List errorMessage = validates.stream()
+ .map(ConstraintViolation::getMessage).toList();
+ return String.join(",", errorMessage);
+ }
+ return null;
+ }
+
+
+ /**
+ * 验证
+ *
+ * @param validObject 有效对象
+ * @return boolean
+ */
+ public boolean validate(T validObject) {
+ // validation 编程式校验
+ Set> validates = globalValidator.validate(validObject);
+ return CollUtil.isEmpty(validates);
+ }
+
+}
diff --git a/src/main/java/com/metis/flow/validator/ValidatorResult.java b/metis-starter/src/main/java/com/metis/validator/ValidatorResult.java
similarity index 93%
rename from src/main/java/com/metis/flow/validator/ValidatorResult.java
rename to metis-starter/src/main/java/com/metis/validator/ValidatorResult.java
index 8666b6e..a9e502a 100644
--- a/src/main/java/com/metis/flow/validator/ValidatorResult.java
+++ b/metis-starter/src/main/java/com/metis/validator/ValidatorResult.java
@@ -1,4 +1,4 @@
-package com.metis.flow.validator;
+package com.metis.validator;
import lombok.Data;
diff --git a/src/main/java/com/metis/flow/validator/ValidatorService.java b/metis-starter/src/main/java/com/metis/validator/ValidatorService.java
similarity index 66%
rename from src/main/java/com/metis/flow/validator/ValidatorService.java
rename to metis-starter/src/main/java/com/metis/validator/ValidatorService.java
index ac5f11f..6cf5afb 100644
--- a/src/main/java/com/metis/flow/validator/ValidatorService.java
+++ b/metis-starter/src/main/java/com/metis/validator/ValidatorService.java
@@ -1,6 +1,6 @@
-package com.metis.flow.validator;
+package com.metis.validator;
-import com.metis.flow.domain.bo.BuildApp;
+import com.metis.domain.bo.BuildApp;
public interface ValidatorService {
diff --git a/src/main/java/com/metis/flow/validator/factory/EdgeValidatorFactory.java b/metis-starter/src/main/java/com/metis/validator/factory/EdgeValidatorFactory.java
similarity index 81%
rename from src/main/java/com/metis/flow/validator/factory/EdgeValidatorFactory.java
rename to metis-starter/src/main/java/com/metis/validator/factory/EdgeValidatorFactory.java
index d3e357f..03c2eab 100644
--- a/src/main/java/com/metis/flow/validator/factory/EdgeValidatorFactory.java
+++ b/metis-starter/src/main/java/com/metis/validator/factory/EdgeValidatorFactory.java
@@ -1,7 +1,7 @@
-package com.metis.flow.validator.factory;
+package com.metis.validator.factory;
-import com.metis.flow.enums.EdgeType;
-import com.metis.flow.validator.EdgeValidator;
+import com.metis.enums.EdgeType;
+import com.metis.validator.EdgeValidator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
diff --git a/metis-starter/src/main/java/com/metis/validator/factory/NodeValidatorFactory.java b/metis-starter/src/main/java/com/metis/validator/factory/NodeValidatorFactory.java
new file mode 100644
index 0000000..14a501b
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/factory/NodeValidatorFactory.java
@@ -0,0 +1,56 @@
+package com.metis.validator.factory;
+
+import cn.hutool.core.lang.Assert;
+import com.metis.enums.NodeType;
+import com.metis.validator.CustomNodeValidator;
+import com.metis.validator.NodeValidator;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public final class NodeValidatorFactory {
+
+ /**
+ * 内置节点验证器
+ */
+ private static final Map NODE_MAP = new ConcurrentHashMap<>(8);
+ /**
+ * 自定义节点验证器
+ */
+ private static final Map CUSTOM_NODE_MAP = new ConcurrentHashMap<>(8);
+
+
+ /**
+ * 注册
+ *
+ * @param validator 验证器
+ */
+ static void register(NodeValidator validator) {
+ NODE_MAP.put(validator.getType(), validator);
+ }
+
+ /**
+ * 得到
+ *
+ * @param type 类型
+ * @return {@link NodeValidator }
+ */
+ public static NodeValidator get(NodeType type) {
+ return NODE_MAP.get(type);
+ }
+
+ /**
+ * @param validator 验证器
+ */
+ static void registerCustom(CustomNodeValidator validator) {
+ Assert.isTrue(!CUSTOM_NODE_MAP.containsKey(validator.getCustomNodeType()), "已存在类型:{}, class:{}的验证器", validator.getCustomNodeType(), validator.getClass());
+ CUSTOM_NODE_MAP.put(validator.getCustomNodeType(), validator);
+ }
+
+ public static NodeValidator getCustom(String type) {
+ return CUSTOM_NODE_MAP.get(type);
+ }
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/validator/factory/ValidatorInitialize.java b/metis-starter/src/main/java/com/metis/validator/factory/ValidatorInitialize.java
new file mode 100644
index 0000000..40b92fe
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/factory/ValidatorInitialize.java
@@ -0,0 +1,43 @@
+package com.metis.validator.factory;
+
+
+import cn.hutool.core.lang.Assert;
+import com.metis.enums.NodeType;
+import com.metis.validator.CustomNodeValidator;
+import com.metis.validator.EdgeValidator;
+import com.metis.validator.NodeValidator;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+@Service
+public class ValidatorInitialize implements ApplicationContextAware {
+
+
+ @Override
+ public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
+
+ Map edgeMap = applicationContext.getBeansOfType(EdgeValidator.class);
+
+ edgeMap.forEach((edgeValidatorBeanName, edgeValidator) -> {
+ EdgeValidatorFactory.register(edgeValidator);
+ });
+
+ Map nodeMap = applicationContext.getBeansOfType(NodeValidator.class);
+
+ nodeMap.forEach((nodeValidatorBeanName, nodeValidator) -> {
+ if (NodeType.CUSTOM.equals(nodeValidator.getType())) {
+ Assert.isTrue(nodeValidator instanceof CustomNodeValidator, "自定义节点必须实现CustomNodeValidator接口");
+ NodeValidatorFactory.registerCustom((CustomNodeValidator) nodeValidator);
+ } else {
+ NodeValidatorFactory.register(nodeValidator);
+ }
+ });
+ }
+
+
+}
diff --git a/metis-starter/src/main/java/com/metis/validator/impl/ValidatorServiceImpl.java b/metis-starter/src/main/java/com/metis/validator/impl/ValidatorServiceImpl.java
new file mode 100644
index 0000000..bd42907
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/impl/ValidatorServiceImpl.java
@@ -0,0 +1,268 @@
+package com.metis.validator.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.metis.domain.bo.BuildApp;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Graph;
+import com.metis.domain.entity.base.Node;
+import com.metis.enums.EdgeType;
+import com.metis.enums.NodeType;
+import com.metis.validator.*;
+import com.metis.validator.factory.EdgeValidatorFactory;
+import com.metis.validator.factory.NodeValidatorFactory;
+import com.metis.utils.GenericInterfacesUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ValidatorServiceImpl implements ValidatorService {
+
+ private final ValidatorCodeService validatorCodeService;
+
+ @Override
+ public void validate(BuildApp buildApp) {
+ // validation 编程式校验
+ validatorCodeService.validateThrow(buildApp);
+ Graph graph = buildApp.getGraph();
+ // 节点参数校验
+ validateNode(graph.getNodes());
+ // 线参数校验
+ validateEdge(graph.getEdges());
+ // 关系验证
+ validateRelation(graph.getNodes(), graph.getEdges());
+ }
+
+
+ /**
+ * 验证节点
+ *
+ * @param nodes 节点
+ */
+ private void validateNode(List nodes) {
+ List errorMessage = new ArrayList<>();
+ for (Node node : nodes) {
+ NodeType type = node.getType();
+ NodeValidator validator = getNodeValidator(node);
+ node.setConfigClass(GenericInterfacesUtils.getClass(validator));
+ // 节点校验器
+ Assert.isTrue(ObjectUtil.isNotNull(validator), "无:{}类型的节点校验器", type.getName());
+ ValidatorResult result = validator.validateValue(node);
+ // 返回值检查
+ Assert.isTrue(ObjectUtil.isNotNull(result), "类型:{} 的校验器无返回值", validator.getType().getName());
+ if (!result.getValid()) {
+ errorMessage.add(result.getMessage());
+ }
+ }
+ Assert.isTrue(CollUtil.isEmpty(errorMessage), String.join(",", errorMessage));
+ }
+
+
+ /**
+ * 验证边缘
+ *
+ * @param edges 线
+ */
+ private void validateEdge(List edges) {
+ List errorMessage = new ArrayList<>();
+ for (Edge edge : edges) {
+ EdgeType type = edge.getType();
+ EdgeValidator validator = EdgeValidatorFactory.get(type);
+ // 节点校验器
+ Assert.isTrue(ObjectUtil.isNotNull(validator), "无:{}类型的边校验器", type.getName());
+ ValidatorResult result = validator.validate(edge);
+ // 返回值检查
+ Assert.isTrue(ObjectUtil.isNotNull(result), "类型:{} 的校验器无返回值", validator.getType().getName());
+ if (!result.getValid()) {
+ errorMessage.add(result.getMessage());
+ }
+ }
+ Assert.isTrue(CollUtil.isEmpty(errorMessage), String.join(",", errorMessage));
+ }
+
+
+ /**
+ * 验证关系
+ *
+ * @param nodes 节点
+ * @param edges 边缘
+ */
+ private void validateRelation(List nodes, List edges) {
+ // 0. 检查是否只有一个起始节点
+ validateSingleStartNode(nodes);
+
+ // 1. 检查线是否连接有效节点
+ validateEdgeConnections(nodes, edges);
+
+ // 2. 检查是否存在环结构
+ validateCycle(nodes, edges);
+
+ // 3. 检查是否存在孤立节点
+ validateIsolatedNodes(nodes, edges);
+
+ // 4. 检查两个节点间是否有多根连线
+ validateMultipleEdgesBetweenNodes(edges);
+
+ // 5. 检查节点自己的关系信息
+ validateNodeRelations(nodes, edges);
+
+ }
+
+ /**
+ * 检查是否只有一个起始节点
+ *
+ * @param nodes 节点
+ */
+ private void validateSingleStartNode(List nodes) {
+ Long startNodeCount = nodes.stream()
+ .filter(node -> NodeType.START.equals(node.getType()))
+ .count();
+ Assert.isTrue(startNodeCount.equals(1L), "图中必须且只能有一个起始节点,当前起始节点数量: {}", startNodeCount);
+ }
+
+
+ /**
+ * 检查线是否连接有效节点
+ *
+ * @param nodes 节点
+ * @param edges 边缘
+ */
+ private void validateEdgeConnections(List nodes, List edges) {
+ Map nodeMap = nodes.stream().collect(Collectors.toMap(Node::getId, Function.identity()));
+ for (Edge edge : edges) {
+ Long source = edge.getSource();
+ Long target = edge.getTarget();
+ Assert.isTrue(nodeMap.containsKey(source), "边 {} 的源节点 {} 不存在", edge.getLabel(), source);
+ Assert.isTrue(nodeMap.containsKey(target), "边 {} 的目标节点 {} 不存在", edge.getLabel(), target);
+ }
+ }
+
+ /**
+ * 检查是否存在环结构
+ *
+ * @param nodes 节点
+ * @param edges 边缘
+ */
+ private void validateCycle(List nodes, List edges) {
+ Map> adjacencyList = buildAdjacencyList(edges);
+ for (Node node : nodes) {
+ if (hasCycle(node.getId(), adjacencyList, new HashSet<>())) {
+ throw new IllegalArgumentException("图中存在环结构,起始节点: " + node.getData().getLabel());
+ }
+ }
+ }
+
+ /**
+ * 检查是否存在孤立节点
+ *
+ * @param nodes 节点
+ * @param edges 边缘
+ */
+ private void validateIsolatedNodes(List nodes, List edges) {
+ Set connectedNodes = new HashSet<>();
+ for (Edge edge : edges) {
+ connectedNodes.add(edge.getSource());
+ connectedNodes.add(edge.getTarget());
+ }
+ for (Node node : nodes) {
+ Assert.isTrue(connectedNodes.contains(node.getId()), "节点 {} 是孤立节点,未与任何边连接", node.getId());
+ }
+ }
+
+ /**
+ * 构建邻接表
+ *
+ * @param edges 边缘
+ * @return {@link Map }<{@link Long }, {@link List }<{@link Long }>>
+ */
+ private Map> buildAdjacencyList(List edges) {
+ Map> adjacencyList = new HashMap<>();
+ for (Edge edge : edges) {
+ adjacencyList.computeIfAbsent(edge.getSource(), k -> new ArrayList<>()).add(edge.getTarget());
+ }
+ return adjacencyList;
+ }
+
+ /**
+ * 深度优先搜索(DFS)检查环
+ *
+ * @param nodeId 节点id
+ * @param adjacencyList 邻接表
+ * @param visited 是否已经访问过了
+ * @return boolean
+ */
+ private boolean hasCycle(Long nodeId, Map> adjacencyList, Set visited) {
+ if (visited.contains(nodeId)) {
+ return true; // 发现环
+ }
+ visited.add(nodeId);
+ for (Long neighbor : adjacencyList.getOrDefault(nodeId, new ArrayList<>())) {
+ if (hasCycle(neighbor, adjacencyList, visited)) {
+ return true;
+ }
+ }
+ visited.remove(nodeId);
+ return false;
+ }
+
+ /**
+ * 检查两个节点间是否有多根连线
+ *
+ * @param edges 边缘
+ */
+ private void validateMultipleEdgesBetweenNodes(List edges) {
+ Map edgeCountMap = new HashMap<>();
+ for (Edge edge : edges) {
+ String key = edge.getSource() + "-" + edge.getTarget(); // 使用 source 和 target 作为唯一标识
+ edgeCountMap.put(key, edgeCountMap.getOrDefault(key, 0) + 1);
+ if (edgeCountMap.get(key) > 1) {
+ throw new IllegalArgumentException("节点 " + edge.getSource() + " 和节点 " + edge.getTarget() + " 之间存在多根连线");
+ }
+ }
+ }
+
+
+ /**
+ * 检查节点的关系信息
+ */
+ private void validateNodeRelations(List nodes, List edges) {
+ // 构建 source 和 target 的映射
+ Map> sourceMap = edges.stream().collect(Collectors.groupingBy(Edge::getSource));
+ Map> targetMap = edges.stream().collect(Collectors.groupingBy(Edge::getTarget));
+
+ for (Node node : nodes) {
+ NodeValidator validator = getNodeValidator(node);
+ // 获取当前节点的 source 和 target
+ List sources = targetMap.getOrDefault(node.getId(), new ArrayList<>());
+ List targets = sourceMap.getOrDefault(node.getId(), new ArrayList<>());
+ // 检查节点关系
+ ValidatorResult result = validator.validateRelation(node, sources, targets);
+ Assert.isTrue(result.getValid(), result.getMessage());
+ }
+ }
+
+
+ /**
+ * 获取节点验证器
+ *
+ * @param node 节点
+ * @return {@link NodeValidator }
+ */
+ private NodeValidator getNodeValidator(Node node) {
+ if (NodeType.CUSTOM.equals(node.getType())) {
+ Assert.isTrue(StrUtil.isNotBlank(node.getCustomType()), "自定义节点类型不能为空");
+ return NodeValidatorFactory.getCustom(node.getCustomType());
+ }
+ return NodeValidatorFactory.get(node.getType());
+ }
+
+}
diff --git a/src/main/java/com/metis/flow/validator/impl/edge/DefaultEdgeValidator.java b/metis-starter/src/main/java/com/metis/validator/impl/edge/DefaultEdgeValidator.java
similarity index 61%
rename from src/main/java/com/metis/flow/validator/impl/edge/DefaultEdgeValidator.java
rename to metis-starter/src/main/java/com/metis/validator/impl/edge/DefaultEdgeValidator.java
index 3012647..086d98e 100644
--- a/src/main/java/com/metis/flow/validator/impl/edge/DefaultEdgeValidator.java
+++ b/metis-starter/src/main/java/com/metis/validator/impl/edge/DefaultEdgeValidator.java
@@ -1,9 +1,9 @@
-package com.metis.flow.validator.impl.edge;
+package com.metis.validator.impl.edge;
-import com.metis.flow.domain.entity.base.Edge;
-import com.metis.flow.enums.EdgeType;
-import com.metis.flow.validator.EdgeValidator;
-import com.metis.flow.validator.ValidatorResult;
+import com.metis.domain.entity.base.Edge;
+import com.metis.enums.EdgeType;
+import com.metis.validator.EdgeValidator;
+import com.metis.validator.ValidatorResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
diff --git a/metis-starter/src/main/java/com/metis/validator/impl/node/DocumentExtractorNodeValidator.java b/metis-starter/src/main/java/com/metis/validator/impl/node/DocumentExtractorNodeValidator.java
new file mode 100644
index 0000000..7fd2ca8
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/impl/node/DocumentExtractorNodeValidator.java
@@ -0,0 +1,43 @@
+package com.metis.validator.impl.node;
+
+import cn.hutool.core.lang.Assert;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.config.node.DocumentExtractorNodeConfig;
+import com.metis.enums.NodeType;
+import com.metis.validator.NodeValidator;
+import com.metis.validator.ValidatorCodeService;
+import com.metis.validator.ValidatorResult;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DocumentExtractorNodeValidator implements NodeValidator {
+ private final ValidatorCodeService validatorCodeService;
+
+ @Override
+ public ValidatorResult validateValue(Node node) {
+ DocumentExtractorNodeConfig config = node.getConfig();
+ validatorCodeService.validateThrow(config);
+ // 业务检查未通过
+ return ValidatorResult.invalid("业务报错");
+ }
+
+ @Override
+ public ValidatorResult validateRelation(Node node, List sources, List targets) {
+ // 1. 检查 targets 数量是否等于 1(只允许一个出)
+ Assert.isTrue(targets.size() == 1, "节点 {} 的出连接数必须为 1,当前数量: {}", node.getId(), targets.size());
+
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public NodeType getType() {
+ return NodeType.DOCUMENT_EXTRACTOR;
+ }
+}
diff --git a/metis-starter/src/main/java/com/metis/validator/impl/node/EndNodeValidator.java b/metis-starter/src/main/java/com/metis/validator/impl/node/EndNodeValidator.java
new file mode 100644
index 0000000..9e62e56
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/impl/node/EndNodeValidator.java
@@ -0,0 +1,44 @@
+package com.metis.validator.impl.node;
+
+import cn.hutool.core.lang.Assert;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.config.node.EndNodeConfig;
+import com.metis.enums.NodeType;
+import com.metis.validator.NodeValidator;
+import com.metis.validator.ValidatorResult;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class EndNodeValidator implements NodeValidator {
+
+
+
+ @Override
+ public ValidatorResult validateValue(Node node) {
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public ValidatorResult validateRelation(Node node, List sources, List targets) {
+ // 1. 结束节点不允许有目标连接
+ Assert.isTrue(targets.isEmpty(), "结束节点 {} 不允许有目标连接", node.getId());
+
+ // 2. 检查 sources 数量是否小于 handles 数量
+// int handleCount = node.getData().getHandles().size();
+// Assert.isTrue(sources.size() <= handleCount, "结束节点 {} 的源连接数超过 handles 数量", node.getId());
+
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public NodeType getType() {
+ return NodeType.END;
+ }
+}
diff --git a/metis-starter/src/main/java/com/metis/validator/impl/node/LLMNodeValidator.java b/metis-starter/src/main/java/com/metis/validator/impl/node/LLMNodeValidator.java
new file mode 100644
index 0000000..2dd04a6
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/impl/node/LLMNodeValidator.java
@@ -0,0 +1,33 @@
+package com.metis.validator.impl.node;
+
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.config.node.LLMNodeConfig;
+import com.metis.enums.NodeType;
+import com.metis.validator.NodeValidator;
+import com.metis.validator.ValidatorResult;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LLMNodeValidator implements NodeValidator {
+ @Override
+ public ValidatorResult validateValue(Node node) {
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public ValidatorResult validateRelation(Node node, List sources, List targets) {
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public NodeType getType() {
+ return NodeType.LLM;
+ }
+}
diff --git a/metis-starter/src/main/java/com/metis/validator/impl/node/QuestionClassifierValidator.java b/metis-starter/src/main/java/com/metis/validator/impl/node/QuestionClassifierValidator.java
new file mode 100644
index 0000000..23c3e19
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/impl/node/QuestionClassifierValidator.java
@@ -0,0 +1,31 @@
+package com.metis.validator.impl.node;
+
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.config.node.QuestionClassifierConfig;
+import com.metis.enums.NodeType;
+import com.metis.validator.NodeValidator;
+import com.metis.validator.ValidatorResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+public class QuestionClassifierValidator implements NodeValidator {
+ @Override
+ public ValidatorResult validateValue(Node node) {
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public ValidatorResult validateRelation(Node node, List sources, List targets) {
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public NodeType getType() {
+ return NodeType.QUESTION_CLASSIFIER;
+ }
+}
diff --git a/metis-starter/src/main/java/com/metis/validator/impl/node/StartNodeValidator.java b/metis-starter/src/main/java/com/metis/validator/impl/node/StartNodeValidator.java
new file mode 100644
index 0000000..797873d
--- /dev/null
+++ b/metis-starter/src/main/java/com/metis/validator/impl/node/StartNodeValidator.java
@@ -0,0 +1,98 @@
+package com.metis.validator.impl.node;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjectUtil;
+import com.metis.domain.entity.base.Edge;
+import com.metis.domain.entity.base.Node;
+import com.metis.domain.entity.base.NodeVariable;
+import com.metis.domain.entity.config.node.StartNodeConfig;
+import com.metis.enums.NodeType;
+import com.metis.validator.NodeValidator;
+import com.metis.validator.ValidatorCodeService;
+import com.metis.validator.ValidatorResult;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StartNodeValidator implements NodeValidator {
+
+ private final ValidatorCodeService validatorCodeService;
+
+
+ @Override
+ public ValidatorResult validateValue(Node node) {
+ StartNodeConfig config = node.getConfig();
+ validatorCodeService.validateThrow(config);
+
+ List variables = config.getVariables();
+ if (CollUtil.isEmpty(variables)) {
+ return ValidatorResult.valid();
+ }
+
+ for (NodeVariable variable : variables) {
+ switch (variable.getVariableType()) {
+ // 文本类型校验
+ case TEXT_INPUT, PARAGRAPH -> textVariableValidator(variable);
+ // 下拉框变量校验
+ case SELECT -> selectVariableValidator(variable);
+ // 文件变量校验
+ case FILE, FILE_LIST -> fileVariableValidator(variable);
+ }
+ }
+ return ValidatorResult.valid();
+ }
+
+ /**
+ * 文本变量验证器
+ *
+ * @param variable 变量
+ */
+ private void textVariableValidator(NodeVariable variable) {
+ Assert.isTrue(ObjectUtil.isNotNull(variable.getMaxLength()), "文本变量 {} {} 的最大长度不能为空", variable.getLabel(), variable.getLabel(), variable.getVariable());
+ }
+
+ /**
+ * 选择变量验证器
+ *
+ * @param variable 变量
+ */
+ private void selectVariableValidator(NodeVariable variable) {
+ Assert.isTrue(CollUtil.isNotEmpty(variable.getOptions()), "选择变量 {} {} 的选项不能为空", variable.getLabel(), variable.getLabel(), variable.getVariable());
+ }
+
+ /**
+ * 文件变量验证器
+ *
+ * @param variable 变量
+ */
+ private void fileVariableValidator(NodeVariable variable) {
+ Assert.isTrue(ObjectUtil.isNotNull(variable.getMaxLength()), "文本变量 {} {} 的最大长度不能为空", variable.getLabel(), variable.getVariable());
+ Assert.isTrue(CollUtil.isNotEmpty(variable.getAllowedFileUploadMethods()), "文本变量 {} {} 的允许上传方式不能为空", variable.getLabel(), variable.getVariable());
+ Assert.isTrue(CollUtil.isNotEmpty(variable.getAllowedFileTypes()) || CollUtil.isNotEmpty(variable.getAllowedFileExtensions()), "文本变量 {} 的允许上传文件类型不能为空", variable.getLabel(), variable.getVariable());
+ }
+
+
+ @Override
+ public ValidatorResult validateRelation(Node node, List sources, List targets) {
+ // 1. 开始节点不允许有源连接
+ Assert.isTrue(sources.isEmpty(), "开始节点 {} 不允许有源连接", node.getId());
+
+ // 2. 检查 targets 数量是否小于 handles 数量
+// int handleCount = node.getData().getHandles().size();
+// Assert.isTrue(targets.size() <= handleCount, "开始节点 {} 的目标连接数超过 handles 数量", node.getId());
+
+ return ValidatorResult.valid();
+ }
+
+ @Override
+ public NodeType getType() {
+ return NodeType.START;
+ }
+
+}
diff --git a/metis-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/metis-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..0e5852e
--- /dev/null
+++ b/metis-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.metis.config.MetisStarterAutoConfiguration
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index dbb661b..299c6b8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,6 +7,11 @@
com.metis
metis
1.0.0-SNAPSHOT
+ pom
+
+ metis-starter
+ metis-applicant
+
org.springframework.boot
@@ -19,8 +24,9 @@
17
UTF-8
3.3.4
- 2.0.45
+ 2.0.56
1.18.34
+ 1.6.2
1.2.3
1.0.0-beta2
3.5.8
@@ -36,126 +42,60 @@
pom
import
+
+ dev.langchain4j
+ langchain4j-open-ai
+ ${langchain4j.version}
+
+
+ dev.langchain4j
+ langchain4j-mcp
+ 1.0.0-beta2
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ ${fastjson.version}
+
+
+ com.mikesamuel
+ json-sanitizer
+ ${sanitizer.version}
+
+
+ com.baomidou
+ mybatis-plus-spring-boot3-starter
+ ${mybatis-plus.version}
+
+
+ mysql
+ mysql-connector-java
+ 8.0.33
+
+
+ cn.hutool
+ hutool-all
+ 5.8.24
+
+
+
+ org.mapstruct
+ mapstruct
+ ${org.mapstruct.version}
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-api
+ 2.2.0
+
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
-
- dev.langchain4j
- langchain4j-open-ai
- ${langchain4j.version}
-
-
- dev.langchain4j
- langchain4j-mcp
- 1.0.0-beta2
-
-
- com.alibaba.fastjson2
- fastjson2
- ${fastjson.version}
-
-
- org.projectlombok
- lombok
-
-
- com.mikesamuel
- json-sanitizer
- ${sanitizer.version}
-
-
- com.baomidou
- mybatis-plus-spring-boot3-starter
- ${mybatis-plus.version}
-
-
- mysql
- mysql-connector-java
- 8.0.33
-
-
- cn.hutool
- hutool-all
- 5.8.22
-
-
- org.mapstruct
- mapstruct
- 1.6.2
-
-
- org.projectlombok
- lombok-mapstruct-binding
- 0.2.0
-
-
- org.springdoc
- springdoc-openapi-starter-webmvc-api
- 2.2.0
-
-
- com.github.xiaoymin
- knife4j-openapi3-jakarta-spring-boot-starter
- 4.4.0
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- 17
- 17
- UTF-8
-
- -parameters
-
-
-
-
-
- org.mapstruct
- mapstruct-processor
- 1.6.2
-
-
- org.projectlombok
- lombok
- 1.18.34
-
-
- org.projectlombok
- lombok-mapstruct-binding
- 0.2.0
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/java/com/metis/controller/TestController.java b/src/main/java/com/metis/controller/TestController.java
deleted file mode 100644
index 53dbd80..0000000
--- a/src/main/java/com/metis/controller/TestController.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.metis.controller;
-
-import com.metis.result.Result;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequestMapping("/test")
-public class TestController {
-
-
- @GetMapping
- public Result test() {
- return Result.ok("测试成功");
- }
-
-}
diff --git a/src/main/java/com/metis/domain/package-info.java b/src/main/java/com/metis/domain/package-info.java
deleted file mode 100644
index 8890ae6..0000000
--- a/src/main/java/com/metis/domain/package-info.java
+++ /dev/null
@@ -1 +0,0 @@
-package com.metis.domain;
\ No newline at end of file
diff --git a/src/main/java/com/metis/flow/config/FlowMybatisPlusConfiguration.java b/src/main/java/com/metis/flow/config/FlowMybatisPlusConfiguration.java
deleted file mode 100644
index 77d35d6..0000000
--- a/src/main/java/com/metis/flow/config/FlowMybatisPlusConfiguration.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.metis.flow.config;
-
-import org.mybatis.spring.annotation.MapperScan;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@MapperScan(basePackages = {"com.metis.flow.mapper"})
-public class FlowMybatisPlusConfiguration {
-}
diff --git a/src/main/java/com/metis/flow/domain/bo/Graph.java b/src/main/java/com/metis/flow/domain/bo/Graph.java
deleted file mode 100644
index 626974c..0000000
--- a/src/main/java/com/metis/flow/domain/bo/Graph.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.metis.flow.domain.bo;
-
-import com.metis.flow.domain.entity.base.Edge;
-import com.metis.flow.domain.entity.base.Node;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import lombok.Data;
-
-import java.util.List;
-
-@Data
-public class Graph {
-
- /**
- * 边缘
- */
- @Valid
- @NotNull(message = "连线不能为空")
- private List edges;
-
- /**
- * 节点
- */
- @Valid
- @NotNull(message = "节点不能为空")
- @Size(min = 1, message = "节点不能为空")
- private List nodes;
-}
diff --git a/src/main/java/com/metis/flow/domain/context/RunningContext.java b/src/main/java/com/metis/flow/domain/context/RunningContext.java
deleted file mode 100644
index 891b196..0000000
--- a/src/main/java/com/metis/flow/domain/context/RunningContext.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.metis.flow.domain.context;
-
-import com.alibaba.fastjson2.JSONObject;
-import lombok.Data;
-
-@Data
-public class RunningContext {
-
- /**
- * 系统数据
- */
- private SysContext sys;
-
- /**
- * 自定义数据
- */
- private JSONObject custom;
-
-}
diff --git a/src/main/java/com/metis/flow/domain/context/SysContext.java b/src/main/java/com/metis/flow/domain/context/SysContext.java
deleted file mode 100644
index 59be745..0000000
--- a/src/main/java/com/metis/flow/domain/context/SysContext.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.metis.flow.domain.context;
-
-import lombok.Data;
-
-import java.util.List;
-
-@Data
-public class SysContext {
-
- private List file;
-
- private Long appId;
-
- private Long userId;
-
- private Long workflowId;
-
- private Long instanceId;
-}
diff --git a/src/main/java/com/metis/flow/domain/entity/base/Node.java b/src/main/java/com/metis/flow/domain/entity/base/Node.java
deleted file mode 100644
index c5b0fd9..0000000
--- a/src/main/java/com/metis/flow/domain/entity/base/Node.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.metis.flow.domain.entity.base;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjectUtil;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.metis.flow.enums.NodeType;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-import java.util.Map;
-import java.util.stream.Collectors;
-
-@Data
-public class Node {
-
- /**
- * id
- */
- @NotBlank(message = "节点id不能为空")
- private String id;
-
- /**
- * 类型
- */
- @NotNull(message = "节点类型不能为空")
- private NodeType type;
-
- /**
- * 位置
- */
- @Valid
- @NotNull(message = "节点位置不能为空")
- private Position position;
-
- /**
- * 业务数据
- */
- @Valid
- @NotNull(message = "节点业务数据不能为空")
- private NodeData data;
-
-
- @JsonIgnore
- public Map getHandleMap() {
- if (CollUtil.isEmpty(data.getHandles())) {
- return Map.of();
- }
- return data.getHandles().stream().collect(Collectors.toMap(Handle::getId, handle -> handle));
- }
-
- @JsonIgnore
- public T getConfig() {
- if (ObjectUtil.isNull(data.getConfig())) {
- return null;
- }
- return (T) data.getConfig().to(type.getConfigClass());
- }
-
-}
diff --git a/src/main/java/com/metis/flow/engine/AppEngineRunnerService.java b/src/main/java/com/metis/flow/engine/AppEngineRunnerService.java
deleted file mode 100644
index 8bf6396..0000000
--- a/src/main/java/com/metis/flow/engine/AppEngineRunnerService.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.metis.flow.engine;
-
-public interface AppEngineRunnerService {
-
-
-}
diff --git a/src/main/java/com/metis/flow/runner/NodeRunner.java b/src/main/java/com/metis/flow/runner/NodeRunner.java
deleted file mode 100644
index c9a8420..0000000
--- a/src/main/java/com/metis/flow/runner/NodeRunner.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.metis.flow.runner;
-
-import com.metis.flow.domain.context.RunningContext;
-import com.metis.flow.domain.entity.base.Node;
-import com.metis.flow.enums.NodeType;
-
-public interface NodeRunner {
-
-
- /**
- * 运行
- *
- * @param context 上下文
- * @param node 节点配置信息
- * @return {@link RunningContext }
- */
- RunningContext run(RunningContext context, Node node);
-
-
- /**
- * 获取节点类型
- *
- * @return {@link NodeType }
- */
- NodeType getType();
-
-}
diff --git a/src/main/java/com/metis/flow/runner/RunnerInitialize.java b/src/main/java/com/metis/flow/runner/RunnerInitialize.java
deleted file mode 100644
index b2dafa0..0000000
--- a/src/main/java/com/metis/flow/runner/RunnerInitialize.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.metis.flow.runner;
-
-import com.metis.flow.runner.factory.RunnerFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.stereotype.Service;
-
-import java.util.Map;
-
-@Service
-public class RunnerInitialize implements ApplicationContextAware {
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- Map runnerMap = applicationContext.getBeansOfType(NodeRunner.class);
- runnerMap.forEach((k, v) -> {
- RunnerFactory.register(v);
- });
- }
-
-}
diff --git a/src/main/java/com/metis/flow/runner/factory/RunnerFactory.java b/src/main/java/com/metis/flow/runner/factory/RunnerFactory.java
deleted file mode 100644
index 3a71a82..0000000
--- a/src/main/java/com/metis/flow/runner/factory/RunnerFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.metis.flow.runner.factory;
-
-import com.metis.flow.enums.NodeType;
-import com.metis.flow.runner.NodeRunner;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class RunnerFactory {
-
- private static final Map MAP = new ConcurrentHashMap<>(8);
-
-
- /**
- * 注册
- *
- * @param runner 跑步者
- */
- public static void register(NodeRunner runner) {
- MAP.put(runner.getType(), runner);
- }
-
-
- /**
- * 得到
- *
- * @param type 类型
- * @return {@link NodeRunner }
- */
- public static NodeRunner get(NodeType type) {
- return MAP.get(type);
- }
-
-
-
-}
diff --git a/src/main/java/com/metis/flow/runner/impl/EndNodeRunner.java b/src/main/java/com/metis/flow/runner/impl/EndNodeRunner.java
deleted file mode 100644
index 35191e1..0000000
--- a/src/main/java/com/metis/flow/runner/impl/EndNodeRunner.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.metis.flow.runner.impl;
-
-
-import com.metis.flow.domain.context.RunningContext;
-import com.metis.flow.domain.entity.base.Node;
-import com.metis.flow.enums.NodeType;
-import com.metis.flow.runner.NodeRunner;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-@Slf4j
-@Service
-public class EndNodeRunner implements NodeRunner {
-
- @Override
- public RunningContext run(RunningContext context, Node node) {
- return context;
- }
-
- @Override
- public NodeType getType() {
- return NodeType.END;
- }
-
-}
diff --git a/src/main/java/com/metis/flow/runner/impl/StartNodeRunner.java b/src/main/java/com/metis/flow/runner/impl/StartNodeRunner.java
deleted file mode 100644
index f43a570..0000000
--- a/src/main/java/com/metis/flow/runner/impl/StartNodeRunner.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.metis.flow.runner.impl;
-
-import com.metis.flow.domain.context.RunningContext;
-import com.metis.flow.domain.entity.base.Node;
-import com.metis.flow.enums.NodeType;
-import com.metis.flow.runner.NodeRunner;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-@Slf4j
-@Service
-public class StartNodeRunner implements NodeRunner {
-
- @Override
- public RunningContext run(RunningContext context, Node node) {
- return context;
- }
-
- @Override
- public NodeType getType() {
- return NodeType.START;
- }
-}
diff --git a/src/main/java/com/metis/flow/validator/NodeValidator.java b/src/main/java/com/metis/flow/validator/NodeValidator.java
deleted file mode 100644
index ecaa51f..0000000
--- a/src/main/java/com/metis/flow/validator/NodeValidator.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.metis.flow.validator;
-
-import com.metis.flow.domain.entity.base.Node;
-import com.metis.flow.enums.NodeType;
-
-public interface NodeValidator {
-
-
- /**
- * 节点验证
- *
- * @param node 节点
- * @return {@link ValidatorResult }
- */
- ValidatorResult validate(Node node);
-
-
- /**
- * 得到类型
- *
- * @return {@link NodeType }
- */
- NodeType getType();
-
-}
diff --git a/src/main/java/com/metis/flow/validator/ValidatorInitialize.java b/src/main/java/com/metis/flow/validator/ValidatorInitialize.java
deleted file mode 100644
index 7201eb7..0000000
--- a/src/main/java/com/metis/flow/validator/ValidatorInitialize.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.metis.flow.validator;
-
-
-import com.metis.flow.validator.factory.EdgeValidatorFactory;
-import com.metis.flow.validator.factory.NodeValidatorFactory;
-import org.jetbrains.annotations.NotNull;
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.stereotype.Service;
-
-import java.util.Map;
-
-@Service
-public class ValidatorInitialize implements ApplicationContextAware {
-
-
- @Override
- public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
- Map edgeMap = applicationContext.getBeansOfType(EdgeValidator.class);
-
- edgeMap.forEach((k, v) -> {
- EdgeValidatorFactory.register(v);
- });
-
-
- Map nodeMap = applicationContext.getBeansOfType(NodeValidator.class);
- nodeMap.forEach((k, v) -> {
- NodeValidatorFactory.register(v);
- });
- }
-
-
-}
diff --git a/src/main/java/com/metis/flow/validator/factory/NodeValidatorFactory.java b/src/main/java/com/metis/flow/validator/factory/NodeValidatorFactory.java
deleted file mode 100644
index ac00792..0000000
--- a/src/main/java/com/metis/flow/validator/factory/NodeValidatorFactory.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.metis.flow.validator.factory;
-
-import com.metis.flow.enums.NodeType;
-import com.metis.flow.validator.NodeValidator;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-
-public class NodeValidatorFactory {
-
- private static final Map MAP = new ConcurrentHashMap<>(8);
-
- /**
- * 注册
- *
- * @param validator 验证器
- */
- public static void register(NodeValidator validator) {
- MAP.put(validator.getType(), validator);
- }
-
- /**
- * 得到
- *
- * @param type 类型
- * @return {@link NodeValidator }
- */
- public static NodeValidator get(NodeType type) {
- return MAP.get(type);
- }
-
-
-}
diff --git a/src/main/java/com/metis/flow/validator/impl/ValidatorServiceImpl.java b/src/main/java/com/metis/flow/validator/impl/ValidatorServiceImpl.java
deleted file mode 100644
index f11af15..0000000
--- a/src/main/java/com/metis/flow/validator/impl/ValidatorServiceImpl.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package com.metis.flow.validator.impl;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.ObjectUtil;
-import com.metis.flow.domain.bo.BuildApp;
-import com.metis.flow.domain.bo.Graph;
-import com.metis.flow.domain.entity.base.Edge;
-import com.metis.flow.domain.entity.base.Node;
-import com.metis.flow.enums.EdgeType;
-import com.metis.flow.enums.NodeType;
-import com.metis.flow.validator.EdgeValidator;
-import com.metis.flow.validator.NodeValidator;
-import com.metis.flow.validator.ValidatorResult;
-import com.metis.flow.validator.ValidatorService;
-import com.metis.flow.validator.factory.EdgeValidatorFactory;
-import com.metis.flow.validator.factory.NodeValidatorFactory;
-import jakarta.validation.ConstraintViolation;
-import jakarta.validation.Validator;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-@Slf4j
-@Service
-@RequiredArgsConstructor
-public class ValidatorServiceImpl implements ValidatorService {
-
- private final Validator globalValidator;
-
- @Override
- public void validate(BuildApp graph) {
- // validation 编程式校验
- Set> validates = globalValidator.validate(graph);
- if (CollUtil.isNotEmpty(validates)) {
- List errorMessage = validates.stream()
- .map(ConstraintViolation::getMessage).toList();
- throw new RuntimeException(String.join(",", errorMessage));
- }
-
- Graph model = graph.getGraph();
- // 节点参数校验
- validateNode(model.getNodes());
- // 线参数校验
- validateEdge(model.getEdges());
- // 关系验证
- validateRelation(model.getNodes(), model.getEdges());
- }
-
-
- /**
- * 验证节点
- *
- * @param nodes 节点
- */
- private void validateNode(List nodes) {
- List errorMessage = new ArrayList<>();
- for (Node node : nodes) {
- NodeType type = node.getType();
- NodeValidator validator = NodeValidatorFactory.get(type);
- // 节点校验器
- Assert.isTrue(ObjectUtil.isNotNull(validator), "无:{}类型的节点校验器", type.getName());
- ValidatorResult result = validator.validate(node);
- // 返回值检查
- Assert.isTrue(ObjectUtil.isNotNull(result), "类型:{} 的校验器无返回值", validator.getType().getName());
- if (!result.getValid()) {
- errorMessage.add(result.getMessage());
- }
- }
- Assert.isTrue(CollUtil.isEmpty(errorMessage), String.join(",", errorMessage));
- }
-
-
- /**
- * 验证边缘
- *
- * @param edges
- */
- private void validateEdge(List edges) {
- List errorMessage = new ArrayList<>();
- for (Edge edge : edges) {
- EdgeType type = edge.getType();
- EdgeValidator validator = EdgeValidatorFactory.get(type);
- // 节点校验器
- Assert.isTrue(ObjectUtil.isNotNull(validator), "无:{}类型的边校验器", type.getName());
- ValidatorResult result = validator.validate(edge);
- // 返回值检查
- Assert.isTrue(ObjectUtil.isNotNull(result), "类型:{} 的校验器无返回值", validator.getType().getName());
- if (!result.getValid()) {
- errorMessage.add(result.getMessage());
- }
- }
- Assert.isTrue(CollUtil.isEmpty(errorMessage), String.join(",", errorMessage));
- }
-
-
- /**
- * 验证关系
- *
- * @param nodes 节点
- * @param edges 边缘
- */
- private void validateRelation(List nodes, List edges) {
-
- }
-
-}
diff --git a/src/main/java/com/metis/flow/validator/impl/node/EndNodeValidator.java b/src/main/java/com/metis/flow/validator/impl/node/EndNodeValidator.java
deleted file mode 100644
index caebef9..0000000
--- a/src/main/java/com/metis/flow/validator/impl/node/EndNodeValidator.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.metis.flow.validator.impl.node;
-
-import com.metis.flow.domain.entity.base.Node;
-import com.metis.flow.enums.NodeType;
-import com.metis.flow.validator.NodeValidator;
-import com.metis.flow.validator.ValidatorResult;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-@Slf4j
-@Service
-public class EndNodeValidator implements NodeValidator {
-
- @Override
- public ValidatorResult validate(Node node) {
- return ValidatorResult.valid();
- }
-
- @Override
- public NodeType getType() {
- return NodeType.END;
- }
-}
diff --git a/src/main/java/com/metis/flow/validator/impl/node/StartNodeValidator.java b/src/main/java/com/metis/flow/validator/impl/node/StartNodeValidator.java
deleted file mode 100644
index f32d657..0000000
--- a/src/main/java/com/metis/flow/validator/impl/node/StartNodeValidator.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.metis.flow.validator.impl.node;
-
-import com.metis.flow.domain.entity.base.Node;
-import com.metis.flow.enums.NodeType;
-import com.metis.flow.validator.NodeValidator;
-import com.metis.flow.validator.ValidatorResult;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-@Slf4j
-@Service
-public class StartNodeValidator implements NodeValidator {
-
- @Override
- public ValidatorResult validate(Node node) {
- return ValidatorResult.valid();
- }
-
- @Override
- public NodeType getType() {
- return NodeType.START;
- }
-
-}
diff --git a/src/test/java/AnsMsgHandler.java b/src/test/java/AnsMsgHandler.java
deleted file mode 100644
index 8d79f62..0000000
--- a/src/test/java/AnsMsgHandler.java
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import java.io.InputStream;
-
-public interface AnsMsgHandler {
-
- void actMsg(InputStream is, String line);
-
-}
diff --git a/src/test/java/SSeTest.java b/src/test/java/SSeTest.java
deleted file mode 100644
index 63f3d19..0000000
--- a/src/test/java/SSeTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.metis.sseclient.check.SseCheck;
-import dev.langchain4j.agent.tool.ToolSpecification;
-import dev.langchain4j.mcp.client.DefaultMcpClient;
-import dev.langchain4j.mcp.client.transport.McpTransport;
-import dev.langchain4j.mcp.client.transport.http.HttpMcpTransport;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.List;
-
-
-@Slf4j
-public class SSeTest {
- public static void main(String[] args) throws JsonProcessingException {
- McpTransport transport = new HttpMcpTransport.Builder()
- .sseUrl("http://localhost:8081/sse")
- .build();
- new DefaultMcpClient.Builder()
- .transport(transport)
- .build()
- .listTools();
-
- SseCheck sseCheck = new SseCheck("http://localhost:8081/sse");
- List listTools = sseCheck.listTools();
- System.out.println(listTools);
- }
-}
diff --git a/src/test/java/SseClient.java b/src/test/java/SseClient.java
deleted file mode 100644
index b3615f6..0000000
--- a/src/test/java/SseClient.java
+++ /dev/null
@@ -1,77 +0,0 @@
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URL;
-
-/**
- *
- * @author hyd
- *
- */
-public class SseClient {
-
- /**
- * 获取SSE输入流。
- *
- * @param urlPath
- * @return
- * @throws IOException
- */
- public static InputStream getSseInputStream(String urlPath) throws IOException {
- URL url = new URL(urlPath);
- HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
- // 这儿根据自己的情况选择get或post
- urlConnection.setRequestMethod("GET");
- urlConnection.setDoOutput(true);
- urlConnection.setDoInput(true);
- urlConnection.setUseCaches(false);
- urlConnection.setRequestProperty("Connection", "Keep-Alive");
- urlConnection.setRequestProperty("Charset", "UTF-8");
- //读取过期时间(很重要,建议加上)
- urlConnection.setReadTimeout(60 * 1000);
- // text/plain模式
- urlConnection.setRequestProperty("Content-Type", "text/plain; charset=UTF-8");
- InputStream inputStream = urlConnection.getInputStream();
- InputStream is = new BufferedInputStream(inputStream);
- return is;
- }
-
- /**
- * 读取数据。
- *
- * @param is
- * @param ansMsgHandler
- * @throws IOException
- */
- public static void readStream(InputStream is, AnsMsgHandler ansMsgHandler) throws IOException {
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(is));
- String line = "";
- while ((line = reader.readLine()) != null) {
- // 处理数据接口
- ansMsgHandler.actMsg(is, line);
- }
- // 当服务器端主动关闭的时候,客户端无法获取到信号。现在还不清楚原因。所以无法执行的此处。
- reader.close();
- } catch (IOException e) {
- e.printStackTrace();
- throw new IOException("关闭数据流!");
- }
- }
-
- public static void main(String[] args) throws IOException {
- String urlPath = "http://localhost:8081/sse";
- InputStream inputStream = getSseInputStream(urlPath);
- readStream(inputStream, new AnsMsgHandler() {
-
- public void actMsg(InputStream is, String line) {
- System.out.println(line);
- }
- });
- }
-
-}