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>
相关推荐
Sugobet3 小时前
【安卓][Mac/Windows】永久理论免费 无限ip代理池 - 适合临时快速作战
android·tcp/ip·macos·网络安全·渗透测试·ip代理池·接入点
郑州吴彦祖7724 小时前
MySQL锁的分类 && MVCC和S/X锁的互补关系
mysql·mvcc·s/x锁
fatiaozhang95277 小时前
创维智能融合终端SK-M424_S905L3芯片_2+8G_安卓9_线刷固件包
android·电视盒子·刷机固件·机顶盒刷机
来来走走8 小时前
Flutter开发 了解Scaffold
android·开发语言·flutter
哆啦A梦的口袋呀9 小时前
Android 底层实现基础
android
闻道且行之10 小时前
Android Studio下载及安装配置
android·ide·android studio
alexhilton10 小时前
初探Compose中的着色器RuntimeShader
android·kotlin·android jetpack
小墙程序员10 小时前
kotlin元编程(二)使用 Kotlin 来生成源代码
android·kotlin·android studio
ykuaile_h810 小时前
MySQL梳理三:查询与优化
数据库·mysql
小墙程序员11 小时前
kotlin元编程(一)一文理解 Kotlin 反射
android·kotlin·android studio