修改了历史列表和历史记录的方法接口

This commit is contained in:
2025-08-12 15:11:36 +08:00
parent 7654e806e3
commit 96cd764bb0
2 changed files with 327 additions and 41 deletions

View File

@@ -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());
}
}
}*/
}

View File

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