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

06-01 1440阅读

准备工作

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

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

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

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

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

3.阅读 API 文档:

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

 开始调用                 

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

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

代码:

  
  
    
      
        

快来和我聊天吧~~~

发送 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;
    }
}

最终效果:

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

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码