From 1d51a103d0d515aefb083bd5330d2866008a49a1 Mon Sep 17 00:00:00 2001
From: zhouweiyi <>
Date: Wed, 4 Jun 2025 17:55:47 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90=E9=98=BF=E9=87=8C?=
=?UTF-8?q?=E7=99=BE=E7=82=BCAPI=E5=AE=9E=E7=8E=B0=E5=9B=BE=E7=89=87?=
=?UTF-8?q?=E5=86=85=E5=AE=B9=E8=AF=86=E5=88=AB=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
添加DashscopeService接口及实现,用于调用阿里百炼API进行图片内容识别
修改PdfImageExtractService增加基于百炼API的图片处理逻辑
新增OSS服务方法支持临时文件处理和删除
更新配置文件添加百炼模型相关配置
---
.../src/main/resources/application-local.yml | 111 +++++++++++++
.../src/main/resources/application-prod.yml | 6 +-
ruoyi-modules-api/ruoyi-knowledge-api/pom.xml | 6 +
.../org/ruoyi/service/DashscopeService.java | 23 +++
.../ruoyi/service/PdfImageExtractService.java | 16 ++
.../service/impl/DashscopeServiceImpl.java | 149 ++++++++++++++++++
.../impl/PdfImageExtractServiceImpl.java | 59 +++++--
.../ruoyi/system/service/ISysOssService.java | 9 ++
.../service/impl/SysOssServiceImpl.java | 44 ++++++
.../knowledge/KnowledgeController.java | 16 +-
.../service/knowledge/DealFileService.java | 53 +++----
.../knowledge/KnowledgeInfoServiceImpl.java | 61 ++++---
script/sql/update/202506041541.sql | 2 +
13 files changed, 472 insertions(+), 83 deletions(-)
create mode 100644 ruoyi-admin/src/main/resources/application-local.yml
create mode 100644 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/DashscopeService.java
create mode 100644 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/DashscopeServiceImpl.java
create mode 100644 script/sql/update/202506041541.sql
diff --git a/ruoyi-admin/src/main/resources/application-local.yml b/ruoyi-admin/src/main/resources/application-local.yml
new file mode 100644
index 00000000..edf30cb5
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/application-local.yml
@@ -0,0 +1,111 @@
+--- # 监控中心配置
+spring.boot.admin.client:
+ # 增加客户端开关
+ enabled: false
+ url: http://localhost:9090/admin
+ instance:
+ service-host-type: IP
+ username: ruoyi
+ password: 123456
+
+--- # 数据源配置
+spring:
+ datasource:
+ type: com.zaxxer.hikari.HikariDataSource
+ # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
+ dynamic:
+ # 性能分析插件(有性能损耗 不建议生产环境使用)
+ p6spy: true
+ # 设置默认的数据源或者数据源组,默认值即为 master
+ primary: master
+ # 严格模式 匹配不到数据源则报错
+ strict: true
+ datasource:
+ # 主库数据源
+ master:
+ type: ${spring.datasource.type}
+ driverClassName: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://localhost:3306/ruoyi-ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
+ username: root
+ password: root
+
+
+ hikari:
+ # 最大连接池数量
+ maxPoolSize: 20
+ # 最小空闲线程数量
+ minIdle: 10
+ # 配置获取连接等待超时的时间
+ connectionTimeout: 30000
+ # 校验超时时间
+ validationTimeout: 5000
+ # 空闲连接存活最大时间,默认10分钟
+ idleTimeout: 600000
+ # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
+ maxLifetime: 1800000
+ # 连接测试query(配置检测连接是否有效)
+ connectionTestQuery: SELECT 1
+ # 多久检查一次连接的活性
+ keepaliveTime: 30000
+
+--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
+spring.data:
+ redis:
+ # 地址
+ host: 127.0.0.1
+ # 端口,默认为6379
+ port: 6379
+ # 数据库索引
+ database: 0
+ # 密码(如没有密码请注释掉)
+ password: root
+ # 连接超时时间
+ timeout: 10S
+ # 是否开启ssl
+ ssl: false
+
+redisson:
+ # redis key前缀
+ keyPrefix:
+ # 线程池数量
+ threads: 4
+ # Netty线程池数量
+ nettyThreads: 8
+ # 单节点配置
+ singleServerConfig:
+ # 客户端名称
+ clientName: ${ruoyi.name}
+ # 最小空闲连接数
+ connectionMinimumIdleSize: 8
+ # 连接池大小
+ connectionPoolSize: 32
+ # 连接空闲超时,单位:毫秒
+ idleConnectionTimeout: 10000
+ # 命令等待超时,单位:毫秒
+ timeout: 3000
+ # 发布和订阅连接池大小
+ subscriptionConnectionPoolSize: 50
+
+--- # sms 短信
+sms:
+ enabled: false
+ # 阿里云 dysmsapi.aliyuncs.com
+ # 腾讯云 sms.tencentcloudapi.com
+ endpoint: "dysmsapi.aliyuncs.com"
+ accessKeyId: xxxxxxx
+ accessKeySecret: xxxxxx
+ signName: 测试
+ # 腾讯专用
+ sdkAppId:
+
+pdf:
+ extract:
+ service:
+ url: http://localhost:8080
+ ai-api:
+ url: https://api.pandarobot.chat/v1/chat/completions
+ key: sk-xxxx
+#百炼模型配置
+dashscope:
+ key: sk-0a4a86f3712b47ac825c1632319a8b1a
+ model: qvq-max
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml
index cb495f9f..bbad32bd 100644
--- a/ruoyi-admin/src/main/resources/application-prod.yml
+++ b/ruoyi-admin/src/main/resources/application-prod.yml
@@ -179,4 +179,8 @@ pdf:
url: http://localhost:8080
ai-api:
url: https://api.pandarobot.chat/v1/chat/completions
- key: sk-XXXXXX
\ No newline at end of file
+ key: sk-XXXXXX
+#百炼模型配置
+dashscope:
+ key: sk-XXXX
+ model: qvq-max
\ No newline at end of file
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml b/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
index 4b4c1fe6..1065ac13 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
@@ -119,6 +119,12 @@
ruoyi-system-api
+
+ com.alibaba
+ dashscope-sdk-java
+ 2.19.0
+
+
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/DashscopeService.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/DashscopeService.java
new file mode 100644
index 00000000..3c8f498e
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/DashscopeService.java
@@ -0,0 +1,23 @@
+package org.ruoyi.service;
+
+import java.io.IOException;
+
+/**
+ * @Description: 阿里百炼api
+ * @Date: 2025/6/4 下午2:24
+ */
+public interface DashscopeService {
+
+ /**
+ * 视觉推理(QVQ)
+ * @param imageUrl 图片可访问的地址
+ * @return
+ */
+ String qvq(String imageUrl) throws IOException;
+ /**
+ * 视觉推理(QVQ) 使用本地文件(输入Base64编码或本地路径)
+ * @param localPath 图片文件的绝对路径
+ * @return
+ */
+ String qvq4LocalPath(String localPath) throws IOException;
+}
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java
index 759b4397..1b68243f 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java
@@ -30,7 +30,23 @@ public interface PdfImageExtractService {
* @throws IOException 如果API调用过程中发生错误
*/
List dealFileContent(String[] unzip) throws IOException;
+ /**
+ *利用百炼接口处理文件内容
+ *
+ * @param imageUrl 传入图片地址
+ * @return 文件内容结果列表
+ * @throws IOException 如果API调用过程中发生错误
+ */
+ List dealFileContent4Dashscope(String imageUrl) throws IOException;
+ /**
+ * 利用百炼接口处理文件内容
+ *
+ * 视觉推理(QVQ) 使用本地文件(输入Base64编码或本地路径)
+ * @param localPath 图片文件的绝对路径
+ * @return
+ */
+ List dealFileContent4DashscopeBase64(String localPath)throws IOException;
/**
* 提取PDF中的图片并调用gpt-4o-mini,识别图片内容并返回
* @param file
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/DashscopeServiceImpl.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/DashscopeServiceImpl.java
new file mode 100644
index 00000000..c325c4f0
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/DashscopeServiceImpl.java
@@ -0,0 +1,149 @@
+package org.ruoyi.service.impl;
+
+import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
+import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
+import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
+import com.alibaba.dashscope.common.MultiModalMessage;
+import com.alibaba.dashscope.common.Role;
+import io.reactivex.Flowable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import lombok.extern.slf4j.Slf4j;
+import org.ruoyi.domain.PdfFileContentResult;
+import org.ruoyi.service.DashscopeService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+/**
+ * @Description: 阿里百炼API
+ * @Date: 2025/6/4 下午2:28
+ */
+@Service
+@Slf4j
+public class DashscopeServiceImpl implements DashscopeService {
+
+ private static StringBuilder reasoningContent = new StringBuilder();
+ private static StringBuilder finalContent = new StringBuilder();
+ private static boolean isFirstPrint = true;
+
+ @Value("${dashscope.model}")
+ private String serviceModel;
+ @Value("${dashscope.key}")
+ private String serviceKey;
+
+ /**
+ * 视觉推理(QVQ)
+ * @param imageUrl 图片可访问地址
+ * @return
+ */
+ @Override
+ public String qvq(String imageUrl) throws IOException {
+ try {
+ // 构建多模态消息
+ MultiModalMessage userMessage = MultiModalMessage.builder()
+ .role(Role.USER.getValue())
+ .content(Arrays.asList(
+ Collections.singletonMap("text", "这张图片有什么"),
+ Collections.singletonMap("image", imageUrl)
+ ))
+ .build();
+
+ // 构建请求参数
+ MultiModalConversationParam param = MultiModalConversationParam.builder()
+ .apiKey(serviceKey) // 使用配置文件中的API Key
+ .model(serviceModel)
+ .message(userMessage)
+ .build();
+
+ MultiModalConversation conv = new MultiModalConversation();
+
+ // 调用API
+ Flowable result = conv.streamCall(
+ param);
+
+ reasoningContent = new StringBuilder();
+ finalContent = new StringBuilder();
+ isFirstPrint = true;
+
+ result.blockingForEach(DashscopeServiceImpl::handleGenerationResult);
+
+ return finalContent.toString().replaceAll("[\n\r\s]", "");
+ } catch (Exception e) {
+ log.error("调用百炼API失败: {}", e.getMessage(), e);
+ throw new IOException("百炼API调用失败: " + e.getMessage(), e);
+ }
+ }
+ /**
+ * 视觉推理(QVQ) 使用本地文件(输入Base64编码或本地路径)
+ * @param localPath 图片文件的绝对路径
+ * @return
+ */
+ @Override
+ public String qvq4LocalPath(String localPath) throws IOException {
+ try {
+ // 构建多模态消息
+ String filePath = "file://"+ localPath;
+ log.info("filePath: {}", filePath);
+ MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
+ .content(Arrays.asList(new HashMap(){{put("image", filePath);}},
+ new HashMap(){{put("text", "这张图片有什么");}})).build();
+
+ // 构建请求参数
+ MultiModalConversationParam param = MultiModalConversationParam.builder()
+ .apiKey(serviceKey) // 使用配置文件中的API Key
+ .model(serviceModel)
+ .message(userMessage)
+ .build();
+ MultiModalConversation conv = new MultiModalConversation();
+
+ // 调用API
+ Flowable result = conv.streamCall(
+ param);
+
+ reasoningContent = new StringBuilder();
+ finalContent = new StringBuilder();
+ isFirstPrint = true;
+
+ result.blockingForEach(DashscopeServiceImpl::handleGenerationResult);
+
+ return finalContent.toString().replaceAll("[\n\r\s]", "");
+ } catch (Exception e) {
+ log.error("调用百炼API失败: {}", e.getMessage(), e);
+ throw new IOException("百炼API调用失败: " + e.getMessage(), e);
+ }
+ }
+
+
+ private static void handleGenerationResult(MultiModalConversationResult message) {
+
+ String re = message.getOutput().getChoices().get(0).getMessage().getReasoningContent();
+ String reasoning = Objects.isNull(re) ? "" : re; // 默认值
+
+ List