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对话的资料请关注代码网其它相关文章!
发表评论