当前位置: 首页 > web >正文

基于Spring Boot + Vue 项目中引入deepseek方法

准备工作
在开始调用 DeepSeek API 之前,你需要完成以下准备工作:

1.访问 DeepSeek 官网,注册一个账号。

2.获取 API 密钥:登录 DeepSeek 平台,进入 API 管理 页面。创建一个新的 API 密钥(API Key),并妥善保存。

3.阅读 API 文档:

访问 DeepSeek 的 API 文档,了解支持的 API 端点、请求参数和返回格式


 开始调用                 

1.前端只需要简单的渲染页面渲染,将所需问题传递到后端,页面效果:

代码:

<template><div class="total"><div class="chat-container"><div class="chat-header"><h1>快来和我聊天吧~~~</h1></div><div class="chat-messages" ref="messagesContainer"><divv-for="(message, index) in messages":key="index":class="['chat-message', message.sender]"><div v-html="formatContent(message.content)"></div></div></div><div class="chat-input"><inputtype="text"v-model="inputText"placeholder="输入消息..."@keydown.enter="sendMessage":disabled="isLoading"/><button @click="sendMessage" :disabled="!canSend"><svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" /></svg>发送</button></div></div></div>
</template><script>
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\*(.*?)\*/g, "<em>$1</em>").replace(/`(.*?)`/g, "<code>$1</code>");},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;}});},},
};
</script><style scoped>
/* 新增的样式 */
/* 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;
}
</style>

后端,这里并没有将历史记录保存到数据库,如果有需要根据实际情况自行添加:

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 {@Resourceprivate 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<Object, ArrayList<Map<String, String>>> sessionHistory = new ConcurrentHashMap<Object, ArrayList<Map<String, String>>>();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*/@Overridepublic SseEmitter chat(String userId,String question) {SseEmitter emitter = new SseEmitter(-1L);executorService.execute(() -> {try {log.info("流式回答开始, 问题: {}", question);// 获取当前用户的对话历史ArrayList<Map<String, String>> messages = sessionHistory.getOrDefault(userId, new ArrayList<Map<String, String>>());// 添加用户的新问题到对话历史Map<String, String> userMessage = new HashMap<>();userMessage.put("role", "user");userMessage.put("content", question);Map<String, String> systemMessage = new HashMap<>();systemMessage.put("role", "system");systemMessage.put("content", "senfel的AI助手");messages.add(userMessage);messages.add(systemMessage);// 调用 DeepSeek APItry (CloseableHttpClient client = HttpClients.createDefault()) {HttpPost request = new HttpPost(dsUrl);request.setHeader("Content-Type", "application/json");request.setHeader("Authorization", "Bearer " + dsKey);Map<String, Object> 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<String, String> 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;}
}

最终效果:

http://www.xdnf.cn/news/3207.html

相关文章:

  • PostgreSQL事务与并发清理
  • 阿里云服务迁移实战: 06-切换DNS
  • scGPT-spatial:持续预训练scGPT用于空间转录组
  • 【diffusers 进阶之 PEFT 入门(五)】LoRA 权重如何接着训练?踩坑总结和解决方案
  • 在宝塔面板中安装OpenJDK-17的三种方法
  • K8S - 从零构建 Docker 镜像与容器
  • OpenCV 图形API(73)图像与通道拼接函数-----执行 查找表操作图像处理函数LUT()
  • AdaBoost算法的原理及Python实现
  • Vue ui初始化项目并使用iview写一个菜单导航
  • BUUCTF——Fakebook 1
  • UE 材质 条纹循环发光
  • Android compileSdkVersion、minSdkVersion、targetSdkVersion的关系以及和Unity的关系
  • Qwen3本地化部署,准备工作:SGLang
  • K8S - 从单机到集群 - 核心对象与实战解析
  • 同时启动俩个tomcat压缩版
  • C# 在VS2022中开发常用设置
  • Python 爬取微店商品列表接口(item_search)的实战指南
  • 如何在Windows上实现MacOS中的open命令
  • 网工_ICMP协议
  • Linux-04-用户管理命令
  • Java List分页工具
  • 排序算法——选择排序
  • 微格式:为Web内容赋予语义的力量
  • 【Linux 网络】网络工具ifconfig和iproute/iproute2工具详解
  • 端到端观测分析:从前端负载均衡到后端服务
  • 进程、线程、进程间通信Unix Domain Sockets (UDS)
  • 《操作系统真象还原》第十一章——用户进程
  • Spring 框架中的常见注解讲解
  • Qt窗口关闭特效:自底而上逐渐消失
  • google colab设置python环境为python3.7