spring boot + vue 实现 deepseek 对话效果详细步骤
一、整体架构设计
我们需要构建一个前后端分离的应用:
- 后端:spring boot 提供 api 接口,处理与 ai 模型的交互
- 前端:vue 实现聊天界面,展示对话内容并发送用户输入
二、后端实现 (spring boot)
1. 创建 spring boot 项目
<!-- pom.xml 主要依赖 --> <dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-data-jpa</artifactid> </dependency> <dependency> <groupid>com.h2database</groupid> <artifactid>h2</artifactid> <scope>runtime</scope> </dependency> <!-- 如果需要持久化存储 --> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> </dependency> </dependencies>
2. 设计 api 接口
// 对话控制器 @restcontroller @requestmapping("/api/chat") public class chatcontroller { @autowired private chatservice chatservice; @postmapping("/message") public responseentity<chatresponse> sendmessage(@requestbody chatrequest request) { chatresponse response = chatservice.processmessage(request); return responseentity.ok(response); } @getmapping("/history/{sessionid}") public responseentity<list<chatmessage>> gethistory(@pathvariable string sessionid) { list<chatmessage> history = chatservice.getchathistory(sessionid); return responseentity.ok(history); } }
3. 定义数据传输对象
public class chatrequest { private string sessionid; private string message; // getter, setter } public class chatresponse { private string message; private string sessionid; // getter, setter } public class chatmessage { private string role; // "user" 或 "assistant" private string content; // getter, setter }
4. 实现对话服务
@service public class chatservice { // 可以使用 map 临时存储会话,生产环境建议使用数据库 private map<string, list<chatmessage>> chatsessions = new concurrenthashmap<>(); // 处理用户消息 public chatresponse processmessage(chatrequest request) { string sessionid = request.getsessionid(); if (sessionid == null || sessionid.isempty()) { sessionid = uuid.randomuuid().tostring(); } string usermessage = request.getmessage(); // 保存用户消息 chatmessage usermsg = new chatmessage("user", usermessage); // 调用 ai 模型 api string airesponse = callaiapi(usermessage, sessionid); // 保存 ai 回复 chatmessage aimsg = new chatmessage("assistant", airesponse); // 更新会话历史 chatsessions.computeifabsent(sessionid, k -> new arraylist<>()).add(usermsg); chatsessions.get(sessionid).add(aimsg); return new chatresponse(airesponse, sessionid); } // 获取聊天历史 public list<chatmessage> getchathistory(string sessionid) { return chatsessions.getordefault(sessionid, collections.emptylist()); } // 调用 ai 模型 api 的方法 private string callaiapi(string message, string sessionid) { // 这里实现与 deepseek api 的实际交互 // 可以使用 resttemplate 或 webclient // 示例代码: try { // 实际开发中替换为真实 api 调用 return "这是 ai 对 "" + message + "" 的回复"; } catch (exception e) { return "抱歉,我遇到了一些问题,请稍后再试。"; } } }
5. 配置 cors
@configuration public class webconfig implements webmvcconfigurer { @override public void addcorsmappings(corsregistry registry) { registry.addmapping("/api/**") .allowedorigins("http://localhost:5173") // vue 默认端口 .allowedmethods("get", "post", "put", "delete") .allowcredentials(true); } }
三、前端实现 (vue)
1. 创建 vue 项目
# 使用 npm npm create vue@latest my-chat-app cd my-chat-app npm install # 安装必要的依赖 npm install axios
2. 创建聊天组件
<!-- chatwindow.vue --> <template> <div class="chat-container"> <div class="chat-header"> <h2>ai 助手对话</h2> </div> <div class="chat-messages" ref="messagecontainer"> <div v-for="(msg, index) in messages" :key="index" :class="['message', msg.role === 'user' ? 'user-message' : 'assistant-message']"> <div class="message-content"> {{ msg.content }} </div> </div> <div v-if="loading" class="message assistant-message"> <div class="message-content"> <span class="loading-dots">思考中<span>.</span><span>.</span><span>.</span></span> </div> </div> </div> <div class="chat-input"> <textarea v-model="userinput" placeholder="请输入您的问题..." @keyup.enter.ctrl="sendmessage" ></textarea> <button @click="sendmessage" :disabled="loading || !userinput.trim()"> 发送 </button> </div> </div> </template> <script> import axios from 'axios'; export default { name: 'chatwindow', data() { return { messages: [], userinput: '', loading: false, sessionid: this.generatesessionid(), apibaseurl: 'http://localhost:8080/api/chat' }; }, methods: { generatesessionid() { return math.random().tostring(36).substring(2, 15); }, async sendmessage() { if (!this.userinput.trim() || this.loading) return; const usermessage = this.userinput.trim(); this.messages.push({ role: 'user', content: usermessage }); this.userinput = ''; this.loading = true; // 滚动到底部 this.$nexttick(() => { this.scrolltobottom(); }); try { const response = await axios.post(`${this.apibaseurl}/message`, { sessionid: this.sessionid, message: usermessage }); this.messages.push({ role: 'assistant', content: response.data.message }); this.sessionid = response.data.sessionid; } catch (error) { console.error('error sending message:', error); this.messages.push({ role: 'assistant', content: '抱歉,发生了错误,请稍后再试。' }); } finally { this.loading = false; this.$nexttick(() => { this.scrolltobottom(); }); } }, scrolltobottom() { if (this.$refs.messagecontainer) { this.$refs.messagecontainer.scrolltop = this.$refs.messagecontainer.scrollheight; } }, loadhistory() { axios.get(`${this.apibaseurl}/history/${this.sessionid}`) .then(response => { this.messages = response.data; this.$nexttick(() => { this.scrolltobottom(); }); }) .catch(error => { console.error('error loading history:', error); }); } }, mounted() { // 在实际应用中,可以从 url 参数或本地存储获取 sessionid // this.loadhistory(); } } </script> <style scoped> .chat-container { display: flex; flex-direction: column; height: 100vh; max-width: 800px; margin: 0 auto; padding: 1rem; } .chat-header { text-align: center; padding: 1rem 0; border-bottom: 1px solid #eee; } .chat-messages { flex: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 1rem; } .message { max-width: 75%; padding: 0.75rem; border-radius: 0.5rem; margin-bottom: 0.5rem; } .user-message { align-self: flex-end; background-color: #e1f5fe; } .assistant-message { align-self: flex-start; background-color: #f5f5f5; } .chat-input { display: flex; padding: 1rem 0; gap: 0.5rem; } .chat-input textarea { flex: 1; padding: 0.75rem; border: 1px solid #ddd; border-radius: 0.5rem; resize: none; min-height: 60px; } .chat-input button { padding: 0.75rem 1.5rem; background-color: #2196f3; color: white; border: none; border-radius: 0.5rem; cursor: pointer; } .chat-input button:disabled { background-color: #cccccc; cursor: not-allowed; } .loading-dots span { animation: loading 1.4s infinite; display: inline-block; } .loading-dots span:nth-child(2) { animation-delay: 0.2s; } .loading-dots span:nth-child(3) { animation-delay: 0.4s; } @keyframes loading { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; } } </style>
3. 在主应用中使用聊天组件
<!-- app.vue --> <template> <div class="app"> <chatwindow /> </div> </template> <script> import chatwindow from './components/chatwindow.vue' export default { name: 'app', components: { chatwindow } } </script> <style> body { margin: 0; padding: 0; font-family: arial, sans-serif; } .app { width: 100%; height: 100vh; } </style>
四、前后端集成与部署
1. 开发环境配置
启动后端服务
./mvnw spring-boot:run
启动前端开发服务器
npm run dev
2. 生产环境部署
构建前端应用
npm run build
配置后端服务提供静态文件
// spring boot 配置类 @configuration public class webconfig implements webmvcconfigurer { @value("${frontend.resources.path:${user.home}/my-chat-app/dist}") private resource frontendresources; @override public void addresourcehandlers(resourcehandlerregistry registry) { registry.addresourcehandler("/**") .addresourcelocations("file:" + frontendresources.getfile().getabsolutepath() + "/") .resourcechain(true) .addresolver(new pathresourceresolver() { @override protected resource getresource(string resourcepath, resource location) throws ioexception { resource requestedresource = location.createrelative(resourcepath); return requestedresource.exists() && requestedresource.isreadable() ? requestedresource : new classpathresource("/static/index.html"); } }); } }
将前端构建文件复制到后端资源目录
五、进阶优化
1. websocket 实现实时通信
// 后端 websocket 配置 @configuration @enablewebsocketmessagebroker public class websocketconfig implements websocketmessagebrokerconfigurer { @override public void configuremessagebroker(messagebrokerregistry config) { config.enablesimplebroker("/topic"); config.setapplicationdestinationprefixes("/app"); } @override public void registerstompendpoints(stompendpointregistry registry) { registry.addendpoint("/ws") .setallowedoriginpatterns("*") .withsockjs(); } } // websocket 控制器 @controller public class websocketcontroller { @autowired private chatservice chatservice; @messagemapping("/chat.sendmessage") @sendtouser("/queue/reply") public chatmessage sendmessage(@payload chatmessage chatmessage, principal principal) { return chatservice.processwebsocketmessage(chatmessage); } }
2. 使用 redis 缓存会话历史
@configuration @enableredishttpsession public class sessionconfig { @bean public lettuceconnectionfactory connectionfactory() { return new lettuceconnectionfactory(); } }
3. 前端连接 websocket
// 在 chatwindow.vue 中添加 websocket 连接 export default { // ... 其他代码 ... data() { return { // ... 其他数据 ... stompclient: null }; }, methods: { // ... 其他方法 ... connectwebsocket() { const socket = new sockjs(this.apibaseurl.replace('/api', '')); this.stompclient = stomp.over(socket); this.stompclient.connect({}, frame => { console.log('connected: ' + frame); this.stompclient.subscribe(`/user/queue/reply`, response => { const receivedmessage = json.parse(response.body); this.messages.push(receivedmessage); this.$nexttick(() => { this.scrolltobottom(); }); }); }, error => { console.error('websocket error: ' + error); // 重连逻辑 settimeout(() => { this.connectwebsocket(); }, 5000); }); }, disconnectwebsocket() { if (this.stompclient !== null) { this.stompclient.disconnect(); } }, sendwebsocketmessage() { if (!this.userinput.trim() || this.loading) return; const usermessage = this.userinput.trim(); this.stompclient.send("/app/chat.sendmessage", {}, json.stringify({ sessionid: this.sessionid, message: usermessage })); this.messages.push({ role: 'user', content: usermessage }); this.userinput = ''; } }, mounted() { // ... 其他代码 ... this.connectwebsocket(); }, beforeunmount() { this.disconnectwebsocket(); } }
总结
通过上述步骤,你可以实现一个基于 spring boot 和 vue 的对话系统,类似 deepseek 的交互效果。这个实现包含了:
- 后端 api 设计与实现
- 前端聊天界面设计
- 会话管理与历史记录
- websocket 实现实时通信(可选)
- 部署配置
根据实际需求,你可以进一步扩展功能,如支持 markdown 渲染、代码高亮、对话导出等高级特性。
以上就是基于springboot+vue实现deepseek对话效果的详细步骤的详细内容,更多关于springboot vue deepseek对话的资料请关注代码网其它相关文章!
发表评论