From e2f706a71827e752c84847ed1e3976141a40a0da Mon Sep 17 00:00:00 2001 From: NickBai <876337011@qq.com> Date: Wed, 30 Jul 2025 14:51:41 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E6=94=AF=E6=8C=81deepseek=E7=9A=84?= =?UTF-8?q?=E6=80=9D=E8=80=83=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/pom.xml | 15 +- server/sparkx-common/pom.xml | 4 +- server/sparkx-service/pom.xml | 9 +- .../service/helper/SseEmitterHelper.java | 137 ++++++++++++------ .../helper/StreamChatModelBuildHelper.java | 1 + server/sparkx-web/pom.xml | 4 +- .../src/main/resources/application.yml | 2 +- server/sparkx-web/src/main/resources/license | 2 +- 8 files changed, 115 insertions(+), 59 deletions(-) diff --git a/server/pom.xml b/server/pom.xml index 54f1b47..085d184 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -5,7 +5,7 @@ cn.sparkshop.ai cn.sparkshop - 1.1.1 + 1.1.2 pom SparkX 基于大语言模型和 RAG 的知识库问答系统。开箱即用、模型中立、灵活编排,支持快速嵌入到第三方业务系统。 @@ -22,7 +22,7 @@ true 17 5.8.26 - 1.1.1 + 1.1.2 1.18.24 3.4.2 3.5.10.1 @@ -31,13 +31,13 @@ 3.5.10.1 9.0.0.CR1 2.18.2 - 1.1.0 + 1.2.0 1.1.0-beta7 0.64.8 portable-1.8.6 3.1.0 2.9.0 - 1.1.0-rc1 + 1.2.0 1.21.3 @@ -126,6 +126,13 @@ ${langchain4j.version} + + dev.langchain4j + langchain4j-core + ${langchain4j.version} + compile + + dev.langchain4j langchain4j-document-parser-apache-pdfbox diff --git a/server/sparkx-common/pom.xml b/server/sparkx-common/pom.xml index a9e425d..134dde6 100644 --- a/server/sparkx-common/pom.xml +++ b/server/sparkx-common/pom.xml @@ -5,11 +5,11 @@ cn.sparkshop.ai cn.sparkshop - 1.1.1 + 1.1.2 sparkx-common - 1.1.1 + 1.1.2 jar diff --git a/server/sparkx-service/pom.xml b/server/sparkx-service/pom.xml index 356454f..8d83081 100644 --- a/server/sparkx-service/pom.xml +++ b/server/sparkx-service/pom.xml @@ -5,11 +5,11 @@ cn.sparkshop.ai cn.sparkshop - 1.1.1 + 1.1.2 sparkx-service - 1.1.1 + 1.1.2 jar @@ -126,6 +126,11 @@ com.squareup.retrofit2 converter-jackson + + + dev.langchain4j + langchain4j-core + diff --git a/server/sparkx-service/src/main/java/sparkx/service/helper/SseEmitterHelper.java b/server/sparkx-service/src/main/java/sparkx/service/helper/SseEmitterHelper.java index bf74be2..fe125ab 100644 --- a/server/sparkx-service/src/main/java/sparkx/service/helper/SseEmitterHelper.java +++ b/server/sparkx-service/src/main/java/sparkx/service/helper/SseEmitterHelper.java @@ -12,6 +12,7 @@ package sparkx.service.helper; import cn.hutool.core.date.TimeInterval; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; +import dev.langchain4j.model.chat.response.PartialThinking; import dev.langchain4j.service.TokenStream; import dev.langchain4j.service.tool.ToolExecution; import lombok.extern.slf4j.Slf4j; @@ -25,6 +26,7 @@ import sparkx.service.extend.workflow.SendEndCallback; import java.io.IOException; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; @Slf4j @Component @@ -45,11 +47,14 @@ public class SseEmitterHelper { // 消息开始 sendStartSse(emitter); + AtomicBoolean hasReasoningContent = new AtomicBoolean(false); + AtomicBoolean hasSendStart = new AtomicBoolean(false); + AtomicBoolean hasSendEnd = new AtomicBoolean(false); final TimeInterval timer = new TimeInterval(); tokenStream + // 整理并转换召回的片段数据,返回前端 .onRetrieved((retrievedList) -> { - // 整理并转换召回的片段数据,返回前端 List> retiredMapList = new ArrayList<>(); retrievedList.forEach(item -> { Map map = new HashMap<>(); @@ -64,35 +69,35 @@ public class SseEmitterHelper { // 召回知识库片段 sendMetaSse(emitter, retiredMapList); }) - .onToolExecuted((ToolExecution toolExecution) -> sendToolSse(emitter, toolExecution.request().name())) - .onPartialResponse((content) -> { - // 加空格配合前端的fetchEventSource进行解析, - // 见https://github.com/Azure/fetch-event-source/blob/45ac3cfffd30b05b79fbf95c21e67d4ef59aa56a/src/parse.ts#L129-L133 + // 思考过程 + .onPartialThinking((PartialThinking reasoningContent) -> { try { + hasReasoningContent.set(true); - String[] lines = content.split("[\\n]", -1); - if (lines.length > 1) { - - emitter.send(Tool.buildSendData(runtimeId, nodeId, lines[0])); - - for (int i = 1; i < lines.length; i++) { - /** - * 当响应结果的content中包含有多行文本时, - * 前端的fetch-event-source框架的BUG会将包含有换行符的那一行内容替换为空字符串, - * 故需要先将换行符与后面的内容拆分并转成,前端碰到换行标志时转成换行符处理 - */ - emitter.send(Tool.buildSendData(runtimeId, nodeId, "-_-_wrap_-_-")); - emitter.send(Tool.buildSendData(runtimeId, nodeId, lines[i])); - } - } else { + if (!hasSendStart.get()) { + emitter.send(Tool.buildSendData(runtimeId, nodeId, "")); + hasSendStart.set(true); + } - emitter.send(Tool.buildSendData(runtimeId, nodeId, content)); + sendSseData(reasoningContent.text(), emitter, runtimeId, nodeId); + } catch (Exception e) { + emitter.completeWithError(e); + } + }) + // 工具调用 + .onToolExecuted((ToolExecution toolExecution) -> { + sendToolSse(emitter, toolExecution.request().name()); + }) + .onPartialResponse((content) -> { + try { + if (hasReasoningContent.get() && !hasSendEnd.get()) { + emitter.send(Tool.buildSendData(runtimeId, nodeId, "")); + hasSendEnd.set(true); } - } catch (IOException e) { - //log.error("拆解AI返回信息失败:", e); - sendErrorSse(emitter, e.getMessage()); - //emitter.complete(); + sendSseData(content, emitter, runtimeId, nodeId); + } catch (Exception e) { + emitter.completeWithError(e); } }) .onCompleteResponse((response) -> { @@ -131,37 +136,37 @@ public class SseEmitterHelper { public void asyncSend2Client(TokenStream tokenStream, SseEmitter emitter, long runtimeId, String nodeId, boolean needSend, SendEndCallback sendEndCallback) { + AtomicBoolean hasReasoningContent = new AtomicBoolean(false); tokenStream .onPartialResponse((content) -> { - // 加空格配合前端的fetchEventSource进行解析, - // 见https://github.com/Azure/fetch-event-source/blob/45ac3cfffd30b05b79fbf95c21e67d4ef59aa56a/src/parse.ts#L129-L133 if (needSend) { try { - - String[] lines = content.split("[\\n]", -1); - if (lines.length > 1) { - - emitter.send(Tool.buildSendData(runtimeId, nodeId, lines[0])); - for (int i = 1; i < lines.length; i++) { - /** - * 当响应结果的content中包含有多行文本时, - * 前端的fetch-event-source框架的BUG会将包含有换行符的那一行内容替换为空字符串, - * 故需要先将换行符与后面的内容拆分并转成,前端碰到换行标志时转成换行符处理 - */ - emitter.send(Tool.buildSendData(runtimeId, nodeId, "-_-_wrap_-_-")); - emitter.send(Tool.buildSendData(runtimeId, nodeId, lines[i])); - } - } else { - emitter.send(Tool.buildSendData(runtimeId, nodeId, content)); + if (hasReasoningContent.get()) { + emitter.send(Tool.buildSendData(runtimeId, nodeId, "")); } - } catch (IOException e) { - //log.error("拆解AI返回信息失败:", e); - sendErrorSse(emitter, e.getMessage()); + sendSseData(content, emitter, runtimeId, nodeId); + } catch (Exception e) { + emitter.completeWithError(e); + } + } + }) + // 思考过程 + .onPartialThinking((PartialThinking reasoningContent) -> { + if (needSend) { + try { + hasReasoningContent.set(true); + emitter.send(Tool.buildSendData(runtimeId, nodeId, "")); + sendSseData(reasoningContent.text(), emitter, runtimeId, nodeId); + } catch (Exception e) { + emitter.completeWithError(e); } } }) - .onToolExecuted((ToolExecution toolExecution) -> sendToolSse(emitter, toolExecution.request().name())) + // 工具调用 + .onToolExecuted((ToolExecution toolExecution) -> { + sendToolSse(emitter, toolExecution.request().name()); + }) .onCompleteResponse((response) -> { // 输入的token int inputTokenCount = response.tokenUsage().totalTokenCount(); @@ -262,4 +267,42 @@ public class SseEmitterHelper { sseEmitter.completeWithError(e); } } + + /** + * 发送模型返回数据 + * @param content String + * @param emitter SseEmitter + * @param runtimeId Long + * @param nodeId String + */ + private void sendSseData(String content, SseEmitter emitter, Long runtimeId, String nodeId) { + // 加空格配合前端的fetchEventSource进行解析, + // 见https://github.com/Azure/fetch-event-source/blob/45ac3cfffd30b05b79fbf95c21e67d4ef59aa56a/src/parse.ts#L129-L133 + try { + + String[] lines = content.split("[\\n]", -1); + if (lines.length > 1) { + + emitter.send(Tool.buildSendData(runtimeId, nodeId, lines[0])); + + for (int i = 1; i < lines.length; i++) { + /** + * 当响应结果的content中包含有多行文本时, + * 前端的fetch-event-source框架的BUG会将包含有换行符的那一行内容替换为空字符串, + * 故需要先将换行符与后面的内容拆分并转成,前端碰到换行标志时转成换行符处理 + */ + emitter.send(Tool.buildSendData(runtimeId, nodeId, "-_-_wrap_-_-")); + emitter.send(Tool.buildSendData(runtimeId, nodeId, lines[i])); + } + } else { + + emitter.send(Tool.buildSendData(runtimeId, nodeId, content)); + } + + } catch (IOException e) { + //log.error("拆解AI返回信息失败:", e); + sendErrorSse(emitter, e.getMessage()); + //emitter.complete(); + } + } } \ No newline at end of file diff --git a/server/sparkx-service/src/main/java/sparkx/service/helper/StreamChatModelBuildHelper.java b/server/sparkx-service/src/main/java/sparkx/service/helper/StreamChatModelBuildHelper.java index 2e010a8..c57ffdb 100644 --- a/server/sparkx-service/src/main/java/sparkx/service/helper/StreamChatModelBuildHelper.java +++ b/server/sparkx-service/src/main/java/sparkx/service/helper/StreamChatModelBuildHelper.java @@ -106,6 +106,7 @@ public class StreamChatModelBuildHelper { return OpenAiStreamingChatModel.builder() .baseUrl(url) .apiKey(key) + .returnThinking(true) .modelName(applicationInfo.getModelName()) .listeners(List.of(applicationHelper.observability())) .build(); diff --git a/server/sparkx-web/pom.xml b/server/sparkx-web/pom.xml index 718a5ee..032350e 100644 --- a/server/sparkx-web/pom.xml +++ b/server/sparkx-web/pom.xml @@ -5,11 +5,11 @@ cn.sparkshop.ai cn.sparkshop - 1.1.1 + 1.1.2 sparkx-web - 1.1.1 + 1.1.2 jar diff --git a/server/sparkx-web/src/main/resources/application.yml b/server/sparkx-web/src/main/resources/application.yml index 56e4ce6..c170015 100644 --- a/server/sparkx-web/src/main/resources/application.yml +++ b/server/sparkx-web/src/main/resources/application.yml @@ -4,7 +4,7 @@ server: spring: datasource: driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://127.0.0.1:5432/sparkx + url: jdbc:postgresql://127.0.0.1:6432/sparkx username: sparkx password: 123123 jpa: diff --git a/server/sparkx-web/src/main/resources/license b/server/sparkx-web/src/main/resources/license index 201a4ff..e9514cc 100644 --- a/server/sparkx-web/src/main/resources/license +++ b/server/sparkx-web/src/main/resources/license @@ -2,5 +2,5 @@ APP_NUM:999999 DATASET_NUM:999999 USER_NUM:999999 COMPANY_NAME:\u793e\u533a\u7248 -VERSION:1.1.1 +VERSION:1.1.2 LICENSE_ID:2b96f5aa \ No newline at end of file -- Gitee From 3f308e3f6fb03c820fe760100ede3cac7178bc84 Mon Sep 17 00:00:00 2001 From: NickBai <876337011@qq.com> Date: Wed, 30 Jul 2025 14:54:49 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E6=94=AF=E6=8C=81deepseek=E7=9A=84?= =?UTF-8?q?=E6=80=9D=E8=80=83=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/helper/SseEmitterHelper.java | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/server/sparkx-service/src/main/java/sparkx/service/helper/SseEmitterHelper.java b/server/sparkx-service/src/main/java/sparkx/service/helper/SseEmitterHelper.java index fe125ab..2fdb9c0 100644 --- a/server/sparkx-service/src/main/java/sparkx/service/helper/SseEmitterHelper.java +++ b/server/sparkx-service/src/main/java/sparkx/service/helper/SseEmitterHelper.java @@ -47,9 +47,9 @@ public class SseEmitterHelper { // 消息开始 sendStartSse(emitter); - AtomicBoolean hasReasoningContent = new AtomicBoolean(false); - AtomicBoolean hasSendStart = new AtomicBoolean(false); - AtomicBoolean hasSendEnd = new AtomicBoolean(false); + AtomicBoolean hasReasoningContent = new AtomicBoolean(false); // 是否有思考过程 + AtomicBoolean hasSendStart = new AtomicBoolean(false); // 是有发送了思考开始标识 + AtomicBoolean hasSendEnd = new AtomicBoolean(false); // 是否发送了思考结束标识 final TimeInterval timer = new TimeInterval(); tokenStream @@ -136,37 +136,46 @@ public class SseEmitterHelper { public void asyncSend2Client(TokenStream tokenStream, SseEmitter emitter, long runtimeId, String nodeId, boolean needSend, SendEndCallback sendEndCallback) { - AtomicBoolean hasReasoningContent = new AtomicBoolean(false); + AtomicBoolean hasReasoningContent = new AtomicBoolean(false); // 是否有思考过程 + AtomicBoolean hasSendStart = new AtomicBoolean(false); // 是有发送了思考开始标识 + AtomicBoolean hasSendEnd = new AtomicBoolean(false); // 是否发送了思考结束标识 + tokenStream - .onPartialResponse((content) -> { + // 思考过程 + .onPartialThinking((PartialThinking reasoningContent) -> { if (needSend) { try { - if (hasReasoningContent.get()) { - emitter.send(Tool.buildSendData(runtimeId, nodeId, "")); + hasReasoningContent.set(true); + + if (!hasSendStart.get()) { + emitter.send(Tool.buildSendData(runtimeId, nodeId, "")); + hasSendStart.set(true); } - sendSseData(content, emitter, runtimeId, nodeId); + sendSseData(reasoningContent.text(), emitter, runtimeId, nodeId); } catch (Exception e) { emitter.completeWithError(e); } } }) - // 思考过程 - .onPartialThinking((PartialThinking reasoningContent) -> { + // 工具调用 + .onToolExecuted((ToolExecution toolExecution) -> { + sendToolSse(emitter, toolExecution.request().name()); + }) + .onPartialResponse((content) -> { if (needSend) { try { - hasReasoningContent.set(true); - emitter.send(Tool.buildSendData(runtimeId, nodeId, "")); - sendSseData(reasoningContent.text(), emitter, runtimeId, nodeId); + if (hasReasoningContent.get() && !hasSendEnd.get()) { + emitter.send(Tool.buildSendData(runtimeId, nodeId, "")); + hasSendStart.set(true); + } + + sendSseData(content, emitter, runtimeId, nodeId); } catch (Exception e) { emitter.completeWithError(e); } } }) - // 工具调用 - .onToolExecuted((ToolExecution toolExecution) -> { - sendToolSse(emitter, toolExecution.request().name()); - }) .onCompleteResponse((response) -> { // 输入的token int inputTokenCount = response.tokenUsage().totalTokenCount(); -- Gitee From 69ea1f5d609b759aca93a93e1f52dcb655c63f92 Mon Sep 17 00:00:00 2001 From: NickBai <876337011@qq.com> Date: Wed, 30 Jul 2025 15:24:38 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E6=94=AF=E6=8C=81deepseek=E7=9A=84?= =?UTF-8?q?=E6=80=9D=E8=80=83=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/sql/sparkx.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/sql/sparkx.sql b/docker/sql/sparkx.sql index 1360072..0638c91 100644 --- a/docker/sql/sparkx.sql +++ b/docker/sql/sparkx.sql @@ -404,6 +404,7 @@ INSERT INTO "public"."models" VALUES ('6f4f2e21-df9d-419d-a65b-ed272b6cf5c8', ' INSERT INTO "public"."models" VALUES ('6f6f2e81-eg9b-429d-a57b-ed371g6cf8c9', 'Ollama', 'ollama', 1, '[{"field":"apiKey","value":"SparkX"}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "http://localhost:11434"}]', 1, '', '/icons/ollama.png', '', NULL, '2025-07-29 12:05:50'); INSERT INTO "public"."models" VALUES ('6f6f2e88-eg9c-429e-a58b-ed381g6cf9c9', 'Ollama', 'ollama', 2, '[{"field":"apiKey","value":"SparkX"}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "http://localhost:11434"}]', 1, '', '/icons/ollama.png', '', NULL, '2025-07-29 12:05:50'); INSERT INTO "public"."models" VALUES ('6f6f2e89-eg9f-439e-a68b-ed382g7cf9e9', 'Ollama', 'ollama', 3, '[{"field":"apiKey","value":"SparkX"}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "http://localhost:11434"}]', 1, '', '/icons/ollama.png', '', NULL, '2025-07-29 12:05:50'); +INSERT INTO "public"."models" VALUES ('5f6f2e81-ef9b-419d-a56b-ed271g6cf6c9', 'DeepSeek', 'deepseek', 1, '[{"field":"apiKey","value":"sk-cf918addea9f4433b11c2d38e9a5ba3b"}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "https://api.deepseek.com/v1"}]', 1, 'deepseek-chat,deepseek-reasoner', '/icons/deepseek.png', 'deepseek-chat', NULL, '2025-07-29 12:05:50'); CREATE TABLE "public"."application_workflow" ( "id" INT8 NOT NULL GENERATED ALWAYS AS IDENTITY (INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1), -- Gitee From 0382db42916702a5ed22efe6fd40d3ac38cf88b7 Mon Sep 17 00:00:00 2001 From: NickBai <876337011@qq.com> Date: Wed, 30 Jul 2025 16:42:10 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A8=A1=E5=8A=9B?= =?UTF-8?q?=E6=96=B9=E8=88=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/sql/sparkx.sql | 3 ++- .../src/main/resources/static/icons/gitee.png | Bin 2026 -> 6094 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/sql/sparkx.sql b/docker/sql/sparkx.sql index 0638c91..f7cd7c1 100644 --- a/docker/sql/sparkx.sql +++ b/docker/sql/sparkx.sql @@ -404,7 +404,8 @@ INSERT INTO "public"."models" VALUES ('6f4f2e21-df9d-419d-a65b-ed272b6cf5c8', ' INSERT INTO "public"."models" VALUES ('6f6f2e81-eg9b-429d-a57b-ed371g6cf8c9', 'Ollama', 'ollama', 1, '[{"field":"apiKey","value":"SparkX"}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "http://localhost:11434"}]', 1, '', '/icons/ollama.png', '', NULL, '2025-07-29 12:05:50'); INSERT INTO "public"."models" VALUES ('6f6f2e88-eg9c-429e-a58b-ed381g6cf9c9', 'Ollama', 'ollama', 2, '[{"field":"apiKey","value":"SparkX"}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "http://localhost:11434"}]', 1, '', '/icons/ollama.png', '', NULL, '2025-07-29 12:05:50'); INSERT INTO "public"."models" VALUES ('6f6f2e89-eg9f-439e-a68b-ed382g7cf9e9', 'Ollama', 'ollama', 3, '[{"field":"apiKey","value":"SparkX"}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "http://localhost:11434"}]', 1, '', '/icons/ollama.png', '', NULL, '2025-07-29 12:05:50'); -INSERT INTO "public"."models" VALUES ('5f6f2e81-ef9b-419d-a56b-ed271g6cf6c9', 'DeepSeek', 'deepseek', 1, '[{"field":"apiKey","value":"sk-cf918addea9f4433b11c2d38e9a5ba3b"}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "https://api.deepseek.com/v1"}]', 1, 'deepseek-chat,deepseek-reasoner', '/icons/deepseek.png', 'deepseek-chat', NULL, '2025-07-29 12:05:50'); +INSERT INTO "public"."models" VALUES ('5f6f2e81-ef9b-419d-a56b-ed271g6cf6c9', 'DeepSeek', 'deepseek', 1, '[{"field":"apiKey","value":""}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value": "https://api.deepseek.com/v1"}]', 1, 'deepseek-chat,deepseek-reasoner', '/icons/deepseek.png', 'deepseek-chat', NULL, '2025-07-29 12:05:50'); +INSERT INTO "public"."models" VALUES ('4f6f2e82-ef9c-439d-a57b-ed271g6cf8c9', '模力方舟', 'gitee', 1, '[{"field":"apiKey","value":""}]', '[{"field":"temperature","name":"温度","range":[0.01,1],"value":0.95},{"field":"url","name":"模型地址","value":"https://ai.gitee.com/v1"}]', 1, 'kimi-k2-instruct,internlm3-8b-instruct,Qwen3-235B-A22B', '/icons/gitee.png', '', NULL, '2025-07-30 16:26:00'); CREATE TABLE "public"."application_workflow" ( "id" INT8 NOT NULL GENERATED ALWAYS AS IDENTITY (INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1), diff --git a/server/sparkx-web/src/main/resources/static/icons/gitee.png b/server/sparkx-web/src/main/resources/static/icons/gitee.png index eda812da52caff6a1825af690780c9d2f5b15298..d6135149805d34b87eb7d2b085be7f48a25e6721 100644 GIT binary patch literal 6094 zcmV;<7cuCGP)005u}1^@s6i_d2*00009a7bBm001mY z001mY0i`{bsQ>@~0drDELIAGL9O(c600d`2O+f$vv5yPeej=3x-sY zN~KbFnOzqG?lFM-0PGtEqK^Nr0>l3iysZx2KU&AfM~{?#ckSCo1%8Kax7&sVLu{B+ zNNm=XTiA-`$o!>x%by zQ)@6dYxfYCL*_<+o^@MVcY!rHgD298NwqO4ECKn#eCtWz-)i8827YrLzkFd=mZ4T# zZsUDxqx#=CzY7%PQ3vLLnHJ!!TdPgrd<|V?6TfUqy@7P08Noh5Mvy4c)srwc?Fs|5 zlr0xv--5xxEW7QQ6JWKvTDPz84SX>-Ju8==ZDT^HyG_BW(zmPA5H9Nvy3w#Kyt6Vvb=#W7`e12i7s!KIxdWh>ZI=lwAyw2^c*eYbFz&1==M&!N3UT9 zD763sYlkoF7FxV2%m+;dtcg}}8>QKD`tHjzTSX2fgkn3i1aHwId_ZfrtxtSRTjoku zk-`jm4sO=&{zPv^C?Ta1U?4oq9^V4FTFr5o>Y^BXDVeBqR|4R zNQ|NA^6tt-L1d7qdmUXCcq%WUBr6YsDcZx=ry3%0yLpTfN}|~vP?C- zMTV=NEvvHpPM1qAT!6HiF)J>YF*XyHn_M!LYihMx;jc-d0=!xKc7s+kX2<2uPUt`) z6krz~7ZxT!O8*(Sd$7Q4C@w6pR8xomD?oQ*n|b8h8h~A7t4uU1h|O# zW{$1A+;QdtoG03U*Q}DrW3lPs>fgTw9IjgnfsRby9{8VUpExUL~0CY%XN!OfokM>Z6 zS~q8p^O*6zeuUYfTB(o-r|+VfT7Y(C`9+rE^Le9k5hek9us{nm%Z6Tin3AakD3gvn z3eDVot8&?3u%vxxPgysFPYWf3SR-7YHYu4n385z*1Jdn02AeTQZR%97*Xhx`J)Kl4 zj*=TK2wG+s|&Qzp0iIhbcy-14_z09hO0P(L6F6t^|G90II#e|xG$e} zBvGRJnh=&TEm>7tOb711Q*JR#_J zkR|yPNI2j8q2}|mo#*jy+aY$?~ z@B`Cgv`az*crbLI{xwo)`H3uzE!51e%IIJv0h0BD0>RAkhU&HWq*Kv!^1p>{)ODd{ zs$~mYXVif@MDWmsCZ)w;R~WVOrzRw+0xaJ80ljVSDPPOPCm3gmLC8~z4mgN*zYi^w z27G(jCNu-9Cgf+#@C!UpCqtSvgdnYLD^-9+TR%d;7+EdFfJrOb)Wpizdo1Gan`GUB zqr&jPHp5~I4$}cRPXdP2h6;|LG8Pt&*06Nc93Bka{!q2UNPF!B547fF`B8MD8fZl&LuC%XpLUJo@Q%%0m`fNu z!H?+z^!GS4At$_z<>ED!NluOrTc8oLa)MQ!Ts+WvGAAjRw1CMhIX!UZF9!69OhzsV z(6ZPTWK7u7=@eZ!mP4s+2)W9TRaC~BX;<`)jyk&cu;?Tz8M*X!q%$Nki~q1Fj2~a> zEC`1WvmP#O<;0+WdL!*|)^6YVP^LXESwhQ#y#6ZbS1);?w+zF})Zs4#s&fZTSQFeRjNr+@mW*DcoK z(UK2vzDD!xyk#y8FS5(2`y*L^%ze*fRz0^gAy$B1rZN?1VxJZ5Zpm^3bKjEablMib zMwrZeu7;e$*cwgr2qZFmDzeCa{h2N;D4a?M%pldWaW0Hp-6@bJgu0!E0>tchYPUf9 zRzU+aA;FNjfz`BuJyeW_Imfb=)`NeaI|B~uFfAFL97Hi4X5`AdtC%4JAgRogbh}fT zdkaXh-tajZ3Xlnw9>1hhpJrrNAiA5iyT5uTD#3Hr;Xf4IgApWq5?z%6!*~+NL<+E4 ztyfWjS!8BZ=pCz&O`@YwA7RmjYju{T7MGf00aaE+g26xYieHYAud&R6ZTqZ|S(^}J zFHlsiq-5k$olc0@zplDT-IPufl@g2VLVKLe_p?Y)GY!fxO~?|oWN6X$oWA=qSo;gF zNPsUoCAeC0)bCquyJEybe?9dI0UzhEE${-&VGkh?Aljb?SwA0C5Vk996NT9G$7j@G z0`TV)V^b={{00V4P3sL5Xszd5e?!9}V?cBpb;t?1(`$;gPo|jIP$|Zg*eWe
ov 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 UoQJznH~;_u07*qoM6N<$f<&r&RsaA1 literal 2026 zcmVpF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H12X{$C zK~z|Ut(kj_TvZvsf8V(?vn}oRu`}oH-g|dxHw{v?3AAXfrbLPj^-=#QQK=a55ld{g zi4aIgnwWrzf7$?25(Fhti3CWbh>b}}i2^OL4-~cZq20FY%Az>NwKFcIf-9RR)`d`&Um5cF@( z>4Bl4fn%!b=ACOkfa!G9<$110;oBf#6_b3N&$UkhhCfnxv@BJ>qR+abJT*L=z0hn< z(~MCRMH$;3K4lX6Z$%!e0S9Pt_DVEWp$i0ddr3Xx`_U5}9UcBP0kk%{d!nePQ0_%Z z)ioBG1r(L)wn|FT9_P#nYoqN~4JaZ>+aCS`(dSUw8U>n8UYflWN!B+djp*;Jjh>V` zwp9V6D7r3VgMBDFngY8l3uggT$D8sH-I=z*AG*7{7c>PZBFTwKeF^wdh3dm5(k(9w z+>HHHPS`SXY5e!qdU6h6#t-&Z!YTS}A?|Ng;m!uRTDrQr76Pa?4>&Dfv^IRSvN2J9 zV{P~?WbSJfH3wAHiO9Z;4IFj8-LWxQ4veZ{ROVK&0aVU|_N(Zys2NgWSX76MVc0o0 zBq9mb%hg~OFp8oKhR3N=7S*_A0IKS&AC3S^YVawPS^+4@QAB@e4ChKEIp5lvI@jCV zJ2v~!B3@ft7Et|*hi7gI`P`4ol_G0W)(&q_kaM z33o?*lA5x>825eP5dpl64bC$)+0V!s6?rBhrI!W<2F_M`M^Tg+pLAaV`KrJcU~!rH ztvH|CC?b;agRzFQPi^Tf#Qm$J&5u57)E&CqE|aMwVK$pBRP)OCL60Dh0bY&3mkM$J zQdKQkKRg9|>?#3Kr=KyNx=j_$o^x(7Ku1T1pYekp>xXYF>Fi8T@yo?JZ~%+a>0la4 zPG2qDR4Vyg0=T83-4vZ1^Zn?wVm>X?FsY)h1zr=!dq{)G9=8DRA$q#ec&JaP8;mM9 z&QU0Bif*8+r$pUa0MBzLX)p{0)&b!7NIeL$+F%Hpvel`i0|Nt>6l#{2a$95MA0G{n zwBgDTSVv^->=oK5@%rxW?ggooc?V>&ihdmUr8->?@@joRrP+PL4a=6TSPr0=Lk(;f zelR+*v9GUh6!h<9?IQ?#)Oo8GFKXF^VJGO;I&q8vdA~_OlS~#j0vPnIhQ`iq1`zaZ zpn%9D<0-Q;9?U)9d2YqXrSW$J`CfxC9~e}Jn*;=-w*f3#)N+KW*Hlv!xdXr$-QyXz zG9Ju6oE#mU^!?!HPW267b)$5O^czI~*)*W?d4S&D-Z6z^wGHKU75NFkz`#K7;Nak> zwb2iZ*K%5sT{O8_13hJsdWq)9&24RMO94dn%?jNp@Qe}frZ}Iyy)c-4c}8Jv@FJp5 z1MTz31;|mOqJNo(hv#{zTLFxbgHtg$A#mTsxYrryb2|nH``#51v9@EYZ4ZA2pd!8V z1Dh7-4hkS?8=Mtb*^G}Md-M71t|*GGD;8NhIGCLsFd~vn`@vmCc}QUm;7Ud5HYvG5 zoO^9vaKeQ`zQ5b3sv3B{371&WXPx0?fNVDV;S5|51lIS%-5DF473DY6Z~(?6xO;i) z!Z(4DdGRUFsj3rI#oFsbr1&48eCy`~PN?dBW6Uf0eC}}N*-8)uD@!Hwu%g?6ma3VQ zf5mZbjrGI*%-t#0l5ut3szRYJn*fO83%QIR?h(;7qViW~%-cFSdSqt#UIAq%Cs|o2 z^dE69u|z3-r+!!PNo&I$NX>mr6&HOvF7#!g$-|>4iZbIx_hnJ@bzlqVU?J{bsj4Mw z!*d8LnoZsqj-g~yR?n^ogj^RWO*8J~65N)IJiaCb6JBmET;YG+b50arXICQa&NblT=w^smSi>r+=YIuyH}3 zm>Bm~H-dwCAEI}5isyC;+yiu8rqoR(PYa$i??TNzU!TBlo%8kHigO3A_HT&)1KuYtu;_k4^8f$<07*qo IM6N<$g1=_T*8l(j -- Gitee From 3f3dca117cf99825925db9123c5429ff97406f5f Mon Sep 17 00:00:00 2001 From: NickBai <876337011@qq.com> Date: Wed, 30 Jul 2025 17:03:44 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/tools/index.vue | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/web/src/components/tools/index.vue b/web/src/components/tools/index.vue index 815bf31..8109eea 100644 --- a/web/src/components/tools/index.vue +++ b/web/src/components/tools/index.vue @@ -1,8 +1,8 @@