Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -10,6 +10,7 @@ import com.srs.common.core.controller.BaseController;
|
|||||||
import com.srs.common.exception.ServiceException;
|
import com.srs.common.exception.ServiceException;
|
||||||
import com.srs.common.utils.SecurityUtils;
|
import com.srs.common.utils.SecurityUtils;
|
||||||
import com.srs.teacher.domain.dto.ConversationDTO;
|
import com.srs.teacher.domain.dto.ConversationDTO;
|
||||||
|
import com.srs.web.core.config.DifyConfig;
|
||||||
import okhttp3.*;
|
import okhttp3.*;
|
||||||
|
|
||||||
// Spring 显式导入(不要用 *)
|
// Spring 显式导入(不要用 *)
|
||||||
@@ -42,61 +43,6 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
@RequestMapping("/aitutor/aichat")
|
@RequestMapping("/aitutor/aichat")
|
||||||
public class AiChatController extends BaseController {
|
public class AiChatController extends BaseController {
|
||||||
|
|
||||||
// private static final String DIFY_BASE_URL = "http://172.16.129.101:6100";
|
|
||||||
private static final String DIFY_BASE_URL = "http://47.112.118.149:8100";
|
|
||||||
/**
|
|
||||||
* Dify API的访问密钥
|
|
||||||
* 用于身份验证,授权访问Dify服务
|
|
||||||
*/
|
|
||||||
// private static final String DIFY_API_KEY = "app-BfPtBZDNBuHOS9K1PaZrxQYE";
|
|
||||||
private static final String DIFY_API_KEY = "app-2wjqcYI9n6igHTVHdH8qXlnh";
|
|
||||||
/**
|
|
||||||
* Dify API的URL地址
|
|
||||||
* 用于发送聊天消息请求到Dify服务
|
|
||||||
*/
|
|
||||||
|
|
||||||
private static final String DIFY_API_URL = DIFY_BASE_URL+"/v1/chat-messages";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dify反馈API的基础URL
|
|
||||||
* 用于提交消息反馈(点赞、点踩等)
|
|
||||||
*/
|
|
||||||
private static final String DIFY_FEEDBACK_BASE_URL = DIFY_BASE_URL+"/v1/messages";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dify获取反馈API的基础URL
|
|
||||||
* 用于获取消息反馈(点赞、点踩等)
|
|
||||||
*/
|
|
||||||
private static final String DIFY_API_FEEDBACK_URL = DIFY_BASE_URL+"/v1/app/feedbacks?page=";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dify消息历史记录API的基础URL
|
|
||||||
* 用于获取消息历史记录
|
|
||||||
*/
|
|
||||||
private static final String DIFY_API_HISTORY_URL = DIFY_BASE_URL+"/v1/messages";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dify会话API的基础URL
|
|
||||||
* 用于获取会话列表
|
|
||||||
*/
|
|
||||||
private static final String DIFY_CONVERSATIONS_URL = DIFY_BASE_URL+"/v1/conversations";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dify文件上传API的URL地址
|
|
||||||
* 用于上传文件到Dify服务,支持图文多模态理解
|
|
||||||
*/
|
|
||||||
private static final String DIFY_FILES_URL = DIFY_BASE_URL+"/v1/files/upload";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redis中会话ID的key前缀
|
|
||||||
*/
|
|
||||||
private static final String CONVERSATION_ID_CACHE_PREFIX = "dify:conversation:id:";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redis中会话ID的过期时间(小时)
|
|
||||||
*/
|
|
||||||
private static final int CONVERSATION_ID_CACHE_EXPIRE_HOURS = 1;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP客户端实例
|
* HTTP客户端实例
|
||||||
* 配置了5分钟的读取超时时间,用于与Dify API进行通信
|
* 配置了5分钟的读取超时时间,用于与Dify API进行通信
|
||||||
@@ -114,6 +60,13 @@ public class AiChatController extends BaseController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private StringRedisTemplate stringRedisTemplate;
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dify配置类
|
||||||
|
* 用于获取Dify的API密钥和其他配置参数
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
private DifyConfig difyConfig;
|
||||||
|
|
||||||
/** 为本次请求设置 “总超时”(含连接/读写),避免无限挂起 */
|
/** 为本次请求设置 “总超时”(含连接/读写),避免无限挂起 */
|
||||||
private Response execWithTimeouts(Request req, int callSecs, int readSecs, int writeSecs) throws IOException {
|
private Response execWithTimeouts(Request req, int callSecs, int readSecs, int writeSecs) throws IOException {
|
||||||
OkHttpClient shortClient = client.newBuilder()
|
OkHttpClient shortClient = client.newBuilder()
|
||||||
@@ -254,8 +207,8 @@ public class AiChatController extends BaseController {
|
|||||||
|
|
||||||
// 构建HTTP请求
|
// 构建HTTP请求
|
||||||
Request httpRequest = new Request.Builder()
|
Request httpRequest = new Request.Builder()
|
||||||
.url(DIFY_API_URL) // 设置请求URL
|
.url(difyConfig.getApiUrl()) // 设置请求URL
|
||||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY) // 添加认证头
|
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey()) // 添加认证头
|
||||||
.addHeader("Content-Type", "application/json") // 设置内容类型
|
.addHeader("Content-Type", "application/json") // 设置内容类型
|
||||||
.post(body) // 设置为POST请求
|
.post(body) // 设置为POST请求
|
||||||
.build();
|
.build();
|
||||||
@@ -385,8 +338,8 @@ public class AiChatController extends BaseController {
|
|||||||
|
|
||||||
// 调用 Dify API
|
// 调用 Dify API
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(DIFY_FEEDBACK_BASE_URL + "/" + messageId + "/feedbacks")
|
.url(difyConfig.getFeedbackBaseUrl() + "/" + messageId + "/feedbacks")
|
||||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||||
.addHeader("Content-Type", "application/json")
|
.addHeader("Content-Type", "application/json")
|
||||||
.post(body)
|
.post(body)
|
||||||
.build();
|
.build();
|
||||||
@@ -432,8 +385,8 @@ public class AiChatController extends BaseController {
|
|||||||
|
|
||||||
// 构建请求
|
// 构建请求
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(DIFY_API_FEEDBACK_URL + page + "&limit=" + limitValue)
|
.url(difyConfig.getApiFeedbackUrl() + page + "&limit=" + limitValue)
|
||||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||||
.addHeader("Content-Type", "application/json")
|
.addHeader("Content-Type", "application/json")
|
||||||
.get()
|
.get()
|
||||||
.build();
|
.build();
|
||||||
@@ -594,7 +547,7 @@ public class AiChatController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user = user.trim();
|
user = user.trim();
|
||||||
String cacheKey = CONVERSATION_ID_CACHE_PREFIX + user;
|
String cacheKey = difyConfig.getConversationCachePrefix() + user;
|
||||||
|
|
||||||
// 从Redis中获取会话ID
|
// 从Redis中获取会话ID
|
||||||
String conversationId = stringRedisTemplate.opsForValue().get(cacheKey);
|
String conversationId = stringRedisTemplate.opsForValue().get(cacheKey);
|
||||||
@@ -626,11 +579,11 @@ public class AiChatController extends BaseController {
|
|||||||
conversationId = conversationId.trim();
|
conversationId = conversationId.trim();
|
||||||
|
|
||||||
// 将用户与会话ID绑定存储到Redis中
|
// 将用户与会话ID绑定存储到Redis中
|
||||||
String cacheKey = CONVERSATION_ID_CACHE_PREFIX + user;
|
String cacheKey = difyConfig.getConversationCachePrefix() + user;
|
||||||
stringRedisTemplate.opsForValue().set(
|
stringRedisTemplate.opsForValue().set(
|
||||||
cacheKey,
|
cacheKey,
|
||||||
conversationId,
|
conversationId,
|
||||||
Duration.ofHours(CONVERSATION_ID_CACHE_EXPIRE_HOURS));
|
Duration.ofHours(difyConfig.getConversationCacheExpireHours()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println("绑定会话ID时发生错误: " + e.getMessage());
|
System.out.println("绑定会话ID时发生错误: " + e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -642,7 +595,7 @@ public class AiChatController extends BaseController {
|
|||||||
public List<ConversationDTO> getConversations(String user, String lastId, int limit, String sortBy)
|
public List<ConversationDTO> getConversations(String user, String lastId, int limit, String sortBy)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
// 构建带查询参数的 URL
|
// 构建带查询参数的 URL
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(DIFY_CONVERSATIONS_URL).newBuilder();
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(difyConfig.getConversationsUrl()).newBuilder();
|
||||||
urlBuilder.addQueryParameter("user", user);
|
urlBuilder.addQueryParameter("user", user);
|
||||||
if (lastId != null && !lastId.trim().isEmpty()) {
|
if (lastId != null && !lastId.trim().isEmpty()) {
|
||||||
urlBuilder.addQueryParameter("last_id", lastId);
|
urlBuilder.addQueryParameter("last_id", lastId);
|
||||||
@@ -653,7 +606,7 @@ public class AiChatController extends BaseController {
|
|||||||
// 构建请求
|
// 构建请求
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(urlBuilder.build())
|
.url(urlBuilder.build())
|
||||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||||
.get()
|
.get()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -725,7 +678,7 @@ public class AiChatController extends BaseController {
|
|||||||
int finalLimit = limit != null && limit > 0 ? Math.min(limit, 100) : 20;
|
int finalLimit = limit != null && limit > 0 ? Math.min(limit, 100) : 20;
|
||||||
|
|
||||||
// 构建请求 URL
|
// 构建请求 URL
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(DIFY_API_HISTORY_URL).newBuilder();
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(difyConfig.getApiHistoryUrl()).newBuilder();
|
||||||
urlBuilder.addQueryParameter("conversation_id", conversationId);
|
urlBuilder.addQueryParameter("conversation_id", conversationId);
|
||||||
urlBuilder.addQueryParameter("user", user);
|
urlBuilder.addQueryParameter("user", user);
|
||||||
if (firstId != null && !firstId.trim().isEmpty()) {
|
if (firstId != null && !firstId.trim().isEmpty()) {
|
||||||
@@ -735,7 +688,7 @@ public class AiChatController extends BaseController {
|
|||||||
|
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(urlBuilder.build())
|
.url(urlBuilder.build())
|
||||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||||
.addHeader("Accept", "application/json")
|
.addHeader("Accept", "application/json")
|
||||||
.get()
|
.get()
|
||||||
.build();
|
.build();
|
||||||
@@ -823,8 +776,8 @@ public class AiChatController extends BaseController {
|
|||||||
|
|
||||||
// 构建请求
|
// 构建请求
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(DIFY_FILES_URL)
|
.url(difyConfig.getFilesUrl())
|
||||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||||
.post(requestBody)
|
.post(requestBody)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
105
srs-admin/src/main/java/com/srs/web/core/config/DifyConfig.java
Normal file
105
srs-admin/src/main/java/com/srs/web/core/config/DifyConfig.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package com.srs.web.core.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@Component // 让 Spring 扫描到
|
||||||
|
@ConfigurationProperties(prefix = "dify")
|
||||||
|
public class DifyConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dify 服务的基础 URL,例如:http://47.112.118.149:8100
|
||||||
|
*/
|
||||||
|
private String baseUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dify API 密钥
|
||||||
|
*/
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时时间配置(可选)
|
||||||
|
*/
|
||||||
|
private Duration timeout = Duration.ofSeconds(30);
|
||||||
|
|
||||||
|
// ================== API URLs ==================
|
||||||
|
// 这些 URL 基于 baseUrl 自动生成,不直接暴露在配置文件中
|
||||||
|
private String apiUrl;
|
||||||
|
private String feedbackBaseUrl;
|
||||||
|
private String apiFeedbackUrl;
|
||||||
|
private String apiHistoryUrl;
|
||||||
|
private String conversationsUrl;
|
||||||
|
private String filesUrl;
|
||||||
|
|
||||||
|
// ================== Redis 缓存配置 ==================
|
||||||
|
private String conversationCachePrefix = "dify:conversation:id:";
|
||||||
|
private int conversationCacheExpireHours = 1;
|
||||||
|
|
||||||
|
// -------------------- Getters and Setters --------------------
|
||||||
|
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBaseUrl(String baseUrl) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiKey() {
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApiKey(String apiKey) {
|
||||||
|
this.apiKey = apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration getTimeout() {
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeout(Duration timeout) {
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiUrl() {
|
||||||
|
return baseUrl + "/v1/chat-messages";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFeedbackBaseUrl() {
|
||||||
|
return baseUrl + "/v1/messages";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiFeedbackUrl() {
|
||||||
|
return baseUrl + "/v1/app/feedbacks?page=";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiHistoryUrl() {
|
||||||
|
return baseUrl + "/v1/messages";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConversationsUrl() {
|
||||||
|
return baseUrl + "/v1/conversations";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilesUrl() {
|
||||||
|
return baseUrl + "/v1/files/upload";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConversationCachePrefix() {
|
||||||
|
return conversationCachePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConversationCachePrefix(String conversationCachePrefix) {
|
||||||
|
this.conversationCachePrefix = conversationCachePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConversationCacheExpireHours() {
|
||||||
|
return conversationCacheExpireHours;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConversationCacheExpireHours(int conversationCacheExpireHours) {
|
||||||
|
this.conversationCacheExpireHours = conversationCacheExpireHours;
|
||||||
|
}
|
||||||
|
}
|
@@ -42,6 +42,11 @@ logging:
|
|||||||
com.srs: debug
|
com.srs: debug
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
|
|
||||||
|
#dify配置
|
||||||
|
dify:
|
||||||
|
base-url: http://47.112.118.149:8100 #http://172.16.129.101:6100
|
||||||
|
api-key: app-2wjqcYI9n6igHTVHdH8qXlnh #app-BfPtBZDNBuHOS9K1PaZrxQYE
|
||||||
|
|
||||||
# 用户配置
|
# 用户配置
|
||||||
user:
|
user:
|
||||||
password:
|
password:
|
||||||
|
Reference in New Issue
Block a user