node
import express from 'express';
import mysql from 'mysql2';
import cors from 'cors';
import bodyParser from 'body-parser';
import axios from 'axios';
import { WebSocketServer } from 'ws'; // 正确导入 WebSocketServer
const app = express();
// Middlewares
app.use(cors());
app.use(bodyParser.json()); // 解析JSON请求体
app.use(express.json());
// MySQL数据库配置
const db = mysql.createConnection({
host: '127.0.0.1',
user: 'root',
password: '123456',
database: 'love_strategist',
port: 3307,
});
db.connect((err) => {
if (err) throw err;
console.log('✅ MySQL 连接成功');
});
// 创建 WebSocket 服务器
const wss = new WebSocketServer({ port: 8080 }); // 使用 WebSocketServer
wss.on('connection', (ws) => {
console.log('客户端已连接');
ws.on('message', async (message) => {
const { message: userMessage } = JSON.parse(message);
try {
const response = await axios.post(
'https://api.deepseek.com/v1/chat/completions',
{
model: "deepseek-chat",
messages: [{ role: "user", content: userMessage }],
stream: true
},
{
headers: {
'Authorization': `Bearer 你的deepseekKey`,
'Content-Type': 'application/json'
},
responseType: 'stream'
}
);
let buffer = ''; // 用于存储未完整的数据块
response.data.on('data', (chunk) => {
buffer += chunk.toString(); // 将数据块拼接到缓冲区
// 按行分割数据
const lines = buffer.split('\n');
for (let i = 0; i < lines.length - 1; i++) {
const line = lines[i].trim();
if (line.startsWith('data: ')) {
const jsonStr = line.replace('data: ', '');
try {
const data = JSON.parse(jsonStr);
if (data.choices?.[0]?.delta?.content) {
// 逐字发送内容
const content = data.choices[0].delta.content;
ws.send(JSON.stringify({ content }));
}
} catch (error) {
console.error('解析JSON失败:', error);
}
}
}
// 保留未完整的数据行
buffer = lines[lines.length - 1];
});
response.data.on('end', () => {
ws.send(JSON.stringify({ content: '[DONE]' }));
});
} catch (error) {
console.error('Error:', error);
ws.send(JSON.stringify({ content: '系统错误,请稍后再试。' }));
}
});
ws.on('close', () => {
console.log('客户端已断开连接');
});
});
// 启动 HTTP 服务器
app.listen(3000, () => {
console.log('✅ HTTP 服务器运行在 http://localhost:3000');
});
console.log('WebSocket 服务器运行在 ws://localhost:8080');
uniapp
<template>
<view class="container">
<!-- 顶部导航栏 -->
<view class="navbar">恋爱指导</view>
<!-- 聊天对话区域 -->
<scroll-view class="chat-box" scroll-y :style="{ height: (chatHeight - 70) + 'px' }" scroll-top="999999">
<view v-for="(message, index) in messages" :key="index" class="chat-item">
<view :class="message.from === 'user' ? 'user-message' : 'bot-message'">
<text>{{ message.content }}</text>
</view>
</view>
</scroll-view>
<!-- 输入框区域 -->
<view class="input-area">
<input v-model="userInput" class="input-box" placeholder="请输入你的问题..." />
<button class="send-btn" @click="sendMessage">发送</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
userInput: '', // 用户输入的消息
messages: [], // 存储消息记录
chatHeight: 0, // 聊天框高度
adviceList: [], // 存储恋爱建议
socket: null // WebSocket 实例
};
},
mounted() {
this.adjustChatHeight(); // 调整聊天框高度
},
onShow() {
setTimeout(() => {
this.initWebSocket(); // 初始化 WebSocket
}, 300)
},
// 页面隐藏时关闭 WebSocket
onHide() {
if (this.socket) {
this.socket.close(); // 关闭 WebSocket
console.log('WebSocket 连接已关闭');
}
},
methods: {
// 选中标签填充输入框并发送
selectAdvice(content) {
this.userInput = content;
this.sendMessage();
},
// 动态计算聊天框高度
adjustChatHeight() {
const windowHeight = uni.getSystemInfoSync().windowHeight;
this.chatHeight = windowHeight - 120; // 留出顶部和输入框的高度
},
// 初始化 WebSocket 连接
initWebSocket() {
this.socket = uni.connectSocket({
url: 'ws://localhost:8080',
success: () => {
console.log('WebSocket 连接成功4');
},
fail: (err) => {
console.error('WebSocket 连接失败:', err);
}
});
// 监听 WebSocket 消息
this.socket.onMessage((res) => {
const data = JSON.parse(res.data);
if (data.content === '[DONE]') {
this.isStreaming = false; // 结束流式传输
} else {
// 逐字追加内容
this.messages[this.messages.length - 1].content += data.content;
this.$nextTick(() => {
this.scrollToBottom();
});
}
});
// 监听 WebSocket 关闭
this.socket.onClose(() => {
console.log('WebSocket 连接已关闭');
});
},
// 发送消息
sendMessage() {
if (this.userInput.trim() && !this.isStreaming) {
this.addUserMessage(this.userInput); // 显示用户输入的消息
this.isStreaming = true;
// 通过 WebSocket 发送消息
this.socket.send({
data: JSON.stringify({
message: this.userInput
}),
success: () => {
console.log('消息发送成功');
this.addBotMessage(''); // 初始化一个空消息用于逐步填充
},
fail: (err) => {
console.error('消息发送失败:', err);
}
});
this.userInput = ''; // 清空输入框
}
},
// 添加用户消息
addUserMessage(content) {
this.messages.push({
from: 'user',
content: content
});
},
// 添加机器人回复
addBotMessage(content) {
this.messages.push({
from: 'bot',
content: content
});
},
// 滚动到底部
scrollToBottom() {
const query = uni.createSelectorQuery().in(this);
query.select('.chat-box').boundingClientRect(res => {
if (res) {
uni.pageScrollTo({
scrollTop: res.height,
duration: 0
});
}
}).exec();
}
}
};
</script>
<style scoped>
/* 页面容器 */
.container {
background-color: #fff;
height: 100%;
/* padding: 0 15px; */
/* 加入左右边距 */
}
/* 顶部导航栏 */
.navbar {
background-color: #ff65a3;
color: #fff;
text-align: center;
padding: 12px 0;
font-size: 18px;
font-weight: bold;
}
/* 聊天框 */
.chat-box {
padding: 10px;
margin-bottom: 70px;
/* 留出输入框的空间 */
padding-left: 0;
margin: 0 15px;
width: auto;
/* 去掉左边距,给消息更多空间 */
padding-right: 0;
/* 去掉右边距 */
}
/* 聊天消息 */
.chat-item {
margin: 10px 0;
display: flex;
}
.user-message {
text-align: right;
background-color: #ff65a3;
color: #fff;
padding: 10px;
border-radius: 10px;
max-width: 80%;
margin-left: auto;
}
.bot-message {
text-align: left;
background-color: #f2f2f2;
color: #000;
padding: 10px;
border-radius: 10px;
max-width: 80%;
margin-right: auto;
}
/* 输入框 */
.input-area {
display: flex;
position: fixed;
bottom: 0;
left: 0;
width: 93%;
padding: 10px;
background-color: #fff;
border-top: 1px solid #ddd;
padding-left: 15px;
/* 左边距 */
padding-right: 15px;
/* 右边距 */
}
.input-box {
flex: 1;
height: 40px;
border-radius: 20px;
border: 1px solid #ddd;
padding: 0 15px;
font-size: 16px;
}
.send-btn {
background-color: #ff65a3;
color: #fff;
font-size: 16px;
border: none;
padding: 0 20px;
border-radius: 20px;
margin-left: 10px;
}
/* 恋爱指导标签 */
.guidance-tags {
display: flex;
overflow-x: scroll;
white-space: nowrap;
padding: 10px;
background-color: #ffe5f1;
}
.tag-item {
display: inline-block;
background-color: #ff65a3;
color: white;
padding: 8px 15px;
margin-right: 10px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
}
</style>