修改了历史列表和历史记录的方法接口
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
package com.srs.web.controller.aitutor;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.srs.common.core.controller.BaseController;
|
||||
|
||||
// OkHttp 显式导入
|
||||
import com.srs.common.core.domain.model.LoginUser;
|
||||
import com.srs.common.exception.ServiceException;
|
||||
import com.srs.common.utils.SecurityUtils;
|
||||
import com.srs.teacher.domain.dto.ConversationDTO;
|
||||
import okhttp3.*;
|
||||
|
||||
// Spring 显式导入(不要用 *)
|
||||
@@ -19,11 +25,9 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@@ -61,7 +65,7 @@ public class AiChatController extends BaseController {
|
||||
private static final String DIFY_CONVERSATIONS_URL = "http://47.112.118.149:8100/v1/conversations";
|
||||
//private static final String DIFY_CONVERSATIONS_URL = "http://localhost:8080/v1/conversations";
|
||||
|
||||
//文件上传API
|
||||
//文件上传
|
||||
private static final String DIFY_FILES_URL = "http://47.112.118.149:8100/v1/files/upload";
|
||||
|
||||
/**
|
||||
@@ -118,10 +122,13 @@ public class AiChatController extends BaseController {
|
||||
emitter.completeWithError(ex);
|
||||
});
|
||||
|
||||
// 在主线程中获取当前用户名,避免在异步线程中获取安全上下文
|
||||
String currentUsername = SecurityUtils.getLoginUser().getUsername();
|
||||
|
||||
// 异步执行请求处理,避免阻塞主线程
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
sendToDifyAndStream(requestData, emitter);
|
||||
sendToDifyAndStream(requestData, emitter, currentUsername);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
@@ -142,16 +149,17 @@ public class AiChatController extends BaseController {
|
||||
* 根据Dify API返回的不同事件类型,将数据通过SSE发送给客户端。
|
||||
* </p>
|
||||
*
|
||||
* @param requestData 包含聊天请求数据的Map,包含用户消息、用户标识等信息
|
||||
* @param emitter 用于向客户端发送SSE事件的发射器
|
||||
* @param requestData 包含聊天请求数据的Map,包含用户消息、用户标识等信息
|
||||
* @param emitter 用于向客户端发送SSE事件的发射器
|
||||
* @param currentUsername
|
||||
* @throws IOException 当网络请求或IO操作失败时抛出
|
||||
*/
|
||||
private void sendToDifyAndStream(Map<String, Object> requestData, SseEmitter emitter) throws IOException {
|
||||
private void sendToDifyAndStream(Map<String, Object> requestData, SseEmitter emitter, String currentUsername) throws IOException {
|
||||
|
||||
// 构建请求体参数
|
||||
Map<String, Object> bodyMap = new HashMap<>();
|
||||
bodyMap.put("query", requestData.get("query")); // 用户消息内容
|
||||
bodyMap.put("user", requestData.get("user")); // 用户标识
|
||||
bodyMap.put("user", currentUsername); // 用户标识
|
||||
bodyMap.put("response_mode", "streaming"); // 设置为流式响应模式
|
||||
|
||||
// 如果存在对话ID,则添加到请求参数中
|
||||
@@ -162,11 +170,7 @@ public class AiChatController extends BaseController {
|
||||
// 添加输入参数,默认为空HashMap
|
||||
Map<String, Object> inputs = (Map<String, Object>) requestData.getOrDefault("inputs", new HashMap<>());
|
||||
|
||||
// 从requestData中获取用户相关信息并添加到inputs中(如果存在且不为null)
|
||||
Object userId = requestData.get("user_id");
|
||||
if (userId != null) {
|
||||
inputs.put("user_id", userId.toString());
|
||||
}
|
||||
inputs.put("user_id", currentUsername);
|
||||
|
||||
Object userName = requestData.get("user_name");
|
||||
if (userName != null) {
|
||||
@@ -192,7 +196,7 @@ public class AiChatController extends BaseController {
|
||||
String jsonBody = mapper.writeValueAsString(bodyMap);
|
||||
|
||||
// 创建请求体对象
|
||||
RequestBody body = RequestBody.create( MediaType.get("application/json; charset=utf-8"),jsonBody);
|
||||
RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), jsonBody);
|
||||
|
||||
// 构建HTTP请求
|
||||
Request httpRequest = new Request.Builder()
|
||||
@@ -370,10 +374,10 @@ public class AiChatController extends BaseController {
|
||||
try {
|
||||
// 参数校验和限制
|
||||
int limitValue = Math.min(Math.max(Integer.parseInt(limit), 1), 100);
|
||||
|
||||
|
||||
// 构建请求URL
|
||||
String url = "http://47.112.118.149:8100/v1/app/feedbacks?page=" + page + "&limit=" + limitValue;
|
||||
|
||||
|
||||
// 构建请求
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
@@ -414,7 +418,7 @@ public class AiChatController extends BaseController {
|
||||
userMap.put("email", userNode.has("email") ? userNode.get("email").asText() : null);
|
||||
feedbackItem.put("from_end_user", userMap);
|
||||
}
|
||||
|
||||
|
||||
// 提取消息内容
|
||||
if (feedbackNode.has("message")) {
|
||||
JsonNode messageNode = feedbackNode.get("message");
|
||||
@@ -425,7 +429,7 @@ public class AiChatController extends BaseController {
|
||||
messageMap.put("created_at", messageNode.has("created_at") ? messageNode.get("created_at").asLong() : null);
|
||||
feedbackItem.put("message", messageMap);
|
||||
}
|
||||
|
||||
|
||||
feedbackList.add(feedbackItem);
|
||||
}
|
||||
}
|
||||
@@ -438,7 +442,7 @@ public class AiChatController extends BaseController {
|
||||
if (errorBody != null) {
|
||||
String errorBodyString = errorBody.string();
|
||||
errorMsg += " - 响应体: " + errorBodyString;
|
||||
|
||||
|
||||
// 尝试解析响应体中的错误信息
|
||||
try {
|
||||
JsonNode errorNode = mapper.readTree(errorBodyString);
|
||||
@@ -460,39 +464,59 @@ public class AiChatController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
/* @GetMapping("/history")
|
||||
public AjaxResult getHistoryMessagesToAdmin(@RequestParam String user,
|
||||
@RequestParam(required = false) String firstId,
|
||||
@RequestParam(defaultValue = "20") int limit) {
|
||||
//调用查询会话
|
||||
String conversation_id = conversations(user);
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 获取会话历史消息的端点
|
||||
* <p>
|
||||
* 该方法接收客户端发送的请求,获取指定会话的历史消息记录。
|
||||
* </p>
|
||||
* <p>
|
||||
* //@param conversation_id 会话ID
|
||||
*
|
||||
* @param conversationId 会话ID
|
||||
* @param user 用户标识符
|
||||
* @param firstId 当前页第一条聊天记录的ID,默认null
|
||||
* @param limit 一次请求返回多少条记录,默认20条
|
||||
* @param firstId 当前页第一条聊天记录的ID,默认null
|
||||
* @param limit 一次请求返回多少条记录,默认20条
|
||||
* @return AjaxResult 返回会话历史消息的结果
|
||||
*/
|
||||
@GetMapping("/history")
|
||||
public AjaxResult getHistoryMessages(
|
||||
@RequestParam(required = false) String conversationId,
|
||||
@RequestParam String user,
|
||||
//@RequestParam(required = false) String conversation_id,
|
||||
@RequestParam(required = false) String firstId,
|
||||
@RequestParam(defaultValue = "20") int limit) {
|
||||
try {
|
||||
/* // 获取当前用户
|
||||
String currentUsername = SecurityUtils.getLoginUser().getUsername();
|
||||
// 获取会话列表的id
|
||||
String conversation_id = conversations(currentUsername);
|
||||
if(conversation_id == null){
|
||||
return error("没有会话");
|
||||
}*/
|
||||
|
||||
|
||||
// 验证conversationId是否为空
|
||||
if (conversationId == null || conversationId.trim().isEmpty()) {
|
||||
return error("会话ID不能为空");
|
||||
}
|
||||
/*if (conversation_id == null || conversation_id.trim().isEmpty()) {
|
||||
return error("会话ID不能为空后端");
|
||||
}*/
|
||||
String conversation_id = "8fb04ac4-ac9f-470b-9ac4-fee1ebec6412";
|
||||
String currentUsername = SecurityUtils.getLoginUser().getUsername();
|
||||
System.out.println(currentUsername);
|
||||
System.out.println(conversation_id);
|
||||
|
||||
// 构建请求参数
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(DIFY_API_HISTORY_URL).newBuilder();
|
||||
urlBuilder.addQueryParameter("conversation_id", conversationId);
|
||||
urlBuilder.addQueryParameter("user", user);
|
||||
urlBuilder.addQueryParameter("conversation_id", conversation_id);
|
||||
urlBuilder.addQueryParameter("user", currentUsername);
|
||||
if (firstId != null) {
|
||||
urlBuilder.addQueryParameter("first_id", firstId);
|
||||
}
|
||||
urlBuilder.addQueryParameter("limit", String.valueOf(limit));
|
||||
|
||||
System.out.println(currentUsername);
|
||||
// 构建HTTP请求
|
||||
Request request = new Request.Builder()
|
||||
.url(urlBuilder.build())
|
||||
@@ -537,14 +561,20 @@ public class AiChatController extends BaseController {
|
||||
* @param sortBy 排序字段,默认 -updated_at (按更新时间倒序排列)
|
||||
* @return AjaxResult 返回会话列表的结果
|
||||
*/
|
||||
@GetMapping("/conversations")
|
||||
public AjaxResult getConversations(
|
||||
/*@GetMapping("/getConversationsToUser")
|
||||
public AjaxResult getConversationsToUser(
|
||||
@RequestParam String user,
|
||||
@RequestParam(required = false) String lastId,
|
||||
@RequestParam(defaultValue = "20") int limit,
|
||||
@RequestParam(defaultValue = "-updated_at") String sortBy) {
|
||||
try {
|
||||
// 参数校验
|
||||
// 获取当前用户
|
||||
String currentUsername = SecurityUtils.getLoginUser().getUsername();
|
||||
// 获取会话列表的id
|
||||
String conversation_id = conversations(currentUsername);
|
||||
|
||||
return AjaxResult.success(conversation_id);*/
|
||||
/*// 参数校验
|
||||
if (user == null || user.trim().isEmpty()) {
|
||||
return error("用户标识不能为空");
|
||||
}
|
||||
@@ -606,13 +636,216 @@ public class AiChatController extends BaseController {
|
||||
result.put("data", data);
|
||||
|
||||
return success(result);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}*/
|
||||
/* } catch (IOException e) {
|
||||
return error("获取会话列表失败: " + e.getMessage());
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
// 权限标识为辅导员
|
||||
@GetMapping("/getMessagesToAdmin")
|
||||
public AjaxResult getMessagesToAdmin(@RequestParam String user,
|
||||
@RequestParam(required = false) String lastId,
|
||||
@RequestParam(defaultValue = "20") int limit,
|
||||
@RequestParam(defaultValue = "-updated_at") String sortBy) {
|
||||
try {
|
||||
List<ConversationDTO> conversations = getConversations(user);
|
||||
if (conversations == null || conversations.isEmpty()) {
|
||||
return AjaxResult.error("暂无会话记录");
|
||||
}
|
||||
String conversation_id = conversations.get(0).getId();
|
||||
System.out.println(conversation_id);
|
||||
return success(getConversationHistoryMessages(conversation_id, user, null, 20));
|
||||
} catch (IOException e) {
|
||||
return AjaxResult.error("网络请求失败,请稍后重试",error());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
// 权限标识为学生
|
||||
@GetMapping("/getMessagesToUser")
|
||||
public AjaxResult getMessagesToUser(@RequestParam(required = false) String lastId,
|
||||
@RequestParam(defaultValue = "20") int limit,
|
||||
@RequestParam(defaultValue = "-updated_at") String sortBy) {
|
||||
try {
|
||||
String user = SecurityUtils.getLoginUser().getUsername();
|
||||
List<ConversationDTO> conversations = getConversations(user);
|
||||
if (conversations == null || conversations.isEmpty()) {
|
||||
return AjaxResult.error("暂无会话记录");
|
||||
}
|
||||
String conversation_id = conversations.get(0).getId();
|
||||
return success(getConversationHistoryMessages(conversation_id, user, null, 20));
|
||||
} catch (IOException e) {
|
||||
return AjaxResult.error("网络请求失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 获取会话列表
|
||||
* */
|
||||
public List<ConversationDTO> getConversations(String user, String lastId, int limit, String sortBy) throws IOException {
|
||||
// 构建带查询参数的 URL
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(DIFY_CONVERSATIONS_URL).newBuilder();
|
||||
urlBuilder.addQueryParameter("user", user);
|
||||
if (lastId != null && !lastId.trim().isEmpty()) {
|
||||
urlBuilder.addQueryParameter("last_id", lastId);
|
||||
}
|
||||
urlBuilder.addQueryParameter("limit", String.valueOf(limit));
|
||||
urlBuilder.addQueryParameter("sort_by", sortBy);
|
||||
|
||||
// 构建请求
|
||||
Request request = new Request.Builder()
|
||||
.url(urlBuilder.build())
|
||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
||||
.get()
|
||||
.build();
|
||||
|
||||
// 执行请求
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IOException("Dify API 请求失败: " + response.code() + " " + response.message());
|
||||
}
|
||||
|
||||
// 读取响应体
|
||||
String responseBodyString = response.body().string();
|
||||
JsonNode rootNode = mapper.readTree(responseBodyString);
|
||||
|
||||
// 提取 data 数组
|
||||
JsonNode dataArray = rootNode.path("data");
|
||||
List<ConversationDTO> conversations = new ArrayList<>();
|
||||
|
||||
if (dataArray.isArray()) {
|
||||
for (JsonNode node : dataArray) {
|
||||
ConversationDTO dto = new ConversationDTO();
|
||||
dto.setId(node.path("id").asText(null));
|
||||
dto.setName(node.path("name").asText(null));
|
||||
dto.setIntroduction(node.path("introduction").asText(null));
|
||||
|
||||
// 处理时间戳(假设接口返回的是 Unix 时间戳,单位:秒)
|
||||
long createdAt = node.path("created_at").asLong(0L);
|
||||
long updatedAt = node.path("updated_at").asLong(0L);
|
||||
|
||||
dto.setCreated_at(createdAt > 0 ? new Date(createdAt * 1000) : null);
|
||||
dto.setUpdated_at(updatedAt > 0 ? new Date(updatedAt * 1000) : null);
|
||||
|
||||
conversations.add(dto);
|
||||
}
|
||||
}
|
||||
|
||||
return conversations;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ConversationDTO> getConversations(String user) throws IOException {
|
||||
return getConversations(user, null, 0, null);
|
||||
}
|
||||
|
||||
public List<ConversationDTO> getConversations(String user, String lastId) throws IOException {
|
||||
return getConversations(user, lastId, 0, null);
|
||||
}
|
||||
|
||||
public List<ConversationDTO> getConversations(String user, Integer limit, String sortBy) throws IOException {
|
||||
return getConversations(user, null, limit, sortBy);
|
||||
}
|
||||
|
||||
|
||||
//获取历史消息
|
||||
private Map<String, Object> getConversationHistoryMessages(
|
||||
String conversationId,
|
||||
String user,
|
||||
String firstId,
|
||||
Integer limit) throws ServiceException {
|
||||
|
||||
// 参数校验
|
||||
if (conversationId == null || conversationId.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("conversationId 不能为空");
|
||||
}
|
||||
if (user == null || user.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("user 不能为空");
|
||||
}
|
||||
|
||||
conversationId = conversationId.trim();
|
||||
user = user.trim();
|
||||
int finalLimit = limit != null && limit > 0 ? Math.min(limit, 100) : 20;
|
||||
|
||||
// 构建请求 URL
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(DIFY_API_HISTORY_URL).newBuilder();
|
||||
urlBuilder.addQueryParameter("conversation_id", conversationId);
|
||||
urlBuilder.addQueryParameter("user", user);
|
||||
if (firstId != null && !firstId.trim().isEmpty()) {
|
||||
urlBuilder.addQueryParameter("first_id", firstId.trim());
|
||||
}
|
||||
urlBuilder.addQueryParameter("limit", String.valueOf(finalLimit));
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(urlBuilder.build())
|
||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
||||
.addHeader("Accept", "application/json")
|
||||
.get()
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
String body = response.body() != null ? response.body().string() : "No body";
|
||||
String msg = String.format("HTTP %d %s - %s", response.code(), response.message(), body);
|
||||
throw new ServiceException("请求失败");
|
||||
}
|
||||
|
||||
ResponseBody responseBody = response.body();
|
||||
if (responseBody == null) {
|
||||
return emptyResult(finalLimit);
|
||||
}
|
||||
|
||||
String responseBodyString = responseBody.string();
|
||||
JsonNode rootNode;
|
||||
try {
|
||||
rootNode = mapper.readTree(responseBodyString);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new ServiceException("响应数据格式错误");
|
||||
}
|
||||
|
||||
// 提取 data 数组
|
||||
List<Map<String, Object>> data = new ArrayList<>();
|
||||
JsonNode dataArray = rootNode.get("data");
|
||||
if (dataArray != null && dataArray.isArray()) {
|
||||
data = mapper.convertValue(dataArray, new TypeReference<List<Map<String, Object>>>() {
|
||||
});
|
||||
}
|
||||
|
||||
// 提取 has_more
|
||||
boolean hasMore = rootNode.path("has_more").asBoolean(false);
|
||||
|
||||
// ✅ 直接构建前端需要的返回结构
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("data", data);
|
||||
result.put("has_more", hasMore);
|
||||
result.put("limit", finalLimit);
|
||||
|
||||
return result; // 直接返回,调用方直接丢给前端
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("网络请求失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:返回空结果
|
||||
private Map<String, Object> emptyResult(int limit) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("data", new ArrayList<>());
|
||||
result.put("has_more", false);
|
||||
result.put("limit", limit);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*//**
|
||||
* 文件上传接口
|
||||
* <p>
|
||||
* 上传文件并在发送消息时使用,可实现图文多模态理解。支持应用程序所支持的所有格式。
|
||||
@@ -622,7 +855,7 @@ public class AiChatController extends BaseController {
|
||||
* @param file 要上传的文件
|
||||
* @param user 用户标识,用于定义终端用户的身份
|
||||
* @return 包含文件信息的统一响应结果
|
||||
*/
|
||||
*//*
|
||||
@PostMapping("/files/upload")
|
||||
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam("user") String user) {
|
||||
@@ -670,5 +903,5 @@ public class AiChatController extends BaseController {
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("文件上传异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
@@ -0,0 +1,53 @@
|
||||
package com.srs.teacher.domain.dto;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class ConversationDTO {
|
||||
private String id; // 会话 ID
|
||||
private String name; // 会话名称,默认由大语言模型生成。
|
||||
private String introduction; // 开场白
|
||||
private Date created_at; // 创建时间
|
||||
private Date updated_at; // 更新时间
|
||||
|
||||
// Getters and Setters for the properties
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getIntroduction() {
|
||||
return introduction;
|
||||
}
|
||||
|
||||
public void setIntroduction(String introduction) {
|
||||
this.introduction = introduction;
|
||||
}
|
||||
|
||||
public Date getCreated_at() {
|
||||
return created_at;
|
||||
}
|
||||
|
||||
public void setCreated_at(Date created_at) {
|
||||
this.created_at = created_at;
|
||||
}
|
||||
|
||||
public Date getUpdated_at() {
|
||||
return updated_at;
|
||||
}
|
||||
|
||||
public void setUpdated_at(Date updated_at) {
|
||||
this.updated_at = updated_at;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user