1.直接上代码-后端:
@RestController
@CrossOrigin(origins = "*")
public class DeepSeekController {private static final String API_URL = "https://api.deepseek.com/v1/chat/completions";private final ObjectMapper objectMapper = new ObjectMapper();@GetMapping(value = "/stream", produces = "text/event-stream")public SseEmitter streamChat(@RequestParam String message) {SseEmitter emitter = new SseEmitter(60_000L); OkHttpClient client = new OkHttpClient.Builder().connectTimeout(200, TimeUnit.SECONDS) .readTimeout(200, TimeUnit.SECONDS) .build();String jsonBody = buildRequestJson(message); Request request = new Request.Builder().url(API_URL).header("Authorization", "XXXXXXX").post(RequestBody.create(jsonBody, MediaType.get("application/json"))).build();client.newCall(request).enqueue(new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream()))) {String line;while ((line = reader.readLine()) != null) {if (line.startsWith("data: ")) {String content = parseContent(line.substring(6));emitter.send(SseEmitter.event().data(content));}}emitter.complete();}}@Overridepublic void onFailure(Call call, IOException e) {emitter.completeWithError(e);}});return emitter;}private String parseContent(String jsonLine) {try {System.out.println(jsonLine);JsonNode node = objectMapper.readTree(jsonLine);return node.path("choices").get(0).path("delta").path("content").asText();} catch (Exception e) {return "[解析错误]";}}private String buildRequestJson(String message) {return String.format("{\"model\":\"deepseek-chat\",\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"%s\"}]}",message.replace("\"", "\\\""));}
}
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.10.0</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.10.3</version></dependency>
2.前端
<!DOCTYPE html>
<meta charset="UTF-8"/>
<html>
<head><title>DeepSeek流式对话</title><link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet"><style>.chat-container {max-width: 800px;margin: 20px auto;background: #F9FAFB;border-radius: 12px;box-shadow: 0 8px 30px rgba(0,0,0,0.1);}.chat-messages {height: 70vh;padding: 20px;overflow-y: auto;}.message {margin: 12px 0;padding: 14px 20px;border-radius: 12px;max-width: 80%;animation: fadeIn 0.3s ease;}.user-message {background: #E3F2FD;margin-left: auto;border-bottom-right-radius: 4px;}.ai-message {background: #FFFFFF;box-shadow: 0 2px 8px rgba(0,0,0,0.05);border-bottom-left-radius: 4px;}@keyframes fadeIn {from { opacity: 0; transform: translateY(10px); }to { opacity: 1; transform: translateY(0); }}</style>
</head>
<body>
<div class="chat-container"><div class="chat-messages" id="chatMessages"><!-- 消息动态插入 --></div><div class="input-area"><input type="text" id="inputField" placeholder="输入您的问题..."><button id="sendBtn"><i class="mdi mdi-send"></i></button></div>
</div>
<script>document.getElementById('sendBtn').addEventListener('click', () => {const input = document.getElementById('inputField').value.trim();if (!input) return;document.getElementById('inputField').value = '';appendMessage(input, 'user');const aiMessageDiv = appendMessage('', 'ai');const eventSource = new EventSource(`/stream?message=${encodeURIComponent(input)}`);eventSource.onmessage = (e) => {aiMessageDiv.textContent += e.data;aiMessageDiv.scrollIntoView({ behavior: 'smooth' });};eventSource.onerror = () => {eventSource.close();aiMessageDiv.textContent += '(对话结束)';};});function appendMessage(content, type) {const container = document.getElementById('chatMessages');const div = document.createElement('div');div.className = `message ${type}-message`;div.textContent = content;container.appendChild(div);return div;}</script>
</body>
</html>