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.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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
    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:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user