基于Spring Boot + Vue 项目中引入deepseek方法
准备工作
在开始调用 DeepSeek API 之前,你需要完成以下准备工作:
1.访问 DeepSeek 官网,注册一个账号。
2.获取 API 密钥:登录 DeepSeek 平台,进入 API 管理 页面。创建一个新的 API 密钥(API Key),并妥善保存。
3.阅读 API 文档:
访问 DeepSeek 的 API 文档,了解支持的 API 端点、请求参数和返回格式
开始调用
1.前端只需要简单的渲染页面渲染,将所需问题传递到后端,页面效果:
代码:
快来和我聊天吧~~~
发送 import { getToken } from "@/utils/storage.js"; const token = getToken(); export default { data() { return { inputText: "", messages: [], isLoading: false, }; }, computed: { canSend() { return this.inputText.trim() && !this.isLoading; }, }, methods: { formatContent(text) { // 基础Markdown转换 return text .replace(/&/g, "&") .replace(//g, ">") .replace(/\*\*(.*?)\*\*/g, "$1") .replace(/\*(.*?)\*/g, "$1") .replace(/`(.*?)`/g, "$1"); }, async sendMessage() { if (!this.canSend) return; const question = this.inputText.trim(); this.inputText = ""; // 添加用户消息 this.messages.push({ sender: "user", content: question, }); // 添加初始bot消息 const botMessage = { sender: "bot", content: "", }; this.messages.push(botMessage); this.scrollToBottom(); try { this.isLoading = true; const response = await fetch( "http://localhost:21090/api/online-travel-sys/v1.0/deepSeek/chat", { method: "POST", headers: { "Content-Type": "application/json", token: token, }, body: JSON.stringify({ question }), } ); if (!response.ok || !response.body) { throw new Error("请求失败"); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // 处理SSE格式数据 let position; while ((position = buffer.indexOf("\n\n")) >= 0) { const chunk = buffer.slice(0, position); buffer = buffer.slice(position + 2); const event = this.parseSSEEvent(chunk); if (event && event.data) { if (event.data === "[DONE]") { break; // 流结束 } botMessage.content += event.data; this.scrollToBottom(); } } } } catch (error) { botMessage.content = "请求出错,请稍后重试"; } finally { this.isLoading = false; this.scrollToBottom(); } }, parseSSEEvent(chunk) { const lines = chunk.split("\n"); const event = {}; lines.forEach((line) => { if (line.startsWith("data:")) { event.data = line.replace(/^data:\s*/, ""); } }); return event; }, scrollToBottom() { this.$nextTick(() => { const container = this.$refs.messagesContainer; if (container) { container.scrollTop = container.scrollHeight; } }); }, }, }; /* 新增的样式 */ /* body 样式 */ html, body { margin: 0; padding: 0; height: 100%; /* 确保高度占满整个视口 */ } .total { width: 100%; height: 100%; /* 继承父容器的高度 */ display: flex; justify-content: center; align-items: center; background-image: url("../../assets/img/seek.jpg"); background-size: cover; background-position: center; } .chat-container { width: 100%; max-width: 800px; height: 75vh; background: rgba(255, 255, 255, 0.5); border-radius: 20px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); display: flex; flex-direction: column; overflow: hidden; margin: auto; /* 水平居中 */ margin-top: 95px; margin-bottom: 20px; } .chat-header { padding: 24px; background: linear-gradient(135deg, #497bf1 0%, #4874ed 100%); color: white; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .chat-header h1 { margin: 0; font-size: 17px; font-weight: 400; letter-spacing: -0.5px; } .chat-messages { flex: 1; padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; } .chat-message { max-width: 75%; padding: 16px 20px; border-radius: 20px; line-height: 1.5; animation: messageAppear 0.3s ease-out; position: relative; word-break: break-word; } .chat-message.user { background: linear-gradient(135deg, #5b8cff 0%, #3d6ef7 100%); color: white; align-self: flex-end; border-bottom-right-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); } .chat-message.bot { background: linear-gradient(135deg, #f0f8ff 0%, #e6f3ff 100%); color: #2d3748; align-self: flex-start; border-bottom-left-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); } .chat-input { padding: 20px; background: rgba(255, 255, 255, 0.9); border-top: 1px solid rgba(0, 0, 0, 0.05); display: flex; gap: 12px; } .chat-input input { flex: 1; padding: 14px 20px; border: 2px solid rgba(0, 0, 0, 0.1); border-radius: 16px; font-size: 1rem; transition: all 0.2s ease; background: rgba(255, 255, 255, 0.8); } .chat-input input:focus { outline: none; border-color: #5b8cff; box-shadow: 0 0 0 3px rgba(91, 140, 255, 0.2); } .chat-input button { padding: 12px 24px; border: none; border-radius: 16px; background: #5b8cff; color: white; font-size: 1rem; font-weight: 500; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; gap: 8px; } .chat-input button:hover:not(:disabled) { background: #406cff; transform: translateY(-1px); } .chat-input button:disabled { background: #c2d1ff; cursor: not-allowed; transform: none; } .chat-input button svg { width: 18px; height: 18px; fill: currentColor; } .typing-indicator { display: inline-flex; gap: 6px; padding: 12px 20px; background: linear-gradient(135deg, #f0f8ff 0%, #e6f3ff 100%); border-radius: 20px; } .typing-dot { width: 8px; height: 8px; background: rgba(0, 0, 0, 0.3); border-radius: 50%; animation: typing 1.4s infinite ease-in-out; } .typing-dot:nth-child(2) { animation-delay: 0.2s; } .typing-dot:nth-child(3) { animation-delay: 0.4s; } @keyframes messageAppear { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @keyframes typing { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } } @media (max-width: 640px) { .chat-container { height: 95vh; border-radius: 16px; } .chat-message { max-width: 85%; } } /* Markdown内容样式 */ .chat-message :deep(pre) { background: rgba(0, 0, 0, 0.05); padding: 12px; border-radius: 8px; overflow-x: auto; margin: 8px 0; } .chat-message :deep(code) { font-family: monospace; background: rgba(0, 0, 0, 0.08); padding: 2px 4px; border-radius: 4px; } .chat-message :deep(strong) { font-weight: 600; } .chat-message :deep(em) { font-style: italic; } .chat-message :deep(blockquote) { border-left: 3px solid #5b8cff; margin: 8px 0; padding-left: 12px; color: #666; } .chat-message :deep(a) { color: #5b8cff; text-decoration: none; border-bottom: 1px solid transparent; transition: all 0.2s; } .chat-message :deep(a:hover) { border-bottom-color: currentColor; }
后端,这里并没有将历史记录保存到数据库,如果有需要根据实际情况自行添加:
1.在yml文件中添加相关配置
ds: key: 填写在官方自己申请的key,需要一定费用但很少 url: https://api.deepseek.com/chat/completions
2.控制层:
package cn.kmbeast.controller; import cn.kmbeast.service.DsChatService; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import javax.annotation.Resource; /** * DsController * @author senfel * @version 1.0 * @date 2025/3/13 17:21 */ @RestController @RequestMapping("/deepSeek") @Slf4j public class DsController { @Resource private DsChatService dsChatService; /** * chat page * @param modelAndView * @author senfel * @date 2025/3/13 17:39 * @return org.springframework.web.servlet.ModelAndView */ @GetMapping() public ModelAndView chat(ModelAndView modelAndView) { modelAndView.setViewName("chat"); return modelAndView; } /** * chat * @param question * @author senfel * @date 2025/3/13 17:39 * @return org.springframework.web.servlet.mvc.method.annotation.SseEmitter */ @PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter chat(@RequestBody String question) { //TODO 默认用户ID,实际场景从token获取 String userId = "senfel"; return dsChatService.chat(userId, question); } }
3.service层:
package cn.kmbeast.service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; /** * DsChatService * @author senfel * @version 1.0 * @date 2025/4/13 17:30 */ public interface DsChatService { /** * chat * @param userId * @param question * @author senfel * @date 2025/3/13 17:30 * @return org.springframework.web.servlet.mvc.method.annotation.SseEmitter */ SseEmitter chat(String userId, String question); }
4.service实现:
package cn.kmbeast.service.impl; import cn.kmbeast.service.DsChatService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; 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.web.servlet.mvc.method.annotation.SseEmitter; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * DsChatServiceImpl * @author senfel * @version 1.0 * @date 2025/3/13 17:31 */ @Service @Slf4j public class DsChatServiceImpl implements DsChatService { @Value("${ds.key}") private String dsKey; @Value("${ds.url}") private String dsUrl; // 用于保存每个用户的对话历史 private final Map sessionHistory = new ConcurrentHashMap(); private final ExecutorService executorService = Executors.newCachedThreadPool(); private final ObjectMapper objectMapper = new ObjectMapper(); /** * chat * @param userId * @param question * @author senfel * @date 2025/3/13 17:36 * @return org.springframework.web.servlet.mvc.method.annotation.SseEmitter */ @Override public SseEmitter chat(String userId,String question) { SseEmitter emitter = new SseEmitter(-1L); executorService.execute(() -> { try { log.info("流式回答开始, 问题: {}", question); // 获取当前用户的对话历史 ArrayList messages = sessionHistory.getOrDefault(userId, new ArrayList()); // 添加用户的新问题到对话历史 Map userMessage = new HashMap(); userMessage.put("role", "user"); userMessage.put("content", question); Map systemMessage = new HashMap(); systemMessage.put("role", "system"); systemMessage.put("content", "senfel的AI助手"); messages.add(userMessage); messages.add(systemMessage); // 调用 DeepSeek API try (CloseableHttpClient client = HttpClients.createDefault()) { HttpPost request = new HttpPost(dsUrl); request.setHeader("Content-Type", "application/json"); request.setHeader("Authorization", "Bearer " + dsKey); Map requestMap = new HashMap(); requestMap.put("model", "deepseek-chat"); requestMap.put("messages", messages); requestMap.put("stream", true); String requestBody = objectMapper.writeValueAsString(requestMap); request.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8)); try (CloseableHttpResponse response = client.execute(request); BufferedReader reader = new BufferedReader( new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8))) { StringBuilder aiResponse = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { if (line.startsWith("data: ")) { System.err.println(line); String jsonData = line.substring(6); if ("[DONE]".equals(jsonData)) { break; } JsonNode node = objectMapper.readTree(jsonData); String content = node.path("choices") .path(0) .path("delta") .path("content") .asText(""); if (!content.isEmpty()) { emitter.send(content); aiResponse.append(content); // 收集 AI 的回复 } } } // 将 AI 的回复添加到对话历史 Map aiMessage = new HashMap(); aiMessage.put("role", "assistant"); aiMessage.put("content", aiResponse.toString()); messages.add(aiMessage); // 更新会话状态 sessionHistory.put(userId, messages); log.info("流式回答结束, 问题: {}", question); emitter.complete(); } } catch (Exception e) { log.error("处理 DeepSeek 请求时发生错误", e); emitter.completeWithError(e); } } catch (Exception e) { log.error("处理 DeepSeek 请求时发生错误", e); emitter.completeWithError(e); } }); return emitter; } }
最终效果:
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。