uniapp+node+mysql接入deepseek实现流式输出

node

javascript 复制代码
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

javascript 复制代码
<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>
相关推荐
Mr_li11 小时前
给 Vue 开发者的 uni-app 快速指南
vue.js·uni-app
anyup14 小时前
🔥2026最推荐的跨平台方案:H5/小程序/App/鸿蒙,一套代码搞定
前端·uni-app·harmonyos
阿巴斯甜17 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker17 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952718 小时前
Andorid Google 登录接入文档
android
黄林晴20 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android