Merge remote-tracking branch 'origin/main'

This commit is contained in:
2025-08-21 18:06:04 +08:00
3 changed files with 133 additions and 70 deletions

View File

@@ -10,6 +10,7 @@ import com.srs.common.core.controller.BaseController;
import com.srs.common.exception.ServiceException;
import com.srs.common.utils.SecurityUtils;
import com.srs.teacher.domain.dto.ConversationDTO;
import com.srs.web.core.config.DifyConfig;
import okhttp3.*;
// Spring 显式导入(不要用 *)
@@ -42,61 +43,6 @@ import java.util.concurrent.CompletableFuture;
@RequestMapping("/aitutor/aichat")
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客户端实例
* 配置了5分钟的读取超时时间用于与Dify API进行通信
@@ -114,6 +60,13 @@ public class AiChatController extends BaseController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* Dify配置类
* 用于获取Dify的API密钥和其他配置参数
*/
@Autowired
private DifyConfig difyConfig;
/** 为本次请求设置 “总超时”(含连接/读写),避免无限挂起 */
private Response execWithTimeouts(Request req, int callSecs, int readSecs, int writeSecs) throws IOException {
OkHttpClient shortClient = client.newBuilder()
@@ -254,8 +207,8 @@ public class AiChatController extends BaseController {
// 构建HTTP请求
Request httpRequest = new Request.Builder()
.url(DIFY_API_URL) // 设置请求URL
.addHeader("Authorization", "Bearer " + DIFY_API_KEY) // 添加认证头
.url(difyConfig.getApiUrl()) // 设置请求URL
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey()) // 添加认证头
.addHeader("Content-Type", "application/json") // 设置内容类型
.post(body) // 设置为POST请求
.build();
@@ -385,8 +338,8 @@ public class AiChatController extends BaseController {
// 调用 Dify API
Request request = new Request.Builder()
.url(DIFY_FEEDBACK_BASE_URL + "/" + messageId + "/feedbacks")
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
.url(difyConfig.getFeedbackBaseUrl() + "/" + messageId + "/feedbacks")
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
.addHeader("Content-Type", "application/json")
.post(body)
.build();
@@ -432,8 +385,8 @@ public class AiChatController extends BaseController {
// 构建请求
Request request = new Request.Builder()
.url(DIFY_API_FEEDBACK_URL + page + "&limit=" + limitValue)
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
.url(difyConfig.getApiFeedbackUrl() + page + "&limit=" + limitValue)
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
.addHeader("Content-Type", "application/json")
.get()
.build();
@@ -594,7 +547,7 @@ public class AiChatController extends BaseController {
}
user = user.trim();
String cacheKey = CONVERSATION_ID_CACHE_PREFIX + user;
String cacheKey = difyConfig.getConversationCachePrefix() + user;
// 从Redis中获取会话ID
String conversationId = stringRedisTemplate.opsForValue().get(cacheKey);
@@ -626,11 +579,11 @@ public class AiChatController extends BaseController {
conversationId = conversationId.trim();
// 将用户与会话ID绑定存储到Redis中
String cacheKey = CONVERSATION_ID_CACHE_PREFIX + user;
String cacheKey = difyConfig.getConversationCachePrefix() + user;
stringRedisTemplate.opsForValue().set(
cacheKey,
conversationId,
Duration.ofHours(CONVERSATION_ID_CACHE_EXPIRE_HOURS));
Duration.ofHours(difyConfig.getConversationCacheExpireHours()));
} catch (Exception e) {
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)
throws IOException {
// 构建带查询参数的 URL
HttpUrl.Builder urlBuilder = HttpUrl.parse(DIFY_CONVERSATIONS_URL).newBuilder();
HttpUrl.Builder urlBuilder = HttpUrl.parse(difyConfig.getConversationsUrl()).newBuilder();
urlBuilder.addQueryParameter("user", user);
if (lastId != null && !lastId.trim().isEmpty()) {
urlBuilder.addQueryParameter("last_id", lastId);
@@ -653,7 +606,7 @@ public class AiChatController extends BaseController {
// 构建请求
Request request = new Request.Builder()
.url(urlBuilder.build())
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
.get()
.build();
@@ -725,7 +678,7 @@ public class AiChatController extends BaseController {
int finalLimit = limit != null && limit > 0 ? Math.min(limit, 100) : 20;
// 构建请求 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("user", user);
if (firstId != null && !firstId.trim().isEmpty()) {
@@ -735,7 +688,7 @@ public class AiChatController extends BaseController {
Request request = new Request.Builder()
.url(urlBuilder.build())
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
.addHeader("Accept", "application/json")
.get()
.build();
@@ -823,8 +776,8 @@ public class AiChatController extends BaseController {
// 构建请求
Request request = new Request.Builder()
.url(DIFY_FILES_URL)
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
.url(difyConfig.getFilesUrl())
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
.post(requestBody)
.build();

View 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;
}
}

View File

@@ -42,6 +42,11 @@ logging:
com.srs: debug
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:
password: