ai对话修复
continuous-integration/drone Build is passing
Details
continuous-integration/drone Build is passing
Details
parent
accbc9e869
commit
1c61edb659
@ -0,0 +1,39 @@
|
||||
package com.example.venue_reservation_service.controller;
|
||||
|
||||
|
||||
import com.example.venue_reservation_service.entity.ChatRequest;
|
||||
import com.example.venue_reservation_service.service.ChatService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/chat")
|
||||
@CrossOrigin
|
||||
public class ChatController {
|
||||
|
||||
@Resource
|
||||
private ChatService chatService;
|
||||
|
||||
@PostMapping(value = "/ask", produces = MediaType.TEXT_PLAIN_VALUE)
|
||||
public void chat(@RequestBody ChatRequest request, HttpServletRequest httpRequest, HttpServletResponse response) throws IOException {
|
||||
|
||||
// 设置流式响应头
|
||||
response.setContentType("text/event-stream");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
|
||||
// 获取输出流
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
|
||||
// 处理流式响应
|
||||
chatService.streamChatResponse(request, outputStream, httpRequest);
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package com.example.venue_reservation_service.controller;
|
||||
|
||||
import com.example.venue_reservation_service.domain.VeMessage;
|
||||
import com.example.venue_reservation_service.domain.VeSession;
|
||||
import com.example.venue_reservation_service.service.VeMessageService;
|
||||
import com.example.venue_reservation_service.service.VeSessionService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/session")
|
||||
@Api("ai会话管理模块")
|
||||
@CrossOrigin
|
||||
public class SessionController {
|
||||
|
||||
@Resource
|
||||
private VeSessionService sessionService;
|
||||
|
||||
@Resource
|
||||
private VeMessageService messageService;
|
||||
|
||||
@ApiOperation("创建新会话")
|
||||
@PostMapping("/create")
|
||||
public ResponseEntity<VeSession> createSession(@RequestBody VeSession session) {
|
||||
VeSession savedSession = sessionService.saveSession(session);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(savedSession);
|
||||
}
|
||||
|
||||
@ApiOperation("获取用户会话历史")
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<List<VeSession>> getUserSessions(@RequestParam(name = "userId") Integer userId) {
|
||||
List<VeSession> sessions = sessionService.getSessionsByUserId(userId);
|
||||
return ResponseEntity.ok(sessions);
|
||||
}
|
||||
|
||||
@ApiOperation("获取会话详情")
|
||||
@GetMapping("/{sessionId}")
|
||||
public ResponseEntity<VeSession> getSession(@PathVariable("sessionId") Integer sessionId) {
|
||||
VeSession session = sessionService.getById(sessionId);
|
||||
if (session != null) {
|
||||
return ResponseEntity.ok(session);
|
||||
} else {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation("获取指定会话的所有消息")
|
||||
@GetMapping("/message/{sessionId}")
|
||||
public ResponseEntity<List<VeMessage>> getSessionMessages(@PathVariable("sessionId") Integer sessionId) {
|
||||
List<VeMessage> messages = messageService.getMessagesBySessionId(sessionId);
|
||||
return ResponseEntity.ok(messages);
|
||||
}
|
||||
|
||||
@ApiOperation("创建新消息")
|
||||
@PostMapping("/message/create")
|
||||
public ResponseEntity<VeMessage> createMessage(@RequestBody VeMessage message) {
|
||||
VeMessage savedMessage = messageService.saveMessage(message);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(savedMessage);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.example.venue_reservation_service.entity;
|
||||
|
||||
import lombok.Data;
|
||||
@Data
|
||||
public class ChatRequest {
|
||||
|
||||
private String content;
|
||||
|
||||
private String sessionToken;
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.example.venue_reservation_service.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ChatResponse {
|
||||
|
||||
private String content;
|
||||
|
||||
private String sessionToken;
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.example.venue_reservation_service.service;
|
||||
|
||||
import com.example.venue_reservation_service.entity.ChatRequest;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
// ChatService.java
|
||||
public interface ChatService {
|
||||
void streamChatResponse(ChatRequest request, ServletOutputStream outputStream, HttpServletRequest httpRequest);
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
package com.example.venue_reservation_service.service.impl;
|
||||
|
||||
import com.example.venue_reservation_service.domain.VeMessage;
|
||||
import com.example.venue_reservation_service.domain.VeSession;
|
||||
import com.example.venue_reservation_service.entity.ChatRequest;
|
||||
import com.example.venue_reservation_service.mapper.VeMessageMapper;
|
||||
import com.example.venue_reservation_service.mapper.VeSessionMapper;
|
||||
import com.example.venue_reservation_service.service.ChatService;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
|
||||
@Resource
|
||||
private VeSessionMapper sessionMapper;
|
||||
|
||||
@Resource
|
||||
private VeMessageMapper messageMapper;
|
||||
@Value("${internai.api-url}")
|
||||
private String apiUrl;
|
||||
@Value("${internai.api-key}")
|
||||
private String apiKey;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void streamChatResponse(ChatRequest request, ServletOutputStream outputStream, HttpServletRequest httpRequest) {
|
||||
// 获取或创建会话
|
||||
VeSession session = getOrCreateSession(request.getSessionToken(), httpRequest);
|
||||
String sessionToken = session.getSessionToken();
|
||||
|
||||
// 保存用户消息
|
||||
saveMessage(request.getContent(), "user", session.getId());
|
||||
|
||||
// 构建完整对话历史
|
||||
List<Map<String, String>> messages = buildMessages(session.getId());
|
||||
|
||||
// 调用第三方API
|
||||
streamApiResponse(messages, outputStream, sessionToken, session.getId());
|
||||
|
||||
// 更新会话结束时间
|
||||
updateSessionEndTime(session);
|
||||
}
|
||||
|
||||
private VeSession getOrCreateSession(String sessionToken, HttpServletRequest request) {
|
||||
if (sessionToken != null && !sessionToken.isEmpty()) {
|
||||
VeSession session = sessionMapper.selectBySessionToken(sessionToken);
|
||||
if (session != null) {
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新会话
|
||||
VeSession newSession = new VeSession();
|
||||
newSession.setUserIp(request.getRemoteAddr());
|
||||
newSession.setStartTime(LocalDateTime.now());
|
||||
newSession.setSessionToken(UUID.randomUUID().toString());
|
||||
newSession.setTitle("New Chat");
|
||||
newSession.setUserId(1);
|
||||
sessionMapper.insert(newSession);
|
||||
return newSession;
|
||||
}
|
||||
|
||||
private void saveMessage(String content, String sender, Integer sessionId) {
|
||||
VeMessage message = new VeMessage();
|
||||
message.setSessionId(sessionId);
|
||||
message.setContent(content);
|
||||
message.setSender(sender);
|
||||
message.setCreatedAt(LocalDateTime.now());
|
||||
|
||||
messageMapper.insert(message);
|
||||
}
|
||||
|
||||
private List<Map<String, String>> buildMessages(Integer sessionId) {
|
||||
List<VeMessage> history = messageMapper.selectMessagesBySessionId(sessionId);
|
||||
|
||||
return history.stream().map(msg -> {
|
||||
Map<String, String> messageMap = new HashMap<>();
|
||||
messageMap.put("role", msg.getSender().equals("user") ? "user" : "assistant");
|
||||
messageMap.put("content", msg.getContent());
|
||||
return messageMap;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void streamApiResponse(List<Map<String, String>> messages,
|
||||
ServletOutputStream outputStream,
|
||||
String sessionToken,
|
||||
Integer sessionId) {
|
||||
|
||||
StringBuilder assistantResponse = new StringBuilder();
|
||||
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(apiUrl);
|
||||
httpPost.setHeader("Authorization", "Bearer " + apiKey);
|
||||
httpPost.setHeader("Content-Type", "application/json");
|
||||
|
||||
// 构建请求体
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("model", "internlm3-latest");
|
||||
requestBody.put("messages", messages);
|
||||
requestBody.put("temperature", 0.8);
|
||||
requestBody.put("top_p", 0.9);
|
||||
requestBody.put("stream", true);
|
||||
|
||||
httpPost.setEntity(new StringEntity(new ObjectMapper().writeValueAsString(requestBody)));
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
InputStream contentStream = response.getEntity().getContent();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(contentStream))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("data: ")) {
|
||||
String json = line.substring(6).trim();
|
||||
if (json.equals("[DONE]")) break;
|
||||
|
||||
JsonNode data = new ObjectMapper().readTree(json);
|
||||
JsonNode choices = data.path("choices");
|
||||
if (!choices.isEmpty()) {
|
||||
String chunk = choices.get(0).path("delta").path("content").asText();
|
||||
if (!chunk.isEmpty()) {
|
||||
assistantResponse.append(chunk);
|
||||
outputStream.write(chunk.getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存助手回复
|
||||
if (!assistantResponse.isEmpty()) {
|
||||
saveMessage(assistantResponse.toString(), "assistant", sessionId);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("API call failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSessionEndTime(VeSession session) {
|
||||
session.setEndTime(LocalDateTime.now());
|
||||
sessionMapper.updateById(session);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue