基于spring boot 集成 deepseek 流式输出 的vue3使用指南

本文使用deepseek API接口流式输出的文章。

环境要求 jdk17 spring boot 3.4

代码如下:

java 复制代码
package com.example.controller;

import jakarta.annotation.PostConstruct;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.Disposable;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@RestController
public class ChatController {

    // 注入OpenAI聊天模型实例,已通过配置文件完成API密钥等参数配置
    @Autowired
    private OpenAiChatModel chatModel;

    // 使用List维护对话上下文,包含系统消息和用户消息
    private List<Message> chatHistory = new ArrayList<>();


    @PostConstruct
    public void init() {
        // 添加系统消息定义AI行为准则
        chatHistory.add(new SystemMessage("You are a helpful assistant."));
    }
    /**
     * 流式聊天内容生成接口(Server-Sent Events 实现)
     *
     * @param message 用户输入的聊天消息
     * @return SseEmitter 用于服务端推送事件的发射器对象
     */
    @GetMapping("/generateStream")
    public SseEmitter streamChat(@RequestParam String message) {
        // 创建 SSE 发射器并设置 180 秒超时(可根据业务需求调整)
        SseEmitter emitter = new SseEmitter(180_000L);

        // 记录请求开始(实际项目建议使用 SLF4J 等日志框架)
        System.out.println("SSE connection established for message: " + message);

        // 构建对话请求参数
        Prompt prompt = new Prompt(new UserMessage(message));

        // 获取流式处理订阅对象(用于后续资源清理)
        Disposable subscription = chatModel.stream(prompt)
                .subscribe(
                        // 处理每个响应块
                        response -> {
                            try {
                                // 安全获取响应文本
                                String text = Optional.ofNullable(response)
                                        .map(ChatResponse::getResult)
                                        .map(Generation::getOutput)
                                        .map(AssistantMessage::getText)
                                        .orElse("");  // 空值处理

                                // 发送 SSE 事件(建议实际项目添加 JSON 包装)
                                emitter.send(SseEmitter.event()
                                        .id(String.valueOf(System.currentTimeMillis()))  // 事件ID用于客户端排序
                                        .data(text)      // 事件数据内容
                                        .name("message")// 定义事件类型
                                        .build());

                                // 打印到控制台用于调试
                                System.out.print(text);
                            } catch (Exception e) {
                                // 发送异常时关闭连接并记录错误
                                emitter.completeWithError(e);
                                System.err.println("Error sending SSE event: " + e.getMessage());
                            }
                        },
                        // 处理流式响应异常
                        error -> {
                            emitter.completeWithError(error);
                            System.err.println("Stream error: " + error.getMessage());
                        },
                        // 处理流式响应完成
                        () -> {
                            emitter.complete();
                            System.out.println("\nStream completed");
                        }
                );

        // 注册连接完成回调(客户端断开/正常结束)
        emitter.onCompletion(() -> {
            // 取消订阅释放资源
            if (!subscription.isDisposed()) {
                subscription.dispose();
            }
            System.out.println("SSE connection completed");
        });

        // 注册超时回调
        emitter.onTimeout(() -> {
            // 主动完成连接并释放资源
            emitter.complete();
            subscription.dispose();
            System.out.println("SSE connection timed out");
        });

        return emitter;
    }
}

配置key

text 复制代码
spring.ai.openai.api-key=你申请的key
spring.ai.openai.base-url=https://api.deepseek.com
spring.ai.openai.chat.options.model=deepseek-chat
spring.ai.openai.chat.options.temperature=0.7

# The DeepSeek API doesn't support embeddings, so we need to disable it.
spring.ai.openai.embedding.enabled=false

Vue 3 使用指南

1. Vue 3 简介

Vue 3 是 Vue.js 框架的最新主要版本,于 2020 年 9 月正式发布。它带来了许多改进和新特性:

  • 性能提升:更快的渲染速度,更小的包体积
  • Composition API:新的代码组织方式
  • 更好的 TypeScript 支持
  • 新的响应式系统
  • Fragment、Teleport、Suspense 等新特性

2. 创建 Vue 3 项目

使用 Vite 创建项目

bash 复制代码
npm create vite@latest my-vue-app --template vue

使用 Vue CLI 创建项目

bash 复制代码
npm install -g @vue/cli
vue create my-vue-app
# 选择 Vue 3 预设

3. Composition API

Vue 3 引入了 Composition API 作为 Options API 的补充,提供了更好的代码组织和复用。

基本示例

vue 复制代码
<script setup>
import { ref, computed, onMounted } from 'vue'

// 响应式状态
const count = ref(0)

// 计算属性
const doubleCount = computed(() => count.value * 2)

// 方法
function increment() {
  count.value++
}

// 生命周期钩子
onMounted(() => {
  console.log('组件已挂载')
})
</script>

<template>
  <button @click="increment">
    计数: {{ count }}, 双倍: {{ doubleCount }}
  </button>
</template>

响应式基础

  • ref(): 用于基本类型
  • reactive(): 用于对象
  • toRefs(): 将 reactive 对象转换为 ref 集合
javascript 复制代码
import { ref, reactive, toRefs } from 'vue'

const counter = ref(0)
const state = reactive({
  name: 'Vue 3',
  version: '3.x'
})

// 在模板中使用时不需要 .value
const { name, version } = toRefs(state)

4. 组件系统

组件定义

vue 复制代码
<script setup>
// 使用 <script setup> 语法糖
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const message = ref('Hello from parent')
</script>

<template>
  <ChildComponent :message="message" />
</template>

Props 和 Emits

vue 复制代码
<script setup>
// ChildComponent.vue
const props = defineProps({
  message: {
    type: String,
    required: true
  }
})

const emit = defineEmits(['response'])

function sendResponse() {
  emit('response', 'Hello from child')
}
</script>

<template>
  <div>
    {{ message }}
    <button @click="sendResponse">Reply</button>
  </div>
</template>

5. 生命周期钩子

Vue 3 生命周期钩子(Composition API 版本):

Options API Composition API
beforeCreate 不需要 (setup 替代)
created 不需要 (setup 替代)
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered

6. 新特性

Teleport

将子节点渲染到 DOM 中的其他位置:

vue 复制代码
<template>
  <button @click="open = true">打开模态框</button>
  
  <Teleport to="body">
    <div v-if="open" class="modal">
      <p>模态框内容</p>
      <button @click="open = false">关闭</button>
    </div>
  </Teleport>
</template>

Suspense

处理异步组件加载状态:

vue 复制代码
<template>
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>
    <template #fallback>
      <div>加载中...</div>
    </template>
  </Suspense>
</template>

<script setup>
const AsyncComponent = defineAsyncComponent(() => 
  import('./AsyncComponent.vue')
)
</script>

7. 状态管理 (Pinia)

Vue 3 推荐使用 Pinia 作为状态管理库:

安装 Pinia

bash 复制代码
npm install pinia

基本使用

javascript 复制代码
// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  actions: {
    increment() {
      this.count++
    }
  },
  getters: {
    doubleCount: (state) => state.count * 2
  }
})
vue 复制代码
<script setup>
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()
</script>

<template>
  <div>{{ counter.count }}</div>
  <div>{{ counter.doubleCount }}</div>
  <button @click="counter.increment()">+</button>
</template>

8. 路由 (Vue Router 4)

安装

bash 复制代码
npm install vue-router@4

基本配置

javascript 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

在组件中使用

vue 复制代码
<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

function goToAbout() {
  router.push('/about')
}
</script>

<template>
  <div>当前路由: {{ route.path }}</div>
  <button @click="goToAbout">前往关于页面</button>
  <router-view />
</template>

9. 最佳实践

  1. 优先使用 Composition API:特别是对于复杂组件
  2. 合理拆分组件:保持组件单一职责
  3. 使用 <script setup> 语法:简化代码
  4. 利用 TypeScript:Vue 3 对 TS 支持更好
  5. 按需引入:减少打包体积
  6. 使用 Vite:获得更好的开发体验

10. 资源

希望这篇文档能帮助你快速上手 Vue 3 开发!

Stream completed

相关推荐
小可爱的大笨蛋5 分钟前
Spring AI 开发 - 快速入门
java·人工智能·spring
刘 大 望14 分钟前
Java写数据结构:栈
java·开发语言·数据结构
刘大猫2616 分钟前
Arthas monitor(方法执行监控)
人工智能·后端·监控
小研学术19 分钟前
如何用AI辅助数据分析及工具推荐
论文阅读·人工智能·ai·数据挖掘·数据分析·deepseek
追逐时光者22 分钟前
MongoDB从入门到实战之MongoDB简介
后端·mongodb
格子先生Lab31 分钟前
Java反射机制深度解析与应用案例
java·开发语言·python·反射
Huazie1 小时前
在WSL2 Ubuntu中部署FastDFS服务的完整指南
服务器·后端·ubuntu
Java知识库1 小时前
Java BIO、NIO、AIO、Netty面试题(已整理全套PDF版本)
java·开发语言·jvm·面试·程序员
西瓜本瓜@2 小时前
在 Android 中实现通话录音
android·java·开发语言·学习·github·android-studio
行者无疆xcc2 小时前
【Django】设置让局域网内的人访问
后端·python·django