集成ai部分

This commit is contained in:
sh
2026-01-05 15:39:01 +08:00
parent deb775ff5c
commit a31fc4cc72
23 changed files with 1885 additions and 1 deletions

View File

@@ -0,0 +1,235 @@
package com.ruoyi.cms.controller.app;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.cms.domain.ai.AiChatHistory;
import com.ruoyi.cms.domain.chat.ChatRequest;
import com.ruoyi.cms.service.AiChatHistoryService;
import com.ruoyi.common.annotation.BussinessLog;
import com.ruoyi.cms.config.ChatClient;
import com.ruoyi.cms.config.ChatConfig;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.SiteSecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static com.ruoyi.common.enums.BusinessType.QUERY;
@RestController
@RequestMapping(value = "/app/chat")
@Slf4j
@Api(tags = "移动端:ai对话")
public class ChatController extends BaseController {
@Autowired
ChatClient chatClient;
@Autowired
ChatConfig chatConfig;
@Autowired
AiChatHistoryService aiChatHistoryService;
//private final ExecutorService executor = Executors.newCachedThreadPool();
// 可优化线程池配置,避免无限创建线程
private final ExecutorService executor = new ThreadPoolExecutor(
2, // 核心线程数
10, // 最大线程数
30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(50), // 任务队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略(避免任务丢失)
);
@BussinessLog(title = "查询用户的聊天历史记录",businessType = QUERY)
@ApiOperation("查询用户的聊天历史记录")
@GetMapping(value = "/getHistory")
public AjaxResult getChatHistoryList(AiChatHistory chatHistory) {
return AjaxResult.success(aiChatHistoryService.getList(chatHistory));
}
@BussinessLog(title = "查询用户的聊天详情",businessType = QUERY)
@ApiOperation("查询用户的聊天详情")
@GetMapping(value = "/detail")
public AjaxResult getChatDetail(ChatRequest request) {
return AjaxResult.success(aiChatHistoryService.getDetailList(request.getSessionId()));
}
// 处理前端聊天请求,返回 SSE 发射器
@ApiOperation("用户AI聊天")
@PostMapping("/chat")
@ResponseBody
@BussinessLog(title = "用户AI聊天",businessType = QUERY)
public SseEmitter chatStream(@RequestBody ChatRequest request) {
// 设置超时时间30分钟
SseEmitter emitter = new SseEmitter(1800000L);
long userId = 0;
if(ObjectUtil.isNotEmpty(request.getUserId())&&request.getUserId()!=0){
userId = request.getUserId();
}else{
try {
userId = SiteSecurityUtils.getLoginUser().getUserId();
}catch (Exception e) {
}
}
JSONObject contentObject = new JSONObject();
contentObject.put("content",request.getData());
contentObject.put("role","user");
JSONArray array = aiChatHistoryService.getChatHistoryData(request.getSessionId());
if(array==null||array.isEmpty()||array.size()==0){
array = new JSONArray();
}
array.add(contentObject);
request.setMessages(array);
List<String> answerList = new ArrayList<>();
long[] timeStart = {0};
// 异步处理请求并推送数据
executor.submit(() -> {
try {
// 2. 调用ChatClient的流式聊天方法正确传递参数
chatClient.sendStreamingChat(
request,
new ChatClient.StreamCallback() { // 使用内部类完整路径
@Override
public void onData(String chunk) {
try {
if(timeStart[0] == 0){
timeStart[0] = System.currentTimeMillis();
}
// 1. 处理SSE协议格式
String processedChunk = chunk.trim();
// 解析返回的chunk数据
String content = parseChatChunk(processedChunk);
if (StringUtils.isNotEmpty(content)) {
answerList.add(content);
}
emitter.send(processedChunk, MediaType.TEXT_EVENT_STREAM);
} catch (IOException e) {
emitter.completeWithError(e);
}
}
@Override
public void onComplete() {
emitter.complete();
}
@Override
public void onError(Throwable e) {
try {
emitter.send(SseEmitter.event()
.data("{\"status\":\"error\",\"message\":\"" + e.getMessage() + "\"}")
.name("error"));
} catch (IOException ex) {
// 记录日志
} finally {
emitter.completeWithError(e);
}
}
}
);
} catch (Exception e) {
emitter.completeWithError(e);
}
});
// 处理连接关闭
long finalUserId = userId;
emitter.onCompletion(() -> {
// 此处仅做资源清理,不关闭线程池
log.info("连接关闭,清理资源");
long timeEnd = System.currentTimeMillis();
double duration = (timeEnd - timeStart[0]) / 1000.0;
AiChatHistory chatHistory = new AiChatHistory();
chatHistory.setChatId(request.getSessionId());
chatHistory.setUserId((finalUserId==0)?null:finalUserId);
chatHistory.setAppId(chatConfig.getAppId());
chatHistory.setDataId(request.getDataId());
chatHistory.setTitle(request.getData());
chatHistory.setAnswerStringList(answerList);
chatHistory.setDurationSeconds(duration);
aiChatHistoryService.saveChatHistory(chatHistory);
});
return emitter;
}
@BussinessLog(title = "提供推测出的问询建议",businessType = QUERY)
@ApiOperation("提供推测出的问询建议")
@PostMapping(value = "/guest")
public AjaxResult getChatGuest(@RequestBody ChatRequest request) throws IOException {
JSONArray array = aiChatHistoryService.getChatHistoryData(request.getSessionId());
request.setMessages(array);
String result = chatClient.sendChatGuest(request);
try {
String[] strList = result.split("");
List<String> list = new ArrayList<>();
for(String str:strList){
if(StringUtils.isNotEmpty(str)){
str = str+"";
list.add(str);
}
}
return AjaxResult.success(list);
}catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
// 解析返回的流式数据
private String parseChatChunk(String chunk) {
try {
String processed = chunk.trim();
if (processed.startsWith("data:")) {
processed = processed.substring("data:".length()).trim();
}
if (processed.isEmpty() || "[DONE]".equals(processed)) {
return null;
}
JSONObject json = JSONObject.parseObject(processed);
String choices = json.getString("choices");
if (choices != null && !choices.trim().isEmpty()) {
JSONArray jsonArray = JSONArray.parseArray(choices);
json = JSONObject.parseObject(jsonArray.getString(0));
json = json.getJSONObject("delta");
String content = json.getString("content");// 消息内容
return content;
}
return null;
} catch (Exception e) {
return null;
}
}
@PreDestroy
public void destroy() {
executor.shutdown(); // 应用退出前关闭线程池
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制关闭未完成的任务
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
executor.shutdownNow();
log.error(e.getMessage());
}
}
}